Initial import of libmojo r405848

This brings libmojo in sync with libchrome.

Bug: 27569341
Test: mma -j32 libmojo
Change-Id: Ia7cb877e46dd3f86f18888b5d8d80bef5468b266
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
new file mode 100644
index 0000000..2dac654
--- /dev/null
+++ b/mojo/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("//build/config/ui.gni")
+
+group("mojo") {
+  # Meta-target, don't link into production code.
+  testonly = true
+  deps = [
+    ":tests",
+    "//mojo/common",
+  ]
+
+  if (!(is_linux && current_cpu == "x86")) {
+    # TODO(GYP): Figure out if this needs to be supported. Right now
+    # it won't work on x86 official builds because it needs stuff in the
+    # sysroot that doesn't exist.
+    deps += [ "//mojo/public" ]
+  }
+
+  if (is_android) {
+    deps += [ "//mojo/android" ]
+  }
+
+  deps += [ "//services/shell:all" ]
+}
+
+group("tests") {
+  testonly = true
+  deps = [
+    "//ipc:ipc_tests",
+    "//mojo/common:mojo_common_unittests",
+    "//mojo/converters/blink:blink_converters_unittests",
+    "//mojo/edk/js/test:js_integration_tests",
+    "//mojo/edk/js/test:js_unittests",
+    "//mojo/edk/system:mojo_message_pipe_perftests",
+    "//mojo/edk/system:mojo_system_unittests",
+    "//mojo/edk/test:mojo_public_bindings_perftests",
+    "//mojo/edk/test:mojo_public_bindings_unittests",
+    "//mojo/edk/test:mojo_public_system_perftests",
+    "//mojo/edk/test:mojo_public_system_unittests",
+    "//services/shell/public/cpp/tests:mojo_public_application_unittests",
+    "//services/shell/runner/host:mojo_runner_host_unittests",
+    "//services/shell/tests",
+  ]
+}
diff --git a/mojo/DEPS b/mojo/DEPS
new file mode 100644
index 0000000..a659200
--- /dev/null
+++ b/mojo/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+base",
+  "+build",
+  "+testing",
+
+  "+services/shell",
+]
diff --git a/mojo/OWNERS b/mojo/OWNERS
new file mode 100644
index 0000000..a2ccd25
--- /dev/null
+++ b/mojo/OWNERS
@@ -0,0 +1,6 @@
+amistry@chromium.org
+ben@chromium.org
+jam@chromium.org
+rockot@chromium.org
+sky@chromium.org
+yzshen@chromium.org
diff --git a/mojo/PRESUBMIT.py b/mojo/PRESUBMIT.py
new file mode 100644
index 0000000..2766055
--- /dev/null
+++ b/mojo/PRESUBMIT.py
@@ -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.
+
+"""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")
+  # For the python bindings:
+  mojo_python_bindings_path = os.path.join(
+      input_api.PresubmitLocalPath(), "public", "python")
+  # 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,
+      mojo_python_bindings_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..237d1d4
--- /dev/null
+++ b/mojo/README.md
@@ -0,0 +1,7 @@
+Mojo
+====
+
+[Mojo](https://www.chromium.org/developers/design-documents/mojo) is an IPC &
+binding mechanism for Chromium.
+
+TODO(rockot): Describe the important subdirectories.
\ No newline at end of file
diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn
new file mode 100644
index 0000000..813701c
--- /dev/null
+++ b/mojo/android/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+
+group("android") {
+  testonly = true
+  deps = [
+    ":mojo_javatests",
+    ":mojo_test_apk",
+    ":system_java",
+  ]
+}
+
+generate_jni("jni_headers") {
+  sources = [
+    "javatests/src/org/chromium/mojo/MojoTestCase.java",
+    "javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java",
+  ]
+  public_deps = [
+    ":system_java_jni_headers",
+  ]
+
+  jni_package = "mojo"
+}
+
+generate_jni("system_java_jni_headers") {
+  sources = [
+    "system/src/org/chromium/mojo/system/impl/BaseRunLoop.java",
+    "system/src/org/chromium/mojo/system/impl/CoreImpl.java",
+  ]
+
+  jni_package = "mojo"
+}
+
+source_set("libsystem_java") {
+  sources = [
+    "system/base_run_loop.cc",
+    "system/base_run_loop.h",
+    "system/core_impl.cc",
+    "system/core_impl.h",
+  ]
+
+  deps = [
+    ":system_java_jni_headers",
+    "//base",
+    "//mojo/message_pump",
+  ]
+}
+
+android_library("system_java") {
+  java_files = [
+    "system/src/org/chromium/mojo/system/impl/BaseRunLoop.java",
+    "system/src/org/chromium/mojo/system/impl/CoreImpl.java",
+    "system/src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.java",
+    "system/src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java",
+    "system/src/org/chromium/mojo/system/impl/HandleBase.java",
+    "system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java",
+    "system/src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java",
+    "system/src/org/chromium/mojo/system/impl/UntypedHandleImpl.java",
+  ]
+
+  deps = [
+    "//base:base_java",
+    "//mojo/public/java:system",
+  ]
+}
+
+android_library("mojo_javatests") {
+  testonly = true
+  java_files = [
+    "javatests/src/org/chromium/mojo/HandleMock.java",
+    "javatests/src/org/chromium/mojo/MojoTestCase.java",
+    "javatests/src/org/chromium/mojo/TestUtils.java",
+    "javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java",
+    "javatests/src/org/chromium/mojo/bindings/BindingsTest.java",
+    "javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java",
+    "javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java",
+    "javatests/src/org/chromium/mojo/bindings/CallbacksTest.java",
+    "javatests/src/org/chromium/mojo/bindings/ConnectorTest.java",
+    "javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java",
+    "javatests/src/org/chromium/mojo/bindings/InterfacesTest.java",
+    "javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java",
+    "javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java",
+    "javatests/src/org/chromium/mojo/bindings/RouterTest.java",
+    "javatests/src/org/chromium/mojo/bindings/SerializationTest.java",
+    "javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterfaceTestHelper.java",
+    "javatests/src/org/chromium/mojo/bindings/ValidationTest.java",
+    "javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java",
+    "javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java",
+    "javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java",
+  ]
+
+  deps = [
+    ":system_java",
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces_java",
+    "//mojo/public/interfaces/bindings/tests:test_mojom_import2_java",
+    "//mojo/public/interfaces/bindings/tests:test_mojom_import_java",
+    "//mojo/public/java:bindings",
+    "//mojo/public/java:system",
+  ]
+}
+
+shared_library("mojo_java_unittests") {
+  testonly = true
+
+  sources = [
+    "javatests/init_library.cc",
+    "javatests/mojo_test_case.cc",
+    "javatests/mojo_test_case.h",
+    "javatests/validation_test_util.cc",
+    "javatests/validation_test_util.h",
+  ]
+
+  deps = [
+    ":jni_headers",
+    ":libsystem_java",
+    ":system_java_jni_headers",
+    "//base",
+    "//base/test/:test_support",
+    "//build/config/sanitizers:deps",
+    "//mojo/edk/system",
+    "//mojo/message_pump",
+    "//mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils",
+    "//mojo/public/cpp/test_support:test_utils",
+  ]
+  defines = [ "UNIT_TEST" ]
+}
+
+instrumentation_test_apk("mojo_test_apk") {
+  deps = [
+    ":mojo_javatests",
+    ":system_java",
+    "//base:base_java",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+    "//mojo/public/java:bindings",
+  ]
+  shared_libraries = [ ":mojo_java_unittests" ]
+  apk_name = "MojoTest"
+  android_manifest = "javatests/AndroidManifest.xml"
+  isolate_file = "../mojo_test_apk.isolate"
+}
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..32a3927
--- /dev/null
+++ b/mojo/android/javatests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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"
+      xmlns:tools="http://schemas.android.com/tools"
+      package="org.chromium.mojo.tests">
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
+
+    <uses-permission android:name="android.permission.INJECT_EVENTS"
+        tools:ignore="ProtectedPermissions"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
+
+    <!-- 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>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="org.chromium.mojo.tests"
+        android:label="Tests for org.chromium.mojo"/>
+
+</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..d2d9423
--- /dev/null
+++ b/mojo/android/javatests/init_library.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 "base/android/base_jni_onload.h"
+#include "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/bind.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"
+
+namespace {
+
+base::android::RegistrationMethod kMojoRegisteredMethods[] = {
+  { "CoreImpl", mojo::android::RegisterCoreImpl },
+  { "MojoTestCase", mojo::android::RegisterMojoTestCase },
+  { "ValidationTestUtil", mojo::android::RegisterValidationTestUtil },
+};
+
+bool RegisterJNI(JNIEnv* env) {
+  return base::android::RegisterJni(env) &&
+      RegisterNativeMethods(env, kMojoRegisteredMethods,
+                            arraysize(kMojoRegisteredMethods));
+}
+
+bool Init() {
+  mojo::edk::Init();
+  return true;
+}
+
+}  // namespace
+
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  std::vector<base::android::RegisterCallback> register_callbacks;
+  register_callbacks.push_back(base::Bind(&RegisterJNI));
+  std::vector<base::android::InitCallback> init_callbacks;
+  init_callbacks.push_back(base::Bind(&Init));
+  if (!base::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks) ||
+      !base::android::OnJNIOnLoadInit(init_callbacks))
+    return -1;
+
+  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..6d88985
--- /dev/null
+++ b/mojo/android/javatests/mojo_test_case.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/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/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/test_support_android.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "jni/MojoTestCase_jni.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+
+namespace {
+
+struct TestEnvironment {
+  TestEnvironment() : message_loop(mojo::common::MessagePumpMojo::Create()) {}
+
+  base::ShadowingAtExitManager at_exit;
+  base::MessageLoop message_loop;
+};
+
+}  // namespace
+
+namespace mojo {
+namespace android {
+
+static void Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller) {
+  base::InitAndroidTestMessageLoop();
+}
+
+static jlong SetupTestEnvironment(JNIEnv* env,
+                                  const JavaParamRef<jobject>& jcaller) {
+  return reinterpret_cast<intptr_t>(new TestEnvironment());
+}
+
+static void TearDownTestEnvironment(JNIEnv* env,
+                                    const JavaParamRef<jobject>& jcaller,
+                                    jlong test_environment) {
+  delete reinterpret_cast<TestEnvironment*>(test_environment);
+}
+
+static void RunLoop(JNIEnv* env,
+                    const JavaParamRef<jobject>& jcaller,
+                    jlong timeout_ms) {
+  base::RunLoop run_loop;
+  if (timeout_ms) {
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
+        base::TimeDelta::FromMilliseconds(timeout_ms));
+    run_loop.Run();
+  } else {
+    run_loop.RunUntilIdle();
+  }
+}
+
+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..6783c09
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java
@@ -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.
+
+package org.chromium.mojo;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Core.WaitResult;
+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.ResultAnd;
+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 WaitResult wait(Core.HandleSignals signals, long deadline) {
+        // Do nothing.
+        WaitResult result = new WaitResult();
+        result.setMojoResult(MojoResult.OK);
+        return result;
+    }
+
+    /**
+     * @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 ResultAnd<Integer> readData(ByteBuffer elements, DataPipe.ReadFlags flags) {
+        // Do nothing.
+        return new ResultAnd<Integer>(MojoResult.OK, 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 ResultAnd<Integer> writeData(ByteBuffer elements, DataPipe.WriteFlags flags) {
+        // Do nothing.
+        return new ResultAnd<Integer>(MojoResult.OK, 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 ResultAnd<ReadMessageResult> readMessage(
+            ByteBuffer bytes, int maxNumberOfHandles, ReadFlags flags) {
+        // Do nothing.
+        return new ResultAnd<ReadMessageResult>(MojoResult.OK, 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..c7348ca
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.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;
+
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.library_loader.LibraryProcessType;
+
+/**
+ * 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();
+        Context appContext = getInstrumentation().getTargetContext().getApplicationContext();
+        ContextUtils.initApplicationContext(appContext);
+        LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(appContext);
+        nativeInit();
+        mTestEnvironmentPointer = nativeSetupTestEnvironment();
+    }
+
+    /**
+     * @see android.test.InstrumentationTestCase#tearDown()
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        nativeTearDownTestEnvironment(mTestEnvironmentPointer);
+        super.tearDown();
+    }
+
+    /**
+     * Runs the run loop for the given time.
+     */
+    protected void runLoop(long timeoutMS) {
+        nativeRunLoop(timeoutMS);
+    }
+
+    /**
+     * Runs the run loop until no handle or task are immediately available.
+     */
+    protected void runLoopUntilIdle() {
+        nativeRunLoop(0);
+    }
+
+    private native void nativeInit();
+
+    private native long nativeSetupTestEnvironment();
+
+    private native void nativeTearDownTestEnvironment(long testEnvironment);
+
+    private 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..d10d0d7
--- /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.LITTLE_ENDIAN);
+        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..6886023
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
@@ -0,0 +1,212 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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;
+
+/**
+ * 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;
+    }
+
+    /**
+     * 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;
+    }
+
+    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);
+        assertEquals(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..5554f80
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.TestUtils;
+import org.chromium.mojo.system.Handle;
+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.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>());
+    }
+
+    public static <I extends Interface, P extends Interface.Proxy> P newProxyOverPipe(
+            Interface.Manager<I, P> manager, I impl, List<Closeable> toClose) {
+        Pair<MessagePipeHandle, MessagePipeHandle> handles =
+                CoreImpl.getInstance().createMessagePipe(null);
+        P proxy = manager.attachProxy(handles.first, 0);
+        toClose.add(proxy);
+        manager.bind(impl, handles.second);
+        return proxy;
+    }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
new file mode 100644
index 0000000..f0c0f7c
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
@@ -0,0 +1,211 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStruct;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV0;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV1;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV3;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV5;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV7;
+import org.chromium.mojo.bindings.test.mojom.test_structs.Rect;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+/**
+ * Testing generated classes with the [MinVersion] annotation. Struct in this test are from:
+ * mojo/public/interfaces/bindings/tests/rect.mojom and
+ * mojo/public/interfaces/bindings/tests/test_structs.mojom
+ */
+public class BindingsVersioningTest extends MojoTestCase {
+    private static Rect newRect(int factor) {
+        Rect rect = new Rect();
+        rect.x = factor;
+        rect.y = 2 * factor;
+        rect.width = 10 * factor;
+        rect.height = 20 * factor;
+        return rect;
+    }
+
+    private static MultiVersionStruct newStruct() {
+        MultiVersionStruct struct = new MultiVersionStruct();
+        struct.fInt32 = 123;
+        struct.fRect = newRect(5);
+        struct.fString = "hello";
+        struct.fArray = new byte[] {10, 9, 8};
+        struct.fBool = true;
+        struct.fInt16 = 256;
+        return struct;
+    }
+
+    /**
+     * Testing serializing old struct version to newer one.
+     */
+    @SmallTest
+    public void testOldToNew() {
+        {
+            MultiVersionStructV0 v0 = new MultiVersionStructV0();
+            v0.fInt32 = 123;
+            MultiVersionStruct expected = new MultiVersionStruct();
+            expected.fInt32 = 123;
+
+            MultiVersionStruct output = MultiVersionStruct.deserialize(v0.serialize(null));
+            assertEquals(expected, output);
+            assertEquals(0, v0.getVersion());
+            assertEquals(0, output.getVersion());
+        }
+
+        {
+            MultiVersionStructV1 v1 = new MultiVersionStructV1();
+            v1.fInt32 = 123;
+            v1.fRect = newRect(5);
+            MultiVersionStruct expected = new MultiVersionStruct();
+            expected.fInt32 = 123;
+            expected.fRect = newRect(5);
+
+            MultiVersionStruct output = MultiVersionStruct.deserialize(v1.serialize(null));
+            assertEquals(expected, output);
+            assertEquals(1, v1.getVersion());
+            assertEquals(1, output.getVersion());
+        }
+
+        {
+            MultiVersionStructV3 v3 = new MultiVersionStructV3();
+            v3.fInt32 = 123;
+            v3.fRect = newRect(5);
+            v3.fString = "hello";
+            MultiVersionStruct expected = new MultiVersionStruct();
+            expected.fInt32 = 123;
+            expected.fRect = newRect(5);
+            expected.fString = "hello";
+
+            MultiVersionStruct output = MultiVersionStruct.deserialize(v3.serialize(null));
+            assertEquals(expected, output);
+            assertEquals(3, v3.getVersion());
+            assertEquals(3, output.getVersion());
+        }
+
+        {
+            MultiVersionStructV5 v5 = new MultiVersionStructV5();
+            v5.fInt32 = 123;
+            v5.fRect = newRect(5);
+            v5.fString = "hello";
+            v5.fArray = new byte[] {10, 9, 8};
+            MultiVersionStruct expected = new MultiVersionStruct();
+            expected.fInt32 = 123;
+            expected.fRect = newRect(5);
+            expected.fString = "hello";
+            expected.fArray = new byte[] {10, 9, 8};
+
+            MultiVersionStruct output = MultiVersionStruct.deserialize(v5.serialize(null));
+            assertEquals(expected, output);
+            assertEquals(5, v5.getVersion());
+            assertEquals(5, output.getVersion());
+        }
+
+        {
+            int expectedHandle = 42;
+            MultiVersionStructV7 v7 = new MultiVersionStructV7();
+            v7.fInt32 = 123;
+            v7.fRect = newRect(5);
+            v7.fString = "hello";
+            v7.fArray = new byte[] {10, 9, 8};
+            v7.fMessagePipe = CoreImpl.getInstance()
+                                      .acquireNativeHandle(expectedHandle)
+                                      .toMessagePipeHandle();
+            v7.fBool = true;
+            MultiVersionStruct expected = new MultiVersionStruct();
+            expected.fInt32 = 123;
+            expected.fRect = newRect(5);
+            expected.fString = "hello";
+            expected.fArray = new byte[] {10, 9, 8};
+            expected.fBool = true;
+
+            MultiVersionStruct output = MultiVersionStruct.deserialize(v7.serialize(null));
+
+            // Handles must be tested separately.
+            assertEquals(expectedHandle, output.fMessagePipe.releaseNativeHandle());
+            output.fMessagePipe = expected.fMessagePipe;
+
+            assertEquals(expected, output);
+            assertEquals(7, v7.getVersion());
+            assertEquals(7, output.getVersion());
+        }
+    }
+
+    /**
+     * Testing serializing new struct version to older one.
+     */
+    @SmallTest
+    public void testNewToOld() {
+        MultiVersionStruct struct = newStruct();
+        {
+            MultiVersionStructV0 expected = new MultiVersionStructV0();
+            expected.fInt32 = 123;
+
+            MultiVersionStructV0 output = MultiVersionStructV0.deserialize(struct.serialize(null));
+            assertEquals(expected, output);
+            assertEquals(9, output.getVersion());
+        }
+
+        {
+            MultiVersionStructV1 expected = new MultiVersionStructV1();
+            expected.fInt32 = 123;
+            expected.fRect = newRect(5);
+
+            MultiVersionStructV1 output = MultiVersionStructV1.deserialize(struct.serialize(null));
+            assertEquals(expected, output);
+            assertEquals(9, output.getVersion());
+        }
+
+        {
+            MultiVersionStructV3 expected = new MultiVersionStructV3();
+            expected.fInt32 = 123;
+            expected.fRect = newRect(5);
+            expected.fString = "hello";
+
+            MultiVersionStructV3 output = MultiVersionStructV3.deserialize(struct.serialize(null));
+            assertEquals(expected, output);
+            assertEquals(9, output.getVersion());
+        }
+
+        {
+            MultiVersionStructV5 expected = new MultiVersionStructV5();
+            expected.fInt32 = 123;
+            expected.fRect = newRect(5);
+            expected.fString = "hello";
+            expected.fArray = new byte[] {10, 9, 8};
+
+            MultiVersionStructV5 output = MultiVersionStructV5.deserialize(struct.serialize(null));
+            assertEquals(expected, output);
+            assertEquals(9, output.getVersion());
+        }
+
+        {
+            int expectedHandle = 42;
+            MultiVersionStructV7 expected = new MultiVersionStructV7();
+            expected.fInt32 = 123;
+            expected.fRect = newRect(5);
+            expected.fString = "hello";
+            expected.fArray = new byte[] {10, 9, 8};
+            expected.fBool = true;
+
+            MultiVersionStruct input = struct;
+            input.fMessagePipe = CoreImpl.getInstance()
+                                         .acquireNativeHandle(expectedHandle)
+                                         .toMessagePipeHandle();
+
+            MultiVersionStructV7 output = MultiVersionStructV7.deserialize(input.serialize(null));
+
+            assertEquals(expectedHandle, output.fMessagePipe.releaseNativeHandle());
+            output.fMessagePipe = expected.fMessagePipe;
+
+            assertEquals(expected, output);
+            assertEquals(9, output.getVersion());
+        }
+    }
+}
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..9c26875
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
@@ -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.
+
+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.ResultAnd;
+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 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);
+        ResultAnd<MessagePipeHandle.ReadMessageResult> result =
+                mHandle.readMessage(received, 0, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(DATA_LENGTH, result.getValue().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);
+        runLoopUntilIdle();
+        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();
+        runLoopUntilIdle();
+        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..56f4b40
--- /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());
+        runLoop(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());
+        runLoop(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/InterfaceControlMessageTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
new file mode 100644
index 0000000..c101675
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
@@ -0,0 +1,129 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.Callbacks.Callback1;
+import org.chromium.mojo.bindings.test.mojom.sample.Enum;
+import org.chromium.mojo.bindings.test.mojom.sample.IntegerAccessor;
+import org.chromium.mojo.system.MojoException;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for interface control messages.
+ */
+public class InterfaceControlMessageTest extends MojoTestCase {
+    private final List<Closeable> mCloseablesToClose = new ArrayList<Closeable>();
+
+    /**
+     * See mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.
+     */
+    class IntegerAccessorImpl extends SideEffectFreeCloseable implements IntegerAccessor {
+        private long mValue = 0;
+        private int mEnum = 0;
+        private boolean mEncounteredError = false;
+
+        /**
+         * @see ConnectionErrorHandler#onConnectionError(MojoException)
+         */
+        @Override
+        public void onConnectionError(MojoException e) {
+            mEncounteredError = true;
+        }
+
+        /**
+         * @see IntegerAccessor#getInteger(IntegerAccessor.GetIntegerResponse)
+         */
+        @Override
+        public void getInteger(GetIntegerResponse response) {
+            response.call(mValue, mEnum);
+        }
+
+        /**
+         * @see IntegerAccessor#setInteger(long, int)
+         */
+        @Override
+        public void setInteger(long value, int enumValue) {
+            mValue = value;
+            mEnum = enumValue;
+        }
+
+        public long getValue() {
+            return mValue;
+        }
+
+        public boolean encounteredError() {
+            return mEncounteredError;
+        }
+    }
+
+    /**
+     * @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();
+    }
+
+    @SmallTest
+    public void testQueryVersion() {
+        IntegerAccessor.Proxy p = BindingsTestUtils.newProxyOverPipe(
+                IntegerAccessor.MANAGER, new IntegerAccessorImpl(), mCloseablesToClose);
+        assertEquals(0, p.getProxyHandler().getVersion());
+        p.getProxyHandler().queryVersion(new Callback1<Integer>() {
+            @Override
+            public void call(Integer version) {
+                assertEquals(3, version.intValue());
+            }
+        });
+        runLoopUntilIdle();
+        assertEquals(3, p.getProxyHandler().getVersion());
+    }
+
+    @SmallTest
+    public void testRequireVersion() {
+        IntegerAccessorImpl impl = new IntegerAccessorImpl();
+        IntegerAccessor.Proxy p = BindingsTestUtils.newProxyOverPipe(
+                IntegerAccessor.MANAGER, impl, mCloseablesToClose);
+
+        assertEquals(0, p.getProxyHandler().getVersion());
+
+        p.getProxyHandler().requireVersion(1);
+        assertEquals(1, p.getProxyHandler().getVersion());
+        p.setInteger(123, Enum.VALUE);
+        runLoopUntilIdle();
+        assertFalse(impl.encounteredError());
+        assertEquals(123, impl.getValue());
+
+        p.getProxyHandler().requireVersion(3);
+        assertEquals(3, p.getProxyHandler().getVersion());
+        p.setInteger(456, Enum.VALUE);
+        runLoopUntilIdle();
+        assertFalse(impl.encounteredError());
+        assertEquals(456, impl.getValue());
+
+        // Require a version that is not supported by the implementation side.
+        p.getProxyHandler().requireVersion(4);
+        // getVersion() is updated synchronously.
+        assertEquals(4, p.getProxyHandler().getVersion());
+        p.setInteger(789, Enum.VALUE);
+        runLoopUntilIdle();
+        assertTrue(impl.encounteredError());
+        // The call to setInteger() after requireVersion() is ignored.
+        assertEquals(456, impl.getValue());
+    }
+}
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..f96b1e3
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
@@ -0,0 +1,284 @@
+// Copyright 2014 The Chromium 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.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.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 String OBJECT_NAME = "hello world";
+
+    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;
+
+        public boolean isClosed() {
+            return mClosed;
+        }
+
+        /**
+         * @see org.chromium.mojo.bindings.Interface#close()
+         */
+        @Override
+        public void close() {
+            mClosed = true;
+        }
+
+        @Override
+        public void doStuff(Request request, MessagePipeHandle pipe, DoStuffResponse callback) {
+            if (pipe != null) {
+                pipe.close();
+            }
+            Response response = new Response();
+            response.x = 42;
+            callback.call(response, "Hello");
+        }
+
+        @Override
+        public void doStuff2(ConsumerHandle pipe, DoStuff2Response callback) {
+            callback.call("World");
+        }
+
+        @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.");
+        }
+    }
+
+    /**
+     * Implementation of DoStuffResponse that keeps track of if the response is called.
+     */
+    public static class DoStuffResponseImpl implements Factory.DoStuffResponse {
+        private boolean mResponseCalled = false;
+
+        public boolean wasResponseCalled() {
+            return mResponseCalled;
+        }
+
+        @Override
+        public void call(Response response, String string) {
+            mResponseCalled = true;
+        }
+    }
+
+    /**
+     * @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();
+    }
+
+    /**
+     * 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) {
+        RecordingGetNameResponse callback = new RecordingGetNameResponse();
+        CapturingErrorHandler errorHandler = new CapturingErrorHandler();
+        proxy.getProxyHandler().setErrorHandler(errorHandler);
+
+        if (impl != null) {
+            assertNull(impl.getLastMojoException());
+            assertEquals("", impl.getNameSynchronously());
+        }
+
+        proxy.getName(callback);
+        runLoopUntilIdle();
+
+        assertNull(errorHandler.getLastMojoException());
+        assertTrue(callback.wasCalled());
+        assertEquals("", callback.getName());
+
+        callback.reset();
+        proxy.setName(OBJECT_NAME);
+        runLoopUntilIdle();
+
+        assertNull(errorHandler.getLastMojoException());
+        if (impl != null) {
+            assertNull(impl.getLastMojoException());
+            assertEquals(OBJECT_NAME, impl.getNameSynchronously());
+        }
+
+        proxy.getName(callback);
+        runLoopUntilIdle();
+
+        assertNull(errorHandler.getLastMojoException());
+        assertTrue(callback.wasCalled());
+        assertEquals(OBJECT_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 =
+                BindingsTestUtils.newProxyOverPipe(NamedObject.MANAGER, impl, mCloseablesToClose);
+
+        checkProxy(proxy, impl);
+    }
+
+    @SmallTest
+    public void testFactoryOverPipe() {
+        Factory.Proxy proxy = BindingsTestUtils.newProxyOverPipe(
+                Factory.MANAGER, new MockFactoryImpl(), mCloseablesToClose);
+        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();
+        Factory.Proxy proxy =
+                BindingsTestUtils.newProxyOverPipe(Factory.MANAGER, impl, mCloseablesToClose);
+
+        assertFalse(impl.isClosed());
+
+        proxy.close();
+        runLoopUntilIdle();
+
+        assertTrue(impl.isClosed());
+    }
+
+    @SmallTest
+    public void testResponse() {
+        MockFactoryImpl impl = new MockFactoryImpl();
+        Factory.Proxy proxy =
+                BindingsTestUtils.newProxyOverPipe(Factory.MANAGER, impl, mCloseablesToClose);
+        Request request = new Request();
+        request.x = 42;
+        Pair<MessagePipeHandle, MessagePipeHandle> handles =
+                CoreImpl.getInstance().createMessagePipe(null);
+        DoStuffResponseImpl response = new DoStuffResponseImpl();
+        proxy.doStuff(request, handles.first, response);
+
+        assertFalse(response.wasResponseCalled());
+
+        runLoopUntilIdle();
+
+        assertTrue(response.wasResponseCalled());
+    }
+}
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..f2edb01
--- /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).getMojoResult());
+        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).getMojoResult());
+        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..0b632a9
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
@@ -0,0 +1,234 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.annotations.SuppressFBWarnings;
+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.Core.HandleSignals;
+import org.chromium.mojo.system.Core.WaitResult;
+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.ResultAnd;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * Testing {@link Router}
+ */
+public class RouterTest extends MojoTestCase {
+
+    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());
+        ResultAnd<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);
+        runLoopUntilIdle();
+
+        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());
+    }
+
+    /**
+     * Sends a message to the Router.
+     *
+     * @param messageIndex Used when sending multiple messages to indicate the index of this
+     * message.
+     * @param requestMessageType The message type to use in the header of the sent message.
+     * @param requestId The requestId to use in the header of the sent message.
+     */
+    private void sendMessageToRouter(int messageIndex, int requestMessageType, int requestId) {
+        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);
+        runLoopUntilIdle();
+
+        assertEquals(messageIndex + 1, mReceiver.messagesWithReceivers.size());
+        Pair<Message, MessageReceiver> receivedMessage =
+                mReceiver.messagesWithReceivers.get(messageIndex);
+        assertEquals(headerMessage.getData(), receivedMessage.first.getData());
+    }
+
+    /**
+     * Sends a response message from the Router.
+     *
+     * @param messageIndex Used when sending responses to multiple messages to indicate the index
+     * of the message that this message is a response to.
+     * @param responseMessageType The message type to use in the header of the response message.
+     */
+    private void sendResponseFromRouter(int messageIndex, int responseMessageType) {
+        Pair<Message, MessageReceiver> receivedMessage =
+                mReceiver.messagesWithReceivers.get(messageIndex);
+
+        long requestId = receivedMessage.first.asServiceMessage().getHeader().getRequestId();
+
+        MessageHeader responseHeader = new MessageHeader(
+                responseMessageType, MessageHeader.MESSAGE_IS_RESPONSE_FLAG, requestId);
+        Encoder encoder = new Encoder(CoreImpl.getInstance(), responseHeader.getSize());
+        responseHeader.encode(encoder);
+        Message message = encoder.getMessage();
+        receivedMessage.second.accept(message);
+
+        ByteBuffer receivedResponseMessage = ByteBuffer.allocateDirect(responseHeader.getSize());
+        ResultAnd<MessagePipeHandle.ReadMessageResult> result =
+                mHandle.readMessage(receivedResponseMessage, 0, MessagePipeHandle.ReadFlags.NONE);
+
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(message.getData(), receivedResponseMessage);
+    }
+
+    /**
+     * Clears {@code mReceiver.messagesWithReceivers} allowing all message receivers to be
+     * finalized.
+     * <p>
+     * Since there is no way to force the Garbage Collector to actually call finalize and we want to
+     * test the effects of the finalize() method, we explicitly call finalize() on all of the
+     * message receivers. We do this in a custom thread to better approximate what the JVM does.
+     */
+    private void clearAllMessageReceivers() {
+        Thread myFinalizerThread = new Thread() {
+            @Override
+            @SuppressFBWarnings("FI_EXPLICIT_INVOCATION")
+            public void run() {
+                for (Pair<Message, MessageReceiver> receivedMessage :
+                        mReceiver.messagesWithReceivers) {
+                    RouterImpl.ResponderThunk thunk =
+                            (RouterImpl.ResponderThunk) receivedMessage.second;
+                    try {
+                        thunk.finalize();
+                    } catch (Throwable e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+        };
+        myFinalizerThread.start();
+        try {
+            myFinalizerThread.join();
+        } catch (InterruptedException e) {
+            // ignore.
+        }
+        mReceiver.messagesWithReceivers.clear();
+    }
+
+    /**
+     * 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;
+
+        // Send a message expecting a response.
+        sendMessageToRouter(0, requestMessageType, requestId);
+
+        // Sending the response.
+        sendResponseFromRouter(0, responseMessageType);
+    }
+
+    /**
+     * Tests that if a callback is dropped (i.e. becomes unreachable and is finalized
+     * without being used), then the message pipe will be closed.
+     */
+    @SmallTest
+    public void testDroppingReceiverWithoutUsingIt() {
+        // Send 10 messages to the router without sending a response.
+        for (int i = 0; i < 10; i++) {
+            sendMessageToRouter(i, i, i);
+        }
+
+        // Now send the 10 responses. This should work fine.
+        for (int i = 0; i < 10; i++) {
+            sendResponseFromRouter(i, i);
+        }
+
+        // Clear all MessageRecievers so that the ResponderThunks will
+        // be finalized.
+        clearAllMessageReceivers();
+
+        // Send another  message to the router without sending a response.
+        sendMessageToRouter(0, 0, 0);
+
+        // Clear the MessageReciever so that the ResponderThunk will
+        // be finalized. Since the RespondeThunk was never used, this
+        // should close the pipe.
+        clearAllMessageReceivers();
+        // The close() occurs asynchronously on this thread.
+        runLoopUntilIdle();
+
+        // Confirm that the pipe was closed on the Router side.
+        HandleSignals closedFlag = HandleSignals.none().setPeerClosed(true);
+        WaitResult result = mHandle.wait(closedFlag, 0);
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(closedFlag, result.getHandleSignalsState().getSatisfiedSignals());
+    }
+}
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..e5c4549
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
@@ -0,0 +1,234 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+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.IntegrationTestInterface;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterfaceTestHelper;
+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.getIsolatedTestFilePath(
+                "mojo/public/interfaces/bindings/tests/data/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) {
+            // TODO(yzshen, qsr): skip some interface versioning tests.
+            if (pathname.getName().startsWith("conformance_mthd13_good_2")) {
+                return false;
+            }
+            return pathname.isFile() && pathname.getName().startsWith(mPrefix)
+                    && pathname.getName().endsWith(".data");
+        }
+    }
+
+    private static String getStringContent(File f) throws FileNotFoundException {
+        try (Scanner scanner = new Scanner(f)) {
+            scanner.useDelimiter("\\Z");
+            StringBuilder result = new StringBuilder();
+            while (scanner.hasNext()) {
+                result.append(scanner.next());
+            }
+            return result.toString().trim();
+        }
+    }
+
+    private static List<TestData> getTestData(String prefix)
+            throws FileNotFoundException {
+        List<TestData> results = new ArrayList<TestData>();
+
+        // Fail if the test data is not present.
+        if (!VALIDATION_TEST_DATA_PATH.isDirectory()) {
+            fail("No test data directory found. "
+                    + "Expected directory at: " + VALIDATION_TEST_DATA_PATH);
+        }
+
+        File[] files = VALIDATION_TEST_DATA_PATH.listFiles(new DataFileFilter(prefix));
+        if (files != null) {
+            for (File dataFile : files) {
+                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("Unable to read: " + test.dataFile.getName()
+                    + ": " + test.inputData.getErrorMessage(),
+                    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 MessageReceiverWithResponder mRequest;
+        private final MessageReceiver mResponse;
+
+        private RoutingMessageReceiver(MessageReceiverWithResponder 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.acceptWithResponder(message, new SinkMessageReceiver());
+                }
+            } 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 true;
+        }
+
+        @Override
+        public void close() {
+        }
+
+        @Override
+        public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+            return true;
+        }
+    }
+
+    /**
+     * 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 for message headers.
+     */
+    @SmallTest
+    public void testIntegrationMessageHeader() throws FileNotFoundException {
+        runTest("integration_msghdr_",
+                new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null,
+                        IntegrationTestInterface.MANAGER.buildProxy(null,
+                                new SinkMessageReceiver())),
+                        IntegrationTestInterfaceTestHelper
+                                .newIntegrationTestInterfaceMethodCallback()));
+    }
+
+    /**
+     * Testing the integration suite for request messages.
+     */
+    @SmallTest
+    public void testIntegrationRequestMessage() throws FileNotFoundException {
+        runTest("integration_intf_rqst_",
+                new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null,
+                        IntegrationTestInterface.MANAGER.buildProxy(null,
+                                new SinkMessageReceiver())),
+                        IntegrationTestInterfaceTestHelper
+                                .newIntegrationTestInterfaceMethodCallback()));
+    }
+
+    /**
+     * Testing the integration suite for response messages.
+     */
+    @SmallTest
+    public void testIntegrationResponseMessage() throws FileNotFoundException {
+        runTest("integration_intf_resp_",
+                new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null,
+                        IntegrationTestInterface.MANAGER.buildProxy(null,
+                                new SinkMessageReceiver())),
+                        IntegrationTestInterfaceTestHelper
+                                .newIntegrationTestInterfaceMethodCallback()));
+    }
+}
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..91b993c
--- /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.annotations.CalledByNative;
+import org.chromium.base.annotations.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.LITTLE_ENDIAN);
+            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..14259ef
--- /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.LITTLE_ENDIAN);
+
+            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/IntegrationTestInterfaceTestHelper.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterfaceTestHelper.java
new file mode 100644
index 0000000..8fb79d7
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterfaceTestHelper.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.IntegrationTestInterface.Method0Response;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface_Internal.IntegrationTestInterfaceMethod0ResponseParamsForwardToCallback;
+
+/**
+ * Helper class to access {@link IntegrationTestInterface_Internal} package protected method for
+ * tests.
+ */
+public class IntegrationTestInterfaceTestHelper {
+
+    private static final class SinkMethod0Response implements Method0Response {
+        @Override
+        public void call(byte[] arg1) {
+        }
+    }
+
+    /**
+     * Creates a new {@link MessageReceiver} to use for the callback of
+     * |IntegrationTestInterface#method0(Method0Response)|.
+     */
+    public static MessageReceiver newIntegrationTestInterfaceMethodCallback() {
+        return new IntegrationTestInterfaceMethod0ResponseParamsForwardToCallback(
+                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..16c16f2
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
@@ -0,0 +1,930 @@
+// Copyright 2014 The Chromium 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.HandleSignals;
+import org.chromium.mojo.system.Core.HandleSignalsState;
+import org.chromium.mojo.system.Core.WaitManyResult;
+import org.chromium.mojo.system.Core.WaitResult;
+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.ResultAnd;
+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 static final HandleSignals ALL_SIGNALS =
+            HandleSignals.none().setPeerClosed(true).setReadable(true).setWritable(true);
+
+    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);
+        ResultAnd<MessagePipeHandle.ReadMessageResult> result =
+                out.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.RESOURCE_EXHAUSTED, result.getMojoResult());
+        assertEquals(bytes.length, result.getValue().getMessageSize());
+        assertEquals(0, result.getValue().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.getValue().getMessageSize());
+        assertEquals(0, result.getValue().getHandlesCount());
+        assertEquals(0, receiveBuffer.position());
+        assertEquals(result.getValue().getMessageSize(), receiveBuffer.limit());
+        byte[] receivedBytes = new byte[result.getValue().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);
+        ResultAnd<Integer> result = in.writeData(buffer, DataPipe.WriteFlags.NONE);
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(bytes.length, result.getValue().intValue());
+
+        // Query number of bytes available.
+        ResultAnd<Integer> readResult = out.readData(null, DataPipe.ReadFlags.none().query(true));
+        assertEquals(MojoResult.OK, readResult.getMojoResult());
+        assertEquals(bytes.length, readResult.getValue().intValue());
+
+        // Peek data into a buffer.
+        ByteBuffer peekBuffer = ByteBuffer.allocateDirect(bytes.length);
+        readResult = out.readData(peekBuffer, DataPipe.ReadFlags.none().peek(true));
+        assertEquals(MojoResult.OK, readResult.getMojoResult());
+        assertEquals(bytes.length, readResult.getValue().intValue());
+        assertEquals(bytes.length, peekBuffer.limit());
+        byte[] peekBytes = new byte[bytes.length];
+        peekBuffer.get(peekBytes);
+        assertTrue(Arrays.equals(bytes, peekBytes));
+
+        // Read into a buffer.
+        ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(bytes.length);
+        readResult = out.readData(receiveBuffer, DataPipe.ReadFlags.NONE);
+        assertEquals(MojoResult.OK, readResult.getMojoResult());
+        assertEquals(bytes.length, readResult.getValue().intValue());
+        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);
+
+        // Test waiting on handles of a newly created message pipe - each should be writable, but
+        // not readable.
+        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());
+        for (HandleSignalsState state : result.getSignalStates()) {
+            assertEquals(HandleSignals.WRITABLE, state.getSatisfiedSignals());
+            assertEquals(ALL_SIGNALS, state.getSatisfiableSignals());
+        }
+
+        // Same test, but swap the handles around.
+        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());
+        for (HandleSignalsState state : result.getSignalStates()) {
+            assertEquals(HandleSignals.WRITABLE, state.getSatisfiedSignals());
+            assertEquals(ALL_SIGNALS, state.getSatisfiableSignals());
+        }
+    }
+
+    /**
+     * 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);
+        // Test waiting on handles of a newly created message pipe.
+        WaitResult waitResult = handles.first.wait(
+                Core.HandleSignals.none().setReadable(true).setWritable(true), 0);
+        assertEquals(MojoResult.OK, waitResult.getMojoResult());
+        assertEquals(
+                HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals());
+        assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals());
+
+        waitResult = handles.first.wait(Core.HandleSignals.WRITABLE, 0);
+        assertEquals(MojoResult.OK, waitResult.getMojoResult());
+        assertEquals(
+                HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals());
+        assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals());
+
+        waitResult = handles.first.wait(Core.HandleSignals.READABLE, 0);
+        assertEquals(MojoResult.DEADLINE_EXCEEDED, waitResult.getMojoResult());
+        assertEquals(
+                HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals());
+        assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals());
+
+        // Testing read on an empty pipe.
+        ResultAnd<MessagePipeHandle.ReadMessageResult> readResult =
+                handles.first.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.SHOULD_WAIT, readResult.getMojoResult());
+
+        // Closing a pipe while waiting.
+        WORKER.schedule(new CloseHandle(handles.first), 10, TimeUnit.MILLISECONDS);
+        waitResult = handles.first.wait(Core.HandleSignals.READABLE, 1000000L);
+        assertEquals(MojoResult.CANCELLED, waitResult.getMojoResult());
+        assertEquals(
+                HandleSignals.none(), waitResult.getHandleSignalsState().getSatisfiedSignals());
+        assertEquals(
+                HandleSignals.none(), waitResult.getHandleSignalsState().getSatisfiableSignals());
+
+        handles = core.createMessagePipe(null);
+        addHandlePairToClose(handles);
+
+        // Closing the other pipe while waiting.
+        WORKER.schedule(new CloseHandle(handles.first), 10, TimeUnit.MILLISECONDS);
+        waitResult = handles.second.wait(Core.HandleSignals.READABLE, 1000000L);
+        assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult());
+
+        // Waiting on a closed pipe.
+        waitResult = handles.second.wait(Core.HandleSignals.READABLE, 0);
+        assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult());
+        waitResult = handles.second.wait(Core.HandleSignals.WRITABLE, 0);
+        assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult());
+    }
+
+    /**
+     * 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);
+        ResultAnd<MessagePipeHandle.ReadMessageResult> result =
+                handles.second.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(MojoResult.RESOURCE_EXHAUSTED, result.getMojoResult());
+        assertEquals(bytes.length, result.getValue().getMessageSize());
+        assertEquals(0, result.getValue().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());
+        ResultAnd<MessagePipeHandle.ReadMessageResult> readMessageResult =
+                handles.second.readMessage(null, 1, MessagePipeHandle.ReadFlags.NONE);
+        assertEquals(1, readMessageResult.getValue().getHandlesCount());
+        MessagePipeHandle newHandle =
+                readMessageResult.getValue().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 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);
+        ResultAnd<Integer> result = handles.first.writeData(buffer, DataPipe.WriteFlags.NONE);
+        assertEquals(MojoResult.OK, result.getMojoResult());
+        assertEquals(bytes.length, result.getValue().intValue());
+
+        // 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);
+        ResultAnd<Integer> readResult =
+                handles.second.readData(receiveBuffer, DataPipe.ReadFlags.NONE);
+        assertEquals(MojoResult.OK, readResult.getMojoResult());
+        assertEquals(bytes.length - nbBytesToDiscard, readResult.getValue().intValue());
+        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);
+            ResultAnd<MessagePipeHandle.ReadMessageResult> readMessageResult =
+                    handles.second.readMessage(null, 1, MessagePipeHandle.ReadFlags.NONE);
+            assertEquals(1, readMessageResult.getValue().getHandlesCount());
+            assertFalse(readMessageResult.getValue().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);
+        runLoopUntilIdle();
+        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());
+
+        runLoopUntilIdle();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        handles.second.close();
+        runLoopUntilIdle();
+        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());
+
+        core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+                Core.DEADLINE_INFINITE, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        runLoopUntilIdle();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        handles.first.close();
+        runLoopUntilIdle();
+        // 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 testAsyncWaiterWaitingOnInvalidHandle() {
+        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());
+
+        handles.first.close();
+        core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+                Core.DEADLINE_INFINITE, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        runLoopUntilIdle();
+        assertNotNull(asyncWaiterResult.getException());
+        assertEquals(MojoResult.INVALID_ARGUMENT,
+                asyncWaiterResult.getException().getMojoResult());
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+    }
+
+    /**
+     * Testing core {@link AsyncWaiter} implementation.
+     */
+    @SmallTest
+    public void testAsyncWaiterWaitingOnDefaultInvalidHandle() {
+        Core core = CoreImpl.getInstance();
+
+        final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        core.getDefaultAsyncWaiter().asyncWait(InvalidHandle.INSTANCE, Core.HandleSignals.READABLE,
+                Core.DEADLINE_INFINITE, asyncWaiterResult);
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        runLoopUntilIdle();
+        assertNotNull(asyncWaiterResult.getException());
+        assertEquals(MojoResult.INVALID_ARGUMENT, asyncWaiterResult.getException().getMojoResult());
+        assertEquals(Integer.MIN_VALUE, 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());
+
+        runLoopUntilIdle();
+        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());
+
+        runLoopUntilIdle();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        cancellable.cancel();
+        runLoopUntilIdle();
+        assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+        assertEquals(null, asyncWaiterResult.getException());
+
+        handles.second.writeMessage(
+                ByteBuffer.allocateDirect(1), null, MessagePipeHandle.WriteFlags.NONE);
+        runLoopUntilIdle();
+        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();
+
+        runLoopUntilIdle();
+        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..fb4d140
--- /dev/null
+++ b/mojo/android/javatests/validation_test_util.cc
@@ -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.
+
+#include "mojo/android/javatests/validation_test_util.h"
+
+#include <stddef.h>
+#include <stdint.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);
+}
+
+ScopedJavaLocalRef<jobject> ParseData(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<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());
+  }
+  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);
+}
+
+}  // 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/base_run_loop.cc b/mojo/android/system/base_run_loop.cc
new file mode 100644
index 0000000..e48d2f0
--- /dev/null
+++ b/mojo/android/system/base_run_loop.cc
@@ -0,0 +1,81 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/android/system/base_run_loop.h"
+
+#include <jni.h>
+
+#include "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "jni/BaseRunLoop_jni.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+
+namespace mojo {
+namespace android {
+
+static jlong CreateBaseRunLoop(JNIEnv* env,
+                               const JavaParamRef<jobject>& jcaller) {
+  base::MessageLoop* message_loop =
+      new base::MessageLoop(common::MessagePumpMojo::Create());
+  return reinterpret_cast<uintptr_t>(message_loop);
+}
+
+static void Run(JNIEnv* env,
+                const JavaParamRef<jobject>& jcaller,
+                jlong runLoopID) {
+  reinterpret_cast<base::MessageLoop*>(runLoopID)->Run();
+}
+
+static void RunUntilIdle(JNIEnv* env,
+                         const JavaParamRef<jobject>& jcaller,
+                         jlong runLoopID) {
+  reinterpret_cast<base::MessageLoop*>(runLoopID)->RunUntilIdle();
+}
+
+static void Quit(JNIEnv* env,
+                 const JavaParamRef<jobject>& jcaller,
+                 jlong runLoopID) {
+  reinterpret_cast<base::MessageLoop*>(runLoopID)->QuitWhenIdle();
+}
+
+static void RunJavaRunnable(
+    const base::android::ScopedJavaGlobalRef<jobject>& runnable_ref) {
+  Java_BaseRunLoop_runRunnable(base::android::AttachCurrentThread(),
+                               runnable_ref.obj());
+}
+
+static void PostDelayedTask(JNIEnv* env,
+                            const JavaParamRef<jobject>& jcaller,
+                            jlong runLoopID,
+                            const JavaParamRef<jobject>& runnable,
+                            jlong delay) {
+  base::android::ScopedJavaGlobalRef<jobject> runnable_ref;
+  // ScopedJavaGlobalRef do not hold onto the env reference, so it is safe to
+  // use it across threads. |RunJavaRunnable| will acquire a new JNIEnv before
+  // running the Runnable.
+  runnable_ref.Reset(env, runnable);
+  reinterpret_cast<base::MessageLoop*>(runLoopID)->PostDelayedTask(
+      FROM_HERE, base::Bind(&RunJavaRunnable, runnable_ref),
+      base::TimeDelta::FromMicroseconds(delay));
+}
+
+static void DeleteMessageLoop(JNIEnv* env,
+                              const JavaParamRef<jobject>& jcaller,
+                              jlong runLoopID) {
+  base::MessageLoop* message_loop =
+      reinterpret_cast<base::MessageLoop*>(runLoopID);
+  delete message_loop;
+}
+
+bool RegisterBaseRunLoop(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace android
+}  // namespace mojo
+
+
diff --git a/mojo/android/system/base_run_loop.h b/mojo/android/system/base_run_loop.h
new file mode 100644
index 0000000..f225c65
--- /dev/null
+++ b/mojo/android/system/base_run_loop.h
@@ -0,0 +1,20 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_ANDROID_SYSTEM_BASE_RUN_LOOP_H_
+#define MOJO_ANDROID_SYSTEM_BASE_RUN_LOOP_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+
+namespace mojo {
+namespace android {
+
+JNI_EXPORT bool RegisterBaseRunLoop(JNIEnv* env);
+
+}  // namespace android
+}  // namespace mojo
+
+#endif  // MOJO_ANDROID_SYSTEM_BASE_RUN_LOOP_H_
diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc
new file mode 100644
index 0000000..526c050
--- /dev/null
+++ b/mojo/android/system/core_impl.cc
@@ -0,0 +1,430 @@
+// Copyright 2014 The Chromium Authors. All 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#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/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "jni/CoreImpl_jni.h"
+#include "mojo/message_pump/handle_watcher.h"
+#include "mojo/public/c/system/core.h"
+
+namespace {
+
+using MojoAsyncWaitID = uintptr_t;
+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(mojo::common::HandleWatcher* watcher,
+                       void* data,
+                       MojoResult result) {
+  delete watcher;
+  std::unique_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,
+                             const JavaParamRef<jobject>& jcaller) {
+  return MojoGetTimeTicksNow();
+}
+
+static jint WaitMany(JNIEnv* env,
+                     const JavaParamRef<jobject>& jcaller,
+                     const JavaParamRef<jobject>& buffer,
+                     jlong deadline) {
+  // |buffer| contains, in this order
+  // input: The array of N handles (MojoHandle, 4 bytes each)
+  // input: The array of N signals (MojoHandleSignals, 4 bytes each)
+  // space for output: The array of N handle states (MojoHandleSignalsState, 8
+  //                   bytes each)
+  // space for output: The result index (uint32_t, 4 bytes)
+  uint8_t* buffer_start =
+      static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
+  DCHECK(buffer_start);
+  DCHECK_EQ(reinterpret_cast<uintptr_t>(buffer_start) % 8, 0u);
+  // Each handle of the input array contributes 4 (MojoHandle) + 4
+  // (MojoHandleSignals) + 8 (MojoHandleSignalsState) = 16 bytes to the size of
+  // the buffer.
+  const size_t size_per_handle = 16;
+  const size_t buffer_size = env->GetDirectBufferCapacity(buffer);
+  DCHECK_EQ((buffer_size - 4) % size_per_handle, 0u);
+
+  const size_t nb_handles = (buffer_size - 4) / size_per_handle;
+  const MojoHandle* handle_start =
+      reinterpret_cast<const MojoHandle*>(buffer_start);
+  const MojoHandleSignals* signals_start =
+      reinterpret_cast<const MojoHandleSignals*>(buffer_start + 4 * nb_handles);
+  MojoHandleSignalsState* states_start =
+      reinterpret_cast<MojoHandleSignalsState*>(buffer_start + 8 * nb_handles);
+  uint32_t* result_index =
+      reinterpret_cast<uint32_t*>(buffer_start + 16 * nb_handles);
+  *result_index = static_cast<uint32_t>(-1);
+  return MojoWaitMany(handle_start, signals_start, nb_handles, deadline,
+                      result_index, states_start);
+}
+
+static ScopedJavaLocalRef<jobject> CreateMessagePipe(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<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);
+}
+
+static ScopedJavaLocalRef<jobject> CreateDataPipe(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<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);
+}
+
+static ScopedJavaLocalRef<jobject> CreateSharedBuffer(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<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_newResultAndInteger(env, result, handle);
+}
+
+static jint Close(JNIEnv* env,
+                  const JavaParamRef<jobject>& jcaller,
+                  jint mojo_handle) {
+  return MojoClose(mojo_handle);
+}
+
+static jint Wait(JNIEnv* env,
+                 const JavaParamRef<jobject>& jcaller,
+                 const JavaParamRef<jobject>& buffer,
+                 jint mojo_handle,
+                 jint signals,
+                 jlong deadline) {
+  // Buffer contains space for the MojoHandleSignalsState
+  void* buffer_start = env->GetDirectBufferAddress(buffer);
+  DCHECK(buffer_start);
+  DCHECK_EQ(reinterpret_cast<const uintptr_t>(buffer_start) % 8, 0u);
+  DCHECK_EQ(sizeof(struct MojoHandleSignalsState),
+            static_cast<size_t>(env->GetDirectBufferCapacity(buffer)));
+  struct MojoHandleSignalsState* signals_state =
+      static_cast<struct MojoHandleSignalsState*>(buffer_start);
+  return MojoWait(mojo_handle, signals, deadline, signals_state);
+}
+
+static jint WriteMessage(JNIEnv* env,
+                         const JavaParamRef<jobject>& jcaller,
+                         jint mojo_handle,
+                         const JavaParamRef<jobject>& bytes,
+                         jint num_bytes,
+                         const JavaParamRef<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 ScopedJavaLocalRef<jobject> ReadMessage(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint mojo_handle,
+    const JavaParamRef<jobject>& bytes,
+    const JavaParamRef<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);
+}
+
+static ScopedJavaLocalRef<jobject> ReadData(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint mojo_handle,
+    const JavaParamRef<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);
+  return Java_CoreImpl_newResultAndInteger(
+      env, result, (result == MOJO_RESULT_OK) ? buffer_size : 0);
+}
+
+static ScopedJavaLocalRef<jobject> BeginReadData(
+    JNIEnv* env,
+    const JavaParamRef<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_newResultAndBuffer(env, result, byte_buffer);
+}
+
+static jint EndReadData(JNIEnv* env,
+                        const JavaParamRef<jobject>& jcaller,
+                        jint mojo_handle,
+                        jint num_bytes_read) {
+  return MojoEndReadData(mojo_handle, num_bytes_read);
+}
+
+static ScopedJavaLocalRef<jobject> WriteData(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint mojo_handle,
+    const JavaParamRef<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);
+  return Java_CoreImpl_newResultAndInteger(
+      env, result, (result == MOJO_RESULT_OK) ? buffer_size : 0);
+}
+
+static ScopedJavaLocalRef<jobject> BeginWriteData(
+    JNIEnv* env,
+    const JavaParamRef<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_newResultAndBuffer(env, result, byte_buffer);
+}
+
+static jint EndWriteData(JNIEnv* env,
+                         const JavaParamRef<jobject>& jcaller,
+                         jint mojo_handle,
+                         jint num_bytes_written) {
+  return MojoEndWriteData(mojo_handle, num_bytes_written);
+}
+
+static ScopedJavaLocalRef<jobject> Duplicate(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint mojo_handle,
+    const JavaParamRef<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_newResultAndInteger(env, result, handle);
+}
+
+static ScopedJavaLocalRef<jobject> Map(JNIEnv* env,
+                                       const JavaParamRef<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_newResultAndBuffer(env, result, byte_buffer);
+}
+
+static int Unmap(JNIEnv* env,
+                 const JavaParamRef<jobject>& jcaller,
+                 const JavaParamRef<jobject>& buffer) {
+  void* buffer_start = env->GetDirectBufferAddress(buffer);
+  DCHECK(buffer_start);
+  return MojoUnmapBuffer(buffer_start);
+}
+
+static ScopedJavaLocalRef<jobject> AsyncWait(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint mojo_handle,
+    jint signals,
+    jlong deadline,
+    const JavaParamRef<jobject>& callback) {
+  AsyncWaitCallbackData* callback_data =
+      new AsyncWaitCallbackData(env, jcaller, callback);
+  MojoAsyncWaitID cancel_id;
+  if (static_cast<MojoHandle>(mojo_handle) != MOJO_HANDLE_INVALID) {
+    common::HandleWatcher* watcher = new common::HandleWatcher();
+    cancel_id = reinterpret_cast<MojoAsyncWaitID>(watcher);
+    watcher->Start(Handle(static_cast<MojoHandle>(mojo_handle)), signals,
+                   deadline,
+                   base::Bind(&AsyncWaitCallback, watcher, callback_data));
+  } else {
+    cancel_id = kInvalidHandleCancelID;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&AsyncWaitCallback, nullptr, 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;
+}
+
+static void CancelAsyncWait(JNIEnv* env,
+                            const JavaParamRef<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;
+  }
+  std::unique_ptr<AsyncWaitCallbackData> deleter(
+      reinterpret_cast<AsyncWaitCallbackData*>(data_ptr));
+  delete reinterpret_cast<common::HandleWatcher*>(
+      static_cast<MojoAsyncWaitID>(id));
+}
+
+static jint GetNativeBufferOffset(JNIEnv* env,
+                                  const JavaParamRef<jobject>& jcaller,
+                                  const JavaParamRef<jobject>& buffer,
+                                  jint alignment) {
+  jint offset =
+      reinterpret_cast<uintptr_t>(env->GetDirectBufferAddress(buffer)) %
+      alignment;
+  if (offset == 0)
+    return 0;
+  return alignment - offset;
+}
+
+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/BaseRunLoop.java b/mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java
new file mode 100644
index 0000000..dfe92ef
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java
@@ -0,0 +1,74 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.system.impl;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.mojo.system.RunLoop;
+
+/**
+ * Implementation of {@link RunLoop} suitable for the base:: message loop implementation.
+ */
+@JNINamespace("mojo::android")
+class BaseRunLoop implements RunLoop {
+    /**
+     * Pointer to the C run loop.
+     */
+    private long mRunLoopID;
+    private final CoreImpl mCore;
+
+    BaseRunLoop(CoreImpl core) {
+        this.mCore = core;
+        this.mRunLoopID = nativeCreateBaseRunLoop();
+    }
+
+    @Override
+    public void run() {
+        assert mRunLoopID != 0 : "The run loop cannot run once closed";
+        nativeRun(mRunLoopID);
+    }
+
+    @Override
+    public void runUntilIdle() {
+        assert mRunLoopID != 0 : "The run loop cannot run once closed";
+        nativeRunUntilIdle(mRunLoopID);
+    }
+
+    @Override
+    public void quit() {
+        assert mRunLoopID != 0 : "The run loop cannot be quitted run once closed";
+        nativeQuit(mRunLoopID);
+    }
+
+    @Override
+    public void postDelayedTask(Runnable runnable, long delay) {
+        assert mRunLoopID != 0 : "The run loop cannot run tasks once closed";
+        nativePostDelayedTask(mRunLoopID, runnable, delay);
+    }
+
+    @Override
+    public void close() {
+        if (mRunLoopID == 0) {
+            return;
+        }
+        // We don't want to de-register a different run loop!
+        assert mCore.getCurrentRunLoop() == this : "Only the current run loop can be closed";
+        mCore.clearCurrentRunLoop();
+        nativeDeleteMessageLoop(mRunLoopID);
+        mRunLoopID = 0;
+    }
+
+    @CalledByNative
+    private static void runRunnable(Runnable runnable) {
+        runnable.run();
+    }
+
+    private native long nativeCreateBaseRunLoop();
+    private native void nativeRun(long runLoopID);
+    private native void nativeRunUntilIdle(long runLoopID);
+    private native void nativeQuit(long runLoopID);
+    private native void nativePostDelayedTask(long runLoopID, Runnable runnable, long delay);
+    private native void nativeDeleteMessageLoop(long runLoopID);
+}
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..a20ea0a
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
@@ -0,0 +1,638 @@
+// Copyright 2014 The Chromium 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.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+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.ResultAnd;
+import org.chromium.mojo.system.RunLoop;
+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.Arrays;
+import java.util.List;
+
+/**
+ * Implementation of {@link Core}.
+ */
+@JNINamespace("mojo::android")
+@MainDex
+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(); }
+
+    /**
+     * The run loop for the current thread.
+     */
+    private final ThreadLocal<BaseRunLoop> mCurrentRunLoop = new ThreadLocal<BaseRunLoop>();
+
+    /**
+     * The offset needed to get an aligned buffer.
+     */
+    private final int mByteBufferOffset;
+
+    /**
+     * @return the instance.
+     */
+    public static Core getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    private CoreImpl() {
+        // Fix for the ART runtime, before:
+        // https://android.googlesource.com/platform/libcore/+/fb6c80875a8a8d0a9628562f89c250b6a962e824%5E!/
+        // This assumes consistent allocation.
+        mByteBufferOffset = nativeGetNativeBufferOffset(ByteBuffer.allocateDirect(8), 8);
+    }
+
+    /**
+     * @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. The buffer
+        // layout will be:
+        // input: The array of handles (int, 4 bytes each)
+        // input: The array of signals (int, 4 bytes each)
+        // space for output: The array of handle states (2 ints, 8 bytes each)
+        // Space for output: The result index (int, 4 bytes)
+        // The handles and signals will be filled before calling the native method. When the native
+        // method returns, the handle states and the index will have been set.
+        ByteBuffer buffer = allocateDirectBuffer(handles.size() * 16 + 4);
+        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();
+        result.setMojoResult(filterMojoResultForWait(code));
+        result.setHandleIndex(buffer.getInt(handles.size() * 16));
+        if (result.getMojoResult() != MojoResult.INVALID_ARGUMENT
+                && result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED) {
+            HandleSignalsState[] states = new HandleSignalsState[handles.size()];
+            for (int i = 0; i < handles.size(); ++i) {
+                states[i] = new HandleSignalsState(
+                        new HandleSignals(buffer.getInt(8 * (handles.size() + i))),
+                        new HandleSignals(buffer.getInt(8 * (handles.size() + i) + 4)));
+            }
+            result.setSignalStates(Arrays.asList(states));
+        }
+        return result;
+    }
+
+    /**
+     * @see Core#wait(Handle, HandleSignals, long)
+     */
+    @Override
+    public WaitResult wait(Handle handle, HandleSignals signals, long deadline) {
+        // Allocate a direct buffer to allow native code not to reach back to java. Buffer will
+        // contain spaces to write the handle state.
+        ByteBuffer buffer = allocateDirectBuffer(8);
+        WaitResult result = new WaitResult();
+        result.setMojoResult(filterMojoResultForWait(
+                nativeWait(buffer, getMojoHandle(handle), signals.getFlags(), deadline)));
+        HandleSignalsState signalsState = new HandleSignalsState(
+                new HandleSignals(buffer.getInt(0)), new HandleSignals(buffer.getInt(4)));
+        result.setHandleSignalsState(signalsState);
+        return result;
+    }
+
+    /**
+     * @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());
+        }
+        ResultAnd<IntegerPair> result = nativeCreateMessagePipe(optionsBuffer);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return Pair.<MessagePipeHandle, MessagePipeHandle>create(
+                new MessagePipeHandleImpl(this, result.getValue().first),
+                new MessagePipeHandleImpl(this, result.getValue().second));
+    }
+
+    /**
+     * @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());
+        }
+        ResultAnd<IntegerPair> result = nativeCreateDataPipe(optionsBuffer);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return Pair.<ProducerHandle, ConsumerHandle>create(
+                new DataPipeProducerHandleImpl(this, result.getValue().first),
+                new DataPipeConsumerHandleImpl(this, result.getValue().second));
+    }
+
+    /**
+     * @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());
+        }
+        ResultAnd<Integer> result = nativeCreateSharedBuffer(optionsBuffer, numBytes);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return new SharedBufferHandleImpl(this, result.getValue());
+    }
+
+    /**
+     * @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 Core#createDefaultRunLoop()
+     */
+    @Override
+    public RunLoop createDefaultRunLoop() {
+        if (mCurrentRunLoop.get() != null) {
+            throw new MojoException(MojoResult.FAILED_PRECONDITION);
+        }
+        BaseRunLoop runLoop = new BaseRunLoop(this);
+        mCurrentRunLoop.set(runLoop);
+        return runLoop;
+    }
+
+    /**
+     * @see Core#getCurrentRunLoop()
+     */
+    @Override
+    public RunLoop getCurrentRunLoop() {
+        return mCurrentRunLoop.get();
+    }
+
+    /**
+     * Remove the current run loop.
+     */
+    void clearCurrentRunLoop() {
+        mCurrentRunLoop.remove();
+    }
+
+    /**
+     * @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)
+     */
+    ResultAnd<MessagePipeHandle.ReadMessageResult> readMessage(MessagePipeHandleImpl handle,
+            ByteBuffer bytes, int maxNumberOfHandles, MessagePipeHandle.ReadFlags flags) {
+        ByteBuffer handlesBuffer = null;
+        if (maxNumberOfHandles > 0) {
+            handlesBuffer = allocateDirectBuffer(maxNumberOfHandles * HANDLE_SIZE);
+        }
+        ResultAnd<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) {
+            MessagePipeHandle.ReadMessageResult readResult = result.getValue();
+            if (bytes != null) {
+                bytes.position(0);
+                bytes.limit(readResult.getMessageSize());
+            }
+
+            List<UntypedHandle> handles =
+                    new ArrayList<UntypedHandle>(readResult.getHandlesCount());
+            for (int i = 0; i < readResult.getHandlesCount(); ++i) {
+                int mojoHandle = handlesBuffer.getInt(HANDLE_SIZE * i);
+                handles.add(new UntypedHandleImpl(this, mojoHandle));
+            }
+            readResult.setHandles(handles);
+        }
+        return result;
+    }
+
+    /**
+     * @see ConsumerHandle#discardData(int, DataPipe.ReadFlags)
+     */
+    int discardData(DataPipeConsumerHandleImpl handle, int numBytes, DataPipe.ReadFlags flags) {
+        ResultAnd<Integer> result = nativeReadData(handle.getMojoHandle(), null, numBytes,
+                flags.getFlags() | MOJO_READ_DATA_FLAG_DISCARD);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return result.getValue();
+    }
+
+    /**
+     * @see ConsumerHandle#readData(ByteBuffer, DataPipe.ReadFlags)
+     */
+    ResultAnd<Integer> readData(
+            DataPipeConsumerHandleImpl handle, ByteBuffer elements, DataPipe.ReadFlags flags) {
+        ResultAnd<Integer> result = nativeReadData(handle.getMojoHandle(), elements,
+                elements == null ? 0 : elements.capacity(), flags.getFlags());
+        if (result.getMojoResult() != MojoResult.OK
+                && result.getMojoResult() != MojoResult.SHOULD_WAIT) {
+            throw new MojoException(result.getMojoResult());
+        }
+        if (result.getMojoResult() == MojoResult.OK) {
+            if (elements != null) {
+                elements.limit(result.getValue());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @see ConsumerHandle#beginReadData(int, DataPipe.ReadFlags)
+     */
+    ByteBuffer beginReadData(
+            DataPipeConsumerHandleImpl handle, int numBytes, DataPipe.ReadFlags flags) {
+        ResultAnd<ByteBuffer> result =
+                nativeBeginReadData(handle.getMojoHandle(), numBytes, flags.getFlags());
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return result.getValue().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)
+     */
+    ResultAnd<Integer> 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) {
+        ResultAnd<ByteBuffer> result =
+                nativeBeginWriteData(handle.getMojoHandle(), numBytes, flags.getFlags());
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return result.getValue();
+    }
+
+    /**
+     * @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());
+        }
+        ResultAnd<Integer> result = nativeDuplicate(handle.getMojoHandle(), optionsBuffer);
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return new SharedBufferHandleImpl(this, result.getValue());
+    }
+
+    /**
+     * @see SharedBufferHandle#map(long, long, MapFlags)
+     */
+    ByteBuffer map(SharedBufferHandleImpl handle, long offset, long numBytes, MapFlags flags) {
+        ResultAnd<ByteBuffer> result =
+                nativeMap(handle.getMojoHandle(), offset, numBytes, flags.getFlags());
+        if (result.getMojoResult() != MojoResult.OK) {
+            throw new MojoException(result.getMojoResult());
+        }
+        return result.getValue();
+    }
+
+    /**
+     * @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 filterMojoResultForWait(int code) {
+        if (isUnrecoverableError(code)) {
+            throw new MojoException(code);
+        }
+        return code;
+    }
+
+    private ByteBuffer allocateDirectBuffer(int capacity) {
+        ByteBuffer buffer = ByteBuffer.allocateDirect(capacity + mByteBufferOffset);
+        if (mByteBufferOffset != 0) {
+            buffer.position(mByteBufferOffset);
+            buffer = buffer.slice();
+        }
+        return buffer.order(ByteOrder.nativeOrder());
+    }
+
+    /**
+     * 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();
+        if (isUnrecoverableError(mojoResult)) {
+            callback.onError(new MojoException(mojoResult));
+            return;
+        }
+        callback.onResult(mojoResult);
+    }
+
+    @CalledByNative
+    private static ResultAnd<ByteBuffer> newResultAndBuffer(int mojoResult, ByteBuffer buffer) {
+        return new ResultAnd<>(mojoResult, buffer);
+    }
+
+    /**
+     * Trivial alias for Pair<Integer, Integer>. This is needed because our jni generator is unable
+     * to handle class that contains space.
+     */
+    private static final class IntegerPair extends Pair<Integer, Integer> {
+        public IntegerPair(Integer first, Integer second) {
+            super(first, second);
+        }
+    }
+
+    @CalledByNative
+    private static ResultAnd<MessagePipeHandle.ReadMessageResult> newReadMessageResult(
+            int mojoResult, int messageSize, int handlesCount) {
+        MessagePipeHandle.ReadMessageResult result = new MessagePipeHandle.ReadMessageResult();
+        result.setMessageSize(messageSize);
+        result.setHandlesCount(handlesCount);
+        return new ResultAnd<>(mojoResult, result);
+    }
+
+    @CalledByNative
+    private static ResultAnd<Integer> newResultAndInteger(int mojoResult, int numBytesRead) {
+        return new ResultAnd<>(mojoResult, numBytesRead);
+    }
+
+    @CalledByNative
+    private static ResultAnd<IntegerPair> newNativeCreationResult(
+            int mojoResult, int mojoHandle1, int mojoHandle2) {
+        return new ResultAnd<>(mojoResult, new IntegerPair(mojoHandle1, mojoHandle2));
+    }
+
+    private native long nativeGetTimeTicksNow();
+
+    private native int nativeWaitMany(ByteBuffer buffer, long deadline);
+
+    private native ResultAnd<IntegerPair> nativeCreateMessagePipe(ByteBuffer optionsBuffer);
+
+    private native ResultAnd<IntegerPair> nativeCreateDataPipe(ByteBuffer optionsBuffer);
+
+    private native ResultAnd<Integer> nativeCreateSharedBuffer(
+            ByteBuffer optionsBuffer, long numBytes);
+
+    private native int nativeClose(int mojoHandle);
+
+    private native int nativeWait(ByteBuffer buffer, int mojoHandle, int signals, long deadline);
+
+    private native int nativeWriteMessage(
+            int mojoHandle, ByteBuffer bytes, int numBytes, ByteBuffer handlesBuffer, int flags);
+
+    private native ResultAnd<MessagePipeHandle.ReadMessageResult> nativeReadMessage(
+            int mojoHandle, ByteBuffer bytes, ByteBuffer handlesBuffer, int flags);
+
+    private native ResultAnd<Integer> nativeReadData(
+            int mojoHandle, ByteBuffer elements, int elementsSize, int flags);
+
+    private native ResultAnd<ByteBuffer> nativeBeginReadData(
+            int mojoHandle, int numBytes, int flags);
+
+    private native int nativeEndReadData(int mojoHandle, int numBytesRead);
+
+    private native ResultAnd<Integer> nativeWriteData(
+            int mojoHandle, ByteBuffer elements, int limit, int flags);
+
+    private native ResultAnd<ByteBuffer> nativeBeginWriteData(
+            int mojoHandle, int numBytes, int flags);
+
+    private native int nativeEndWriteData(int mojoHandle, int numBytesWritten);
+
+    private native ResultAnd<Integer> nativeDuplicate(int mojoHandle, ByteBuffer optionsBuffer);
+
+    private native ResultAnd<ByteBuffer> 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);
+
+    private native int nativeGetNativeBufferOffset(ByteBuffer buffer, int alignment);
+}
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..83097d7
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.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.ReadFlags;
+import org.chromium.mojo.system.ResultAnd;
+
+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 ResultAnd<Integer> 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..901f26c
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java
@@ -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.
+
+package org.chromium.mojo.system.impl;
+
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+import org.chromium.mojo.system.DataPipe.WriteFlags;
+import org.chromium.mojo.system.ResultAnd;
+
+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 ResultAnd<Integer> 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..a8870a8
--- /dev/null
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java
@@ -0,0 +1,141 @@
+// Copyright 2014 The Chromium 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.Core.WaitResult;
+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 WaitResult 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..b3df0ae
--- /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 org.chromium.mojo.system.ResultAnd;
+
+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 ResultAnd<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/common/BUILD.gn b/mojo/common/BUILD.gn
new file mode 100644
index 0000000..2edfb6d
--- /dev/null
+++ b/mojo/common/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+import("//testing/test.gni")
+
+group("common") {
+  public_deps = [
+    ":common_base",
+    ":common_custom_types",
+  ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_common_custom_types
+mojom("common_custom_types") {
+  sources = [
+    "common_custom_types.mojom",
+  ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_common_lib
+component("common_base") {
+  output_name = "mojo_common_lib"
+
+  sources = [
+    "common_type_converters.cc",
+    "common_type_converters.h",
+    "data_pipe_drainer.cc",
+    "data_pipe_drainer.h",
+    "data_pipe_file_utils.cc",
+    "data_pipe_utils.cc",
+    "data_pipe_utils.h",
+    "user_agent.cc",
+    "user_agent.h",
+  ]
+
+  defines = [ "MOJO_COMMON_IMPLEMENTATION" ]
+
+  public_deps = [
+    "//base",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+  ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_test_common_custom_types
+mojom("test_common_custom_types") {
+  sources = [
+    "test_common_custom_types.mojom",
+  ]
+  public_deps = [
+    ":common_custom_types",
+  ]
+}
+
+# GYP version: mojo/mojo_base.gyp:mojo_common_unittests
+test("mojo_common_unittests") {
+  deps = [
+    ":common",
+    ":common_custom_types",
+    ":test_common_custom_types",
+    "//base",
+    "//base:message_loop_tests",
+    "//base/test:test_support",
+    "//mojo/edk/test:run_all_unittests",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//testing/gtest",
+    "//url",
+  ]
+
+  sources = [
+    "common_custom_types_unittest.cc",
+    "common_type_converters_unittest.cc",
+  ]
+}
+
+test("mojo_common_perftests") {
+  deps = [
+    ":common",
+    "//base",
+    "//mojo/edk/test:run_all_perftests",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//testing/gtest",
+  ]
+}
diff --git a/mojo/common/DEPS b/mojo/common/DEPS
new file mode 100644
index 0000000..588c68d
--- /dev/null
+++ b/mojo/common/DEPS
@@ -0,0 +1,16 @@
+include_rules = [
+  # common must not depend on embedder.
+  "-mojo",
+  "+services/shell/public/cpp",
+  "+mojo/common",
+  "+mojo/public",
+]
+
+specific_include_rules = {
+  "trace_controller_impl\.h": [
+    "+services/tracing/public/interfaces/tracing.mojom.h"
+  ],
+  "tracing_impl\.h": [
+    "+services/tracing/public/interfaces/tracing.mojom.h"
+  ],
+}
diff --git a/mojo/common/common_custom_types.mojom b/mojo/common/common_custom_types.mojom
new file mode 100644
index 0000000..aa87106
--- /dev/null
+++ b/mojo/common/common_custom_types.mojom
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium 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.common.mojom;
+
+[Native]
+struct FilePath;
+
+[Native]
+struct ListValue;
+
+[Native]
+struct DictionaryValue;
+
+[Native]
+struct Time;
+
+[Native]
+struct TimeDelta;
+
+[Native]
+struct TimeTicks;
diff --git a/mojo/common/common_custom_types.typemap b/mojo/common/common_custom_types.typemap
new file mode 100644
index 0000000..8d88109
--- /dev/null
+++ b/mojo/common/common_custom_types.typemap
@@ -0,0 +1,23 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/common_custom_types.mojom"
+public_headers = [
+  "//base/files/file_path.h",
+  "//base/values.h",
+  "//base/time/time.h",
+]
+traits_headers = [ "//ipc/ipc_message_utils.h" ]
+public_deps = [
+  "//ipc",
+]
+
+type_mappings = [
+  "mojo.common.mojom.FilePath=base::FilePath",
+  "mojo.common.mojom.DictionaryValue=base::DictionaryValue",
+  "mojo.common.mojom.ListValue=base::ListValue",
+  "mojo.common.mojom.Time=base::Time",
+  "mojo.common.mojom.TimeDelta=base::TimeDelta",
+  "mojo.common.mojom.TimeTicks=base::TimeTicks",
+]
diff --git a/mojo/common/common_custom_types_unittest.cc b/mojo/common/common_custom_types_unittest.cc
new file mode 100644
index 0000000..fe6bb5d
--- /dev/null
+++ b/mojo/common/common_custom_types_unittest.cc
@@ -0,0 +1,226 @@
+// Copyright 2016 The Chromium Authors. 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/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "mojo/common/common_custom_types.mojom.h"
+#include "mojo/common/test_common_custom_types.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+namespace {
+
+template <typename T>
+struct BounceTestTraits {
+  static void ExpectEquality(const T& a, const T& b) {
+    EXPECT_EQ(a, b);
+  }
+};
+
+template <>
+struct BounceTestTraits<base::DictionaryValue> {
+  static void ExpectEquality(const base::DictionaryValue& a,
+                             const base::DictionaryValue& b) {
+    EXPECT_TRUE(a.Equals(&b));
+  }
+};
+
+template <>
+struct BounceTestTraits<base::ListValue> {
+  static void ExpectEquality(const base::ListValue& a,
+                             const base::ListValue& b) {
+    EXPECT_TRUE(a.Equals(&b));
+  }
+};
+
+template <typename T>
+void DoExpectResponse(T* expected_value,
+                      const base::Closure& closure,
+                      const T& value) {
+  BounceTestTraits<T>::ExpectEquality(*expected_value, value);
+  closure.Run();
+}
+
+template <typename T>
+base::Callback<void(const T&)> ExpectResponse(T* expected_value,
+                                              const base::Closure& closure) {
+  return base::Bind(&DoExpectResponse<T>, expected_value, closure);
+}
+
+class TestFilePathImpl : public TestFilePath {
+ public:
+  explicit TestFilePathImpl(TestFilePathRequest request)
+      : binding_(this, std::move(request)) {}
+
+  // TestFilePath implementation:
+  void BounceFilePath(const base::FilePath& in,
+                      const BounceFilePathCallback& callback) override {
+    callback.Run(in);
+  }
+
+ private:
+  mojo::Binding<TestFilePath> binding_;
+};
+
+class TestTimeImpl : public TestTime {
+ public:
+  explicit TestTimeImpl(TestTimeRequest request)
+      : binding_(this, std::move(request)) {}
+
+  // TestTime implementation:
+  void BounceTime(const base::Time& in,
+                  const BounceTimeCallback& callback) override {
+    callback.Run(in);
+  }
+
+  void BounceTimeDelta(const base::TimeDelta& in,
+                  const BounceTimeDeltaCallback& callback) override {
+    callback.Run(in);
+  }
+
+  void BounceTimeTicks(const base::TimeTicks& in,
+                  const BounceTimeTicksCallback& callback) override {
+    callback.Run(in);
+  }
+
+ private:
+  mojo::Binding<TestTime> binding_;
+};
+
+class TestValueImpl : public TestValue {
+ public:
+  explicit TestValueImpl(TestValueRequest request)
+      : binding_(this, std::move(request)) {}
+
+  // TestValue implementation:
+  void BounceDictionaryValue(
+      const base::DictionaryValue& in,
+      const BounceDictionaryValueCallback& callback) override {
+    callback.Run(in);
+  }
+  void BounceListValue(const base::ListValue& in,
+                       const BounceListValueCallback& callback) override {
+    callback.Run(in);
+  }
+
+ private:
+  mojo::Binding<TestValue> binding_;
+};
+
+class CommonCustomTypesTest : public testing::Test {
+ protected:
+  CommonCustomTypesTest() {}
+  ~CommonCustomTypesTest() override {}
+
+ private:
+  base::MessageLoop message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(CommonCustomTypesTest);
+};
+
+}  // namespace
+
+TEST_F(CommonCustomTypesTest, FilePath) {
+  base::RunLoop run_loop;
+
+  TestFilePathPtr ptr;
+  TestFilePathImpl impl(GetProxy(&ptr));
+
+  base::FilePath dir(FILE_PATH_LITERAL("hello"));
+  base::FilePath file = dir.Append(FILE_PATH_LITERAL("world"));
+
+  ptr->BounceFilePath(file, ExpectResponse(&file, run_loop.QuitClosure()));
+
+  run_loop.Run();
+}
+
+TEST_F(CommonCustomTypesTest, Time) {
+  base::RunLoop run_loop;
+
+  TestTimePtr ptr;
+  TestTimeImpl impl(GetProxy(&ptr));
+
+  base::Time t = base::Time::Now();
+
+  ptr->BounceTime(t, ExpectResponse(&t, run_loop.QuitClosure()));
+
+  run_loop.Run();
+}
+
+TEST_F(CommonCustomTypesTest, TimeDelta) {
+  base::RunLoop run_loop;
+
+  TestTimePtr ptr;
+  TestTimeImpl impl(GetProxy(&ptr));
+
+  base::TimeDelta t = base::TimeDelta::FromDays(123);
+
+  ptr->BounceTimeDelta(t, ExpectResponse(&t, run_loop.QuitClosure()));
+
+  run_loop.Run();
+}
+
+TEST_F(CommonCustomTypesTest, TimeTicks) {
+  base::RunLoop run_loop;
+
+  TestTimePtr ptr;
+  TestTimeImpl impl(GetProxy(&ptr));
+
+  base::TimeTicks t = base::TimeTicks::Now();
+
+  ptr->BounceTimeTicks(t, ExpectResponse(&t, run_loop.QuitClosure()));
+
+  run_loop.Run();
+}
+
+TEST_F(CommonCustomTypesTest, Value) {
+  TestValuePtr ptr;
+  TestValueImpl impl(GetProxy(&ptr));
+
+  base::DictionaryValue dict;
+  dict.SetBoolean("bool", false);
+  dict.SetInteger("int", 2);
+  dict.SetString("string", "some string");
+  dict.SetBoolean("nested.bool", true);
+  dict.SetInteger("nested.int", 9);
+  dict.Set("some_binary", base::BinaryValue::CreateWithCopiedBuffer("mojo", 4));
+  {
+    std::unique_ptr<base::ListValue> dict_list(new base::ListValue());
+    dict_list->AppendString("string");
+    dict_list->AppendBoolean(true);
+    dict.Set("list", std::move(dict_list));
+  }
+  {
+    base::RunLoop run_loop;
+    ptr->BounceDictionaryValue(
+        dict, ExpectResponse(&dict, run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  base::ListValue list;
+  list.AppendString("string");
+  list.AppendDouble(42.1);
+  list.AppendBoolean(true);
+  list.Append(base::BinaryValue::CreateWithCopiedBuffer("mojo", 4));
+  {
+    std::unique_ptr<base::DictionaryValue> list_dict(
+        new base::DictionaryValue());
+    list_dict->SetString("string", "str");
+    list.Append(std::move(list_dict));
+  }
+  {
+    base::RunLoop run_loop;
+    ptr->BounceListValue(list, ExpectResponse(&list, run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+}
+
+}  // namespace test
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/common_type_converters.cc b/mojo/common/common_type_converters.cc
new file mode 100644
index 0000000..92ae3e2
--- /dev/null
+++ b/mojo/common/common_type_converters.cc
@@ -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/common/common_type_converters.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/strings/utf_string_conversions.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>());
+}
+
+std::string TypeConverter<std::string, Array<uint8_t>>::Convert(
+    const Array<uint8_t>& input) {
+  if (input.is_null() || input.empty())
+    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());
+  if (!input.empty())
+    memcpy(&result.front(), input.c_str(), input.size());
+  return result;
+}
+
+Array<uint8_t> TypeConverter<Array<uint8_t>, base::StringPiece>::Convert(
+    const base::StringPiece& input) {
+  Array<uint8_t> result(input.size());
+  if (!input.empty())
+    memcpy(&result.front(), input.data(), input.size());
+  return result;
+}
+
+base::string16 TypeConverter<base::string16, Array<uint8_t>>::Convert(
+    const Array<uint8_t>& input) {
+  if (input.is_null() || input.empty())
+    return base::string16();
+
+  return base::string16(reinterpret_cast<const base::char16*>(&input.front()),
+                        input.size() / sizeof(base::char16));
+}
+
+Array<uint8_t> TypeConverter<Array<uint8_t>, base::string16>::Convert(
+    const base::string16& input) {
+  Array<uint8_t> result(input.size() * sizeof(base::char16));
+  if (!input.empty())
+    memcpy(&result.front(), input.c_str(), input.size() * sizeof(base::char16));
+  return result;
+}
+
+}  // namespace mojo
diff --git a/mojo/common/common_type_converters.h b/mojo/common/common_type_converters.h
new file mode 100644
index 0000000..a065f05
--- /dev/null
+++ b/mojo/common/common_type_converters.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_COMMON_COMMON_TYPE_CONVERTERS_H_
+#define MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
+
+#include <stdint.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"
+
+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<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);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<Array<uint8_t>, base::StringPiece> {
+  static Array<uint8_t> Convert(const base::StringPiece& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<base::string16, Array<uint8_t>> {
+  static base::string16 Convert(const Array<uint8_t>& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<Array<uint8_t>, base::string16> {
+  static Array<uint8_t> Convert(const base::string16& 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..1740d06
--- /dev/null
+++ b/mojo/common/common_type_converters_unittest.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/common/common_type_converters.h"
+
+#include <stdint.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, 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]);
+}
+
+TEST(CommonTypeConvertersTest, ArrayUint8ToString16) {
+  Array<uint8_t> data(8);
+  data[0] = 'd';
+  data[2] = 'a';
+  data[4] = 't';
+  data[6] = 'a';
+
+  EXPECT_EQ(base::ASCIIToUTF16("data"), data.To<base::string16>());
+}
+
+TEST(CommonTypeConvertersTest, String16ToArrayUint8) {
+  base::string16 input(base::ASCIIToUTF16("data"));
+  Array<uint8_t> data = Array<uint8_t>::From(input);
+
+  ASSERT_EQ(8ul, data.size());
+  EXPECT_EQ('d', data[0]);
+  EXPECT_EQ('a', data[2]);
+  EXPECT_EQ('t', data[4]);
+  EXPECT_EQ('a', data[6]);
+}
+
+TEST(CommonTypeConvertersTest, String16ToArrayUint8AndBack) {
+  base::string16 input(base::ASCIIToUTF16("data"));
+  Array<uint8_t> data = Array<uint8_t>::From(input);
+  EXPECT_EQ(input, data.To<base::string16>());
+}
+
+TEST(CommonTypeConvertersTest, EmptyStringToArrayUint8) {
+  Array<uint8_t> data = Array<uint8_t>::From(std::string());
+
+  ASSERT_EQ(0ul, data.size());
+  EXPECT_FALSE(data.is_null());
+}
+
+}  // namespace test
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/data_pipe_drainer.cc b/mojo/common/data_pipe_drainer.cc
new file mode 100644
index 0000000..1133e11
--- /dev/null
+++ b/mojo/common/data_pipe_drainer.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/common/data_pipe_drainer.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+
+namespace mojo {
+namespace common {
+
+DataPipeDrainer::DataPipeDrainer(Client* client,
+                                 mojo::ScopedDataPipeConsumerHandle source)
+    : client_(client), source_(std::move(source)), weak_factory_(this) {
+  DCHECK(client_);
+  handle_watcher_.Start(
+      source_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+      base::Bind(&DataPipeDrainer::WaitComplete, weak_factory_.GetWeakPtr()));
+}
+
+DataPipeDrainer::~DataPipeDrainer() {}
+
+void DataPipeDrainer::ReadData() {
+  const void* buffer = nullptr;
+  uint32_t num_bytes = 0;
+  MojoResult rv = BeginReadDataRaw(source_.get(), &buffer, &num_bytes,
+                                   MOJO_READ_DATA_FLAG_NONE);
+  if (rv == MOJO_RESULT_OK) {
+    client_->OnDataAvailable(buffer, num_bytes);
+    EndReadDataRaw(source_.get(), num_bytes);
+  } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+    client_->OnDataComplete();
+  } else if (rv != MOJO_RESULT_SHOULD_WAIT) {
+    DCHECK(false) << "Unhandled MojoResult: " << rv;
+  }
+}
+
+void DataPipeDrainer::WaitComplete(MojoResult result) {
+  ReadData();
+}
+
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/data_pipe_drainer.h b/mojo/common/data_pipe_drainer.h
new file mode 100644
index 0000000..d0366fa
--- /dev/null
+++ b/mojo/common/data_pipe_drainer.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_COMMON_DATA_PIPE_DRAINER_H_
+#define MOJO_COMMON_DATA_PIPE_DRAINER_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/watcher.h"
+
+namespace mojo {
+namespace common {
+
+class MOJO_COMMON_EXPORT DataPipeDrainer {
+ public:
+  class Client {
+   public:
+    virtual void OnDataAvailable(const void* data, size_t num_bytes) = 0;
+    virtual void OnDataComplete() = 0;
+
+   protected:
+    virtual ~Client() {}
+  };
+
+  DataPipeDrainer(Client*, mojo::ScopedDataPipeConsumerHandle source);
+  ~DataPipeDrainer();
+
+ private:
+  void ReadData();
+  void WaitComplete(MojoResult result);
+
+  Client* client_;
+  mojo::ScopedDataPipeConsumerHandle source_;
+  mojo::Watcher handle_watcher_;
+
+  base::WeakPtrFactory<DataPipeDrainer> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DataPipeDrainer);
+};
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_DATA_PIPE_DRAINER_H_
diff --git a/mojo/common/data_pipe_file_utils.cc b/mojo/common/data_pipe_file_utils.cc
new file mode 100644
index 0000000..841dfde
--- /dev/null
+++ b/mojo/common/data_pipe_file_utils.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/common/data_pipe_utils.h"
+
+#include <stdint.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/location.h"
+#include "base/task_runner_util.h"
+
+namespace mojo {
+namespace common {
+namespace {
+
+bool BlockingCopyFromFile(const base::FilePath& source,
+                          ScopedDataPipeProducerHandle destination,
+                          uint32_t skip) {
+  base::File file(source, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!file.IsValid())
+    return false;
+  if (file.Seek(base::File::FROM_BEGIN, skip) != skip) {
+    LOG(ERROR) << "Seek of " << skip << " in " << source.value() << " failed";
+    return false;
+  }
+  for (;;) {
+    void* buffer = nullptr;
+    uint32_t buffer_num_bytes = 0;
+    MojoResult result =
+        BeginWriteDataRaw(destination.get(), &buffer, &buffer_num_bytes,
+                          MOJO_WRITE_DATA_FLAG_NONE);
+    if (result == MOJO_RESULT_OK) {
+      int bytes_read =
+          file.ReadAtCurrentPos(static_cast<char*>(buffer), buffer_num_bytes);
+      if (bytes_read >= 0) {
+        EndWriteDataRaw(destination.get(), bytes_read);
+        if (bytes_read == 0) {
+          // eof
+          return true;
+        }
+      } else {
+        // error
+        EndWriteDataRaw(destination.get(), 0);
+        return false;
+      }
+    } else if (result == MOJO_RESULT_SHOULD_WAIT) {
+      result = Wait(destination.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+                    MOJO_DEADLINE_INDEFINITE, nullptr);
+      if (result != MOJO_RESULT_OK) {
+        // If the consumer handle was closed, then treat as EOF.
+        return result == MOJO_RESULT_FAILED_PRECONDITION;
+      }
+    } else {
+      // If the consumer handle was closed, then treat as EOF.
+      return result == MOJO_RESULT_FAILED_PRECONDITION;
+    }
+  }
+#if !defined(OS_WIN)
+  NOTREACHED();
+  return false;
+#endif
+}
+
+}  // namespace
+
+void CopyFromFile(const base::FilePath& source,
+                  ScopedDataPipeProducerHandle destination,
+                  uint32_t skip,
+                  base::TaskRunner* task_runner,
+                  const base::Callback<void(bool)>& callback) {
+  base::PostTaskAndReplyWithResult(task_runner, FROM_HERE,
+                                   base::Bind(&BlockingCopyFromFile, source,
+                                              base::Passed(&destination), skip),
+                                   callback);
+}
+
+}  // 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..8540ac6
--- /dev/null
+++ b/mojo/common/data_pipe_utils.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/common/data_pipe_utils.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <utility>
+
+#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,
+                    nullptr);
+      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(std::move(source),
+                            base::Bind(&CopyToStringHelper, result));
+}
+
+bool MOJO_COMMON_EXPORT BlockingCopyFromString(
+    const std::string& source,
+    const ScopedDataPipeProducerHandle& destination) {
+  auto it = source.begin();
+  for (;;) {
+    void* buffer = nullptr;
+    uint32_t buffer_num_bytes = 0;
+    MojoResult result =
+        BeginWriteDataRaw(destination.get(), &buffer, &buffer_num_bytes,
+                          MOJO_WRITE_DATA_FLAG_NONE);
+    if (result == MOJO_RESULT_OK) {
+      char* char_buffer = static_cast<char*>(buffer);
+      uint32_t byte_index = 0;
+      while (it != source.end() && byte_index < buffer_num_bytes) {
+        char_buffer[byte_index++] = *it++;
+      }
+      EndWriteDataRaw(destination.get(), byte_index);
+      if (it == source.end())
+        return true;
+    } else if (result == MOJO_RESULT_SHOULD_WAIT) {
+      result = Wait(destination.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+                    MOJO_DEADLINE_INDEFINITE, nullptr);
+      if (result != MOJO_RESULT_OK) {
+        // If the consumer handle was closed, then treat as EOF.
+        return result == MOJO_RESULT_FAILED_PRECONDITION;
+      }
+    } else {
+      // If the consumer handle was closed, then treat as EOF.
+      return result == MOJO_RESULT_FAILED_PRECONDITION;
+    }
+  }
+}
+
+bool BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
+                        const base::FilePath& destination) {
+  base::ScopedFILE fp(base::OpenFile(destination, "wb"));
+  if (!fp)
+    return false;
+  return BlockingCopyHelper(std::move(source),
+                            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..426912c
--- /dev/null
+++ b/mojo/common/data_pipe_utils.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_SHELL_DATA_PIPE_UTILS_H_
+#define MOJO_SHELL_DATA_PIPE_UTILS_H_
+
+#include <stdint.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);
+
+void MOJO_COMMON_EXPORT
+CopyFromFile(const base::FilePath& source,
+             ScopedDataPipeProducerHandle destination,
+             uint32_t skip,
+             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);
+
+bool MOJO_COMMON_EXPORT BlockingCopyFromString(
+    const std::string& source,
+    const ScopedDataPipeProducerHandle& destination);
+
+// Synchronously copies data from source to the destination file returning true
+// on success and false on error.  In case of an error, |destination| holds the
+// data that could be read from the source before the error occured.
+bool MOJO_COMMON_EXPORT BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
+                                           const base::FilePath& destination);
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_DATA_PIPE_UTILS_H_
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_common_custom_types.mojom b/mojo/common/test_common_custom_types.mojom
new file mode 100644
index 0000000..db91a4f
--- /dev/null
+++ b/mojo/common/test_common_custom_types.mojom
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium 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.common.test;
+
+import "mojo/common/common_custom_types.mojom";
+
+interface TestFilePath {
+  BounceFilePath(mojo.common.mojom.FilePath in)
+      => (mojo.common.mojom.FilePath out);
+};
+
+interface TestTime {
+  BounceTime(mojo.common.mojom.Time time) => (mojo.common.mojom.Time time);
+  BounceTimeDelta(mojo.common.mojom.TimeDelta time_delta)
+      => (mojo.common.mojom.TimeDelta time_delta);
+  BounceTimeTicks(mojo.common.mojom.TimeTicks time_ticks)
+      => (mojo.common.mojom.TimeTicks time_ticks);
+};
+
+interface TestValue {
+  BounceDictionaryValue(mojo.common.mojom.DictionaryValue in)
+      => (mojo.common.mojom.DictionaryValue out);
+  BounceListValue(mojo.common.mojom.ListValue in)
+      => (mojo.common.mojom.ListValue out);
+};
diff --git a/mojo/common/user_agent.cc b/mojo/common/user_agent.cc
new file mode 100644
index 0000000..6055cbe
--- /dev/null
+++ b/mojo/common/user_agent.cc
@@ -0,0 +1,25 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/common/user_agent.h"
+
+#include "build/build_config.h"
+
+namespace mojo {
+namespace common {
+
+std::string GetUserAgent() {
+  // TODO(jam): change depending on OS
+#if defined(OS_ANDROID)
+  return "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 7 Build/LMY48G) "
+         "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.68 "
+         "Safari/537.36";
+#else
+  return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like "
+         "Gecko) Chrome/42.0.2311.68 Safari/537.36";
+#endif
+}
+
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/common/user_agent.h b/mojo/common/user_agent.h
new file mode 100644
index 0000000..031b102
--- /dev/null
+++ b/mojo/common/user_agent.h
@@ -0,0 +1,20 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_COMMON_USER_AGENT_H_
+#define MOJO_COMMON_USER_AGENT_H_
+
+#include <string>
+
+#include "mojo/common/mojo_common_export.h"
+
+namespace mojo {
+namespace common {
+
+std::string MOJO_COMMON_EXPORT GetUserAgent();
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_USER_AGENT_H_
diff --git a/mojo/converters/blink/BUILD.gn b/mojo/converters/blink/BUILD.gn
new file mode 100644
index 0000000..0bb9295
--- /dev/null
+++ b/mojo/converters/blink/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+component("blink") {
+  output_name = "mojo_blink_lib"
+
+  sources = [
+    "blink_input_events_type_converters.cc",
+    "blink_input_events_type_converters.h",
+    "mojo_blink_export.h",
+  ]
+
+  defines = [ "MOJO_CONVERTERS_BLINK_IMPLEMENTATION" ]
+
+  public_deps = [
+    "//mojo/public/cpp/bindings",
+  ]
+
+  deps = [
+    "//base",
+    "//third_party/WebKit/public:blink",
+    "//ui/events",
+    "//ui/events:dom_keycode_converter",
+  ]
+}
+
+test("blink_converters_unittests") {
+  sources = [
+    "blink_input_events_type_converters_unittest.cc",
+  ]
+  deps = [
+    ":blink",
+    "//base:message_loop_tests",
+    "//base/test:run_all_unittests",
+    "//base/test:test_support",
+    "//mojo/public/cpp/bindings:bindings",
+    "//testing/gtest",
+    "//third_party/WebKit/public:blink",
+    "//ui/events",
+  ]
+}
diff --git a/mojo/converters/blink/DEPS b/mojo/converters/blink/DEPS
new file mode 100644
index 0000000..6678b7c
--- /dev/null
+++ b/mojo/converters/blink/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+base",
+  "+third_party/WebKit/public",
+  "+ui/events"
+]
diff --git a/mojo/converters/blink/blink_input_events_type_converters.cc b/mojo/converters/blink/blink_input_events_type_converters.cc
new file mode 100644
index 0000000..1114a5f
--- /dev/null
+++ b/mojo/converters/blink/blink_input_events_type_converters.cc
@@ -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.
+
+#include "mojo/converters/blink/blink_input_events_type_converters.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/dom/keycode_converter.h"
+
+namespace mojo {
+namespace {
+
+// TODO(majidvp): remove this and directly use ui::EventFlagsToWebEventModifiers
+int EventFlagsToWebEventModifiers(int flags) {
+  int modifiers = 0;
+
+  if (flags & ui::EF_SHIFT_DOWN)
+    modifiers |= blink::WebInputEvent::ShiftKey;
+  if (flags & ui::EF_CONTROL_DOWN)
+    modifiers |= blink::WebInputEvent::ControlKey;
+  if (flags & ui::EF_ALT_DOWN)
+    modifiers |= blink::WebInputEvent::AltKey;
+  // TODO(beng): MetaKey/META_MASK
+  if (flags & ui::EF_LEFT_MOUSE_BUTTON)
+    modifiers |= blink::WebInputEvent::LeftButtonDown;
+  if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
+    modifiers |= blink::WebInputEvent::MiddleButtonDown;
+  if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
+    modifiers |= blink::WebInputEvent::RightButtonDown;
+  if (flags & ui::EF_CAPS_LOCK_ON)
+    modifiers |= blink::WebInputEvent::CapsLockOn;
+  return modifiers;
+}
+
+// TODO(majidvp): remove this and directly use ui::EventFlagsToWebEventModifiers
+int EventFlagsToWebInputEventModifiers(int flags) {
+  return (flags & ui::EF_SHIFT_DOWN ? blink::WebInputEvent::ShiftKey : 0) |
+         (flags & ui::EF_CONTROL_DOWN ? blink::WebInputEvent::ControlKey : 0) |
+         (flags & ui::EF_CAPS_LOCK_ON ? blink::WebInputEvent::CapsLockOn : 0) |
+         (flags & ui::EF_ALT_DOWN ? blink::WebInputEvent::AltKey : 0);
+}
+
+int GetClickCount(int flags) {
+  if (flags & ui::EF_IS_TRIPLE_CLICK)
+    return 3;
+  else if (flags & ui::EF_IS_DOUBLE_CLICK)
+    return 2;
+
+  return 1;
+}
+
+void SetWebMouseEventLocation(const ui::LocatedEvent& located_event,
+                              blink::WebMouseEvent* web_event) {
+  web_event->x = static_cast<int>(located_event.x());
+  web_event->y = static_cast<int>(located_event.y());
+  web_event->globalX = static_cast<int>(located_event.root_location_f().x());
+  web_event->globalY = static_cast<int>(located_event.root_location_f().y());
+}
+
+std::unique_ptr<blink::WebInputEvent> BuildWebMouseEventFrom(
+    const ui::PointerEvent& event) {
+  std::unique_ptr<blink::WebMouseEvent> web_event(new blink::WebMouseEvent);
+
+  web_event->pointerType = blink::WebPointerProperties::PointerType::Mouse;
+  SetWebMouseEventLocation(event, web_event.get());
+
+  web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
+  web_event->timeStampSeconds = ui::EventTimeStampToSeconds(event.time_stamp());
+
+  web_event->button = blink::WebMouseEvent::ButtonNone;
+  if (event.flags() & ui::EF_LEFT_MOUSE_BUTTON)
+    web_event->button = blink::WebMouseEvent::ButtonLeft;
+  if (event.flags() & ui::EF_MIDDLE_MOUSE_BUTTON)
+    web_event->button = blink::WebMouseEvent::ButtonMiddle;
+  if (event.flags() & ui::EF_RIGHT_MOUSE_BUTTON)
+    web_event->button = blink::WebMouseEvent::ButtonRight;
+
+  switch (event.type()) {
+    case ui::ET_POINTER_DOWN:
+      web_event->type = blink::WebInputEvent::MouseDown;
+      break;
+    case ui::ET_POINTER_UP:
+      web_event->type = blink::WebInputEvent::MouseUp;
+      break;
+    case ui::ET_POINTER_MOVED:
+      web_event->type = blink::WebInputEvent::MouseMove;
+      break;
+    case ui::ET_MOUSE_EXITED:
+      web_event->type = blink::WebInputEvent::MouseLeave;
+      break;
+    default:
+      NOTIMPLEMENTED() << "Received unexpected event: " << event.type();
+      break;
+  }
+
+  web_event->clickCount = GetClickCount(event.flags());
+
+  return std::move(web_event);
+}
+
+std::unique_ptr<blink::WebInputEvent> BuildWebKeyboardEvent(
+    const ui::KeyEvent& event) {
+  std::unique_ptr<blink::WebKeyboardEvent> web_event(
+      new blink::WebKeyboardEvent);
+
+  web_event->modifiers = EventFlagsToWebInputEventModifiers(event.flags());
+  web_event->timeStampSeconds = ui::EventTimeStampToSeconds(event.time_stamp());
+
+  switch (event.type()) {
+    case ui::ET_KEY_PRESSED:
+      web_event->type = event.is_char() ? blink::WebInputEvent::Char
+                                        : blink::WebInputEvent::RawKeyDown;
+      break;
+    case ui::ET_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.GetLocatedWindowsKeyboardCode();
+  web_event->nativeKeyCode =
+      ui::KeycodeConverter::DomCodeToNativeKeycode(event.code());
+  web_event->text[0] = event.GetText();
+  web_event->unmodifiedText[0] = event.GetUnmodifiedText();
+  return std::move(web_event);
+}
+
+std::unique_ptr<blink::WebInputEvent> BuildWebMouseWheelEventFrom(
+    const ui::MouseWheelEvent& event) {
+  std::unique_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 = ui::EventTimeStampToSeconds(event.time_stamp());
+
+  SetWebMouseEventLocation(event, web_event.get());
+
+  // TODO(rjkroege): Update the following code once Blink supports
+  // DOM Level 3 wheel events
+  // (http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents)
+  web_event->deltaX = event.x_offset();
+  web_event->deltaY = event.y_offset();
+
+  web_event->wheelTicksX = web_event->deltaX / ui::MouseWheelEvent::kWheelDelta;
+  web_event->wheelTicksY = web_event->deltaY / ui::MouseWheelEvent::kWheelDelta;
+
+  // TODO(moshayedi): ui::WheelEvent currently only supports WHEEL_MODE_LINE.
+  // Add support for other wheel modes once ui::WheelEvent has support for them.
+  web_event->hasPreciseScrollingDeltas = false;
+  web_event->scrollByPage = false;
+
+  return std::move(web_event);
+}
+
+void SetWebTouchEventLocation(const ui::PointerEvent& event,
+                              blink::WebTouchPoint* touch) {
+  touch->position.x = event.x();
+  touch->position.y = event.y();
+  touch->screenPosition.x = event.root_location_f().x();
+  touch->screenPosition.y = event.root_location_f().y();
+}
+
+std::unique_ptr<blink::WebInputEvent> BuildWebTouchEvent(
+    const ui::PointerEvent& event) {
+  std::unique_ptr<blink::WebTouchEvent> web_event(new blink::WebTouchEvent);
+  blink::WebTouchPoint* touch = &web_event->touches[event.pointer_id()];
+
+  // TODO(jonross): we will need to buffer input events, as blink expects all
+  // active touch points to be in each WebInputEvent (crbug.com/578160)
+  SetWebTouchEventLocation(event, touch);
+  touch->pointerType = blink::WebPointerProperties::PointerType::Touch;
+  touch->radiusX = event.pointer_details().radius_x;
+  touch->radiusY = event.pointer_details().radius_y;
+
+  web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
+  web_event->timeStampSeconds = ui::EventTimeStampToSeconds(event.time_stamp());
+  web_event->uniqueTouchEventId = ui::GetNextTouchEventId();
+
+  switch (event.type()) {
+    case ui::ET_POINTER_DOWN:
+      web_event->type = blink::WebInputEvent::TouchStart;
+      touch->state = blink::WebTouchPoint::StatePressed;
+      break;
+    case ui::ET_POINTER_UP:
+      web_event->type = blink::WebInputEvent::TouchEnd;
+      touch->state = blink::WebTouchPoint::StateReleased;
+      break;
+    case ui::ET_POINTER_MOVED:
+      web_event->type = blink::WebInputEvent::TouchMove;
+      touch->state = blink::WebTouchPoint::StateMoved;
+      break;
+    case ui::ET_POINTER_CANCELLED:
+      web_event->type = blink::WebInputEvent::TouchCancel;
+      touch->state = blink::WebTouchPoint::StateCancelled;
+      break;
+    default:
+      NOTIMPLEMENTED() << "Received non touch pointer event action: "
+                       << event.type();
+      break;
+  }
+
+  return std::move(web_event);
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<blink::WebInputEvent>
+TypeConverter<std::unique_ptr<blink::WebInputEvent>, ui::Event>::Convert(
+    const ui::Event& event) {
+  DCHECK(event.IsKeyEvent() || event.IsPointerEvent() ||
+         event.IsMouseWheelEvent());
+  switch (event.type()) {
+    case ui::ET_POINTER_DOWN:
+    case ui::ET_POINTER_UP:
+    case ui::ET_POINTER_CANCELLED:
+    case ui::ET_POINTER_MOVED:
+    case ui::ET_POINTER_EXITED:
+      if (event.IsMousePointerEvent())
+        return BuildWebMouseEventFrom(*event.AsPointerEvent());
+      else if (event.IsTouchPointerEvent())
+        return BuildWebTouchEvent(*event.AsPointerEvent());
+      else
+        return nullptr;
+    case ui::ET_MOUSEWHEEL:
+      return BuildWebMouseWheelEventFrom(*event.AsMouseWheelEvent());
+    case ui::ET_KEY_PRESSED:
+    case ui::ET_KEY_RELEASED:
+      return BuildWebKeyboardEvent(*event.AsKeyEvent());
+    default:
+      return nullptr;
+  }
+}
+
+}  // namespace mojo
diff --git a/mojo/converters/blink/blink_input_events_type_converters.h b/mojo/converters/blink/blink_input_events_type_converters.h
new file mode 100644
index 0000000..3231eab
--- /dev/null
+++ b/mojo/converters/blink/blink_input_events_type_converters.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_CONVERTERS_BLINK_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
+#define MOJO_CONVERTERS_BLINK_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
+
+#include <memory>
+
+#include "mojo/converters/blink/mojo_blink_export.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace blink {
+class WebInputEvent;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+
+template <>
+struct MOJO_BLINK_EXPORT
+    TypeConverter<std::unique_ptr<blink::WebInputEvent>, ui::Event> {
+  static std::unique_ptr<blink::WebInputEvent> Convert(const ui::Event& input);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_CONVERTERS_BLINK_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
diff --git a/mojo/converters/blink/mojo_blink_export.h b/mojo/converters/blink/mojo_blink_export.h
new file mode 100644
index 0000000..1a77184
--- /dev/null
+++ b/mojo/converters/blink/mojo_blink_export.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_CONVERTERS_BLINK_MOJO_BLINK_EXPORT_H_
+#define MOJO_CONVERTERS_BLINK_MOJO_BLINK_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_CONVERTERS_BLINK_IMPLEMENTATION)
+#define MOJO_BLINK_EXPORT __declspec(dllexport)
+#else
+#define MOJO_BLINK_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_CONVERTERS_BLINK_IMPLEMENTATION)
+#define MOJO_BLINK_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_BLINK_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_BLINK_EXPORT
+#endif
+
+#endif  // MOJO_CONVERTERS_BLINK_MOJO_BLINK_EXPORT_H_
diff --git a/mojo/edk/DEPS b/mojo/edk/DEPS
new file mode 100644
index 0000000..77abb21
--- /dev/null
+++ b/mojo/edk/DEPS
@@ -0,0 +1,14 @@
+include_rules = [
+  # This code is checked into the chromium repo so it's fine to depend on this.
+  "+base",
+  "+crypto",
+  "+build",
+  "+gin",
+  "+native_client/src/public",
+  "+testing",
+  "+third_party/ashmem",
+  "+v8",
+
+  # internal includes.
+  "+mojo",
+]
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
new file mode 100644
index 0000000..f20fd40
--- /dev/null
+++ b/mojo/edk/embedder/BUILD.gn
@@ -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.
+
+import("//build/config/nacl/config.gni")
+
+source_set("headers") {
+  sources = [
+    "configuration.h",
+    "embedder.h",
+    "embedder_internal.h",
+    "named_platform_channel_pair.h",
+    "platform_channel_pair.h",
+    "platform_handle.h",
+    "platform_handle_utils.h",
+    "process_delegate.h",
+    "scoped_platform_handle.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//mojo/public/cpp/system",
+  ]
+}
+
+source_set("embedder") {
+  # This isn't really a standalone target; it must be linked into the
+  # mojo_system_impl component.
+  visibility = [
+    "//mojo/edk/system",
+    "//components/nacl:nacl",
+  ]
+
+  sources = [
+    "configuration.h",
+    "embedder.cc",
+    "embedder.h",
+    "embedder_internal.h",
+    "entrypoints.cc",
+    "entrypoints.h",
+    "scoped_ipc_support.cc",
+    "scoped_ipc_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",
+  ]
+
+  defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
+
+  public_deps = [
+    ":delegates",
+    ":headers",
+    ":platform",
+    "//base",
+    "//mojo/public/cpp/system",
+  ]
+
+  if (!is_nacl) {
+    deps = [
+      "//crypto",
+    ]
+  }
+}
+
+source_set("platform") {
+  # This isn't really a standalone target; it must be linked into the
+  # mojo_system_impl component.
+  visibility = [
+    ":embedder",
+    "//mojo/edk/system",
+  ]
+
+  sources = [
+    "named_platform_channel_pair.h",
+    "named_platform_channel_pair_win.cc",
+    "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.cc",
+    "platform_shared_buffer.h",
+    "scoped_platform_handle.h",
+  ]
+
+  defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
+
+  public_deps = [
+    "//mojo/public/cpp/system",
+  ]
+
+  deps = [
+    "//base",
+  ]
+
+  if (is_android) {
+    deps += [ "//third_party/ashmem" ]
+  }
+
+  if (is_nacl && !is_nacl_nonsfi) {
+    sources -= [ "platform_channel_utils_posix.cc" ]
+  }
+}
+
+source_set("delegates") {
+  # This isn't really a standalone target; it must be linked into the
+  # mojo_system_impl component.
+  visibility = [
+    ":embedder",
+    "//mojo/edk/system",
+  ]
+
+  sources = [
+    "process_delegate.h",
+  ]
+
+  defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
+
+  public_deps = [
+    "//mojo/public/cpp/system",
+  ]
+}
+
+source_set("embedder_unittests") {
+  testonly = true
+
+  # TODO: Figure out why this visibility check fails on Android.
+  # visibility = [ "//mojo/edk/system:mojo_system_unittests" ]
+
+  sources = [
+    "embedder_unittest.cc",
+    "platform_channel_pair_posix_unittest.cc",
+    "platform_shared_buffer_unittest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//mojo/edk/system:test_utils",
+    "//mojo/edk/test:test_support",
+    "//testing/gtest",
+  ]
+}
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/configuration.h b/mojo/edk/embedder/configuration.h
new file mode 100644
index 0000000..4b5618d
--- /dev/null
+++ b/mojo/edk/embedder/configuration.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_EDK_EMBEDDER_CONFIGURATION_H_
+#define MOJO_EDK_EMBEDDER_CONFIGURATION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace mojo {
+namespace edk {
+
+// A set of constants that the Mojo system internally uses. These values should
+// be consistent across all processes on the same system.
+//
+// In general, there should be no need to change these values from their
+// defaults. However, if you do change them, you must do so before
+// initialization.
+struct Configuration {
+  // Maximum number of open (Mojo) handles. The default is 1,000,000.
+  //
+  // TODO(vtl): This doesn't count "live" handles, some of which may live in
+  // messages.
+  size_t max_handle_table_size;
+
+  // Maximum number of active memory mappings. The default is 1,000,000.
+  size_t max_mapping_table_sze;
+
+  // Upper limit of |MojoWaitMany()|'s |num_handles|. The default is 1,000,000.
+  // Must be same as or smaller than |max_handle_table_size|.
+  size_t max_wait_many_num_handles;
+
+  // Maximum data size of messages sent over message pipes, in bytes. The
+  // default is 4MB.
+  size_t max_message_num_bytes;
+
+  // Maximum number of handles that can be attached to messages sent over
+  // message pipes. The default is 10,000.
+  size_t max_message_num_handles;
+
+  // Maximum capacity of a data pipe, in bytes. The default is 256MB. 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).
+  size_t max_data_pipe_capacity_bytes;
+
+  // Default data pipe capacity, if not specified explicitly in the creation
+  // options. The default is 1MB.
+  size_t default_data_pipe_capacity_bytes;
+
+  // Alignment for the "start" of the data buffer used by data pipes. (The
+  // alignment of elements will depend on this and the element size.)  The
+  // default is 16 bytes.
+  size_t data_pipe_buffer_alignment_bytes;
+
+  // Maximum size of a single shared memory segment, in bytes. The default is
+  // 1GB.
+  //
+  // 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.)
+  size_t max_shared_memory_num_bytes;
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_CONFIGURATION_H_
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
new file mode 100644
index 0000000..6fb71ba
--- /dev/null
+++ b/mojo/edk/embedder/embedder.cc
@@ -0,0 +1,174 @@
+// Copyright 2014 The Chromium Authors. All 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 <stdint.h>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/embedder/entrypoints.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/process_delegate.h"
+#include "mojo/edk/system/core.h"
+
+#if !defined(OS_NACL)
+#include "crypto/random.h"
+#endif
+
+namespace mojo {
+namespace edk {
+
+class Core;
+class PlatformSupport;
+
+namespace internal {
+
+Core* g_core;
+ProcessDelegate* g_process_delegate;
+
+Core* GetCore() { return g_core; }
+
+}  // namespace internal
+
+void SetMaxMessageSize(size_t bytes) {
+}
+
+void ChildProcessLaunched(base::ProcessHandle child_process,
+                          ScopedPlatformHandle server_pipe,
+                          const std::string& child_token) {
+  ChildProcessLaunched(child_process, std::move(server_pipe),
+                       child_token, ProcessErrorCallback());
+}
+
+void ChildProcessLaunched(base::ProcessHandle child_process,
+                          ScopedPlatformHandle server_pipe,
+                          const std::string& child_token,
+                          const ProcessErrorCallback& process_error_callback) {
+  CHECK(internal::g_core);
+  internal::g_core->AddChild(child_process, std::move(server_pipe),
+                             child_token, process_error_callback);
+}
+
+void ChildProcessLaunchFailed(const std::string& child_token) {
+  CHECK(internal::g_core);
+  internal::g_core->ChildLaunchFailed(child_token);
+}
+
+void SetParentPipeHandle(ScopedPlatformHandle pipe) {
+  CHECK(internal::g_core);
+  internal::g_core->InitChild(std::move(pipe));
+}
+
+void SetParentPipeHandleFromCommandLine() {
+  ScopedPlatformHandle platform_channel =
+      PlatformChannelPair::PassClientHandleFromParentProcess(
+          *base::CommandLine::ForCurrentProcess());
+  CHECK(platform_channel.is_valid());
+  SetParentPipeHandle(std::move(platform_channel));
+}
+
+void Init() {
+  MojoSystemThunks thunks = MakeSystemThunks();
+  size_t expected_size = MojoEmbedderSetSystemThunks(&thunks);
+  DCHECK_EQ(expected_size, sizeof(thunks));
+
+  internal::g_core = new Core();
+}
+
+MojoResult CreatePlatformHandleWrapper(
+    ScopedPlatformHandle platform_handle,
+    MojoHandle* platform_handle_wrapper_handle) {
+  return internal::g_core->CreatePlatformHandleWrapper(
+      std::move(platform_handle), platform_handle_wrapper_handle);
+}
+
+MojoResult PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle,
+                                     ScopedPlatformHandle* platform_handle) {
+  return internal::g_core->PassWrappedPlatformHandle(
+      platform_handle_wrapper_handle, platform_handle);
+}
+
+MojoResult CreateSharedBufferWrapper(
+    base::SharedMemoryHandle shared_memory_handle,
+    size_t num_bytes,
+    bool read_only,
+    MojoHandle* mojo_wrapper_handle) {
+  return internal::g_core->CreateSharedBufferWrapper(
+      shared_memory_handle, num_bytes, read_only, mojo_wrapper_handle);
+}
+
+MojoResult PassSharedMemoryHandle(
+    MojoHandle mojo_handle,
+    base::SharedMemoryHandle* shared_memory_handle,
+    size_t* num_bytes,
+    bool* read_only) {
+  return internal::g_core->PassSharedMemoryHandle(
+      mojo_handle, shared_memory_handle, num_bytes, read_only);
+}
+
+void InitIPCSupport(ProcessDelegate* process_delegate,
+                    scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+  CHECK(internal::g_core);
+  internal::g_core->SetIOTaskRunner(io_thread_task_runner);
+  internal::g_process_delegate = process_delegate;
+}
+
+void ShutdownIPCSupport() {
+  CHECK(internal::g_process_delegate);
+  CHECK(internal::g_core);
+  internal::g_core->RequestShutdown(
+      base::Bind(&ProcessDelegate::OnShutdownComplete,
+                 base::Unretained(internal::g_process_delegate)));
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+void SetMachPortProvider(base::PortProvider* port_provider) {
+  DCHECK(port_provider);
+  internal::g_core->SetMachPortProvider(port_provider);
+}
+#endif
+
+ScopedMessagePipeHandle CreateMessagePipe(
+    ScopedPlatformHandle platform_handle) {
+  CHECK(internal::g_process_delegate);
+  return internal::g_core->CreateMessagePipe(std::move(platform_handle));
+}
+
+ScopedMessagePipeHandle CreateParentMessagePipe(
+    const std::string& token, const std::string& child_token) {
+  CHECK(internal::g_process_delegate);
+  return internal::g_core->CreateParentMessagePipe(token, child_token);
+}
+
+ScopedMessagePipeHandle CreateChildMessagePipe(const std::string& token) {
+  CHECK(internal::g_process_delegate);
+  return internal::g_core->CreateChildMessagePipe(token);
+}
+
+std::string GenerateRandomToken() {
+  char random_bytes[16];
+#if defined(OS_NACL)
+  // Not secure. For NaCl only!
+  base::RandBytes(random_bytes, 16);
+#else
+  crypto::RandBytes(random_bytes, 16);
+#endif
+  return base::HexEncode(random_bytes, 16);
+}
+
+MojoResult SetProperty(MojoPropertyType type, const void* value) {
+  CHECK(internal::g_core);
+  return internal::g_core->SetProperty(type, value);
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h
new file mode 100644
index 0000000..ffd513d
--- /dev/null
+++ b/mojo/edk/embedder/embedder.h
@@ -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.
+
+#ifndef MOJO_EDK_EMBEDDER_EMBEDDER_H_
+#define MOJO_EDK_EMBEDDER_EMBEDDER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/process/process_handle.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/message_pipe.h"
+
+namespace base {
+class PortProvider;
+}
+
+namespace mojo {
+namespace edk {
+
+class ProcessDelegate;
+
+using ProcessErrorCallback = base::Callback<void(const std::string& error)>;
+
+// Basic configuration/initialization ------------------------------------------
+
+// |Init()| sets up the basic Mojo system environment, making the |Mojo...()|
+// functions available and functional. This is never shut down (except in tests
+// -- see test_embedder.h).
+
+// Allows changing the default max message size. Must be called before Init.
+MOJO_SYSTEM_IMPL_EXPORT void SetMaxMessageSize(size_t bytes);
+
+// Called in the parent process for each child process that is launched.
+MOJO_SYSTEM_IMPL_EXPORT void ChildProcessLaunched(
+    base::ProcessHandle child_process,
+    ScopedPlatformHandle server_pipe,
+    const std::string& child_token);
+
+// Called in the parent process for each child process that is launched.
+// |process_error_callback| is called if the system becomes aware of some
+// internal error related to this process, e.g., if the system is notified of a
+// bad message from this process via the |MojoNotifyBadMessage()| API.
+MOJO_SYSTEM_IMPL_EXPORT void ChildProcessLaunched(
+    base::ProcessHandle child_process,
+    ScopedPlatformHandle server_pipe,
+    const std::string& child_token,
+    const ProcessErrorCallback& error_callback);
+
+// Called in the parent process when a child process fails to launch.
+// Exactly one of ChildProcessLaunched() or ChildProcessLaunchFailed() must be
+// called per child process launch attempt.
+MOJO_SYSTEM_IMPL_EXPORT void ChildProcessLaunchFailed(
+    const std::string& child_token);
+
+// Should be called as early as possible in the child process with the handle
+// that the parent received from ChildProcessLaunched.
+MOJO_SYSTEM_IMPL_EXPORT void SetParentPipeHandle(ScopedPlatformHandle pipe);
+
+// Same as above but extracts the pipe handle from the command line. See
+// PlatformChannelPair for details.
+MOJO_SYSTEM_IMPL_EXPORT void SetParentPipeHandleFromCommandLine();
+
+// Must be called first, or just after setting configuration parameters, to
+// initialize the (global, singleton) system.
+MOJO_SYSTEM_IMPL_EXPORT void Init();
+
+// Basic functions -------------------------------------------------------------
+
+// The functions in this section are available once |Init()| has been called.
+
+// 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| is closed
+// on success.
+MOJO_SYSTEM_IMPL_EXPORT MojoResult
+PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle,
+                          ScopedPlatformHandle* platform_handle);
+
+// Creates a |MojoHandle| that wraps the given |SharedMemoryHandle| (taking
+// ownership of it). |num_bytes| is the size of the shared memory object, and
+// |read_only| is whether the handle is a read-only handle to shared memory.
+// This |MojoHandle| is a Mojo shared buffer and can be manipulated using the
+// shared buffer functions and transferred over a message pipe.
+MOJO_SYSTEM_IMPL_EXPORT MojoResult
+CreateSharedBufferWrapper(base::SharedMemoryHandle shared_memory_handle,
+                          size_t num_bytes,
+                          bool read_only,
+                          MojoHandle* mojo_wrapper_handle);
+
+// Retrieves the underlying |SharedMemoryHandle| from a shared buffer
+// |MojoHandle| and closes the handle. If successful, |num_bytes| will contain
+// the size of the shared memory buffer and |read_only| will contain whether the
+// buffer handle is read-only. Both |num_bytes| and |read_only| may be null.
+// Note: The value of |shared_memory_handle| may be
+// base::SharedMemory::NULLHandle(), even if this function returns success.
+// Callers should perform appropriate checks.
+MOJO_SYSTEM_IMPL_EXPORT MojoResult
+PassSharedMemoryHandle(MojoHandle mojo_handle,
+                       base::SharedMemoryHandle* shared_memory_handle,
+                       size_t* num_bytes,
+                       bool* read_only);
+
+// Initialialization/shutdown for interprocess communication (IPC) -------------
+
+// |InitIPCSupport()| sets up the subsystem for interprocess communication,
+// making the IPC functions (in the following section) available and functional.
+// (This may only be done after |Init()|.)
+//
+// This subsystem may be shut down using |ShutdownIPCSupport()|. None of the IPC
+// functions may be called after this is called.
+
+// Initializes a process of the given type; to be called after |Init()|.
+//   - |process_delegate| must be a process delegate of the appropriate type
+//     corresponding to |process_type|; its methods will be called on the same
+//     thread as Shutdown.
+//   - |process_delegate|, and |io_thread_task_runner| should live at least
+//     until |ShutdownIPCSupport()|'s callback has been run.
+MOJO_SYSTEM_IMPL_EXPORT void InitIPCSupport(
+    ProcessDelegate* process_delegate,
+    scoped_refptr<base::TaskRunner> io_thread_task_runner);
+
+// Shuts down the subsystem initialized by |InitIPCSupport()|. It be called from
+// any thread and will attempt to complete shutdown on the I/O thread with which
+// the system was initialized. Upon completion the ProcessDelegate's
+// |OnShutdownComplete()| method is invoked.
+MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport();
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+// Set the |base::PortProvider| for this process. Can be called on any thread,
+// but must be set in the root process before any Mach ports can be transferred.
+MOJO_SYSTEM_IMPL_EXPORT void SetMachPortProvider(
+    base::PortProvider* port_provider);
+#endif
+
+// Creates a message pipe over an arbitrary platform channel. The other end of
+// the channel must also be passed to this function. Either endpoint can be in
+// any process.
+//
+// Note that the channel is only used to negotiate pipe connection, not as the
+// transport for messages on the pipe.
+MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
+CreateMessagePipe(ScopedPlatformHandle platform_handle);
+
+// Creates a message pipe from a token. A child embedder must also have this
+// token and call CreateChildMessagePipe() with it in order for the pipe to get
+// connected. |child_token| identifies the child process and should be the same
+// as the token passed into ChildProcessLaunched(). If they are different, the
+// returned message pipe will not be signaled of peer closure if the child
+// process dies before establishing connection to the pipe.
+MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
+CreateParentMessagePipe(const std::string& token,
+                        const std::string& child_token);
+
+// Creates a message pipe from a token in a child process. The parent must also
+// have this token and call CreateParentMessagePipe() with it in order for the
+// pipe to get connected.
+MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
+CreateChildMessagePipe(const std::string& token);
+
+// Generates a random ASCII token string for use with CreateParentMessagePipe()
+// and CreateChildMessagePipe() above. The generated token is suitably random so
+// as to not have to worry about collisions with other generated tokens.
+MOJO_SYSTEM_IMPL_EXPORT std::string GenerateRandomToken();
+
+// Sets system properties that can be read by the MojoGetProperty() API. See the
+// documentation for MojoPropertyType for supported property types and their
+// corresponding value type.
+//
+// Default property values:
+//   |MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED| - true
+MOJO_SYSTEM_IMPL_EXPORT MojoResult SetProperty(MojoPropertyType type,
+                                               const void* value);
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_EMBEDDER_H_
diff --git a/mojo/edk/embedder/embedder_internal.h b/mojo/edk/embedder/embedder_internal.h
new file mode 100644
index 0000000..7deeca1
--- /dev/null
+++ b/mojo/edk/embedder/embedder_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.
+
+// This header contains internal details for the *implementation* of the
+// embedder API. It should not be included by any public header (nor by users of
+// the embedder API).
+
+#ifndef MOJO_EDK_EMBEDDER_EMBEDDER_INTERNAL_H_
+#define MOJO_EDK_EMBEDDER_EMBEDDER_INTERNAL_H_
+
+#include <stdint.h>
+
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace base {
+class TaskRunner;
+}
+
+namespace mojo {
+
+namespace edk {
+
+class Broker;
+class Core;
+class ProcessDelegate;
+
+namespace internal {
+
+// Instance of |Broker| to use.
+extern Broker* g_broker;
+
+// Instance of |Core| used by the system functions (|Mojo...()|).
+extern MOJO_SYSTEM_IMPL_EXPORT Core* g_core;
+extern base::TaskRunner* g_delegate_thread_task_runner;
+extern ProcessDelegate* g_process_delegate;
+
+}  // namespace internal
+
+}  // namepace edk
+
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_EMBEDDER_INTERNAL_H_
diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc
new file mode 100644
index 0000000..127a74f
--- /dev/null
+++ b/mojo/edk/embedder/embedder_unittest.cc
@@ -0,0 +1,562 @@
+// Copyright 2014 The Chromium Authors. All 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 <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/shared_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/process_handle.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_timeouts.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/mojo_test_base.h"
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+const MojoHandleSignals kSignalReadadableWritable =
+    MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE;
+
+const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE |
+                                     MOJO_HANDLE_SIGNAL_WRITABLE |
+                                     MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+
+// The multiprocess tests that use these don't compile on iOS.
+#if !defined(OS_IOS)
+const char kHelloWorld[] = "hello world";
+const char kByeWorld[] = "bye world";
+#endif
+
+using EmbedderTest = test::MojoTestBase;
+
+TEST_F(EmbedderTest, ChannelBasic) {
+  MojoHandle server_mp, client_mp;
+  CreateMessagePipe(&server_mp, &client_mp);
+
+  const std::string kHello = "hello";
+
+  // We can write to a message pipe handle immediately.
+  WriteMessage(server_mp, kHello);
+  EXPECT_EQ(kHello, ReadMessage(client_mp));
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+}
+
+// Test sending a MP which has read messages out of the OS pipe but which have
+// not been consumed using MojoReadMessage yet.
+TEST_F(EmbedderTest, SendReadableMessagePipe) {
+  MojoHandle server_mp, client_mp;
+  CreateMessagePipe(&server_mp, &client_mp);
+
+  MojoHandle server_mp2, client_mp2;
+  CreateMessagePipe(&server_mp2, &client_mp2);
+
+  // Write to server2 and wait for client2 to be readable before sending it.
+  // client2's MessagePipeDispatcher will have the message below in its
+  // message_queue_. For extra measures, also verify that this pending message
+  // can contain a message pipe.
+  MojoHandle server_mp3, client_mp3;
+  CreateMessagePipe(&server_mp3, &client_mp3);
+
+  const std::string kHello = "hello";
+  WriteMessageWithHandles(server_mp2, kHello, &client_mp3, 1);
+
+  MojoHandleSignalsState state;
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp2, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(kSignalReadadableWritable, state.satisfied_signals);
+  ASSERT_EQ(kSignalAll, state.satisfiable_signals);
+
+  // Now send client2
+  WriteMessageWithHandles(server_mp, kHello, &client_mp2, 1);
+
+  MojoHandle port;
+  std::string message = ReadMessageWithHandles(client_mp, &port, 1);
+  EXPECT_EQ(kHello, message);
+
+  client_mp2 = port;
+  message = ReadMessageWithHandles(client_mp2, &client_mp3, 1);
+  EXPECT_EQ(kHello, message);
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp3));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp3));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp2));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp2));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+}
+
+// Verifies that a MP with pending messages to be written can be sent and the
+// pending messages aren't dropped.
+TEST_F(EmbedderTest, SendMessagePipeWithWriteQueue) {
+  MojoHandle server_mp, client_mp;
+  CreateMessagePipe(&server_mp, &client_mp);
+
+  MojoHandle server_mp2, client_mp2;
+  CreateMessagePipe(&server_mp2, &client_mp2);
+
+  static const size_t kNumMessages = 1001;
+  for (size_t i = 1; i <= kNumMessages; i++)
+    WriteMessage(client_mp2, std::string(i, 'A' + (i % 26)));
+
+  // Now send client2.
+  WriteMessageWithHandles(server_mp, "hey", &client_mp2, 1);
+  client_mp2 = MOJO_HANDLE_INVALID;
+
+  // Read client2 just so we can close it later.
+  EXPECT_EQ("hey", ReadMessageWithHandles(client_mp, &client_mp2, 1));
+  EXPECT_NE(MOJO_HANDLE_INVALID, client_mp2);
+
+  // Now verify that all the messages that were written were sent correctly.
+  for (size_t i = 1; i <= kNumMessages; i++)
+    ASSERT_EQ(std::string(i, 'A' + (i % 26)), ReadMessage(server_mp2));
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp2));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp2));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+}
+
+TEST_F(EmbedderTest, ChannelsHandlePassing) {
+  MojoHandle server_mp, client_mp;
+  CreateMessagePipe(&server_mp, &client_mp);
+  EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
+  EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
+
+  MojoHandle h0, h1;
+  CreateMessagePipe(&h0, &h1);
+
+  // Write a message to |h0| (attaching nothing).
+  const std::string kHello = "hello";
+  WriteMessage(h0, kHello);
+
+  // Write one message to |server_mp|, attaching |h1|.
+  const std::string kWorld = "world!!!";
+  WriteMessageWithHandles(server_mp, kWorld, &h1, 1);
+  h1 = MOJO_HANDLE_INVALID;
+
+  // Write another message to |h0|.
+  const std::string kFoo = "foo";
+  WriteMessage(h0, kFoo);
+
+  // Wait for |client_mp| to become readable and read a message from it.
+  EXPECT_EQ(kWorld, ReadMessageWithHandles(client_mp, &h1, 1));
+  EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+  // Wait for |h1| to become readable and read a message from it.
+  EXPECT_EQ(kHello, ReadMessage(h1));
+
+  // Wait for |h1| to become readable (again) and read its second message.
+  EXPECT_EQ(kFoo, ReadMessage(h1));
+
+  // Write a message to |h1|.
+  const std::string kBarBaz = "barbaz";
+  WriteMessage(h1, kBarBaz);
+
+  // Wait for |h0| to become readable and read a message from it.
+  EXPECT_EQ(kBarBaz, ReadMessage(h0));
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(h0));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+}
+
+TEST_F(EmbedderTest, PipeSetup) {
+  std::string child_token = GenerateRandomToken();
+  std::string pipe_token = GenerateRandomToken();
+
+  ScopedMessagePipeHandle parent_mp =
+      CreateParentMessagePipe(pipe_token, child_token);
+  ScopedMessagePipeHandle child_mp =
+      CreateChildMessagePipe(pipe_token);
+
+  const std::string kHello = "hello";
+  WriteMessage(parent_mp.get().value(), kHello);
+
+  EXPECT_EQ(kHello, ReadMessage(child_mp.get().value()));
+}
+
+TEST_F(EmbedderTest, PipeSetup_LaunchDeath) {
+  PlatformChannelPair pair;
+
+  std::string child_token = GenerateRandomToken();
+  std::string pipe_token = GenerateRandomToken();
+
+  ScopedMessagePipeHandle parent_mp =
+      CreateParentMessagePipe(pipe_token, child_token);
+  ChildProcessLaunched(base::GetCurrentProcessHandle(), pair.PassServerHandle(),
+                       child_token);
+
+  // Close the remote end, simulating child death before the child connects to
+  // the reserved port.
+  ignore_result(pair.PassClientHandle());
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(parent_mp.get().value(),
+                                     MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE,
+                                     nullptr));
+}
+
+TEST_F(EmbedderTest, PipeSetup_LaunchFailure) {
+  PlatformChannelPair pair;
+
+  std::string child_token = GenerateRandomToken();
+  std::string pipe_token = GenerateRandomToken();
+
+  ScopedMessagePipeHandle parent_mp =
+      CreateParentMessagePipe(pipe_token, child_token);
+
+  ChildProcessLaunchFailed(child_token);
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(parent_mp.get().value(),
+                                     MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE,
+                                     nullptr));
+}
+
+// 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.)
+
+#if !defined(OS_IOS)
+
+TEST_F(EmbedderTest, MultiprocessChannels) {
+  RUN_CHILD_ON_PIPE(MultiprocessChannelsClient, server_mp)
+    // 1. Write a message to |server_mp| (attaching nothing).
+    WriteMessage(server_mp, "hello");
+
+    // 2. Read a message from |server_mp|.
+    EXPECT_EQ("world!", ReadMessage(server_mp));
+
+    // 3. Create a new message pipe (endpoints |mp0| and |mp1|).
+    MojoHandle mp0, mp1;
+    CreateMessagePipe(&mp0, &mp1);
+
+    // 4. Write something to |mp0|.
+    WriteMessage(mp0, "FOO");
+
+    // 5. Write a message to |server_mp|, attaching |mp1|.
+    WriteMessageWithHandles(server_mp, "Bar", &mp1, 1);
+    mp1 = MOJO_HANDLE_INVALID;
+
+    // 6. Read a message from |mp0|, which should have |mp2| attached.
+    MojoHandle mp2 = MOJO_HANDLE_INVALID;
+    EXPECT_EQ("quux", ReadMessageWithHandles(mp0, &mp2, 1));
+
+    // 7. Read a message from |mp2|.
+    EXPECT_EQ("baz", ReadMessage(mp2));
+
+    // 8. Close |mp0|.
+    ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp0));
+
+    // 9. Tell the client to quit.
+    WriteMessage(server_mp, "quit");
+
+    // 10. Wait on |mp2| (which should eventually fail) and then close it.
+    MojoHandleSignalsState state;
+    ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE,
+                       &state));
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+
+    ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp2));
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessChannelsClient, EmbedderTest,
+                                  client_mp) {
+  // 1. Read the first message from |client_mp|.
+  EXPECT_EQ("hello", ReadMessage(client_mp));
+
+  // 2. Write a message to |client_mp| (attaching nothing).
+  WriteMessage(client_mp, "world!");
+
+  // 4. Read a message from |client_mp|, which should have |mp1| attached.
+  MojoHandle mp1;
+  EXPECT_EQ("Bar", ReadMessageWithHandles(client_mp, &mp1, 1));
+
+  // 5. Create a new message pipe (endpoints |mp2| and |mp3|).
+  MojoHandle mp2, mp3;
+  CreateMessagePipe(&mp2, &mp3);
+
+  // 6. Write a message to |mp3|.
+  WriteMessage(mp3, "baz");
+
+  // 7. Close |mp3|.
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp3));
+
+  // 8. Write a message to |mp1|, attaching |mp2|.
+  WriteMessageWithHandles(mp1, "quux", &mp2, 1);
+  mp2 = MOJO_HANDLE_INVALID;
+
+  // 9. Read a message from |mp1|.
+  EXPECT_EQ("FOO", ReadMessage(mp1));
+
+  EXPECT_EQ("quit", ReadMessage(client_mp));
+
+  // 10. Wait on |mp1| (which should eventually fail) and then close it.
+  MojoHandleSignalsState state;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
+                      MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp1));
+}
+
+TEST_F(EmbedderTest, MultiprocessBaseSharedMemory) {
+  RUN_CHILD_ON_PIPE(MultiprocessSharedMemoryClient, server_mp)
+    // 1. Create a base::SharedMemory object and create a mojo shared buffer
+    // from it.
+    base::SharedMemoryCreateOptions options;
+    options.size = 123;
+    base::SharedMemory shared_memory;
+    ASSERT_TRUE(shared_memory.Create(options));
+    base::SharedMemoryHandle shm_handle = base::SharedMemory::DuplicateHandle(
+        shared_memory.handle());
+    MojoHandle sb1;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              CreateSharedBufferWrapper(shm_handle, 123, false, &sb1));
+
+    // 2. Map |sb1| and write something into it.
+    char* buffer = nullptr;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0));
+    ASSERT_TRUE(buffer);
+    memcpy(buffer, kHelloWorld, sizeof(kHelloWorld));
+
+    // 3. Duplicate |sb1| into |sb2| and pass to |server_mp|.
+    MojoHandle sb2 = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(sb1, 0, &sb2));
+    EXPECT_NE(MOJO_HANDLE_INVALID, sb2);
+    WriteMessageWithHandles(server_mp, "hello", &sb2, 1);
+
+    // 4. Read a message from |server_mp|.
+    EXPECT_EQ("bye", ReadMessage(server_mp));
+
+    // 5. Expect that the contents of the shared buffer have changed.
+    EXPECT_EQ(kByeWorld, std::string(buffer));
+
+    // 6. Map the original base::SharedMemory and expect it contains the
+    // expected value.
+    ASSERT_TRUE(shared_memory.Map(123));
+    EXPECT_EQ(kByeWorld,
+              std::string(static_cast<char*>(shared_memory.memory())));
+
+    ASSERT_EQ(MOJO_RESULT_OK, MojoClose(sb1));
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessSharedMemoryClient, EmbedderTest,
+                                  client_mp) {
+  // 1. Read the first message from |client_mp|, which should have |sb1| which
+  // should be a shared buffer handle.
+  MojoHandle sb1;
+  EXPECT_EQ("hello", ReadMessageWithHandles(client_mp, &sb1, 1));
+
+  // 2. Map |sb1|.
+  char* buffer = nullptr;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0));
+  ASSERT_TRUE(buffer);
+
+  // 3. Ensure |buffer| contains the values we expect.
+  EXPECT_EQ(kHelloWorld, std::string(buffer));
+
+  // 4. Write into |buffer| and send a message back.
+  memcpy(buffer, kByeWorld, sizeof(kByeWorld));
+  WriteMessage(client_mp, "bye");
+
+  // 5. Extract the shared memory handle and ensure we can map it and read the
+  // contents.
+  base::SharedMemoryHandle shm_handle;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            PassSharedMemoryHandle(sb1, &shm_handle, nullptr, nullptr));
+  base::SharedMemory shared_memory(shm_handle, false);
+  ASSERT_TRUE(shared_memory.Map(123));
+  EXPECT_NE(buffer, shared_memory.memory());
+  EXPECT_EQ(kByeWorld, std::string(static_cast<char*>(shared_memory.memory())));
+
+  // 6. Close |sb1|. Should fail because |PassSharedMemoryHandle()| should have
+  // closed the handle.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(sb1));
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+TEST_F(EmbedderTest, MultiprocessMachSharedMemory) {
+  RUN_CHILD_ON_PIPE(MultiprocessSharedMemoryClient, server_mp)
+    // 1. Create a Mach base::SharedMemory object and create a mojo shared
+    // buffer from it.
+    base::SharedMemoryCreateOptions options;
+    options.size = 123;
+    base::SharedMemory shared_memory;
+    ASSERT_TRUE(shared_memory.Create(options));
+    base::SharedMemoryHandle shm_handle = base::SharedMemory::DuplicateHandle(
+        shared_memory.handle());
+    MojoHandle sb1;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              CreateSharedBufferWrapper(shm_handle, 123, false, &sb1));
+
+    // 2. Map |sb1| and write something into it.
+    char* buffer = nullptr;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0));
+    ASSERT_TRUE(buffer);
+    memcpy(buffer, kHelloWorld, sizeof(kHelloWorld));
+
+    // 3. Duplicate |sb1| into |sb2| and pass to |server_mp|.
+    MojoHandle sb2 = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(sb1, 0, &sb2));
+    EXPECT_NE(MOJO_HANDLE_INVALID, sb2);
+    WriteMessageWithHandles(server_mp, "hello", &sb2, 1);
+
+    // 4. Read a message from |server_mp|.
+    EXPECT_EQ("bye", ReadMessage(server_mp));
+
+    // 5. Expect that the contents of the shared buffer have changed.
+    EXPECT_EQ(kByeWorld, std::string(buffer));
+
+    // 6. Map the original base::SharedMemory and expect it contains the
+    // expected value.
+    ASSERT_TRUE(shared_memory.Map(123));
+    EXPECT_EQ(kByeWorld,
+              std::string(static_cast<char*>(shared_memory.memory())));
+
+    ASSERT_EQ(MOJO_RESULT_OK, MojoClose(sb1));
+  END_CHILD()
+}
+
+enum class HandleType {
+  POSIX,
+  MACH,
+  MACH_NULL,
+};
+
+const HandleType kTestHandleTypes[] = {
+  HandleType::MACH,
+  HandleType::MACH_NULL,
+  HandleType::POSIX,
+  HandleType::POSIX,
+  HandleType::MACH,
+};
+
+// Test that we can mix file descriptors and mach port handles.
+TEST_F(EmbedderTest, MultiprocessMixMachAndFds) {
+  const size_t kShmSize = 1234;
+  RUN_CHILD_ON_PIPE(MultiprocessMixMachAndFdsClient, server_mp)
+    // 1. Create fds or Mach objects and mojo handles from them.
+    MojoHandle platform_handles[arraysize(kTestHandleTypes)];
+    for (size_t i = 0; i < arraysize(kTestHandleTypes); i++) {
+      const auto type = kTestHandleTypes[i];
+      ScopedPlatformHandle scoped_handle;
+      if (type == HandleType::POSIX) {
+        // The easiest source of fds is opening /dev/null.
+        base::File file(base::FilePath("/dev/null"),
+                        base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+        ASSERT_TRUE(file.IsValid());
+        scoped_handle.reset(PlatformHandle(file.TakePlatformFile()));
+        EXPECT_EQ(PlatformHandle::Type::POSIX, scoped_handle.get().type);
+      } else if (type == HandleType::MACH_NULL) {
+        scoped_handle.reset(PlatformHandle(
+            static_cast<mach_port_t>(MACH_PORT_NULL)));
+        EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type);
+      } else {
+        base::SharedMemoryCreateOptions options;
+        options.size = kShmSize;
+        base::SharedMemory shared_memory;
+        ASSERT_TRUE(shared_memory.Create(options));
+        base::SharedMemoryHandle shm_handle =
+            base::SharedMemory::DuplicateHandle(shared_memory.handle());
+        scoped_handle.reset(PlatformHandle(shm_handle.GetMemoryObject()));
+        EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type);
+      }
+      ASSERT_EQ(MOJO_RESULT_OK, CreatePlatformHandleWrapper(
+          std::move(scoped_handle), platform_handles + i));
+    }
+
+    // 2. Send all the handles to the child.
+    WriteMessageWithHandles(server_mp, "hello", platform_handles,
+                            arraysize(kTestHandleTypes));
+
+    // 3. Read a message from |server_mp|.
+    EXPECT_EQ("bye", ReadMessage(server_mp));
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessMixMachAndFdsClient, EmbedderTest,
+                                  client_mp) {
+  const int kNumHandles = arraysize(kTestHandleTypes);
+  MojoHandle platform_handles[kNumHandles];
+
+  // 1. Read from |client_mp|, which should have a message containing
+  // |kNumHandles| handles.
+  EXPECT_EQ("hello",
+            ReadMessageWithHandles(client_mp, platform_handles, kNumHandles));
+
+  // 2. Extract each handle, and verify the type.
+  for (int i = 0; i < kNumHandles; i++) {
+    const auto type = kTestHandleTypes[i];
+    ScopedPlatformHandle scoped_handle;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              PassWrappedPlatformHandle(platform_handles[i], &scoped_handle));
+    if (type == HandleType::POSIX) {
+      EXPECT_NE(0, scoped_handle.get().handle);
+      EXPECT_EQ(PlatformHandle::Type::POSIX, scoped_handle.get().type);
+    } else if (type == HandleType::MACH_NULL) {
+      EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL),
+                scoped_handle.get().port);
+      EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type);
+    } else {
+      EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL),
+                scoped_handle.get().port);
+      EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type);
+    }
+  }
+
+  // 3. Say bye!
+  WriteMessage(client_mp, "bye");
+}
+
+#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
+
+// TODO(vtl): Test immediate write & close.
+// TODO(vtl): Test broken-connection cases.
+
+#endif  // !defined(OS_IOS)
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc
new file mode 100644
index 0000000..f09c5e1
--- /dev/null
+++ b/mojo/edk/embedder/entrypoints.cc
@@ -0,0 +1,308 @@
+// Copyright 2014 The Chromium Authors. All 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/entrypoints.h"
+
+#include <stdint.h>
+
+#include "mojo/edk/embedder/embedder_internal.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"
+#include "mojo/public/c/system/platform_handle.h"
+#include "mojo/public/c/system/wait_set.h"
+
+using mojo::edk::internal::g_core;
+
+// Definitions of the system functions.
+extern "C" {
+
+MojoTimeTicks MojoGetTimeTicksNowImpl() {
+  return g_core->GetTimeTicksNow();
+}
+
+MojoResult MojoCloseImpl(MojoHandle handle) {
+  return g_core->Close(handle);
+}
+
+MojoResult MojoWaitImpl(MojoHandle handle,
+                        MojoHandleSignals signals,
+                        MojoDeadline deadline,
+                        MojoHandleSignalsState* signals_state) {
+  return g_core->Wait(handle, signals, deadline, signals_state);
+}
+
+MojoResult MojoWaitManyImpl(const MojoHandle* handles,
+                            const MojoHandleSignals* signals,
+                            uint32_t num_handles,
+                            MojoDeadline deadline,
+                            uint32_t* result_index,
+                            MojoHandleSignalsState* signals_states) {
+  return g_core->WaitMany(handles, signals, num_handles, deadline, result_index,
+                          signals_states);
+}
+
+MojoResult MojoWatchImpl(MojoHandle handle,
+                         MojoHandleSignals signals,
+                         MojoWatchCallback callback,
+                         uintptr_t context) {
+  return g_core->Watch(handle, signals, callback, context);
+}
+
+MojoResult MojoCancelWatchImpl(MojoHandle handle, uintptr_t context) {
+  return g_core->CancelWatch(handle, context);
+}
+
+MojoResult MojoAllocMessageImpl(uint32_t num_bytes,
+                                const MojoHandle* handles,
+                                uint32_t num_handles,
+                                MojoAllocMessageFlags flags,
+                                MojoMessageHandle* message) {
+  return g_core->AllocMessage(num_bytes, handles, num_handles, flags, message);
+}
+
+MojoResult MojoFreeMessageImpl(MojoMessageHandle message) {
+  return g_core->FreeMessage(message);
+}
+
+MojoResult MojoGetMessageBufferImpl(MojoMessageHandle message, void** buffer) {
+  return g_core->GetMessageBuffer(message, buffer);
+}
+
+MojoResult MojoCreateWaitSetImpl(MojoHandle* wait_set_handle) {
+  return g_core->CreateWaitSet(wait_set_handle);
+}
+
+MojoResult MojoAddHandleImpl(MojoHandle wait_set_handle,
+                             MojoHandle handle,
+                             MojoHandleSignals signals) {
+  return g_core->AddHandle(wait_set_handle, handle, signals);
+}
+
+MojoResult MojoRemoveHandleImpl(MojoHandle wait_set_handle, MojoHandle handle) {
+  return g_core->RemoveHandle(wait_set_handle, handle);
+}
+
+MojoResult MojoGetReadyHandlesImpl(
+    MojoHandle wait_set_handle,
+    uint32_t* count,
+    MojoHandle* handles,
+    MojoResult* results,
+    struct MojoHandleSignalsState* signals_states) {
+  return g_core->GetReadyHandles(wait_set_handle, count, handles, results,
+                                 signals_states);
+}
+
+MojoResult MojoCreateMessagePipeImpl(
+    const MojoCreateMessagePipeOptions* options,
+    MojoHandle* message_pipe_handle0,
+    MojoHandle* message_pipe_handle1) {
+  return g_core->CreateMessagePipe(options, message_pipe_handle0,
+                                   message_pipe_handle1);
+}
+
+MojoResult MojoWriteMessageImpl(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, bytes, num_bytes, handles,
+                              num_handles, flags);
+}
+
+MojoResult MojoWriteMessageNewImpl(MojoHandle message_pipe_handle,
+                                   MojoMessageHandle message,
+                                   MojoWriteMessageFlags flags) {
+  return g_core->WriteMessageNew(message_pipe_handle, message, flags);
+}
+
+MojoResult MojoReadMessageImpl(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, bytes, num_bytes, handles, num_handles, flags);
+}
+
+MojoResult MojoReadMessageNewImpl(MojoHandle message_pipe_handle,
+                                  MojoMessageHandle* message,
+                                  uint32_t* num_bytes,
+                                  MojoHandle* handles,
+                                  uint32_t* num_handles,
+                                  MojoReadMessageFlags flags) {
+  return g_core->ReadMessageNew(
+      message_pipe_handle, message, num_bytes, handles, num_handles, flags);
+}
+
+MojoResult MojoFuseMessagePipesImpl(MojoHandle handle0, MojoHandle handle1) {
+  return g_core->FuseMessagePipes(handle0, handle1);
+}
+
+MojoResult MojoCreateDataPipeImpl(const MojoCreateDataPipeOptions* options,
+                                  MojoHandle* data_pipe_producer_handle,
+                                  MojoHandle* data_pipe_consumer_handle) {
+  return g_core->CreateDataPipe(options, data_pipe_producer_handle,
+                                data_pipe_consumer_handle);
+}
+
+MojoResult MojoWriteDataImpl(MojoHandle data_pipe_producer_handle,
+                             const void* elements,
+                             uint32_t* num_elements,
+                             MojoWriteDataFlags flags) {
+  return g_core->WriteData(data_pipe_producer_handle, elements, num_elements,
+                           flags);
+}
+
+MojoResult MojoBeginWriteDataImpl(MojoHandle data_pipe_producer_handle,
+                                  void** buffer,
+                                  uint32_t* buffer_num_elements,
+                                  MojoWriteDataFlags flags) {
+  return g_core->BeginWriteData(data_pipe_producer_handle, buffer,
+                                buffer_num_elements, flags);
+}
+
+MojoResult MojoEndWriteDataImpl(MojoHandle data_pipe_producer_handle,
+                                uint32_t num_elements_written) {
+  return g_core->EndWriteData(data_pipe_producer_handle, num_elements_written);
+}
+
+MojoResult MojoReadDataImpl(MojoHandle data_pipe_consumer_handle,
+                            void* elements,
+                            uint32_t* num_elements,
+                            MojoReadDataFlags flags) {
+  return g_core->ReadData(data_pipe_consumer_handle, elements, num_elements,
+                          flags);
+}
+
+MojoResult MojoBeginReadDataImpl(MojoHandle data_pipe_consumer_handle,
+                                 const void** buffer,
+                                 uint32_t* buffer_num_elements,
+                                 MojoReadDataFlags flags) {
+  return g_core->BeginReadData(data_pipe_consumer_handle, buffer,
+                               buffer_num_elements, flags);
+}
+
+MojoResult MojoEndReadDataImpl(MojoHandle data_pipe_consumer_handle,
+                               uint32_t num_elements_read) {
+  return g_core->EndReadData(data_pipe_consumer_handle, num_elements_read);
+}
+
+MojoResult MojoCreateSharedBufferImpl(
+    const struct MojoCreateSharedBufferOptions* options,
+    uint64_t num_bytes,
+    MojoHandle* shared_buffer_handle) {
+  return g_core->CreateSharedBuffer(options, num_bytes, shared_buffer_handle);
+}
+
+MojoResult MojoDuplicateBufferHandleImpl(
+    MojoHandle buffer_handle,
+    const struct MojoDuplicateBufferHandleOptions* options,
+    MojoHandle* new_buffer_handle) {
+  return g_core->DuplicateBufferHandle(buffer_handle, options,
+                                       new_buffer_handle);
+}
+
+MojoResult MojoMapBufferImpl(MojoHandle buffer_handle,
+                             uint64_t offset,
+                             uint64_t num_bytes,
+                             void** buffer,
+                             MojoMapBufferFlags flags) {
+  return g_core->MapBuffer(buffer_handle, offset, num_bytes, buffer, flags);
+}
+
+MojoResult MojoUnmapBufferImpl(void* buffer) {
+  return g_core->UnmapBuffer(buffer);
+}
+
+MojoResult MojoWrapPlatformHandleImpl(const MojoPlatformHandle* platform_handle,
+                                      MojoHandle* mojo_handle) {
+  return g_core->WrapPlatformHandle(platform_handle, mojo_handle);
+}
+
+MojoResult MojoUnwrapPlatformHandleImpl(MojoHandle mojo_handle,
+                                        MojoPlatformHandle* platform_handle) {
+  return g_core->UnwrapPlatformHandle(mojo_handle, platform_handle);
+}
+
+MojoResult MojoWrapPlatformSharedBufferHandleImpl(
+    const MojoPlatformHandle* platform_handle,
+    size_t num_bytes,
+    MojoPlatformSharedBufferHandleFlags flags,
+    MojoHandle* mojo_handle) {
+  return g_core->WrapPlatformSharedBufferHandle(platform_handle, num_bytes,
+                                                flags, mojo_handle);
+}
+
+MojoResult MojoUnwrapPlatformSharedBufferHandleImpl(
+    MojoHandle mojo_handle,
+    MojoPlatformHandle* platform_handle,
+    size_t* num_bytes,
+    MojoPlatformSharedBufferHandleFlags* flags) {
+  return g_core->UnwrapPlatformSharedBufferHandle(mojo_handle, platform_handle,
+                                                  num_bytes, flags);
+}
+
+MojoResult MojoNotifyBadMessageImpl(MojoMessageHandle message,
+                                    const char* error,
+                                    size_t error_num_bytes) {
+  return g_core->NotifyBadMessage(message, error, error_num_bytes);
+}
+
+MojoResult MojoGetPropertyImpl(MojoPropertyType type, void* value) {
+  return g_core->GetProperty(type, value);
+}
+
+}  // extern "C"
+
+namespace mojo {
+namespace edk {
+
+MojoSystemThunks MakeSystemThunks() {
+  MojoSystemThunks system_thunks = {sizeof(MojoSystemThunks),
+                                    MojoGetTimeTicksNowImpl,
+                                    MojoCloseImpl,
+                                    MojoWaitImpl,
+                                    MojoWaitManyImpl,
+                                    MojoCreateMessagePipeImpl,
+                                    MojoWriteMessageImpl,
+                                    MojoReadMessageImpl,
+                                    MojoCreateDataPipeImpl,
+                                    MojoWriteDataImpl,
+                                    MojoBeginWriteDataImpl,
+                                    MojoEndWriteDataImpl,
+                                    MojoReadDataImpl,
+                                    MojoBeginReadDataImpl,
+                                    MojoEndReadDataImpl,
+                                    MojoCreateSharedBufferImpl,
+                                    MojoDuplicateBufferHandleImpl,
+                                    MojoMapBufferImpl,
+                                    MojoUnmapBufferImpl,
+                                    MojoCreateWaitSetImpl,
+                                    MojoAddHandleImpl,
+                                    MojoRemoveHandleImpl,
+                                    MojoGetReadyHandlesImpl,
+                                    MojoWatchImpl,
+                                    MojoCancelWatchImpl,
+                                    MojoFuseMessagePipesImpl,
+                                    MojoWriteMessageNewImpl,
+                                    MojoReadMessageNewImpl,
+                                    MojoAllocMessageImpl,
+                                    MojoFreeMessageImpl,
+                                    MojoGetMessageBufferImpl,
+                                    MojoWrapPlatformHandleImpl,
+                                    MojoUnwrapPlatformHandleImpl,
+                                    MojoWrapPlatformSharedBufferHandleImpl,
+                                    MojoUnwrapPlatformSharedBufferHandleImpl,
+                                    MojoNotifyBadMessageImpl,
+                                    MojoGetPropertyImpl};
+  return system_thunks;
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/embedder/entrypoints.h b/mojo/edk/embedder/entrypoints.h
new file mode 100644
index 0000000..8e448c1
--- /dev/null
+++ b/mojo/edk/embedder/entrypoints.h
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All 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_ENTRYPOINTS_H_
+#define MOJO_EDK_EMBEDDER_ENTRYPOINTS_H_
+
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/thunks.h"
+
+namespace mojo {
+namespace edk {
+
+// Creates a MojoSystemThunks struct populated with the EDK's implementation of
+// each function. This may be used by embedders to populate thunks for
+// application loading.
+MOJO_SYSTEM_IMPL_EXPORT MojoSystemThunks MakeSystemThunks();
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_ENTRYPOINTS_H_
diff --git a/mojo/edk/embedder/platform_channel_pair.cc b/mojo/edk/embedder/platform_channel_pair.cc
new file mode 100644
index 0000000..ee1905a
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair.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/edk/embedder/platform_channel_pair.h"
+
+#include <utility>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace edk {
+
+const char PlatformChannelPair::kMojoPlatformChannelHandleSwitch[] =
+    "mojo-platform-channel-handle";
+
+PlatformChannelPair::~PlatformChannelPair() {
+}
+
+ScopedPlatformHandle PlatformChannelPair::PassServerHandle() {
+  return std::move(server_handle_);
+}
+
+ScopedPlatformHandle PlatformChannelPair::PassClientHandle() {
+  return std::move(client_handle_);
+}
+
+void PlatformChannelPair::ChildProcessLaunched() {
+  DCHECK(client_handle_.is_valid());
+  client_handle_.reset();
+}
+
+}  // namespace edk
+}  // 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..f80de89
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair.h
@@ -0,0 +1,105 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
+
+#include <memory>
+
+#include "base/macros.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 edk {
+
+// 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)
+using HandlePassingInformation = base::HandlesToInheritVector;
+#elif defined(OS_POSIX)
+using HandlePassingInformation = base::FileHandleMappingVector;
+#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:
+  static const char kMojoPlatformChannelHandleSwitch[];
+
+  // If |client_is_blocking| is true, then the client handle only supports
+  // blocking reads and writes. The default is nonblocking.
+  PlatformChannelPair(bool client_is_blocking = false);
+  ~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);
+
+  // Like above, but gets the handle from the passed in string.
+  static ScopedPlatformHandle PassClientHandleFromParentProcessFromString(
+      const std::string& value);
+
+  // 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;
+
+  // Like above, but returns a string instead of changing the command line.
+  std::string PrepareToPassClientHandleToChildProcessAsString(
+      HandlePassingInformation* handle_passing_info) const;
+
+  // To be called once the child process has been successfully launched, to do
+  // any cleanup necessary.
+  void ChildProcessLaunched();
+
+ private:
+  ScopedPlatformHandle server_handle_;
+  ScopedPlatformHandle client_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformChannelPair);
+};
+
+}  // namespace edk
+}  // 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..55f3ae2
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair_posix.cc
@@ -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.
+
+#include "mojo/edk/embedder/platform_channel_pair.h"
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/posix/global_descriptors.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/platform_handle.h"
+
+#if !defined(OS_NACL_SFI)
+#include <sys/socket.h>
+#else
+#include "native_client/src/public/imc_syscalls.h"
+#endif
+
+#if !defined(SO_PEEK_OFF)
+#define SO_PEEK_OFF 42
+#endif
+
+namespace mojo {
+namespace edk {
+
+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(bool client_is_blocking) {
+  // Create the Unix domain socket.
+  int fds[2];
+  // TODO(vtl): Maybe fail gracefully if |socketpair()| fails.
+
+#if defined(OS_NACL_SFI)
+  PCHECK(imc_socketpair(fds) == 0);
+#else
+  PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
+
+  // Set the ends to nonblocking.
+  PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
+  if (!client_is_blocking)
+    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)
+#endif  // defined(OS_NACL_SFI)
+
+  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);
+  return PassClientHandleFromParentProcessFromString(client_fd_string);
+}
+
+ScopedPlatformHandle
+PlatformChannelPair::PassClientHandleFromParentProcessFromString(
+    const std::string& value) {
+  int client_fd = -1;
+  if (value.empty() ||
+      !base::StringToInt(value, &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);
+
+  // 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,
+      PrepareToPassClientHandleToChildProcessAsString(handle_passing_info));
+}
+
+std::string
+PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString(
+      HandlePassingInformation* handle_passing_info) const {
+  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().handle, target_fd));
+  return base::IntToString(target_fd);
+}
+
+}  // namespace edk
+}  // 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..e05cd79
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
@@ -0,0 +1,261 @@
+// Copyright 2014 The Chromium Authors. All 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 <stddef.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <deque>
+#include <utility>
+
+#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 edk {
+namespace {
+
+void WaitReadable(PlatformHandle h) {
+  struct pollfd pfds = {};
+  pfds.fd = h.handle;
+  pfds.events = POLLIN;
+  CHECK_EQ(poll(&pfds, 1, -1), 1);
+}
+
+class PlatformChannelPairPosixTest : public testing::Test {
+ public:
+  PlatformChannelPairPosixTest() {}
+  ~PlatformChannelPairPosixTest() override {}
+
+  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_));
+  }
+
+  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();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
+
+  // Write to the client.
+  static const char kHello[] = "hello";
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+            write(client_handle.get().handle, 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().handle, buffer, sizeof(buffer)));
+  EXPECT_STREQ(kHello, buffer);
+
+  // Try reading again.
+  ssize_t result = read(server_handle.get().handle, 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();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
+
+  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();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
+
+// Reduce the number of FDs opened on OS X to avoid test flake.
+#if defined(OS_MACOSX)
+  const size_t kNumHandlesToSend = kPlatformChannelMaxNumHandles / 2;
+#else
+  const size_t kNumHandlesToSend = kPlatformChannelMaxNumHandles;
+#endif
+
+  for (size_t i = 1; i < kNumHandlesToSend; i++) {
+    // Make |i| files, with the j-th file consisting of j copies of the digit
+    // |c|.
+    const char c = '0' + (i % 10);
+    ScopedPlatformHandleVectorPtr platform_handles(new PlatformHandleVector);
+    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, c).data(), 1, j, fp.get()));
+      platform_handles->push_back(
+          test::PlatformHandleFromFILE(std::move(fp)).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->at(0),
+                                                platform_handles->size()));
+
+    WaitReadable(client_handle.get());
+
+    char buf[10000] = {};
+    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[kNumHandlesToSend];
+      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, c), 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();
+  ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
+
+  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()));
+    ScopedPlatformHandleVectorPtr platform_handles(new PlatformHandleVector);
+    platform_handles->push_back(
+        test::PlatformHandleFromFILE(std::move(fp)).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->at(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 edk
+}  // 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..f523ade
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_pair_win.cc
@@ -0,0 +1,123 @@
+// Copyright 2014 The Chromium Authors. All 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 edk {
+
+namespace {
+
+std::wstring GeneratePipeName() {
+  return base::StringPrintf(L"\\\\.\\pipe\\mojo.%u.%u.%I64u",
+                            GetCurrentProcessId(), GetCurrentThreadId(),
+                            base::RandUint64());
+}
+
+}  // namespace
+
+PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) {
+  std::wstring pipe_name = GeneratePipeName();
+
+  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.
+  DWORD kFlags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS;
+  if (!client_is_blocking)
+    kFlags |= 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);
+  return PassClientHandleFromParentProcessFromString(client_handle_string);
+}
+
+ScopedPlatformHandle
+PlatformChannelPair::PassClientHandleFromParentProcessFromString(
+    const std::string& value) {
+  int client_handle_value = 0;
+  if (value.empty() ||
+      !base::StringToInt(value, &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);
+
+  // 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,
+      PrepareToPassClientHandleToChildProcessAsString(handle_passing_info));
+}
+
+std::string
+PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString(
+    HandlePassingInformation* handle_passing_info) const {
+  DCHECK(handle_passing_info);
+  DCHECK(client_handle_.is_valid());
+
+  if (base::win::GetVersion() >= base::win::VERSION_VISTA)
+    handle_passing_info->push_back(client_handle_.get().handle);
+
+  return base::IntToString(HandleToLong(client_handle_.get().handle));
+}
+
+}  // namespace edk
+}  // 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..3171844
--- /dev/null
+++ b/mojo/edk/embedder/platform_channel_utils_posix.cc
@@ -0,0 +1,197 @@
+// Copyright 2014 The Chromium Authors. All 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 <stddef.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+
+#if !defined(OS_NACL)
+#include <sys/uio.h>
+#endif
+
+#if !defined(SO_PEEK_OFF)
+#define SO_PEEK_OFF 42
+#endif
+
+namespace mojo {
+namespace edk {
+
+// 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) || defined(OS_NACL_NONSFI)
+  // send() is not available under NaCl-nonsfi.
+  return HANDLE_EINTR(write(h.handle, bytes, num_bytes));
+#else
+  return send(h.handle, 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.handle, iov, static_cast<int>(num_iov)));
+#else
+  struct msghdr msg = {};
+  msg.msg_iov = iov;
+  msg.msg_iovlen = num_iov;
+  return HANDLE_EINTR(sendmsg(h.handle, &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].handle;
+  }
+
+  return HANDLE_EINTR(sendmsg(h.handle, &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].handle;
+  }
+
+  ssize_t result = HANDLE_EINTR(sendmsg(h.handle, &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,
+                               bool block) {
+  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.handle, &msg, block ? 0 : 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 edk
+}  // 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..8b24bd0
--- /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 <memory>
+
+#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 edk {
+
+// The maximum number of handles that can be sent "at once" using
+// |PlatformChannelSendmsgWithHandles()|. This must be less than the Linux
+// kernel's SCM_MAX_FD which is 253.
+const size_t kPlatformChannelMaxNumHandles = 128;
+
+// 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,
+                       bool block = false);
+
+}  // namespace edk
+}  // 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..b6b2cd2
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle.cc
@@ -0,0 +1,74 @@
+// 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/logging.h"
+
+namespace mojo {
+namespace edk {
+
+void PlatformHandle::CloseIfNecessary() {
+  if (!is_valid())
+    return;
+
+#if defined(OS_POSIX)
+  if (type == Type::POSIX) {
+    bool success = (close(handle) == 0);
+    DPCHECK(success);
+    handle = -1;
+  }
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  else if (type == Type::MACH) {
+    kern_return_t rv = mach_port_deallocate(mach_task_self(), port);
+    DPCHECK(rv == KERN_SUCCESS);
+    port = MACH_PORT_NULL;
+  }
+#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
+#elif defined(OS_WIN)
+  if (owning_process != base::GetCurrentProcessHandle()) {
+    // This handle may have been duplicated to a new target process but not yet
+    // sent there. In this case CloseHandle should NOT be called. From MSDN
+    // documentation for DuplicateHandle[1]:
+    //
+    //    Normally the target process closes a duplicated handle when that
+    //    process is finished using the handle. To close a duplicated handle
+    //    from the source process, call DuplicateHandle with the following
+    //    parameters:
+    //
+    //    * Set hSourceProcessHandle to the target process from the
+    //      call that created the handle.
+    //    * Set hSourceHandle to the duplicated handle to close.
+    //    * Set lpTargetHandle to NULL.
+    //    * Set dwOptions to DUPLICATE_CLOSE_SOURCE.
+    //
+    // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251
+    //
+    // NOTE: It's possible for this operation to fail if the owning process
+    // was terminated or is in the process of being terminated. Either way,
+    // there is nothing we can reasonably do about failure, so we ignore it.
+    DuplicateHandle(owning_process, handle, NULL, &handle, 0, FALSE,
+                    DUPLICATE_CLOSE_SOURCE);
+    return;
+  }
+
+  bool success = !!CloseHandle(handle);
+  DPCHECK(success);
+  handle = INVALID_HANDLE_VALUE;
+#else
+#error "Platform not yet supported."
+#endif
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_handle.h b/mojo/edk/embedder/platform_handle.h
new file mode 100644
index 0000000..4f76009
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle.h
@@ -0,0 +1,87 @@
+// 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>
+
+#include "base/process/process_handle.h"
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#include <mach/mach.h>
+#endif
+
+namespace mojo {
+namespace edk {
+
+#if defined(OS_POSIX)
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle {
+  PlatformHandle() {}
+  explicit PlatformHandle(int handle) : handle(handle) {}
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  explicit PlatformHandle(mach_port_t port)
+      : type(Type::MACH), port(port) {}
+#endif
+
+  void CloseIfNecessary();
+
+  bool is_valid() const {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+    if (type == Type::MACH || type == Type::MACH_NAME)
+      return port != MACH_PORT_NULL;
+#endif
+    return handle != -1;
+  }
+
+  enum class Type {
+    POSIX,
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+    MACH,
+    // MACH_NAME isn't a real Mach port. But rather the "name" of one that can
+    // be resolved to a real port later. This distinction is needed so that the
+    // "port" doesn't try to be closed if CloseIfNecessary() is called. Having
+    // this also allows us to do checks in other places.
+    MACH_NAME,
+#endif
+  };
+  Type type = Type::POSIX;
+
+  int handle = -1;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  mach_port_t port = MACH_PORT_NULL;
+#endif
+};
+#elif defined(OS_WIN)
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle {
+  PlatformHandle() : PlatformHandle(INVALID_HANDLE_VALUE) {}
+  explicit PlatformHandle(HANDLE handle)
+      : handle(handle), owning_process(base::GetCurrentProcessHandle()) {}
+
+  void CloseIfNecessary();
+
+  bool is_valid() const { return handle != INVALID_HANDLE_VALUE; }
+
+  HANDLE handle;
+
+  // A Windows HANDLE may be duplicated to another process but not yet sent to
+  // that process. This tracks the handle's owning process.
+  base::ProcessHandle owning_process;
+
+  // A Windows HANDLE may be an unconnected named pipe. In this case, we need to
+  // wait for a connection before communicating on the pipe.
+  bool needs_connection = false;
+};
+#else
+#error "Platform not yet supported."
+#endif
+
+}  // namespace edk
+}  // 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..fa683e4
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle_utils.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_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 edk {
+
+// 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 edk
+}  // 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..fc2a0db
--- /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 edk {
+
+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.handle)));
+}
+
+}  // namespace edk
+}  // 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..32ed49a
--- /dev/null
+++ b/mojo/edk/embedder/platform_handle_utils_win.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/edk/embedder/platform_handle_utils.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace edk {
+
+ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) {
+  DCHECK(platform_handle.is_valid());
+
+  HANDLE new_handle;
+  CHECK_NE(platform_handle.handle, INVALID_HANDLE_VALUE);
+  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 edk
+}  // 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..9892b23
--- /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 <memory>
+#include <vector>
+
+#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 edk {
+
+using PlatformHandleVector = std::vector<PlatformHandle>;
+
+// 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;
+  }
+};
+
+using ScopedPlatformHandleVectorPtr =
+    std::unique_ptr<PlatformHandleVector, PlatformHandleVectorDeleter>;
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_
diff --git a/mojo/edk/embedder/platform_shared_buffer.cc b/mojo/edk/embedder/platform_shared_buffer.cc
new file mode 100644
index 0000000..53d8edc
--- /dev/null
+++ b/mojo/edk/embedder/platform_shared_buffer.cc
@@ -0,0 +1,318 @@
+// Copyright 2014 The Chromium Authors. All 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_shared_buffer.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/shared_memory.h"
+#include "base/process/process_handle.h"
+#include "base/sys_info.h"
+#include "mojo/edk/embedder/platform_handle_utils.h"
+
+#if defined(OS_NACL)
+// For getpagesize() on NaCl.
+#include <unistd.h>
+#endif
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+// Takes ownership of |memory_handle|.
+ScopedPlatformHandle SharedMemoryToPlatformHandle(
+    base::SharedMemoryHandle memory_handle) {
+#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
+  return ScopedPlatformHandle(PlatformHandle(memory_handle.fd));
+#elif defined(OS_WIN)
+  return ScopedPlatformHandle(PlatformHandle(memory_handle.GetHandle()));
+#else
+  return ScopedPlatformHandle(PlatformHandle(memory_handle.GetMemoryObject()));
+#endif
+}
+
+}  // namespace
+
+// static
+PlatformSharedBuffer* PlatformSharedBuffer::Create(size_t num_bytes) {
+  DCHECK_GT(num_bytes, 0u);
+
+  PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, false);
+  if (!rv->Init()) {
+    // We can't just delete it directly, due to the "in destructor" (debug)
+    // check.
+    scoped_refptr<PlatformSharedBuffer> deleter(rv);
+    return nullptr;
+  }
+
+  return rv;
+}
+
+// static
+PlatformSharedBuffer* PlatformSharedBuffer::CreateFromPlatformHandle(
+    size_t num_bytes,
+    bool read_only,
+    ScopedPlatformHandle platform_handle) {
+  DCHECK_GT(num_bytes, 0u);
+
+  PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, read_only);
+  if (!rv->InitFromPlatformHandle(std::move(platform_handle))) {
+    // We can't just delete it directly, due to the "in destructor" (debug)
+    // check.
+    scoped_refptr<PlatformSharedBuffer> deleter(rv);
+    return nullptr;
+  }
+
+  return rv;
+}
+
+// static
+PlatformSharedBuffer* PlatformSharedBuffer::CreateFromPlatformHandlePair(
+    size_t num_bytes,
+    ScopedPlatformHandle rw_platform_handle,
+    ScopedPlatformHandle ro_platform_handle) {
+  DCHECK_GT(num_bytes, 0u);
+  DCHECK(rw_platform_handle.is_valid());
+  DCHECK(ro_platform_handle.is_valid());
+
+  PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, false);
+  if (!rv->InitFromPlatformHandlePair(std::move(rw_platform_handle),
+                                      std::move(ro_platform_handle))) {
+    // We can't just delete it directly, due to the "in destructor" (debug)
+    // check.
+    scoped_refptr<PlatformSharedBuffer> deleter(rv);
+    return nullptr;
+  }
+
+  return rv;
+}
+
+// static
+PlatformSharedBuffer* PlatformSharedBuffer::CreateFromSharedMemoryHandle(
+    size_t num_bytes,
+    bool read_only,
+    base::SharedMemoryHandle handle) {
+  DCHECK_GT(num_bytes, 0u);
+
+  PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, read_only);
+  rv->InitFromSharedMemoryHandle(handle);
+
+  return rv;
+}
+
+size_t PlatformSharedBuffer::GetNumBytes() const {
+  return num_bytes_;
+}
+
+bool PlatformSharedBuffer::IsReadOnly() const {
+  return read_only_;
+}
+
+std::unique_ptr<PlatformSharedBufferMapping> PlatformSharedBuffer::Map(
+    size_t offset,
+    size_t length) {
+  if (!IsValidMap(offset, length))
+    return nullptr;
+
+  return MapNoCheck(offset, length);
+}
+
+bool PlatformSharedBuffer::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;
+}
+
+std::unique_ptr<PlatformSharedBufferMapping> PlatformSharedBuffer::MapNoCheck(
+    size_t offset,
+    size_t length) {
+  DCHECK(IsValidMap(offset, length));
+  DCHECK(shared_memory_);
+  base::SharedMemoryHandle handle;
+  {
+    base::AutoLock locker(lock_);
+    handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle());
+  }
+  if (handle == base::SharedMemory::NULLHandle())
+    return nullptr;
+
+  std::unique_ptr<PlatformSharedBufferMapping> mapping(
+      new PlatformSharedBufferMapping(handle, read_only_, offset, length));
+  if (mapping->Map())
+    return base::WrapUnique(mapping.release());
+
+  return nullptr;
+}
+
+ScopedPlatformHandle PlatformSharedBuffer::DuplicatePlatformHandle() {
+  DCHECK(shared_memory_);
+  base::SharedMemoryHandle handle;
+  {
+    base::AutoLock locker(lock_);
+    handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle());
+  }
+  if (handle == base::SharedMemory::NULLHandle())
+    return ScopedPlatformHandle();
+
+  return SharedMemoryToPlatformHandle(handle);
+}
+
+ScopedPlatformHandle PlatformSharedBuffer::PassPlatformHandle() {
+  DCHECK(HasOneRef());
+
+  // The only way to pass a handle from base::SharedMemory is to duplicate it
+  // and close the original.
+  ScopedPlatformHandle handle = DuplicatePlatformHandle();
+
+  base::AutoLock locker(lock_);
+  shared_memory_->Close();
+  return handle;
+}
+
+base::SharedMemoryHandle PlatformSharedBuffer::DuplicateSharedMemoryHandle() {
+  DCHECK(shared_memory_);
+
+  base::AutoLock locker(lock_);
+  return base::SharedMemory::DuplicateHandle(shared_memory_->handle());
+}
+
+PlatformSharedBuffer* PlatformSharedBuffer::CreateReadOnlyDuplicate() {
+  DCHECK(shared_memory_);
+
+  if (ro_shared_memory_) {
+    base::AutoLock locker(lock_);
+    base::SharedMemoryHandle handle;
+    handle = base::SharedMemory::DuplicateHandle(ro_shared_memory_->handle());
+    if (handle == base::SharedMemory::NULLHandle())
+      return nullptr;
+    return CreateFromSharedMemoryHandle(num_bytes_, true, handle);
+  }
+
+  base::SharedMemoryHandle handle;
+  bool success;
+  {
+    base::AutoLock locker(lock_);
+    success = shared_memory_->ShareReadOnlyToProcess(
+        base::GetCurrentProcessHandle(), &handle);
+  }
+  if (!success || handle == base::SharedMemory::NULLHandle())
+      return nullptr;
+
+  return CreateFromSharedMemoryHandle(num_bytes_, true, handle);
+}
+
+PlatformSharedBuffer::PlatformSharedBuffer(size_t num_bytes, bool read_only)
+    : num_bytes_(num_bytes), read_only_(read_only) {}
+
+PlatformSharedBuffer::~PlatformSharedBuffer() {}
+
+bool PlatformSharedBuffer::Init() {
+  DCHECK(!shared_memory_);
+  DCHECK(!read_only_);
+
+  base::SharedMemoryCreateOptions options;
+  options.size = num_bytes_;
+  // By default, we can share as read-only.
+  options.share_read_only = true;
+
+  shared_memory_.reset(new base::SharedMemory);
+  return shared_memory_->Create(options);
+}
+
+bool PlatformSharedBuffer::InitFromPlatformHandle(
+    ScopedPlatformHandle platform_handle) {
+  DCHECK(!shared_memory_);
+
+#if defined(OS_WIN)
+  base::SharedMemoryHandle handle(platform_handle.release().handle,
+                                  base::GetCurrentProcId());
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  base::SharedMemoryHandle handle;
+  handle = base::SharedMemoryHandle(platform_handle.release().port, num_bytes_,
+                                    base::GetCurrentProcId());
+#else
+  base::SharedMemoryHandle handle(platform_handle.release().handle, false);
+#endif
+
+  shared_memory_.reset(new base::SharedMemory(handle, read_only_));
+  return true;
+}
+
+bool PlatformSharedBuffer::InitFromPlatformHandlePair(
+    ScopedPlatformHandle rw_platform_handle,
+    ScopedPlatformHandle ro_platform_handle) {
+#if defined(OS_WIN) || defined(OS_MACOSX)
+  NOTREACHED();
+  return false;
+#else
+  DCHECK(!shared_memory_);
+
+  base::SharedMemoryHandle handle(rw_platform_handle.release().handle, false);
+  shared_memory_.reset(new base::SharedMemory(handle, false));
+
+  base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle,
+                                     false);
+  ro_shared_memory_.reset(new base::SharedMemory(ro_handle, true));
+
+  return true;
+#endif
+}
+
+void PlatformSharedBuffer::InitFromSharedMemoryHandle(
+    base::SharedMemoryHandle handle) {
+  DCHECK(!shared_memory_);
+
+  shared_memory_.reset(new base::SharedMemory(handle, read_only_));
+}
+
+PlatformSharedBufferMapping::~PlatformSharedBufferMapping() {
+  Unmap();
+}
+
+void* PlatformSharedBufferMapping::GetBase() const {
+  return base_;
+}
+
+size_t PlatformSharedBufferMapping::GetLength() const {
+  return length_;
+}
+
+bool PlatformSharedBufferMapping::Map() {
+  // Mojo shared buffers can be mapped at any offset. However,
+  // base::SharedMemory must be mapped at a page boundary. So calculate what the
+  // nearest whole page offset is, and build a mapping that's offset from that.
+#if defined(OS_NACL)
+  // base::SysInfo isn't available under NaCl.
+  size_t page_size = getpagesize();
+#else
+  size_t page_size = base::SysInfo::VMAllocationGranularity();
+#endif
+  size_t offset_rounding = offset_ % page_size;
+  size_t real_offset = offset_ - offset_rounding;
+  size_t real_length = length_ + offset_rounding;
+
+  if (!shared_memory_.MapAt(static_cast<off_t>(real_offset), real_length))
+    return false;
+
+  base_ = static_cast<char*>(shared_memory_.memory()) + offset_rounding;
+  return true;
+}
+
+void PlatformSharedBufferMapping::Unmap() {
+  shared_memory_.Unmap();
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/embedder/platform_shared_buffer.h b/mojo/edk/embedder/platform_shared_buffer.h
new file mode 100644
index 0000000..45be723
--- /dev/null
+++ b/mojo/edk/embedder/platform_shared_buffer.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_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_
+#define MOJO_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+class PlatformSharedBufferMapping;
+
+// |PlatformSharedBuffer| is 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.
+class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedBuffer
+    : public base::RefCountedThreadSafe<PlatformSharedBuffer> {
+ public:
+  // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled).
+  // |num_bytes| must be nonzero. Returns null on failure.
+  static PlatformSharedBuffer* Create(size_t num_bytes);
+
+  // Creates a shared buffer of size |num_bytes| from the existing platform
+  // handle |platform_handle|. Returns null on failure.
+  static PlatformSharedBuffer* CreateFromPlatformHandle(
+      size_t num_bytes,
+      bool read_only,
+      ScopedPlatformHandle platform_handle);
+
+  // Creates a shared buffer of size |num_bytes| from the existing pair of
+  // read/write and read-only handles |rw_platform_handle| and
+  // |ro_platform_handle|. Returns null on failure.
+  static PlatformSharedBuffer* CreateFromPlatformHandlePair(
+      size_t num_bytes,
+      ScopedPlatformHandle rw_platform_handle,
+      ScopedPlatformHandle ro_platform_handle);
+
+  // Creates a shared buffer of size |num_bytes| from the existing shared memory
+  // handle |handle|.
+  static PlatformSharedBuffer* CreateFromSharedMemoryHandle(
+      size_t num_bytes,
+      bool read_only,
+      base::SharedMemoryHandle handle);
+
+  // Gets the size of shared buffer (in number of bytes).
+  size_t GetNumBytes() const;
+
+  // Returns whether this shared buffer is read-only.
+  bool IsReadOnly() const;
+
+  // 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.
+  std::unique_ptr<PlatformSharedBufferMapping> Map(size_t offset,
+                                                   size_t length);
+
+  // Checks if |offset| and |length| are valid arguments.
+  bool IsValidMap(size_t offset, size_t length);
+
+  // Like |Map()|, but doesn't check its arguments (which should have been
+  // preflighted using |IsValidMap()|).
+  std::unique_ptr<PlatformSharedBufferMapping> MapNoCheck(size_t offset,
+                                                          size_t length);
+
+  // Duplicates the underlying platform handle and passes it to the caller.
+  ScopedPlatformHandle DuplicatePlatformHandle();
+
+  // Duplicates the underlying shared memory handle and passes it to the caller.
+  base::SharedMemoryHandle DuplicateSharedMemoryHandle();
+
+  // 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.
+  ScopedPlatformHandle PassPlatformHandle();
+
+  // Create and return a read-only duplicate of this shared buffer. If this
+  // shared buffer isn't capable of returning a read-only duplicate, then
+  // nullptr will be returned.
+  PlatformSharedBuffer* CreateReadOnlyDuplicate();
+
+ private:
+  friend class base::RefCountedThreadSafe<PlatformSharedBuffer>;
+
+  PlatformSharedBuffer(size_t num_bytes, bool read_only);
+  ~PlatformSharedBuffer();
+
+  // 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);
+
+  bool InitFromPlatformHandlePair(ScopedPlatformHandle rw_platform_handle,
+                                  ScopedPlatformHandle ro_platform_handle);
+
+  void InitFromSharedMemoryHandle(base::SharedMemoryHandle handle);
+
+  const size_t num_bytes_;
+  const bool read_only_;
+
+  base::Lock lock_;
+  std::unique_ptr<base::SharedMemory> shared_memory_;
+
+  // A separate read-only shared memory for platforms that need it (i.e. Linux
+  // with sync broker).
+  std::unique_ptr<base::SharedMemory> ro_shared_memory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformSharedBuffer);
+};
+
+// A mapping of a |PlatformSharedBuffer| (compararable to a "file view" in
+// Windows); see above. Created by |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:
+  ~PlatformSharedBufferMapping();
+
+  void* GetBase() const;
+  size_t GetLength() const;
+
+ private:
+  friend class PlatformSharedBuffer;
+
+  PlatformSharedBufferMapping(base::SharedMemoryHandle handle,
+                              bool read_only,
+                              size_t offset,
+                              size_t length)
+      : offset_(offset),
+        length_(length),
+        base_(nullptr),
+        shared_memory_(handle, read_only) {}
+
+  bool Map();
+  void Unmap();
+
+  const size_t offset_;
+  const size_t length_;
+  void* base_;
+
+  // Since mapping life cycles are separate from PlatformSharedBuffer and a
+  // buffer can be mapped multiple times, we have our own SharedMemory object
+  // created from a duplicate handle.
+  base::SharedMemory shared_memory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformSharedBufferMapping);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PLATFORM_SHARED_BUFFER_H_
diff --git a/mojo/edk/embedder/process_delegate.h b/mojo/edk/embedder/process_delegate.h
new file mode 100644
index 0000000..144f4a3
--- /dev/null
+++ b/mojo/edk/embedder/process_delegate.h
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_
+#define MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_
+
+#include "base/macros.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+// An interface for process delegates.
+class MOJO_SYSTEM_IMPL_EXPORT ProcessDelegate {
+ public:
+  // Called when |ShutdownIPCSupport()| has completed work on the I/O thread.
+  virtual void OnShutdownComplete() = 0;
+
+ protected:
+  ProcessDelegate() {}
+  virtual ~ProcessDelegate() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProcessDelegate);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_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..15b80ea
--- /dev/null
+++ b/mojo/edk/embedder/scoped_platform_handle.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_SCOPED_PLATFORM_HANDLE_H_
+#define MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/macros.h"
+
+namespace mojo {
+namespace edk {
+
+class MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle {
+ public:
+  ScopedPlatformHandle() {}
+  explicit ScopedPlatformHandle(PlatformHandle handle) : handle_(handle) {}
+  ~ScopedPlatformHandle() { handle_.CloseIfNecessary(); }
+
+  // Move-only constructor and operator=.
+  ScopedPlatformHandle(ScopedPlatformHandle&& other)
+      : handle_(other.release()) {}
+
+  ScopedPlatformHandle& operator=(ScopedPlatformHandle&& other) {
+    if (this != &other)
+      handle_ = other.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_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPlatformHandle);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_
diff --git a/mojo/edk/embedder/test_embedder.cc b/mojo/edk/embedder/test_embedder.cc
new file mode 100644
index 0000000..9658010
--- /dev/null
+++ b/mojo/edk/embedder/test_embedder.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/edk/embedder/test_embedder.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/handle_table.h"
+
+namespace mojo {
+
+namespace edk {
+namespace internal {
+
+bool ShutdownCheckNoLeaks(Core* core) {
+  std::vector<MojoHandle> leaked_handles;
+  core->GetActiveHandlesForTest(&leaked_handles);
+  if (leaked_handles.empty())
+    return true;
+  for (auto handle : leaked_handles)
+    LOG(ERROR) << "Mojo embedder shutdown: Leaking handle " << handle;
+  return false;
+}
+
+}  // namespace internal
+
+namespace test {
+
+bool Shutdown() {
+  CHECK(internal::g_core);
+  bool rv = internal::ShutdownCheckNoLeaks(internal::g_core);
+  delete internal::g_core;
+  internal::g_core = nullptr;
+
+  return rv;
+}
+
+}  // namespace test
+}  // namespace edk
+
+}  // namespace mojo
diff --git a/mojo/edk/embedder/test_embedder.h b/mojo/edk/embedder/test_embedder.h
new file mode 100644
index 0000000..c64ba17
--- /dev/null
+++ b/mojo/edk/embedder/test_embedder.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_EDK_EMBEDDER_TEST_EMBEDDER_H_
+#define MOJO_EDK_EMBEDDER_TEST_EMBEDDER_H_
+
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+// 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.
+//
+// Note: It is up to the caller to ensure that there are not outstanding
+// callbacks from |CreateChannel()| before calling this.
+MOJO_SYSTEM_IMPL_EXPORT bool Shutdown();
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_TEST_EMBEDDER_H_
diff --git a/mojo/edk/js/BUILD.gn b/mojo/edk/js/BUILD.gn
new file mode 100644
index 0000000..429a8c0
--- /dev/null
+++ b/mojo/edk/js/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.
+
+# TODO(hansmuller): The organization of tests in this directory is weird:
+#   * Really, js_unittests tests public stuff, so that should live in public
+#     and be reworked as some sort of apptest.
+#   * Both js_unittests and js_integration_tests should auto-generate their
+#     tests somehow. The .cc files are just test runner stubs, including
+#     explicit lists of .js files.
+group("tests") {
+  testonly = true
+  deps = [
+    "test:js_integration_tests",
+    "test:js_unittests",
+  ]
+}
+
+source_set("js") {
+  sources = [
+    "core.cc",
+    "core.h",
+    "drain_data.cc",
+    "drain_data.h",
+    "handle.cc",
+    "handle.h",
+    "handle_close_observer.h",
+    "mojo_runner_delegate.cc",
+    "mojo_runner_delegate.h",
+    "support.cc",
+    "support.h",
+    "threading.cc",
+    "threading.h",
+    "waiting_callback.cc",
+    "waiting_callback.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//gin",
+    "//v8",
+  ]
+
+  deps = [
+    "//mojo/public/cpp/system",
+  ]
+}
diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc
new file mode 100644
index 0000000..c8ccc37
--- /dev/null
+++ b/mojo/edk/js/core.cc
@@ -0,0 +1,382 @@
+// Copyright 2014 The Chromium Authors. All 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/js/core.h"
+
+#include <stddef.h>
+#include <stdint.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/edk/js/drain_data.h"
+#include "mojo/edk/js/handle.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+namespace {
+
+MojoResult CloseHandle(gin::Handle<HandleWrapper> handle) {
+  if (!handle->get().is_valid())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  handle->Close();
+  return MOJO_RESULT_OK;
+}
+
+gin::Dictionary WaitHandle(const gin::Arguments& args,
+                           mojo::Handle handle,
+                           MojoHandleSignals signals,
+                           MojoDeadline deadline) {
+  v8::Isolate* isolate = args.isolate();
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate);
+
+  MojoHandleSignalsState signals_state;
+  MojoResult result = mojo::Wait(handle, signals, deadline, &signals_state);
+  dictionary.Set("result", result);
+
+  mojo::WaitManyResult wmv(result, 0);
+  if (!wmv.AreSignalsStatesValid()) {
+    dictionary.Set("signalsState", v8::Null(isolate).As<v8::Value>());
+  } else {
+    gin::Dictionary signalsStateDict = gin::Dictionary::CreateEmpty(isolate);
+    signalsStateDict.Set("satisfiedSignals", signals_state.satisfied_signals);
+    signalsStateDict.Set("satisfiableSignals",
+                         signals_state.satisfiable_signals);
+    dictionary.Set("signalsState", signalsStateDict);
+  }
+
+  return dictionary;
+}
+
+gin::Dictionary WaitMany(const gin::Arguments& args,
+                         const std::vector<mojo::Handle>& handles,
+                         const std::vector<MojoHandleSignals>& signals,
+                         MojoDeadline deadline) {
+  v8::Isolate* isolate = args.isolate();
+  gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate);
+
+  std::vector<MojoHandleSignalsState> signals_states(signals.size());
+  mojo::WaitManyResult wmv =
+      mojo::WaitMany(handles, signals, deadline, &signals_states);
+  dictionary.Set("result", wmv.result);
+  if (wmv.IsIndexValid()) {
+    dictionary.Set("index", wmv.index);
+  } else {
+    dictionary.Set("index", v8::Null(isolate).As<v8::Value>());
+  }
+  if (wmv.AreSignalsStatesValid()) {
+    std::vector<gin::Dictionary> vec;
+    for (size_t i = 0; i < handles.size(); ++i) {
+      gin::Dictionary signalsStateDict = gin::Dictionary::CreateEmpty(isolate);
+      signalsStateDict.Set("satisfiedSignals",
+                           signals_states[i].satisfied_signals);
+      signalsStateDict.Set("satisfiableSignals",
+                           signals_states[i].satisfiable_signals);
+      vec.push_back(signalsStateDict);
+    }
+    dictionary.Set("signalsState", vec);
+  } else {
+    dictionary.Set("signalsState", v8::Null(isolate).As<v8::Value>());
+  }
+
+  return dictionary;
+}
+
+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<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, so release them here.
+  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,
+                                  gin::Handle<HandleWrapper> handle) {
+  return (new DrainData(args->isolate(), handle->release()))->GetPromise();
+}
+
+bool IsHandle(gin::Arguments* args, v8::Handle<v8::Value> val) {
+  gin::Handle<mojo::edk::js::HandleWrapper> ignore_handle;
+  return gin::Converter<gin::Handle<mojo::edk::js::HandleWrapper>>::FromV8(
+      args->isolate(), val, &ignore_handle);
+}
+
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+}  // namespace
+
+const char Core::kModuleName[] = "mojo/public/js/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)
+            .SetMethod("isHandle", IsHandle)
+
+            .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("HANDLE_SIGNAL_PEER_CLOSED",
+                      MOJO_HANDLE_SIGNAL_PEER_CLOSED)
+
+            .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("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)
+            .SetValue("READ_DATA_FLAG_PEEK", MOJO_READ_DATA_FLAG_PEEK)
+            .Build();
+
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+
+  return templ->NewInstance();
+}
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/js/core.h b/mojo/edk/js/core.h
new file mode 100644
index 0000000..ca203ea
--- /dev/null
+++ b/mojo/edk/js/core.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_JS_CORE_H_
+#define MOJO_EDK_JS_CORE_H_
+
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+class Core {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_JS_CORE_H_
diff --git a/mojo/edk/js/drain_data.cc b/mojo/edk/js/drain_data.cc
new file mode 100644
index 0000000..ca5fdf4
--- /dev/null
+++ b/mojo/edk/js/drain_data.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/edk/js/drain_data.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/bind.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/system/core.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+DrainData::DrainData(v8::Isolate* isolate, mojo::Handle handle)
+    : isolate_(isolate), handle_(DataPipeConsumerHandle(handle.value())) {
+  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() {
+  resolver_.Reset();
+}
+
+void DrainData::WaitForData() {
+  handle_watcher_.Start(
+      handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+      base::Bind(&DrainData::DataReady, base::Unretained(this)));
+}
+
+void DrainData::DataReady(MojoResult result) {
+  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 edk
+}  // namespace mojo
diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h
new file mode 100644
index 0000000..917ca05
--- /dev/null
+++ b/mojo/edk/js/drain_data.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_JS_DRAIN_DATA_H_
+#define MOJO_EDK_JS_DRAIN_DATA_H_
+
+#include "base/memory/scoped_vector.h"
+#include "gin/runner.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/watcher.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace edk {
+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();
+
+  // Waits for data to be available. DataReady() will be notified.
+  void WaitForData();
+
+  // 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);
+
+  using DataBuffer = std::vector<char>;
+
+  v8::Isolate* isolate_;
+  ScopedDataPipeConsumerHandle handle_;
+  Watcher handle_watcher_;
+  base::WeakPtr<gin::Runner> runner_;
+  v8::UniquePersistent<v8::Promise::Resolver> resolver_;
+  ScopedVector<DataBuffer> data_buffers_;
+};
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_JS_DRAIN_DATA_H_
diff --git a/mojo/edk/js/handle.cc b/mojo/edk/js/handle.cc
new file mode 100644
index 0000000..9f9f161
--- /dev/null
+++ b/mojo/edk/js/handle.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/edk/js/handle.h"
+
+#include "mojo/edk/js/handle_close_observer.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+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());
+}
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+namespace gin {
+
+v8::Handle<v8::Value> Converter<mojo::Handle>::ToV8(v8::Isolate* isolate,
+                                                    const mojo::Handle& val) {
+  if (!val.is_valid())
+    return v8::Null(isolate);
+  return mojo::edk::js::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<mojo::edk::js::HandleWrapper> handle;
+  if (!Converter<gin::Handle<mojo::edk::js::HandleWrapper>>::FromV8(
+          isolate, val, &handle))
+    return false;
+
+  *out = handle->get();
+  return true;
+}
+
+v8::Handle<v8::Value> Converter<mojo::MessagePipeHandle>::ToV8(
+    v8::Isolate* isolate, mojo::MessagePipeHandle val) {
+  return Converter<mojo::Handle>::ToV8(isolate, val);
+}
+
+bool Converter<mojo::MessagePipeHandle>::FromV8(v8::Isolate* isolate,
+                                                v8::Handle<v8::Value> val,
+                                                mojo::MessagePipeHandle* out) {
+  return Converter<mojo::Handle>::FromV8(isolate, val, out);
+}
+
+}  // namespace gin
diff --git a/mojo/edk/js/handle.h b/mojo/edk/js/handle.h
new file mode 100644
index 0000000..2470fac
--- /dev/null
+++ b/mojo/edk/js/handle.h
@@ -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.
+
+#ifndef MOJO_EDK_JS_HANDLE_H_
+#define MOJO_EDK_JS_HANDLE_H_
+
+#include <stdint.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 mojo {
+namespace edk {
+namespace js {
+
+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);
+  ~HandleWrapper() override;
+  void NotifyCloseObservers();
+
+  mojo::ScopedHandle handle_;
+  base::ObserverList<HandleCloseObserver> close_observers_;
+};
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+namespace gin {
+
+// Note: It's important to use this converter rather than the one for
+// MojoHandle, since that will do a simple int32_t 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);
+};
+
+template<>
+struct Converter<mojo::MessagePipeHandle> {
+  static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+                                    mojo::MessagePipeHandle val);
+  static bool FromV8(v8::Isolate* isolate,
+                     v8::Handle<v8::Value> val,
+                     mojo::MessagePipeHandle* 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<mojo::edk::js::HandleWrapper>> {
+  static v8::Handle<v8::Value> ToV8(
+      v8::Isolate* isolate,
+      const gin::Handle<mojo::edk::js::HandleWrapper>& val) {
+    return val.ToV8();
+  }
+
+  static bool FromV8(v8::Isolate* isolate,
+                     v8::Handle<v8::Value> val,
+                     gin::Handle<mojo::edk::js::HandleWrapper>* out) {
+    if (val->IsNull()) {
+      *out = mojo::edk::js::HandleWrapper::Create(isolate, MOJO_HANDLE_INVALID);
+      return true;
+    }
+
+    mojo::edk::js::HandleWrapper* object = NULL;
+    if (!Converter<mojo::edk::js::HandleWrapper*>::FromV8(isolate, val,
+                                                          &object)) {
+      return false;
+    }
+    *out = gin::Handle<mojo::edk::js::HandleWrapper>(val, object);
+    return true;
+  }
+};
+
+}  // namespace gin
+
+#endif  // MOJO_EDK_JS_HANDLE_H_
diff --git a/mojo/edk/js/handle_close_observer.h b/mojo/edk/js/handle_close_observer.h
new file mode 100644
index 0000000..c7b935e
--- /dev/null
+++ b/mojo/edk/js/handle_close_observer.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_JS_HANDLE_CLOSE_OBSERVER_H_
+#define MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+class HandleCloseObserver {
+ public:
+  virtual void OnWillCloseHandle() = 0;
+
+ protected:
+  virtual ~HandleCloseObserver() {}
+};
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_
diff --git a/mojo/edk/js/handle_unittest.cc b/mojo/edk/js/handle_unittest.cc
new file mode 100644
index 0000000..dd2562f
--- /dev/null
+++ b/mojo/edk/js/handle_unittest.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/macros.h"
+#include "mojo/edk/js/handle.h"
+#include "mojo/edk/js/handle_close_observer.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+class HandleWrapperTest : public testing::Test,
+                          public HandleCloseObserver {
+ public:
+  HandleWrapperTest() : closes_observed_(0) {}
+
+  void OnWillCloseHandle() override { closes_observed_++; }
+
+ protected:
+  int closes_observed_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HandleWrapperTest);
+};
+
+class TestHandleWrapper : public 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 edk
+}  // namespace mojo
diff --git a/mojo/edk/js/mojo_runner_delegate.cc b/mojo/edk/js/mojo_runner_delegate.cc
new file mode 100644
index 0000000..dda0b2c
--- /dev/null
+++ b/mojo/edk/js/mojo_runner_delegate.cc
@@ -0,0 +1,80 @@
+// 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/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/edk/js/core.h"
+#include "mojo/edk/js/handle.h"
+#include "mojo/edk/js/support.h"
+#include "mojo/edk/js/threading.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+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(Core::kModuleName, Core::GetModule);
+  AddBuiltinModule(Support::kModuleName, Support::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 js
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/js/mojo_runner_delegate.h b/mojo/edk/js/mojo_runner_delegate.h
new file mode 100644
index 0000000..a76460c
--- /dev/null
+++ b/mojo/edk/js/mojo_runner_delegate.h
@@ -0,0 +1,35 @@
+// 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_JS_MOJO_RUNNER_DELEGATE_H_
+#define MOJO_EDK_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 edk {
+namespace js {
+
+class MojoRunnerDelegate : public gin::ModuleRunnerDelegate {
+ public:
+  MojoRunnerDelegate();
+  ~MojoRunnerDelegate() override;
+
+  void Start(gin::Runner* runner, MojoHandle pipe, const std::string& module);
+
+ private:
+  // From ModuleRunnerDelegate:
+  void UnhandledException(gin::ShellRunner* runner,
+                          gin::TryCatch& try_catch) override;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoRunnerDelegate);
+};
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_
diff --git a/mojo/edk/js/support.cc b/mojo/edk/js/support.cc
new file mode 100644
index 0000000..404cb9b
--- /dev/null
+++ b/mojo/edk/js/support.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 "mojo/edk/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/edk/js/handle.h"
+#include "mojo/edk/js/waiting_callback.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+namespace {
+
+WaitingCallback* AsyncWait(const gin::Arguments& args,
+                           gin::Handle<HandleWrapper> handle,
+                           MojoHandleSignals signals,
+                           v8::Handle<v8::Function> callback) {
+  return WaitingCallback::Create(
+      args.isolate(), callback, handle, signals, true /* one_shot */).get();
+}
+
+void CancelWait(WaitingCallback* waiting_callback) {
+  waiting_callback->Cancel();
+}
+
+WaitingCallback* Watch(const gin::Arguments& args,
+                       gin::Handle<HandleWrapper> handle,
+                       MojoHandleSignals signals,
+                       v8::Handle<v8::Function> callback) {
+  return WaitingCallback::Create(
+      args.isolate(), callback, handle, signals, false /* one_shot */).get();
+}
+
+void CancelWatch(WaitingCallback* waiting_callback) {
+  waiting_callback->Cancel();
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+}  // namespace
+
+const char Support::kModuleName[] = "mojo/public/js/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)
+                // TODO(rockot): Remove asyncWait and cancelWait.
+                .SetMethod("asyncWait", AsyncWait)
+                .SetMethod("cancelWait", CancelWait)
+                .SetMethod("watch", Watch)
+                .SetMethod("cancelWatch", CancelWatch)
+                .Build();
+
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+
+  return templ->NewInstance();
+}
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/js/support.h b/mojo/edk/js/support.h
new file mode 100644
index 0000000..c57cf7a
--- /dev/null
+++ b/mojo/edk/js/support.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_JS_SUPPORT_H_
+#define MOJO_EDK_JS_SUPPORT_H_
+
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+class Support {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_JS_SUPPORT_H_
diff --git a/mojo/edk/js/test/BUILD.gn b/mojo/edk/js/test/BUILD.gn
new file mode 100644
index 0000000..c5a78c3
--- /dev/null
+++ b/mojo/edk/js/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.
+
+import("//testing/test.gni")
+
+test("js_unittests") {
+  output_name = "mojo_js_unittests"
+
+  deps = [
+    "//base",
+    "//gin:gin_test",
+    "//mojo/edk/js",
+    "//mojo/edk/test:run_all_unittests",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/cpp/system",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
+    "//mojo/public/js:tests",
+  ]
+
+  sources = [
+    "//mojo/edk/js/handle_unittest.cc",
+    "run_js_tests.cc",
+  ]
+}
+
+test("js_integration_tests") {
+  output_name = "mojo_js_integration_tests"
+
+  deps = [
+    "//base",
+    "//gin:gin_test",
+    "//mojo/edk/js",
+    "//mojo/edk/js/tests:js_to_cpp_tests",
+    "//mojo/edk/test:run_all_unittests",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+    "//mojo/public/js:bindings",
+  ]
+
+  sources = [
+    "run_js_integration_tests.cc",
+  ]
+}
diff --git a/mojo/edk/js/test/hexdump.js b/mojo/edk/js/test/hexdump.js
new file mode 100644
index 0000000..b36c47f
--- /dev/null
+++ b/mojo/edk/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/edk/js/test/run_js_integration_tests.cc b/mojo/edk/js/test/run_js_integration_tests.cc
new file mode 100644
index 0000000..00d3e96
--- /dev/null
+++ b/mojo/edk/js/test/run_js_integration_tests.cc
@@ -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.
+
+#include "base/files/file_path.h"
+#include "base/macros.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/edk/js/core.h"
+#include "mojo/edk/js/support.h"
+#include "mojo/edk/js/threading.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+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(Threading::kModuleName, Threading::GetModule);
+    AddBuiltinModule(Support::kModuleName, Support::GetModule);
+  }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate);
+};
+
+void RunTest(std::string test) {
+  base::FilePath path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  path = path.AppendASCII("mojo")
+             .AppendASCII("edk")
+             .AppendASCII("js")
+             .AppendASCII("tests")
+             .AppendASCII(test);
+  TestRunnerDelegate delegate;
+  gin::RunTestFromFile(path, &delegate, false);
+}
+
+TEST(JSTest, connection) {
+  RunTest("connection_tests.js");
+}
+
+TEST(JSTest, sample_service) {
+  RunTest("sample_service_tests.js");
+}
+
+}  // namespace
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/js/test/run_js_tests.cc b/mojo/edk/js/test/run_js_tests.cc
new file mode 100644
index 0000000..d0e8edc
--- /dev/null
+++ b/mojo/edk/js/test/run_js_tests.cc
@@ -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.
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/test/file_runner.h"
+#include "gin/test/gtest.h"
+#include "mojo/edk/js/core.h"
+#include "mojo/edk/js/support.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+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) {
+  base::FilePath path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  path = path.AppendASCII("mojo")
+             .AppendASCII("public")
+             .AppendASCII("js")
+             .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, union) {
+  RunTest("union_unittests.js", true);
+}
+
+TEST(JSTest, validation) {
+  RunTest("validation_unittests.js", true);
+}
+
+}  // namespace
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn
new file mode 100644
index 0000000..b21cdbc
--- /dev/null
+++ b/mojo/edk/js/tests/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.
+
+import("../../../../mojo/public/tools/bindings/mojom.gni")
+
+source_set("js_to_cpp_tests") {
+  testonly = true
+
+  deps = [
+    ":js_to_cpp_bindings",
+    "//gin:gin_test",
+    "//mojo/common",
+    "//mojo/edk/js",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
+  ]
+
+  sources = [
+    "js_to_cpp_tests.cc",
+  ]
+
+  data = [
+    "connection_tests.js",
+    "js_to_cpp_tests.js",
+    "sample_service_tests.js",
+  ]
+
+  configs += [ "//v8:external_startup_data" ]
+}
+
+mojom("js_to_cpp_bindings") {
+  sources = [
+    "js_to_cpp.mojom",
+  ]
+}
diff --git a/mojo/edk/js/tests/connection_tests.js b/mojo/edk/js/tests/connection_tests.js
new file mode 100644
index 0000000..0b4b213
--- /dev/null
+++ b/mojo/edk/js/tests/connection_tests.js
@@ -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.
+
+define([
+    "gin/test/expect",
+    "mojo/public/js/connection",
+    "mojo/public/js/core",
+    "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom",
+    "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+    "mojo/public/js/threading",
+    "gc",
+], function(expect,
+            connection,
+            core,
+            sample_interfaces,
+            sample_service,
+            threading,
+            gc) {
+  testClientServer()
+      .then(testWriteToClosedPipe)
+      .then(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() {
+    // ServiceImpl ------------------------------------------------------------
+
+    function ServiceImpl() {
+    }
+
+    ServiceImpl.prototype = Object.create(
+        sample_service.Service.stubClass.prototype);
+
+    ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+      expect(foo.name).toBe("Example name");
+      expect(baz).toBe(sample_service.Service.BazOptions.REGULAR);
+      expect(core.close(port)).toBe(core.RESULT_OK);
+
+      return Promise.resolve({result: 42});
+    };
+
+    var pipe = core.createMessagePipe();
+    var anotherPipe = core.createMessagePipe();
+    var sourcePipe = core.createMessagePipe();
+
+    var connection0 = new connection.Connection(pipe.handle0, ServiceImpl);
+
+    var connection1 = new connection.Connection(
+        pipe.handle1, undefined, sample_service.Service.proxyClass);
+
+    var foo = new sample_service.Foo();
+    foo.bar = new sample_service.Bar();
+    foo.name = "Example name";
+    foo.source = sourcePipe.handle0;
+    var promise = connection1.remote.frobinate(
+        foo, sample_service.Service.BazOptions.REGULAR, anotherPipe.handle0)
+            .then(function(response) {
+      expect(response.result).toBe(42);
+
+      connection0.close();
+      connection1.close();
+
+      return Promise.resolve();
+    });
+
+    // sourcePipe.handle1 hasn't been closed yet.
+    expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK);
+
+    // anotherPipe.handle1 hasn't been closed yet.
+    expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK);
+
+    return promise;
+  }
+
+  function testWriteToClosedPipe() {
+    var pipe = core.createMessagePipe();
+    var anotherPipe = core.createMessagePipe();
+
+    var connection1 = new connection.Connection(
+        pipe.handle1, function() {}, sample_service.Service.proxyClass);
+
+    // 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 promise = connection1.remote.frobinate(
+        null, sample_service.Service.BazOptions.REGULAR, anotherPipe.handle0)
+            .then(function(response) {
+      return Promise.reject("Unexpected response");
+    }).catch(function(e) {
+      // We should observe the closed pipe.
+      expect(connection1.encounteredError()).toBeTruthy();
+      return Promise.resolve();
+    });
+
+    // Write failures are not reported.
+    expect(connection1.encounteredError()).toBeFalsy();
+
+    return promise;
+  }
+
+  function testRequestResponse() {
+    // ProviderImpl ------------------------------------------------------------
+
+    function ProviderImpl() {
+    }
+
+    ProviderImpl.prototype =
+        Object.create(sample_interfaces.Provider.stubClass.prototype);
+
+    ProviderImpl.prototype.echoString = function(a) {
+      return Promise.resolve({a: a});
+    };
+
+    ProviderImpl.prototype.echoStrings = function(a, b) {
+      return Promise.resolve({a: a, b: b});
+    };
+
+    var pipe = core.createMessagePipe();
+
+    var connection0 = new connection.Connection(pipe.handle0, ProviderImpl);
+
+    var connection1 = new connection.Connection(
+        pipe.handle1, undefined, sample_interfaces.Provider.proxyClass);
+
+    var promise = connection1.remote.echoString("hello")
+        .then(function(response) {
+      expect(response.a).toBe("hello");
+      return connection1.remote.echoStrings("hello", "world");
+    }).then(function(response) {
+      expect(response.a).toBe("hello");
+      expect(response.b).toBe("world");
+      // Mock a read failure, expect it to fail.
+      core.readMessage = function() {
+        return { result: core.RESULT_UNKNOWN };
+      };
+      return connection1.remote.echoString("goodbye");
+    }).then(function() {
+      throw Error("Expected echoString to fail.");
+    }, function(error) {
+      expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN);
+
+      return Promise.resolve();
+    });
+
+    return promise;
+  }
+});
diff --git a/mojo/edk/js/tests/js_to_cpp.mojom b/mojo/edk/js/tests/js_to_cpp.mojom
new file mode 100644
index 0000000..688b22b
--- /dev/null
+++ b/mojo/edk/js/tests/js_to_cpp.mojom
@@ -0,0 +1,54 @@
+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);
+};
+
+interface JsSide {
+  SetCppSide(CppSide cpp);
+
+  Ping();
+  Echo(int32 numIterations, EchoArgs arg);
+  BitFlip(EchoArgs arg);
+  BackPointer(EchoArgs arg);
+};
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc
new file mode 100644
index 0000000..6d3426d
--- /dev/null
+++ b/mojo/edk/js/tests/js_to_cpp_tests.cc
@@ -0,0 +1,445 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#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 "gin/v8_initializer.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/edk/js/mojo_runner_delegate.h"
+#include "mojo/edk/js/tests/js_to_cpp.mojom.h"
+#include "mojo/edk/test/test_utils.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+// Global value updated by some checks to prevent compilers from optimizing
+// reads out of existence.
+uint32_t 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_t kExpectedInt8Value = -65;
+const int16_t kExpectedInt16Value = -16961;
+const int32_t kExpectedInt32Value = -1145258561;
+const int64_t kExpectedInt64Value = -77263311946305LL;
+
+// Positive numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const uint8_t kExpectedUInt8Value = 65;
+const uint16_t kExpectedUInt16Value = 16961;
+const uint32_t kExpectedUInt32Value = 1145258561;
+const uint64_t 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)
+
+void CheckDataPipe(ScopedDataPipeConsumerHandle data_pipe_handle) {
+  std::string buffer;
+  bool result = common::BlockingCopyToString(std::move(data_pipe_handle),
+                                             &buffer);
+  EXPECT_TRUE(result);
+  EXPECT_EQ(64u, buffer.size());
+  for (int i = 0; i < 64; ++i) {
+    EXPECT_EQ(i, buffer[i]);
+  }
+}
+
+void CheckMessagePipe(MessagePipeHandle message_pipe_handle) {
+  unsigned char buffer[100];
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  MojoResult result = Wait(
+      message_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE,
+      MOJO_DEADLINE_INDEFINITE, nullptr);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  result = ReadMessageRaw(
+      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 = std::move(string_array);
+  return args;
+}
+
+void CheckSampleEchoArgs(js_to_cpp::EchoArgsPtr 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(std::move(arg->data_handle));
+  CheckMessagePipe(arg->message_handle.get());
+}
+
+void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
+  if (list.is_null())
+    return;
+  CheckSampleEchoArgs(std::move(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_(nullptr),
+        js_side_(nullptr),
+        mishandled_messages_(0),
+        binding_(this) {}
+  ~CppSideConnection() override {}
+
+  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_; }
+
+  void Bind(InterfaceRequest<js_to_cpp::CppSide> request) {
+    binding_.Bind(std::move(request));
+    // Keep the pipe open even after validation errors.
+    binding_.EnableTestingMode();
+  }
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { NOTREACHED(); }
+
+  void TestFinished() override { NOTREACHED(); }
+
+  void PingResponse() override { mishandled_messages_ += 1; }
+
+  void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+  void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+  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_;
+  mojo::Binding<js_to_cpp::CppSide> binding_;
+
+ 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) {}
+  ~PingCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->Ping(); }
+
+  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) {
+  }
+  ~EchoCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override {
+    js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
+  }
+
+  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);
+  }
+
+  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) {}
+  ~BitFlipCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); }
+
+  void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
+    CheckCorruptedEchoArgsList(list);
+  }
+
+  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) {}
+  ~BackPointerCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); }
+
+  void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
+    CheckCorruptedEchoArgsList(list);
+  }
+
+  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_);
+
+    js_to_cpp::JsSidePtr js_side;
+    auto js_side_proxy = GetProxy(&js_side);
+
+    cpp_side->set_js_side(js_side.get());
+    js_to_cpp::CppSidePtr cpp_side_ptr;
+    cpp_side->Bind(GetProxy(&cpp_side_ptr));
+
+    js_side->SetCppSide(std::move(cpp_side_ptr));
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+    gin::V8Initializer::LoadV8Snapshot();
+    gin::V8Initializer::LoadV8Natives();
+#endif
+
+    gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+                                   gin::IsolateHolder::kStableV8Extras,
+                                   gin::ArrayBufferAllocator::SharedInstance());
+    gin::IsolateHolder instance;
+    MojoRunnerDelegate delegate;
+    gin::ShellRunner runner(&delegate, instance.isolate());
+    delegate.Start(&runner, js_side_proxy.PassMessagePipe().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) {
+  PingCppSideConnection cpp_side_connection;
+  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, Echo) {
+  EchoCppSideConnection cpp_side_connection;
+  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BitFlip) {
+  BitFlipCppSideConnection cpp_side_connection;
+  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BackPointer) {
+  BackPointerCppSideConnection cpp_side_connection;
+  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js
new file mode 100644
index 0000000..ddecc4b
--- /dev/null
+++ b/mojo/edk/js/tests/js_to_cpp_tests.js
@@ -0,0 +1,228 @@
+// Copyright 2014 The Chromium 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/edk/js/tests/js_to_cpp_tests', [
+  'console',
+  'mojo/edk/js/tests/js_to_cpp.mojom',
+  'mojo/public/js/bindings',
+  'mojo/public/js/connection',
+  'mojo/public/js/connector',
+  'mojo/public/js/core',
+], function (console, jsToCpp, bindings, connection, connector, core) {
+  var retainedJsSide;
+  var retainedJsSideStub;
+  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() {
+  }
+
+  JsSideConnection.prototype =
+      Object.create(jsToCpp.JsSide.stubClass.prototype);
+
+  JsSideConnection.prototype.setCppSide = function(cppSide) {
+    this.cppSide_ = cppSide;
+    this.cppSide_.startTest();
+  };
+
+  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(jsSideRequestHandle) {
+    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;
+    }
+    retainedJsSideStub =
+        connection.bindHandleToStub(jsSideRequestHandle, jsToCpp.JsSide);
+    retainedJsSide = new JsSideConnection;
+    bindings.StubBindings(retainedJsSideStub).delegate = retainedJsSide;
+  };
+});
diff --git a/mojo/edk/js/tests/sample_service_tests.js b/mojo/edk/js/tests/sample_service_tests.js
new file mode 100644
index 0000000..2b065a8
--- /dev/null
+++ b/mojo/edk/js/tests/sample_service_tests.js
@@ -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.
+
+define([
+    "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",
+    "mojo/public/js/connection",
+    "mojo/public/js/core",
+    "mojo/public/js/threading",
+  ], function(expect, sample, imported, imported2, connection, core,
+              threading) {
+  testDefaultValues()
+      .then(testSampleService)
+      .then(function() {
+    this.result = "PASS";
+    threading.quit();
+  }.bind(this)).catch(function(e) {
+    this.result = "FAIL: " + (e.stack || e);
+    threading.quit();
+  }.bind(this));
+
+  // Checks that values are set to the defaults if we don't override them.
+  function testDefaultValues() {
+    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);
+
+    return Promise.resolve();
+  }
+
+  function testSampleService() {
+    function ServiceImpl() {
+    }
+
+    ServiceImpl.prototype = Object.create(sample.Service.stubClass.prototype);
+
+    ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+      checkFoo(foo);
+      expect(baz).toBe(sample.Service.BazOptions.EXTRA);
+      expect(core.isHandle(port)).toBeTruthy();
+      return Promise.resolve({result: 1234});
+    };
+
+    var foo = makeFoo();
+    checkFoo(foo);
+
+    var sampleServicePipe = core.createMessagePipe();
+    var connection0 = new connection.Connection(sampleServicePipe.handle0,
+                                                ServiceImpl);
+    var connection1 = new connection.Connection(
+        sampleServicePipe.handle1, undefined, sample.Service.proxyClass);
+
+    var pipe = core.createMessagePipe();
+    var promise = connection1.remote.frobinate(
+        foo, sample.Service.BazOptions.EXTRA, pipe.handle0)
+            .then(function(response) {
+      expect(response.result).toBe(1234);
+
+      return Promise.resolve();
+    });
+
+    return promise;
+  }
+
+  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 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;
+
+    // TODO(yzshen): currently setting it to null will cause connection error,
+    // even if the field is defined as nullable. crbug.com/575753
+    foo.source = core.createMessagePipe().handle0;
+
+    return foo;
+  }
+
+  // Checks 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(core.isHandle(foo.source)).toBeTruthy();
+  }
+});
diff --git a/mojo/edk/js/threading.cc b/mojo/edk/js/threading.cc
new file mode 100644
index 0000000..db9f12d
--- /dev/null
+++ b/mojo/edk/js/threading.cc
@@ -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.
+
+#include "mojo/edk/js/threading.h"
+
+#include "base/message_loop/message_loop.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "mojo/edk/js/handle.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+namespace {
+
+void Quit() {
+  base::MessageLoop::current()->QuitNow();
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+}  // namespace
+
+const char Threading::kModuleName[] = "mojo/public/js/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 js
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/js/threading.h b/mojo/edk/js/threading.h
new file mode 100644
index 0000000..10404d5
--- /dev/null
+++ b/mojo/edk/js/threading.h
@@ -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.
+
+#ifndef MOJO_EDK_JS_THREADING_H_
+#define MOJO_EDK_JS_THREADING_H_
+
+#include "gin/public/wrapper_info.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+class Threading {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+ private:
+  Threading();
+};
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_JS_THREADING_H_
diff --git a/mojo/edk/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc
new file mode 100644
index 0000000..4ba2c61
--- /dev/null
+++ b/mojo/edk/js/waiting_callback.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/js/waiting_callback.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "gin/per_context_data.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+namespace {
+
+v8::Handle<v8::Private> GetHiddenPropertyName(v8::Isolate* isolate) {
+  return v8::Private::ForApi(
+      isolate, gin::StringToV8(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<HandleWrapper> handle_wrapper,
+    MojoHandleSignals signals,
+    bool one_shot) {
+  gin::Handle<WaitingCallback> waiting_callback = gin::CreateHandle(
+      isolate, new WaitingCallback(isolate, callback, one_shot));
+  MojoResult result = waiting_callback->watcher_.Start(
+      handle_wrapper->get(), signals,
+      base::Bind(&WaitingCallback::OnHandleReady,
+                 base::Unretained(waiting_callback.get())));
+
+  // The signals may already be unsatisfiable.
+  if (result == MOJO_RESULT_FAILED_PRECONDITION)
+    waiting_callback->OnHandleReady(MOJO_RESULT_FAILED_PRECONDITION);
+
+  return waiting_callback;
+}
+
+void WaitingCallback::Cancel() {
+  if (watcher_.IsWatching())
+    watcher_.Cancel();
+}
+
+WaitingCallback::WaitingCallback(v8::Isolate* isolate,
+                                 v8::Handle<v8::Function> callback,
+                                 bool one_shot)
+    : one_shot_(one_shot),
+      weak_factory_(this) {
+  v8::Handle<v8::Context> context = isolate->GetCurrentContext();
+  runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
+  GetWrapper(isolate)
+      ->SetPrivate(context, GetHiddenPropertyName(isolate), callback)
+      .FromJust();
+}
+
+WaitingCallback::~WaitingCallback() {
+  Cancel();
+}
+
+void WaitingCallback::OnHandleReady(MojoResult result) {
+  if (!runner_)
+    return;
+
+  gin::Runner::Scope scope(runner_.get());
+  v8::Isolate* isolate = runner_->GetContextHolder()->isolate();
+
+  v8::Handle<v8::Value> hidden_value =
+      GetWrapper(isolate)
+          ->GetPrivate(runner_->GetContextHolder()->context(),
+                       GetHiddenPropertyName(isolate))
+          .ToLocalChecked();
+  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);
+
+  if (one_shot_ || result == MOJO_RESULT_CANCELLED) {
+    runner_.reset();
+    Cancel();
+  }
+}
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h
new file mode 100644
index 0000000..1195a98
--- /dev/null
+++ b/mojo/edk/js/waiting_callback.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_EDK_JS_WAITING_CALLBACK_H_
+#define MOJO_EDK_JS_WAITING_CALLBACK_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "gin/handle.h"
+#include "gin/runner.h"
+#include "gin/wrappable.h"
+#include "mojo/edk/js/handle.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/watcher.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+
+class WaitingCallback : public gin::Wrappable<WaitingCallback> {
+ public:
+  static gin::WrapperInfo kWrapperInfo;
+
+  // Creates a new WaitingCallback.
+  //
+  // If |one_shot| is true, the callback will only ever be called at most once.
+  // If false, the callback may be called any number of times until the
+  // WaitingCallback is explicitly cancelled.
+  static gin::Handle<WaitingCallback> Create(
+      v8::Isolate* isolate,
+      v8::Handle<v8::Function> callback,
+      gin::Handle<HandleWrapper> handle_wrapper,
+      MojoHandleSignals signals,
+      bool one_shot);
+
+  // 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,
+                  bool one_shot);
+  ~WaitingCallback() override;
+
+  // Callback from the Watcher.
+  void OnHandleReady(MojoResult result);
+
+  // Indicates whether this is a one-shot callback or not. If so, it uses the
+  // deprecated HandleWatcher to wait for signals; otherwise it uses the new
+  // system Watcher API.
+  const bool one_shot_;
+
+  base::WeakPtr<gin::Runner> runner_;
+  Watcher watcher_;
+  base::WeakPtrFactory<WaitingCallback> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaitingCallback);
+};
+
+}  // namespace js
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_JS_WAITING_CALLBACK_H_
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
new file mode 100644
index 0000000..7e21073
--- /dev/null
+++ b/mojo/edk/system/BUILD.gn
@@ -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("//build/config/nacl/config.gni")
+import("//testing/test.gni")
+import("../../../mojo/public/tools/bindings/mojom.gni")
+
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
+component("system") {
+  output_name = "mojo_system_impl"
+
+  sources = [
+    "atomic_flag.h",
+    "awakable.h",
+    "awakable_list.cc",
+    "awakable_list.h",
+    "broker.h",
+    "broker_host.h",
+    "broker_host_posix.cc",
+    "broker_posix.cc",
+    "channel.cc",
+    "channel.h",
+    "channel_posix.cc",
+    "channel_win.cc",
+    "configuration.cc",
+    "configuration.h",
+    "core.cc",
+    "core.h",
+    "data_pipe_consumer_dispatcher.cc",
+    "data_pipe_consumer_dispatcher.h",
+    "data_pipe_control_message.cc",
+    "data_pipe_control_message.h",
+    "data_pipe_producer_dispatcher.cc",
+    "data_pipe_producer_dispatcher.h",
+    "dispatcher.cc",
+    "dispatcher.h",
+    "handle_signals_state.h",
+    "handle_table.cc",
+    "handle_table.h",
+    "mapping_table.cc",
+    "mapping_table.h",
+    "message_for_transit.cc",
+    "message_for_transit.h",
+    "message_pipe_dispatcher.cc",
+    "message_pipe_dispatcher.h",
+    "node_channel.cc",
+    "node_channel.h",
+    "node_controller.cc",
+    "node_controller.h",
+    "options_validation.h",
+    "platform_handle_dispatcher.cc",
+    "platform_handle_dispatcher.h",
+    "ports_message.cc",
+    "ports_message.h",
+    "remote_message_pipe_bootstrap.cc",
+    "remote_message_pipe_bootstrap.h",
+    "request_context.cc",
+    "request_context.h",
+    "shared_buffer_dispatcher.cc",
+    "shared_buffer_dispatcher.h",
+    "wait_set_dispatcher.cc",
+    "wait_set_dispatcher.h",
+    "waiter.cc",
+    "waiter.h",
+    "watcher.cc",
+    "watcher.h",
+    "watcher_set.cc",
+    "watcher_set.h",
+  ]
+
+  defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
+
+  public_deps = [
+    "//mojo/edk/embedder",
+    "//mojo/edk/embedder:delegates",
+    "//mojo/edk/embedder:platform",
+    "//mojo/edk/system/ports",
+    "//mojo/public/c/system",
+    "//mojo/public/cpp/system",
+  ]
+
+  deps = [
+    "//base",
+  ]
+
+  if (!is_nacl) {
+    deps += [ "//crypto" ]
+  }
+
+  if (is_win) {
+    cflags = [ "/wd4324" ]  # Structure was padded due to __declspec(align()),
+                            # which is uninteresting.
+  }
+
+  if (is_mac && !is_ios) {
+    sources += [
+      "mach_port_relay.cc",
+      "mach_port_relay.h",
+    ]
+  }
+
+  if (is_nacl && !is_nacl_nonsfi) {
+    sources -= [
+      "broker_host_posix.cc",
+      "broker_posix.cc",
+      "channel_posix.cc",
+      "remote_message_pipe_bootstrap.cc",
+    ]
+  }
+
+  # Use target_os == "chromeos" instead of is_chromeos because we need to
+  # build NaCl targets (i.e. IRT) for ChromeOS the same as the rest of ChromeOS.
+  if (is_android || target_os == "chromeos") {
+    defines += [ "MOJO_EDK_LEGACY_PROTOCOL" ]
+  }
+
+  allow_circular_includes_from = [ "//mojo/edk/embedder" ]
+}
+
+group("tests") {
+  testonly = true
+  deps = [
+    ":mojo_system_unittests",
+  ]
+
+  if (!is_ios) {
+    deps += [ ":mojo_message_pipe_perftests" ]
+  }
+}
+
+source_set("test_utils") {
+  testonly = true
+
+  sources = [
+    "test_utils.cc",
+    "test_utils.h",
+  ]
+
+  public_deps = [
+    "//mojo/public/c/system",
+    "//mojo/public/cpp/system",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/test:test_support",
+    "//testing/gtest:gtest",
+  ]
+}
+
+test("mojo_system_unittests") {
+  sources = [
+    "awakable_list_unittest.cc",
+    "core_test_base.cc",
+    "core_test_base.h",
+    "core_unittest.cc",
+    "message_pipe_unittest.cc",
+    "options_validation_unittest.cc",
+    "platform_handle_dispatcher_unittest.cc",
+    "shared_buffer_dispatcher_unittest.cc",
+    "shared_buffer_unittest.cc",
+    "wait_set_dispatcher_unittest.cc",
+    "waiter_test_utils.cc",
+    "waiter_test_utils.h",
+    "waiter_unittest.cc",
+    "watch_unittest.cc",
+  ]
+
+  if (!is_ios) {
+    sources += [
+      "data_pipe_unittest.cc",
+      "multiprocess_message_pipe_unittest.cc",
+      "platform_wrapper_unittest.cc",
+    ]
+  }
+
+  deps = [
+    ":test_utils",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/embedder:embedder_unittests",
+    "//mojo/edk/system",
+    "//mojo/edk/system/ports:tests",
+    "//mojo/edk/test:run_all_unittests",
+    "//mojo/edk/test:test_support",
+    "//testing/gtest",
+  ]
+
+  allow_circular_includes_from = [ "//mojo/edk/embedder:embedder_unittests" ]
+}
+
+if (!is_ios) {
+  test("mojo_message_pipe_perftests") {
+    sources = [
+      "message_pipe_perftest.cc",
+    ]
+
+    deps = [
+      ":test_utils",
+      "//base",
+      "//base/test:test_support",
+      "//mojo/edk/system",
+      "//mojo/edk/test:run_all_perftests",
+      "//mojo/edk/test:test_support",
+      "//testing/gtest",
+    ]
+  }
+}
diff --git a/mojo/edk/system/atomic_flag.h b/mojo/edk/system/atomic_flag.h
new file mode 100644
index 0000000..6bdcfaa
--- /dev/null
+++ b/mojo/edk/system/atomic_flag.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All 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_ATOMIC_FLAG_H_
+#define MOJO_EDK_SYSTEM_ATOMIC_FLAG_H_
+
+#include "base/atomicops.h"
+#include "base/macros.h"
+
+namespace mojo {
+namespace edk {
+
+// AtomicFlag is a boolean flag that can be set and tested atomically. It is
+// intended to be used to fast-path checks where the common case would normally
+// release the governing mutex immediately after checking.
+//
+// Example usage:
+// void DoFoo(Bar* bar) {
+//   AutoLock l(lock_);
+//   queue_.push_back(bar);
+//   flag_.Set(true);
+// }
+//
+// void Baz() {
+//   if (!flag_)  // Assume this is the common case.
+//     return;
+//
+//   AutoLock l(lock_);
+//   ... drain queue_ ...
+//   flag_.Set(false);
+// }
+class AtomicFlag {
+ public:
+  AtomicFlag() : flag_(0) {}
+  ~AtomicFlag() {}
+
+  void Set(bool value) {
+    base::subtle::Release_Store(&flag_, value ? 1 : 0);
+  }
+
+  bool Get() const {
+    return base::subtle::Acquire_Load(&flag_) ? true : false;
+  }
+
+  operator const bool() const { return Get(); }
+
+ private:
+  base::subtle::Atomic32 flag_;
+
+  DISALLOW_COPY_AND_ASSIGN(AtomicFlag);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_ATOMIC_FLAG_H_
diff --git a/mojo/edk/system/awakable.h b/mojo/edk/system/awakable.h
new file mode 100644
index 0000000..2cb10f5
--- /dev/null
+++ b/mojo/edk/system/awakable.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_AWAKABLE_H_
+#define MOJO_EDK_SYSTEM_AWAKABLE_H_
+
+#include <stdint.h>
+
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+// An interface that may be waited on |AwakableList|.
+class MOJO_SYSTEM_IMPL_EXPORT Awakable {
+ public:
+  // |Awake()| must satisfy the following contract:
+  // * As this is called from any thread, this must be thread-safe.
+  // * As this is called inside a lock, this must not call anything that takes
+  //   "non-terminal" locks, i.e., those which are always safe to take.
+  // This should return false if this must not be called again for the same
+  // reason (e.g., for the same call to |AwakableList::Add()|).
+  virtual bool Awake(MojoResult result, uintptr_t context) = 0;
+
+ protected:
+  Awakable() {}
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_AWAKABLE_H_
diff --git a/mojo/edk/system/awakable_list.cc b/mojo/edk/system/awakable_list.cc
new file mode 100644
index 0000000..2045f32
--- /dev/null
+++ b/mojo/edk/system/awakable_list.cc
@@ -0,0 +1,87 @@
+// 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/awakable_list.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "mojo/edk/system/awakable.h"
+#include "mojo/edk/system/handle_signals_state.h"
+
+namespace mojo {
+namespace edk {
+
+AwakableList::AwakableList() {
+}
+
+AwakableList::~AwakableList() {
+  DCHECK(awakables_.empty());
+}
+
+void AwakableList::AwakeForStateChange(const HandleSignalsState& state) {
+  // Instead of deleting elements in-place, swap them with the last element and
+  // erase the elements from the end.
+  auto last = awakables_.end();
+  for (AwakeInfoList::iterator it = awakables_.begin(); it != last;) {
+    bool keep = true;
+    if (state.satisfies(it->signals))
+      keep = it->awakable->Awake(MOJO_RESULT_OK, it->context);
+    else if (!state.can_satisfy(it->signals))
+      keep = it->awakable->Awake(MOJO_RESULT_FAILED_PRECONDITION, it->context);
+
+    if (!keep) {
+      --last;
+      std::swap(*it, *last);
+    } else {
+      ++it;
+    }
+  }
+  awakables_.erase(last, awakables_.end());
+  watchers_.NotifyForStateChange(state);
+}
+
+void AwakableList::CancelAll() {
+  for (AwakeInfoList::iterator it = awakables_.begin(); it != awakables_.end();
+       ++it) {
+    it->awakable->Awake(MOJO_RESULT_CANCELLED, it->context);
+  }
+  awakables_.clear();
+  watchers_.NotifyClosed();
+}
+
+void AwakableList::Add(Awakable* awakable,
+                       MojoHandleSignals signals,
+                       uintptr_t context) {
+  awakables_.push_back(AwakeInfo(awakable, signals, context));
+}
+
+void AwakableList::Remove(Awakable* awakable) {
+  // 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|.
+  auto last = awakables_.end();
+  for (AwakeInfoList::iterator it = awakables_.begin(); it != last;) {
+    if (it->awakable == awakable) {
+      --last;
+      std::swap(*it, *last);
+    } else {
+      ++it;
+    }
+  }
+  awakables_.erase(last, awakables_.end());
+}
+
+MojoResult AwakableList::AddWatcher(MojoHandleSignals signals,
+                                    const Watcher::WatchCallback& callback,
+                                    uintptr_t context,
+                                    const HandleSignalsState& current_state) {
+  return watchers_.Add(signals, callback, context, current_state);
+}
+
+MojoResult AwakableList::RemoveWatcher(uintptr_t context) {
+  return watchers_.Remove(context);
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/awakable_list.h b/mojo/edk/system/awakable_list.h
new file mode 100644
index 0000000..355677f
--- /dev/null
+++ b/mojo/edk/system/awakable_list.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_EDK_SYSTEM_AWAKABLE_LIST_H_
+#define MOJO_EDK_SYSTEM_AWAKABLE_LIST_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/edk/system/watcher.h"
+#include "mojo/edk/system/watcher_set.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+class Awakable;
+struct HandleSignalsState;
+
+// |AwakableList| tracks all the |Waiter|s that are waiting on a given
+// handle/|Dispatcher|. There should be a |AwakableList| for each handle that
+// can be waited on (in any way). In the simple case, the |AwakableList| 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 AwakableList {
+ public:
+  AwakableList();
+  ~AwakableList();
+
+  void AwakeForStateChange(const HandleSignalsState& state);
+  void CancelAll();
+  void Add(Awakable* awakable, MojoHandleSignals signals, uintptr_t context);
+  void Remove(Awakable* awakable);
+
+  // Add and remove Watchers to this AwakableList.
+  MojoResult AddWatcher(MojoHandleSignals signals,
+                        const Watcher::WatchCallback& callback,
+                        uintptr_t context,
+                        const HandleSignalsState& current_state);
+  MojoResult RemoveWatcher(uintptr_t context);
+
+ private:
+  struct AwakeInfo {
+    AwakeInfo(Awakable* awakable, MojoHandleSignals signals, uintptr_t context)
+        : awakable(awakable), signals(signals), context(context) {}
+
+    Awakable* awakable;
+    MojoHandleSignals signals;
+    uintptr_t context;
+  };
+  using AwakeInfoList = std::vector<AwakeInfo>;
+
+  AwakeInfoList awakables_;
+
+  // TODO: Remove AwakableList and instead use WatcherSet directly in
+  // dispatchers.
+  WatcherSet watchers_;
+
+  DISALLOW_COPY_AND_ASSIGN(AwakableList);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_AWAKABLE_LIST_H_
diff --git a/mojo/edk/system/awakable_list_unittest.cc b/mojo/edk/system/awakable_list_unittest.cc
new file mode 100644
index 0000000..9737fce
--- /dev/null
+++ b/mojo/edk/system/awakable_list_unittest.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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonDeadline()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/edk/system/awakable_list.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 edk {
+namespace {
+
+TEST(AwakableListTest, BasicCancel) {
+  MojoResult result;
+  uintptr_t context;
+
+  // Cancel immediately after thread start.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+    thread.Start();
+    awakable_list.CancelAll();
+    // Double-remove okay:
+    awakable_list.Remove(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+  EXPECT_EQ(1u, context);
+
+  // Cancel before after thread start.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+    awakable_list.CancelAll();
+    thread.Start();
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+  EXPECT_EQ(2u, context);
+
+  // Cancel some time after thread start.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+    thread.Start();
+    test::Sleep(2 * test::EpsilonDeadline());
+    awakable_list.CancelAll();
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+  EXPECT_EQ(3u, context);
+}
+
+TEST(AwakableListTest, BasicAwakeSatisfied) {
+  MojoResult result;
+  uintptr_t context;
+
+  // Awake immediately after thread start.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+    thread.Start();
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    awakable_list.Remove(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(1u, context);
+
+  // Awake before after thread start.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_WRITABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    awakable_list.Remove(thread.waiter());
+    // Double-remove okay:
+    awakable_list.Remove(thread.waiter());
+    thread.Start();
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(2u, context);
+
+  // Awake some time after thread start.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+    thread.Start();
+    test::Sleep(2 * test::EpsilonDeadline());
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    awakable_list.Remove(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(3u, context);
+}
+
+TEST(AwakableListTest, BasicAwakeUnsatisfiable) {
+  MojoResult result;
+  uintptr_t context;
+
+  // Awake (for unsatisfiability) immediately after thread start.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+    thread.Start();
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE));
+    awakable_list.Remove(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  EXPECT_EQ(1u, context);
+
+  // Awake (for unsatisfiability) before after thread start.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE));
+    awakable_list.Remove(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.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread(&result, &context);
+    awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+    thread.Start();
+    test::Sleep(2 * test::EpsilonDeadline());
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE));
+    awakable_list.Remove(thread.waiter());
+    // Double-remove okay:
+    awakable_list.Remove(thread.waiter());
+  }  // Join |thread|.
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+  EXPECT_EQ(3u, context);
+}
+
+TEST(AwakableListTest, MultipleAwakables) {
+  MojoResult result1;
+  MojoResult result2;
+  MojoResult result3;
+  MojoResult result4;
+  uintptr_t context1;
+  uintptr_t context2;
+  uintptr_t context3;
+  uintptr_t context4;
+
+  // Cancel two awakables.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread1(&result1, &context1);
+    awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+    thread1.Start();
+    test::SimpleWaiterThread thread2(&result2, &context2);
+    awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+    thread2.Start();
+    test::Sleep(2 * test::EpsilonDeadline());
+    awakable_list.CancelAll();
+  }  // Join threads.
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result1);
+  EXPECT_EQ(1u, context1);
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result2);
+  EXPECT_EQ(2u, context2);
+
+  // Awake one awakable, cancel other.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread1(&result1, &context1);
+    awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+    thread1.Start();
+    test::SimpleWaiterThread thread2(&result2, &context2);
+    awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 4);
+    thread2.Start();
+    test::Sleep(2 * test::EpsilonDeadline());
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    awakable_list.Remove(thread1.waiter());
+    awakable_list.CancelAll();
+  }  // Join threads.
+  EXPECT_EQ(MOJO_RESULT_OK, result1);
+  EXPECT_EQ(3u, context1);
+  EXPECT_EQ(MOJO_RESULT_CANCELLED, result2);
+  EXPECT_EQ(4u, context2);
+
+  // Cancel one awakable, awake other for unsatisfiability.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread1(&result1, &context1);
+    awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 5);
+    thread1.Start();
+    test::SimpleWaiterThread thread2(&result2, &context2);
+    awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 6);
+    thread2.Start();
+    test::Sleep(2 * test::EpsilonDeadline());
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE));
+    awakable_list.Remove(thread2.waiter());
+    awakable_list.CancelAll();
+  }  // 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 awakable, awake other for unsatisfiability.
+  {
+    AwakableList awakable_list;
+    test::SimpleWaiterThread thread1(&result1, &context1);
+    awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 7);
+    thread1.Start();
+
+    test::Sleep(1 * test::EpsilonDeadline());
+
+    // Should do nothing.
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+
+    test::SimpleWaiterThread thread2(&result2, &context2);
+    awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 8);
+    thread2.Start();
+
+    test::Sleep(1 * test::EpsilonDeadline());
+
+    // Awake #1.
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_READABLE,
+        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE));
+    awakable_list.Remove(thread1.waiter());
+
+    test::Sleep(1 * test::EpsilonDeadline());
+
+    test::SimpleWaiterThread thread3(&result3, &context3);
+    awakable_list.Add(thread3.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 9);
+    thread3.Start();
+
+    test::SimpleWaiterThread thread4(&result4, &context4);
+    awakable_list.Add(thread4.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 10);
+    thread4.Start();
+
+    test::Sleep(1 * test::EpsilonDeadline());
+
+    // Awake #2 and #3 for unsatisfiability.
+    awakable_list.AwakeForStateChange(HandleSignalsState(
+        MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE));
+    awakable_list.Remove(thread2.waiter());
+    awakable_list.Remove(thread3.waiter());
+
+    // Cancel #4.
+    awakable_list.CancelAll();
+  }  // 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);
+}
+
+class KeepAwakable : public Awakable {
+ public:
+  KeepAwakable() : awake_count(0) {}
+
+  bool Awake(MojoResult result, uintptr_t context) override {
+    awake_count++;
+    return true;
+  }
+
+  int awake_count;
+
+  DISALLOW_COPY_AND_ASSIGN(KeepAwakable);
+};
+
+class RemoveAwakable : public Awakable {
+ public:
+  RemoveAwakable() : awake_count(0) {}
+
+  bool Awake(MojoResult result, uintptr_t context) override {
+    awake_count++;
+    return false;
+  }
+
+  int awake_count;
+
+  DISALLOW_COPY_AND_ASSIGN(RemoveAwakable);
+};
+
+TEST(AwakableListTest, KeepAwakablesReturningTrue) {
+  KeepAwakable keep0;
+  KeepAwakable keep1;
+  RemoveAwakable remove0;
+  RemoveAwakable remove1;
+  RemoveAwakable remove2;
+
+  HandleSignalsState hss(MOJO_HANDLE_SIGNAL_WRITABLE,
+                         MOJO_HANDLE_SIGNAL_WRITABLE);
+
+  AwakableList remove_all;
+  remove_all.Add(&remove0, MOJO_HANDLE_SIGNAL_WRITABLE, 0);
+  remove_all.Add(&remove1, MOJO_HANDLE_SIGNAL_WRITABLE, 0);
+
+  remove_all.AwakeForStateChange(hss);
+  EXPECT_EQ(remove0.awake_count, 1);
+  EXPECT_EQ(remove1.awake_count, 1);
+
+  remove_all.AwakeForStateChange(hss);
+  EXPECT_EQ(remove0.awake_count, 1);
+  EXPECT_EQ(remove1.awake_count, 1);
+
+  AwakableList remove_first;
+  remove_first.Add(&remove2, MOJO_HANDLE_SIGNAL_WRITABLE, 0);
+  remove_first.Add(&keep0, MOJO_HANDLE_SIGNAL_WRITABLE, 0);
+  remove_first.Add(&keep1, MOJO_HANDLE_SIGNAL_WRITABLE, 0);
+
+  remove_first.AwakeForStateChange(hss);
+  EXPECT_EQ(keep0.awake_count, 1);
+  EXPECT_EQ(keep1.awake_count, 1);
+  EXPECT_EQ(remove2.awake_count, 1);
+
+  remove_first.AwakeForStateChange(hss);
+  EXPECT_EQ(keep0.awake_count, 2);
+  EXPECT_EQ(keep1.awake_count, 2);
+  EXPECT_EQ(remove2.awake_count, 1);
+
+  remove_first.Remove(&keep0);
+  remove_first.Remove(&keep1);
+}
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/broker.h b/mojo/edk/system/broker.h
new file mode 100644
index 0000000..1577972
--- /dev/null
+++ b/mojo/edk/system/broker.h
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium Authors. All 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_BROKER_H_
+#define MOJO_EDK_SYSTEM_BROKER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace mojo {
+namespace edk {
+
+class PlatformSharedBuffer;
+
+// The Broker is a channel to the parent process, which allows synchronous IPCs.
+class Broker {
+ public:
+  // Note: This is blocking, and will wait for the first message over
+  // |platform_handle|.
+  explicit Broker(ScopedPlatformHandle platform_handle);
+  ~Broker();
+
+  // Returns the platform handle that should be used to establish a NodeChannel
+  // to the parent process.
+  ScopedPlatformHandle GetParentPlatformHandle();
+
+  // Request a shared buffer from the parent process. Blocks the current thread.
+  scoped_refptr<PlatformSharedBuffer> GetSharedBuffer(size_t num_bytes);
+
+ private:
+  // Handle to the parent process, used for synchronous IPCs.
+  ScopedPlatformHandle sync_channel_;
+
+  // Handle to the parent process which is recieved in the first first message
+  // over |sync_channel_|.
+  ScopedPlatformHandle parent_channel_;
+
+  // Lock to only allow one sync message at a time. This avoids having to deal
+  // with message ordering since we can only have one request at a time
+  // in-flight.
+  base::Lock lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(Broker);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_BROKER_H_
diff --git a/mojo/edk/system/broker_host.h b/mojo/edk/system/broker_host.h
new file mode 100644
index 0000000..b8f68c4
--- /dev/null
+++ b/mojo/edk/system/broker_host.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All 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_BROKER_HOST_H_
+#define MOJO_EDK_SYSTEM_BROKER_HOST_H_
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/channel.h"
+
+namespace mojo {
+namespace edk {
+
+// The BrokerHost is a channel to the child process, which services synchronous
+// IPCs.
+class BrokerHost : public Channel::Delegate,
+                   public base::MessageLoop::DestructionObserver {
+ public:
+  explicit BrokerHost(ScopedPlatformHandle platform_handle);
+
+  // Send |handle| to the child, to be used to establish a NodeChannel to us.
+  void SendChannel(ScopedPlatformHandle handle);
+
+ private:
+  ~BrokerHost() override;
+
+  // Channel::Delegate:
+  void OnChannelMessage(const void* payload,
+                        size_t payload_size,
+                        ScopedPlatformHandleVectorPtr handles) override;
+  void OnChannelError() override;
+
+  // base::MessageLoop::DestructionObserver:
+  void WillDestroyCurrentMessageLoop() override;
+
+  void OnBufferRequest(size_t num_bytes);
+
+  scoped_refptr<Channel> channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrokerHost);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_BROKER_HOST_H_
diff --git a/mojo/edk/system/broker_host_posix.cc b/mojo/edk/system/broker_host_posix.cc
new file mode 100644
index 0000000..ed8e213
--- /dev/null
+++ b/mojo/edk/system/broker_host_posix.cc
@@ -0,0 +1,129 @@
+// Copyright 2016 The Chromium Authors. All 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/broker_host.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/broker_messages.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+// To prevent abuse, limit the maximum size of shared memory buffers.
+// TODO(amistry): Re-consider this limit, or do something smarter.
+const size_t kMaxSharedBufferSize = 16 * 1024 * 1024;
+}
+
+BrokerHost::BrokerHost(ScopedPlatformHandle platform_handle) {
+  CHECK(platform_handle.is_valid());
+
+  base::MessageLoop::current()->AddDestructionObserver(this);
+
+  channel_ = Channel::Create(this, std::move(platform_handle),
+                             base::ThreadTaskRunnerHandle::Get());
+  channel_->Start();
+}
+
+BrokerHost::~BrokerHost() {
+  // We're always destroyed on the creation thread, which is the IO thread.
+  base::MessageLoop::current()->RemoveDestructionObserver(this);
+
+  if (channel_)
+    channel_->ShutDown();
+}
+
+void BrokerHost::SendChannel(ScopedPlatformHandle handle) {
+  CHECK(handle.is_valid());
+  CHECK(channel_);
+
+  Channel::MessagePtr message =
+      CreateBrokerMessage(BrokerMessageType::INIT, 1, nullptr);
+  ScopedPlatformHandleVectorPtr handles;
+  handles.reset(new PlatformHandleVector(1));
+  handles->at(0) = handle.release();
+  message->SetHandles(std::move(handles));
+
+  channel_->Write(std::move(message));
+}
+
+void BrokerHost::OnBufferRequest(size_t num_bytes) {
+  scoped_refptr<PlatformSharedBuffer> buffer;
+  scoped_refptr<PlatformSharedBuffer> read_only_buffer;
+  if (num_bytes <= kMaxSharedBufferSize) {
+    buffer = PlatformSharedBuffer::Create(num_bytes);
+    if (buffer)
+      read_only_buffer = buffer->CreateReadOnlyDuplicate();
+    if (!read_only_buffer)
+      buffer = nullptr;
+  } else {
+    LOG(ERROR) << "Shared buffer request too large: " << num_bytes;
+  }
+
+  Channel::MessagePtr message = CreateBrokerMessage(
+      BrokerMessageType::BUFFER_RESPONSE, buffer ? 2 : 0, nullptr);
+  if (buffer) {
+    ScopedPlatformHandleVectorPtr handles;
+    handles.reset(new PlatformHandleVector(2));
+    handles->at(0) = buffer->PassPlatformHandle().release();
+    handles->at(1) = read_only_buffer->PassPlatformHandle().release();
+    message->SetHandles(std::move(handles));
+  }
+
+  channel_->Write(std::move(message));
+}
+
+void BrokerHost::OnChannelMessage(const void* payload,
+                                  size_t payload_size,
+                                  ScopedPlatformHandleVectorPtr handles) {
+  if (payload_size < sizeof(BrokerMessageHeader))
+    return;
+
+  const BrokerMessageHeader* header =
+      static_cast<const BrokerMessageHeader*>(payload);
+  switch (header->type) {
+    case BrokerMessageType::BUFFER_REQUEST:
+      if (payload_size ==
+            sizeof(BrokerMessageHeader) + sizeof(BufferRequestData)) {
+        const BufferRequestData* request =
+            reinterpret_cast<const BufferRequestData*>(header + 1);
+        OnBufferRequest(request->size);
+        return;
+      }
+      break;
+
+    default:
+      break;
+  }
+
+  LOG(ERROR) << "Unexpected broker message type: " << header->type;
+}
+
+void BrokerHost::OnChannelError() {
+  if (channel_) {
+    channel_->ShutDown();
+    channel_ = nullptr;
+  }
+
+  delete this;
+}
+
+void BrokerHost::WillDestroyCurrentMessageLoop() {
+  delete this;
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/broker_messages.h b/mojo/edk/system/broker_messages.h
new file mode 100644
index 0000000..a67be15
--- /dev/null
+++ b/mojo/edk/system/broker_messages.h
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All 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_BROKER_MESSAGES_H_
+#define MOJO_EDK_SYSTEM_BROKER_MESSAGES_H_
+
+#include "mojo/edk/system/channel.h"
+
+namespace mojo {
+namespace edk {
+
+#pragma pack(push, 1)
+
+enum BrokerMessageType : uint32_t {
+  INIT,
+  BUFFER_REQUEST,
+  BUFFER_RESPONSE,
+};
+
+struct BrokerMessageHeader {
+  BrokerMessageType type;
+  uint32_t padding;
+};
+
+static_assert(sizeof(BrokerMessageHeader) % kChannelMessageAlignment == 0,
+              "Invalid header size.");
+
+struct BufferRequestData {
+  uint32_t size;
+};
+
+#pragma pack(pop)
+
+template <typename T>
+inline Channel::MessagePtr CreateBrokerMessage(BrokerMessageType type,
+                                               size_t num_handles,
+                                               T** out_message_data) {
+  const size_t message_size = sizeof(BrokerMessageHeader) + sizeof(T);
+  Channel::MessagePtr message(new Channel::Message(message_size, num_handles));
+  BrokerMessageHeader* header =
+      reinterpret_cast<BrokerMessageHeader*>(message->mutable_payload());
+  header->type = type;
+  header->padding = 0;
+  *out_message_data = reinterpret_cast<T*>(header + 1);
+  return message;
+}
+
+inline Channel::MessagePtr CreateBrokerMessage(
+    BrokerMessageType type,
+    size_t num_handles,
+    std::nullptr_t** dummy_out_data) {
+  Channel::MessagePtr message(
+      new Channel::Message(sizeof(BrokerMessageHeader), num_handles));
+  BrokerMessageHeader* header =
+      reinterpret_cast<BrokerMessageHeader*>(message->mutable_payload());
+  header->type = type;
+  header->padding = 0;
+  return message;
+}
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_BROKER_MESSAGES_H_
diff --git a/mojo/edk/system/broker_posix.cc b/mojo/edk/system/broker_posix.cc
new file mode 100644
index 0000000..62b37b3
--- /dev/null
+++ b/mojo/edk/system/broker_posix.cc
@@ -0,0 +1,125 @@
+// Copyright 2016 The Chromium Authors. All 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/broker.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_handle_utils.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/broker_messages.h"
+#include "mojo/edk/system/channel.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+bool WaitForBrokerMessage(PlatformHandle platform_handle,
+                          BrokerMessageType expected_type,
+                          size_t expected_num_handles,
+                          std::deque<PlatformHandle>* incoming_handles) {
+  Channel::MessagePtr message(
+      new Channel::Message(sizeof(BrokerMessageHeader), expected_num_handles));
+  std::deque<PlatformHandle> incoming_platform_handles;
+  ssize_t read_result = PlatformChannelRecvmsg(
+      platform_handle, const_cast<void*>(message->data()),
+      message->data_num_bytes(), &incoming_platform_handles, true /* block */);
+  bool error = false;
+  if (read_result < 0) {
+    PLOG(ERROR) << "Recvmsg error";
+    error = true;
+  } else if (static_cast<size_t>(read_result) != message->data_num_bytes()) {
+    LOG(ERROR) << "Invalid node channel message. Expected " << message->data_num_bytes() << " got " << read_result;
+    error = true;
+  } else if (incoming_platform_handles.size() != expected_num_handles) {
+    LOG(ERROR) << "Received unexpected number of handles";
+    error = true;
+  }
+
+  if (!error) {
+    const BrokerMessageHeader* header =
+        reinterpret_cast<const BrokerMessageHeader*>(message->payload());
+    if (header->type != expected_type) {
+      LOG(ERROR) << "Unexpected message";
+      error = true;
+    }
+  }
+
+  if (error) {
+    CloseAllPlatformHandles(&incoming_platform_handles);
+  } else {
+    if (incoming_handles)
+      incoming_handles->swap(incoming_platform_handles);
+  }
+  return !error;
+}
+
+}  // namespace
+
+Broker::Broker(ScopedPlatformHandle platform_handle)
+    : sync_channel_(std::move(platform_handle)) {
+  CHECK(sync_channel_.is_valid());
+
+  // Mark the channel as blocking.
+  int flags = fcntl(sync_channel_.get().handle, F_GETFL);
+  PCHECK(flags != -1);
+  flags = fcntl(sync_channel_.get().handle, F_SETFL, flags & ~O_NONBLOCK);
+  PCHECK(flags != -1);
+
+  // Wait for the first message, which should contain a handle.
+  std::deque<PlatformHandle> incoming_platform_handles;
+  if (WaitForBrokerMessage(sync_channel_.get(), BrokerMessageType::INIT, 1,
+                           &incoming_platform_handles)) {
+    parent_channel_ = ScopedPlatformHandle(incoming_platform_handles.front());
+  }
+}
+
+Broker::~Broker() = default;
+
+ScopedPlatformHandle Broker::GetParentPlatformHandle() {
+  return std::move(parent_channel_);
+}
+
+scoped_refptr<PlatformSharedBuffer> Broker::GetSharedBuffer(size_t num_bytes) {
+  base::AutoLock lock(lock_);
+
+  BufferRequestData* buffer_request;
+  Channel::MessagePtr out_message = CreateBrokerMessage(
+      BrokerMessageType::BUFFER_REQUEST, 0, &buffer_request);
+  buffer_request->size = num_bytes;
+  ssize_t write_result = PlatformChannelWrite(
+      sync_channel_.get(), out_message->data(), out_message->data_num_bytes());
+  if (write_result < 0) {
+    PLOG(ERROR) << "Error sending sync broker message";
+    return nullptr;
+  } else if (static_cast<size_t>(write_result) !=
+             out_message->data_num_bytes()) {
+    LOG(ERROR) << "Error sending complete broker message";
+    return nullptr;
+  }
+
+  std::deque<PlatformHandle> incoming_platform_handles;
+  if (WaitForBrokerMessage(sync_channel_.get(),
+                           BrokerMessageType::BUFFER_RESPONSE, 2,
+                           &incoming_platform_handles)) {
+    ScopedPlatformHandle rw_handle(incoming_platform_handles.front());
+    incoming_platform_handles.pop_front();
+    ScopedPlatformHandle ro_handle(incoming_platform_handles.front());
+    return PlatformSharedBuffer::CreateFromPlatformHandlePair(
+        num_bytes, std::move(rw_handle), std::move(ro_handle));
+  }
+
+  return nullptr;
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/channel.cc b/mojo/edk/system/channel.cc
new file mode 100644
index 0000000..56509b5
--- /dev/null
+++ b/mojo/edk/system/channel.cc
@@ -0,0 +1,587 @@
+// Copyright 2016 The Chromium Authors. All 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 <string.h>
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/aligned_memory.h"
+#include "base/process/process_handle.h"
+#include "mojo/edk/embedder/platform_handle.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mach_logging.h"
+#elif defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+static_assert(sizeof(Channel::Message::Header) % kChannelMessageAlignment == 0,
+    "Invalid Header size.");
+
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+static_assert(sizeof(Channel::Message::Header) == 8,
+              "Header must be 8 bytes on ChromeOS and Android");
+#endif
+
+}  // namespace
+
+const size_t kReadBufferSize = 4096;
+const size_t kMaxUnusedReadBufferCapacity = 64 * 1024;
+const size_t kMaxChannelMessageSize = 256 * 1024 * 1024;
+const size_t kMaxAttachedHandles = 128;
+
+Channel::Message::Message(size_t payload_size,
+                          size_t max_handles,
+                          Header::MessageType message_type)
+    : max_handles_(max_handles) {
+  DCHECK_LE(max_handles_, kMaxAttachedHandles);
+
+  size_t extra_header_size = 0;
+#if defined(OS_WIN)
+  // On Windows we serialize HANDLEs into the extra header space.
+  extra_header_size = max_handles_ * sizeof(HandleEntry);
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  // On OSX, some of the platform handles may be mach ports, which are
+  // serialised into the message buffer. Since there could be a mix of fds and
+  // mach ports, we store the mach ports as an <index, port> pair (of uint32_t),
+  // so that the original ordering of handles can be re-created.
+  if (max_handles) {
+    extra_header_size =
+        sizeof(MachPortsExtraHeader) + (max_handles * sizeof(MachPortsEntry));
+  }
+#endif
+  // Pad extra header data to be aliged to |kChannelMessageAlignment| bytes.
+  if (extra_header_size % kChannelMessageAlignment) {
+    extra_header_size += kChannelMessageAlignment -
+                         (extra_header_size % kChannelMessageAlignment);
+  }
+  DCHECK_EQ(0u, extra_header_size % kChannelMessageAlignment);
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+  DCHECK_EQ(0u, extra_header_size);
+#endif
+
+  size_ = sizeof(Header) + extra_header_size + payload_size;
+  data_ = static_cast<char*>(base::AlignedAlloc(size_,
+                                                kChannelMessageAlignment));
+  // Only zero out the header and not the payload. Since the payload is going to
+  // be memcpy'd, zeroing the payload is unnecessary work and a significant
+  // performance issue when dealing with large messages. Any sanitizer errors
+  // complaining about an uninitialized read in the payload area should be
+  // treated as an error and fixed.
+  memset(data_, 0, sizeof(Header) + extra_header_size);
+  header_ = reinterpret_cast<Header*>(data_);
+
+  DCHECK_LE(size_, std::numeric_limits<uint32_t>::max());
+  header_->num_bytes = static_cast<uint32_t>(size_);
+
+  DCHECK_LE(sizeof(Header) + extra_header_size,
+            std::numeric_limits<uint16_t>::max());
+  header_->message_type = message_type;
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+  header_->num_handles = static_cast<uint16_t>(max_handles);
+#else
+  header_->num_header_bytes =
+      static_cast<uint16_t>(sizeof(Header) + extra_header_size);
+#endif
+
+  if (max_handles_ > 0) {
+#if defined(OS_WIN)
+    handles_ = reinterpret_cast<HandleEntry*>(mutable_extra_header());
+    // Initialize all handles to invalid values.
+    for (size_t i = 0; i < max_handles_; ++i)
+      handles_[i].handle = base::win::HandleToUint32(INVALID_HANDLE_VALUE);
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+    mach_ports_header_ =
+        reinterpret_cast<MachPortsExtraHeader*>(mutable_extra_header());
+    mach_ports_header_->num_ports = 0;
+    // Initialize all handles to invalid values.
+    for (size_t i = 0; i < max_handles_; ++i) {
+      mach_ports_header_->entries[i] =
+          {0, static_cast<uint32_t>(MACH_PORT_NULL)};
+    }
+#endif
+  }
+}
+
+Channel::Message::~Message() {
+  base::AlignedFree(data_);
+}
+
+// static
+Channel::MessagePtr Channel::Message::Deserialize(const void* data,
+                                                  size_t data_num_bytes) {
+  if (data_num_bytes < sizeof(Header))
+    return nullptr;
+
+  const Header* header = reinterpret_cast<const Header*>(data);
+  if (header->num_bytes != data_num_bytes) {
+    DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes
+                << " != " << data_num_bytes;
+    return nullptr;
+  }
+
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+  size_t payload_size = data_num_bytes - sizeof(Header);
+  const char* payload = static_cast<const char*>(data) + sizeof(Header);
+#else
+  if (header->num_bytes < header->num_header_bytes ||
+      header->num_header_bytes < sizeof(Header)) {
+    DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes << " < "
+                << header->num_header_bytes;
+    return nullptr;
+  }
+
+  uint32_t extra_header_size = header->num_header_bytes - sizeof(Header);
+  size_t payload_size = data_num_bytes - header->num_header_bytes;
+  const char* payload =
+      static_cast<const char*>(data) + header->num_header_bytes;
+#endif  // defined(MOJO_EDK_LEGACY_PROTOCOL)
+
+#if defined(OS_WIN)
+  uint32_t max_handles = extra_header_size / sizeof(HandleEntry);
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  if (extra_header_size < sizeof(MachPortsExtraHeader)) {
+    DLOG(ERROR) << "Decoding invalid message: " << extra_header_size << " < "
+                << sizeof(MachPortsExtraHeader);
+    return nullptr;
+  }
+  uint32_t max_handles = (extra_header_size - sizeof(MachPortsExtraHeader)) /
+      sizeof(MachPortsEntry);
+#else
+  const uint32_t max_handles = 0;
+#endif  // defined(OS_WIN)
+
+  if (header->num_handles > max_handles || max_handles > kMaxAttachedHandles) {
+    DLOG(ERROR) << "Decoding invalid message:" << header->num_handles
+                << " > " << max_handles;
+    return nullptr;
+  }
+
+  MessagePtr message(new Message(payload_size, max_handles));
+  DCHECK_EQ(message->data_num_bytes(), data_num_bytes);
+
+  // Copy all payload bytes.
+  if (payload_size)
+    memcpy(message->mutable_payload(), payload, payload_size);
+
+#if !defined(MOJO_EDK_LEGACY_PROTOCOL)
+  DCHECK_EQ(message->extra_header_size(), extra_header_size);
+  DCHECK_EQ(message->header_->num_header_bytes, header->num_header_bytes);
+
+  if (message->extra_header_size()) {
+    // Copy extra header bytes.
+    memcpy(message->mutable_extra_header(),
+           static_cast<const char*>(data) + sizeof(Header),
+           message->extra_header_size());
+  }
+#endif
+
+  message->header_->num_handles = header->num_handles;
+#if defined(OS_WIN)
+  ScopedPlatformHandleVectorPtr handles(
+      new PlatformHandleVector(header->num_handles));
+  for (size_t i = 0; i < header->num_handles; i++) {
+    (*handles)[i].handle = reinterpret_cast<HANDLE>(
+        static_cast<uintptr_t>(message->handles_[i].handle));
+  }
+  message->SetHandles(std::move(handles));
+#endif
+
+  return message;
+}
+
+size_t Channel::Message::payload_size() const {
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+  return header_->num_bytes - sizeof(Header);
+#else
+  return size_ - header_->num_header_bytes;
+#endif
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+bool Channel::Message::has_mach_ports() const {
+  if (!has_handles())
+    return false;
+
+  for (const auto& handle : (*handle_vector_)) {
+    if (handle.type == PlatformHandle::Type::MACH ||
+        handle.type == PlatformHandle::Type::MACH_NAME) {
+      return true;
+    }
+  }
+  return false;
+}
+#endif
+
+void Channel::Message::SetHandles(ScopedPlatformHandleVectorPtr new_handles) {
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+  // Old semantics for ChromeOS and Android
+  if (header_->num_handles == 0) {
+    CHECK(!new_handles || new_handles->size() == 0);
+    return;
+  }
+  CHECK(new_handles && new_handles->size() == header_->num_handles);
+  std::swap(handle_vector_, new_handles);
+
+#else
+  if (max_handles_ == 0) {
+    CHECK(!new_handles || new_handles->size() == 0);
+    return;
+  }
+
+  CHECK(new_handles && new_handles->size() <= max_handles_);
+  header_->num_handles = static_cast<uint16_t>(new_handles->size());
+  std::swap(handle_vector_, new_handles);
+#if defined(OS_WIN)
+  memset(handles_, 0, extra_header_size());
+  for (size_t i = 0; i < handle_vector_->size(); i++)
+    handles_[i].handle = base::win::HandleToUint32((*handle_vector_)[i].handle);
+#endif  // defined(OS_WIN)
+#endif  // defined(MOJO_EDK_LEGACY_PROTOCOL)
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  size_t mach_port_index = 0;
+  if (mach_ports_header_) {
+    for (size_t i = 0; i < max_handles_; ++i) {
+      mach_ports_header_->entries[i] =
+          {0, static_cast<uint32_t>(MACH_PORT_NULL)};
+    }
+    for (size_t i = 0; i < handle_vector_->size(); i++) {
+      if ((*handle_vector_)[i].type == PlatformHandle::Type::MACH ||
+          (*handle_vector_)[i].type == PlatformHandle::Type::MACH_NAME) {
+        mach_port_t port = (*handle_vector_)[i].port;
+        mach_ports_header_->entries[mach_port_index].index = i;
+        mach_ports_header_->entries[mach_port_index].mach_port = port;
+        mach_port_index++;
+      }
+    }
+    mach_ports_header_->num_ports = static_cast<uint16_t>(mach_port_index);
+  }
+#endif
+}
+
+ScopedPlatformHandleVectorPtr Channel::Message::TakeHandles() {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  if (mach_ports_header_) {
+    for (size_t i = 0; i < max_handles_; ++i) {
+      mach_ports_header_->entries[i] =
+          {0, static_cast<uint32_t>(MACH_PORT_NULL)};
+    }
+    mach_ports_header_->num_ports = 0;
+  }
+  header_->num_handles = 0;
+  return std::move(handle_vector_);
+#else
+  header_->num_handles = 0;
+  return std::move(handle_vector_);
+#endif
+}
+
+ScopedPlatformHandleVectorPtr Channel::Message::TakeHandlesForTransport() {
+#if defined(OS_WIN)
+  // Not necessary on Windows.
+  NOTREACHED();
+  return nullptr;
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  if (handle_vector_) {
+    for (auto it = handle_vector_->begin(); it != handle_vector_->end(); ) {
+      if (it->type == PlatformHandle::Type::MACH ||
+          it->type == PlatformHandle::Type::MACH_NAME) {
+        // For Mach port names, we can can just leak them. They're not real
+        // ports anyways. For real ports, they're leaked because this is a child
+        // process and the remote process will take ownership.
+        it = handle_vector_->erase(it);
+      } else {
+        ++it;
+      }
+    }
+  }
+  return std::move(handle_vector_);
+#else
+  return std::move(handle_vector_);
+#endif
+}
+
+#if defined(OS_WIN)
+// static
+bool Channel::Message::RewriteHandles(base::ProcessHandle from_process,
+                                      base::ProcessHandle to_process,
+                                      PlatformHandleVector* handles) {
+  bool success = true;
+  for (size_t i = 0; i < handles->size(); ++i) {
+    if (!(*handles)[i].is_valid()) {
+      DLOG(ERROR) << "Refusing to duplicate invalid handle.";
+      continue;
+    }
+    DCHECK_EQ((*handles)[i].owning_process, from_process);
+    BOOL result = DuplicateHandle(
+        from_process, (*handles)[i].handle, to_process,
+        &(*handles)[i].handle, 0, FALSE,
+        DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+    if (result) {
+      (*handles)[i].owning_process = to_process;
+    } else {
+      success = false;
+
+      // If handle duplication fails, the source handle will already be closed
+      // due to DUPLICATE_CLOSE_SOURCE. Replace the handle in the message with
+      // an invalid handle.
+      (*handles)[i].handle = INVALID_HANDLE_VALUE;
+      (*handles)[i].owning_process = base::GetCurrentProcessHandle();
+    }
+  }
+  return success;
+}
+#endif
+
+// Helper class for managing a Channel's read buffer allocations. This maintains
+// a single contiguous buffer with the layout:
+//
+//   [discarded bytes][occupied bytes][unoccupied bytes]
+//
+// The Reserve() method ensures that a certain capacity of unoccupied bytes are
+// available. It does not claim that capacity and only allocates new capacity
+// when strictly necessary.
+//
+// Claim() marks unoccupied bytes as occupied.
+//
+// Discard() marks occupied bytes as discarded, signifying that their contents
+// can be forgotten or overwritten.
+//
+// Realign() moves occupied bytes to the front of the buffer so that those
+// occupied bytes are properly aligned.
+//
+// The most common Channel behavior in practice should result in very few
+// allocations and copies, as memory is claimed and discarded shortly after
+// being reserved, and future reservations will immediately reuse discarded
+// memory.
+class Channel::ReadBuffer {
+ public:
+  ReadBuffer() {
+    size_ = kReadBufferSize;
+    data_ = static_cast<char*>(base::AlignedAlloc(size_,
+                                                  kChannelMessageAlignment));
+  }
+
+  ~ReadBuffer() {
+    DCHECK(data_);
+    base::AlignedFree(data_);
+  }
+
+  const char* occupied_bytes() const { return data_ + num_discarded_bytes_; }
+
+  size_t num_occupied_bytes() const {
+    return num_occupied_bytes_ - num_discarded_bytes_;
+  }
+
+  // Ensures the ReadBuffer has enough contiguous space allocated to hold
+  // |num_bytes| more bytes; returns the address of the first available byte.
+  char* Reserve(size_t num_bytes) {
+    if (num_occupied_bytes_ + num_bytes > size_) {
+      size_ = std::max(size_ * 2, num_occupied_bytes_ + num_bytes);
+      void* new_data = base::AlignedAlloc(size_, kChannelMessageAlignment);
+      memcpy(new_data, data_, num_occupied_bytes_);
+      base::AlignedFree(data_);
+      data_ = static_cast<char*>(new_data);
+    }
+
+    return data_ + num_occupied_bytes_;
+  }
+
+  // Marks the first |num_bytes| unoccupied bytes as occupied.
+  void Claim(size_t num_bytes) {
+    DCHECK_LE(num_occupied_bytes_ + num_bytes, size_);
+    num_occupied_bytes_ += num_bytes;
+  }
+
+  // Marks the first |num_bytes| occupied bytes as discarded. This may result in
+  // shrinkage of the internal buffer, and it is not safe to assume the result
+  // of a previous Reserve() call is still valid after this.
+  void Discard(size_t num_bytes) {
+    DCHECK_LE(num_discarded_bytes_ + num_bytes, num_occupied_bytes_);
+    num_discarded_bytes_ += num_bytes;
+
+    if (num_discarded_bytes_ == num_occupied_bytes_) {
+      // We can just reuse the buffer from the beginning in this common case.
+      num_discarded_bytes_ = 0;
+      num_occupied_bytes_ = 0;
+    }
+
+    if (num_discarded_bytes_ > kMaxUnusedReadBufferCapacity) {
+      // In the uncommon case that we have a lot of discarded data at the
+      // front of the buffer, simply move remaining data to a smaller buffer.
+      size_t num_preserved_bytes = num_occupied_bytes_ - num_discarded_bytes_;
+      size_ = std::max(num_preserved_bytes, kReadBufferSize);
+      char* new_data = static_cast<char*>(
+          base::AlignedAlloc(size_, kChannelMessageAlignment));
+      memcpy(new_data, data_ + num_discarded_bytes_, num_preserved_bytes);
+      base::AlignedFree(data_);
+      data_ = new_data;
+      num_discarded_bytes_ = 0;
+      num_occupied_bytes_ = num_preserved_bytes;
+    }
+
+    if (num_occupied_bytes_ == 0 && size_ > kMaxUnusedReadBufferCapacity) {
+      // Opportunistically shrink the read buffer back down to a small size if
+      // it's grown very large. We only do this if there are no remaining
+      // unconsumed bytes in the buffer to avoid copies in most the common
+      // cases.
+      size_ = kMaxUnusedReadBufferCapacity;
+      base::AlignedFree(data_);
+      data_ = static_cast<char*>(
+          base::AlignedAlloc(size_, kChannelMessageAlignment));
+    }
+  }
+
+  void Realign() {
+    size_t num_bytes = num_occupied_bytes();
+    memmove(data_, occupied_bytes(), num_bytes);
+    num_discarded_bytes_ = 0;
+    num_occupied_bytes_ = num_bytes;
+  }
+
+ private:
+  char* data_ = nullptr;
+
+  // The total size of the allocated buffer.
+  size_t size_ = 0;
+
+  // The number of discarded bytes at the beginning of the allocated buffer.
+  size_t num_discarded_bytes_ = 0;
+
+  // The total number of occupied bytes, including discarded bytes.
+  size_t num_occupied_bytes_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(ReadBuffer);
+};
+
+Channel::Channel(Delegate* delegate)
+    : delegate_(delegate), read_buffer_(new ReadBuffer) {
+}
+
+Channel::~Channel() {
+}
+
+void Channel::ShutDown() {
+  delegate_ = nullptr;
+  ShutDownImpl();
+}
+
+char* Channel::GetReadBuffer(size_t *buffer_capacity) {
+  DCHECK(read_buffer_);
+  size_t required_capacity = *buffer_capacity;
+  if (!required_capacity)
+    required_capacity = kReadBufferSize;
+
+  *buffer_capacity = required_capacity;
+  return read_buffer_->Reserve(required_capacity);
+}
+
+bool Channel::OnReadComplete(size_t bytes_read, size_t *next_read_size_hint) {
+  bool did_dispatch_message = false;
+  read_buffer_->Claim(bytes_read);
+  while (read_buffer_->num_occupied_bytes() >= sizeof(Message::Header)) {
+    // Ensure the occupied data is properly aligned. If it isn't, a SIGBUS could
+    // happen on architectures that don't allow misaligned words access (i.e.
+    // anything other than x86). Only re-align when necessary to avoid copies.
+    if (reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()) %
+        kChannelMessageAlignment != 0)
+      read_buffer_->Realign();
+
+    // We have at least enough data available for a MessageHeader.
+    const Message::Header* header = reinterpret_cast<const Message::Header*>(
+        read_buffer_->occupied_bytes());
+    if (header->num_bytes < sizeof(Message::Header) ||
+        header->num_bytes > kMaxChannelMessageSize) {
+      LOG(ERROR) << "Invalid message size: " << header->num_bytes;
+      return false;
+    }
+
+    if (read_buffer_->num_occupied_bytes() < header->num_bytes) {
+      // Not enough data available to read the full message. Hint to the
+      // implementation that it should try reading the full size of the message.
+      *next_read_size_hint =
+          header->num_bytes - read_buffer_->num_occupied_bytes();
+      return true;
+    }
+
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+    size_t extra_header_size = 0;
+    const void* extra_header = nullptr;
+    size_t payload_size = header->num_bytes - sizeof(Message::Header);
+    void* payload = payload_size ? const_cast<Message::Header*>(&header[1])
+                                 : nullptr;
+#else
+    if (header->num_header_bytes < sizeof(Message::Header) ||
+        header->num_header_bytes > header->num_bytes) {
+      LOG(ERROR) << "Invalid message header size: " << header->num_header_bytes;
+      return false;
+    }
+    size_t extra_header_size =
+        header->num_header_bytes - sizeof(Message::Header);
+    const void* extra_header = extra_header_size ? header + 1 : nullptr;
+    size_t payload_size = header->num_bytes - header->num_header_bytes;
+    void* payload =
+        payload_size ? reinterpret_cast<Message::Header*>(
+                           const_cast<char*>(read_buffer_->occupied_bytes()) +
+                           header->num_header_bytes)
+                     : nullptr;
+#endif  // defined(MOJO_EDK_LEGACY_PROTOCOL)
+
+    ScopedPlatformHandleVectorPtr handles;
+    if (header->num_handles > 0) {
+      if (!GetReadPlatformHandles(header->num_handles, extra_header,
+                                 extra_header_size, &handles)) {
+        return false;
+      }
+
+      if (!handles) {
+        // Not enough handles available for this message.
+        break;
+      }
+    }
+
+    // We've got a complete message! Dispatch it and try another.
+    if (header->message_type != Message::Header::MessageType::NORMAL) {
+      if (!OnControlMessage(header->message_type, payload, payload_size,
+                            std::move(handles))) {
+        return false;
+      }
+      did_dispatch_message = true;
+    } else if (delegate_) {
+      delegate_->OnChannelMessage(payload, payload_size, std::move(handles));
+      did_dispatch_message = true;
+    }
+
+    read_buffer_->Discard(header->num_bytes);
+  }
+
+  *next_read_size_hint = did_dispatch_message ? 0 : kReadBufferSize;
+  return true;
+}
+
+void Channel::OnError() {
+  if (delegate_)
+    delegate_->OnChannelError();
+}
+
+bool Channel::OnControlMessage(Message::Header::MessageType message_type,
+                               const void* payload,
+                               size_t payload_size,
+                               ScopedPlatformHandleVectorPtr handles) {
+  return false;
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h
new file mode 100644
index 0000000..aa6d70c
--- /dev/null
+++ b/mojo/edk/system/channel.h
@@ -0,0 +1,292 @@
+// Copyright 2016 The Chromium Authors. All 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 "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/process/process_handle.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace mojo {
+namespace edk {
+
+const size_t kChannelMessageAlignment = 8;
+
+// Channel provides a thread-safe interface to read and write arbitrary
+// delimited messages over an underlying I/O channel, optionally transferring
+// one or more platform handles in the process.
+class Channel : public base::RefCountedThreadSafe<Channel> {
+ public:
+  struct Message;
+
+  using MessagePtr = std::unique_ptr<Message>;
+
+  // A message to be written to a channel.
+  struct Message {
+#pragma pack(push, 1)
+    struct Header {
+      enum class MessageType : uint16_t {
+        // A normal message.
+        NORMAL = 0,
+#if defined(OS_MACOSX)
+        // A control message containing handles to echo back.
+        HANDLES_SENT,
+        // A control message containing handles that can now be closed.
+        HANDLES_SENT_ACK,
+#endif
+      };
+
+      // Message size in bytes, including the header.
+      uint32_t num_bytes;
+
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+      // Old message wire format for ChromeOS and Android.
+      // Number of attached handles.
+      uint16_t num_handles;
+
+      MessageType message_type;
+#else
+      // Total size of header, including extra header data (i.e. HANDLEs on
+      // windows).
+      uint16_t num_header_bytes;
+
+      // Number of attached handles. May be less than the reserved handle
+      // storage size in this message on platforms that serialise handles as
+      // data (i.e. HANDLEs on Windows, Mach ports on OSX).
+      uint16_t num_handles;
+
+      MessageType message_type;
+
+      char padding[6];
+#endif  // defined(MOJO_EDK_LEGACY_PROTOCOL)
+    };
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+    struct MachPortsEntry {
+      // Index of Mach port in the original vector of PlatformHandles.
+      uint16_t index;
+
+      // Mach port name.
+      uint32_t mach_port;
+      static_assert(sizeof(mach_port_t) <= sizeof(uint32_t),
+                    "mach_port_t must be no larger than uint32_t");
+    };
+    static_assert(sizeof(MachPortsEntry) == 6,
+                  "sizeof(MachPortsEntry) must be 6 bytes");
+
+    // Structure of the extra header field when present on OSX.
+    struct MachPortsExtraHeader {
+      // Actual number of Mach ports encoded in the extra header.
+      uint16_t num_ports;
+
+      // Array of encoded Mach ports. If |num_ports| > 0, |entires[0]| through
+      // to |entries[num_ports-1]| inclusive are valid.
+      MachPortsEntry entries[0];
+    };
+    static_assert(sizeof(MachPortsExtraHeader) == 2,
+                  "sizeof(MachPortsExtraHeader) must be 2 bytes");
+#elif defined(OS_WIN)
+    struct HandleEntry {
+      // The windows HANDLE. HANDLEs are guaranteed to fit inside 32-bits.
+      // See: https://msdn.microsoft.com/en-us/library/aa384203(VS.85).aspx
+      uint32_t handle;
+    };
+    static_assert(sizeof(HandleEntry) == 4,
+                  "sizeof(HandleEntry) must be 4 bytes");
+#endif
+#pragma pack(pop)
+
+    // Allocates and owns a buffer for message data with enough capacity for
+    // |payload_size| bytes plus a header, plus |max_handles| platform handles.
+    Message(size_t payload_size,
+            size_t max_handles,
+            Header::MessageType message_type = Header::MessageType::NORMAL);
+
+    ~Message();
+
+    // Constructs a Message from serialized message data.
+    static MessagePtr Deserialize(const void* data, size_t data_num_bytes);
+
+    const void* data() const { return data_; }
+    size_t data_num_bytes() const { return size_; }
+
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+    void* mutable_payload() { return static_cast<void*>(header_ + 1); }
+    const void* payload() const {
+      return static_cast<const void*>(header_ + 1);
+    }
+    size_t payload_size() const;
+#else
+    const void* extra_header() const { return data_ + sizeof(Header); }
+    void* mutable_extra_header() { return data_ + sizeof(Header); }
+    size_t extra_header_size() const {
+      return header_->num_header_bytes - sizeof(Header);
+    }
+
+    void* mutable_payload() { return data_ + header_->num_header_bytes; }
+    const void* payload() const { return data_ + header_->num_header_bytes; }
+    size_t payload_size() const;
+#endif  // defined(MOJO_EDK_LEGACY_PROTOCOL)
+
+    size_t num_handles() const { return header_->num_handles; }
+    bool has_handles() const { return header_->num_handles > 0; }
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+    bool has_mach_ports() const;
+#endif
+
+    // Note: SetHandles() and TakeHandles() invalidate any previous value of
+    // handles().
+    void SetHandles(ScopedPlatformHandleVectorPtr new_handles);
+    ScopedPlatformHandleVectorPtr TakeHandles();
+    // Version of TakeHandles that returns a vector of platform handles suitable
+    // for transfer over an underlying OS mechanism. i.e. file descriptors over
+    // a unix domain socket. Any handle that cannot be transferred this way,
+    // such as Mach ports, will be removed.
+    ScopedPlatformHandleVectorPtr TakeHandlesForTransport();
+
+#if defined(OS_WIN)
+    // Prepares the handles in this message for use in a different process.
+    // Upon calling this the handles should belong to |from_process|; after the
+    // call they'll belong to |to_process|. The source handles are always
+    // closed by this call. Returns false iff one or more handles failed
+    // duplication.
+    static bool RewriteHandles(base::ProcessHandle from_process,
+                               base::ProcessHandle to_process,
+                               PlatformHandleVector* handles);
+#endif
+
+   private:
+    size_t size_;
+    size_t max_handles_;
+    char* data_;
+    Header* header_;
+
+    ScopedPlatformHandleVectorPtr handle_vector_;
+
+#if defined(OS_WIN)
+    // On Windows, handles are serialised into the extra header section.
+    HandleEntry* handles_ = nullptr;
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+    // On OSX, handles are serialised into the extra header section.
+    MachPortsExtraHeader* mach_ports_header_ = nullptr;
+#endif
+
+    DISALLOW_COPY_AND_ASSIGN(Message);
+  };
+
+  // Delegate methods are called from the I/O task runner with which the Channel
+  // was created (see Channel::Create).
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    // Notify of a received message. |payload| is not owned and must not be
+    // retained; it will be null if |payload_size| is 0. |handles| are
+    // transferred to the callee.
+    virtual void OnChannelMessage(const void* payload,
+                                  size_t payload_size,
+                                  ScopedPlatformHandleVectorPtr handles) = 0;
+
+    // Notify that an error has occured and the Channel will cease operation.
+    virtual void OnChannelError() = 0;
+  };
+
+  // Creates a new Channel around a |platform_handle|, taking ownership of the
+  // handle. All I/O on the handle will be performed on |io_task_runner|.
+  // Note that ShutDown() MUST be called on the Channel some time before
+  // |delegate| is destroyed.
+  static scoped_refptr<Channel> Create(
+      Delegate* delegate,
+      ScopedPlatformHandle platform_handle,
+      scoped_refptr<base::TaskRunner> io_task_runner);
+
+  // Request that the channel be shut down. This should always be called before
+  // releasing the last reference to a Channel to ensure that it's cleaned up
+  // on its I/O task runner's thread.
+  //
+  // Delegate methods will no longer be invoked after this call.
+  void ShutDown();
+
+  // Begin processing I/O events. Delegate methods must only be invoked after
+  // this call.
+  virtual void Start() = 0;
+
+  // Stop processing I/O events.
+  virtual void ShutDownImpl() = 0;
+
+  // Queues an outgoing message on the Channel. This message will either
+  // eventually be written or will fail to write and trigger
+  // Delegate::OnChannelError.
+  virtual void Write(MessagePtr message) = 0;
+
+  // Causes the platform handle to leak when this channel is shut down instead
+  // of closing it.
+  virtual void LeakHandle() = 0;
+
+ protected:
+  explicit Channel(Delegate* delegate);
+  virtual ~Channel();
+
+  // Called by the implementation when it wants somewhere to stick data.
+  // |*buffer_capacity| may be set by the caller to indicate the desired buffer
+  // size. If 0, a sane default size will be used instead.
+  //
+  // Returns the address of a buffer which can be written to, and indicates its
+  // actual capacity in |*buffer_capacity|.
+  char* GetReadBuffer(size_t* buffer_capacity);
+
+  // Called by the implementation when new data is available in the read
+  // buffer. Returns false to indicate an error. Upon success,
+  // |*next_read_size_hint| will be set to a recommended size for the next
+  // read done by the implementation.
+  bool OnReadComplete(size_t bytes_read, size_t* next_read_size_hint);
+
+  // Called by the implementation when something goes horribly wrong. It is NOT
+  // OK to call this synchronously from any public interface methods.
+  void OnError();
+
+  // Retrieves the set of platform handles read for a given message.
+  // |extra_header| and |extra_header_size| correspond to the extra header data.
+  // Depending on the Channel implementation, this body may encode platform
+  // handles, or handles may be stored and managed elsewhere by the
+  // implementation.
+  //
+  // Returns |false| on unrecoverable error (i.e. the Channel should be closed).
+  // Returns |true| otherwise. Note that it is possible on some platforms for an
+  // insufficient number of handles to be available when this call is made, but
+  // this is not necessarily an error condition. In such cases this returns
+  // |true| but |*handles| will also be reset to null.
+  virtual bool GetReadPlatformHandles(
+      size_t num_handles,
+      const void* extra_header,
+      size_t extra_header_size,
+      ScopedPlatformHandleVectorPtr* handles) = 0;
+
+  // Handles a received control message. Returns |true| if the message is
+  // accepted, or |false| otherwise.
+  virtual bool OnControlMessage(Message::Header::MessageType message_type,
+                                const void* payload,
+                                size_t payload_size,
+                                ScopedPlatformHandleVectorPtr handles);
+
+ private:
+  friend class base::RefCountedThreadSafe<Channel>;
+
+  class ReadBuffer;
+
+  Delegate* delegate_;
+  const std::unique_ptr<ReadBuffer> read_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(Channel);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_CHANNEL_H_
diff --git a/mojo/edk/system/channel_posix.cc b/mojo/edk/system/channel_posix.cc
new file mode 100644
index 0000000..16a9304
--- /dev/null
+++ b/mojo/edk/system/channel_posix.cc
@@ -0,0 +1,520 @@
+// Copyright 2016 The Chromium Authors. All 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 <errno.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <deque>
+#include <limits>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+
+#if !defined(OS_NACL)
+#include <sys/uio.h>
+#endif
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+const size_t kMaxBatchReadCapacity = 256 * 1024;
+
+// A view over a Channel::Message object. The write queue uses these since
+// large messages may need to be sent in chunks.
+class MessageView {
+ public:
+  // Owns |message|. |offset| indexes the first unsent byte in the message.
+  MessageView(Channel::MessagePtr message, size_t offset)
+      : message_(std::move(message)),
+        offset_(offset),
+        handles_(message_->TakeHandlesForTransport()) {
+    DCHECK_GT(message_->data_num_bytes(), offset_);
+  }
+
+  MessageView(MessageView&& other) { *this = std::move(other); }
+
+  MessageView& operator=(MessageView&& other) {
+    message_ = std::move(other.message_);
+    offset_ = other.offset_;
+    handles_ = std::move(other.handles_);
+    return *this;
+  }
+
+  ~MessageView() {}
+
+  const void* data() const {
+    return static_cast<const char*>(message_->data()) + offset_;
+  }
+
+  size_t data_num_bytes() const { return message_->data_num_bytes() - offset_; }
+
+  size_t data_offset() const { return offset_; }
+  void advance_data_offset(size_t num_bytes) {
+    DCHECK_GT(message_->data_num_bytes(), offset_ + num_bytes);
+    offset_ += num_bytes;
+  }
+
+  ScopedPlatformHandleVectorPtr TakeHandles() { return std::move(handles_); }
+  Channel::MessagePtr TakeMessage() { return std::move(message_); }
+
+  void SetHandles(ScopedPlatformHandleVectorPtr handles) {
+    handles_ = std::move(handles);
+  }
+
+ private:
+  Channel::MessagePtr message_;
+  size_t offset_;
+  ScopedPlatformHandleVectorPtr handles_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageView);
+};
+
+class ChannelPosix : public Channel,
+                     public base::MessageLoop::DestructionObserver,
+                     public base::MessageLoopForIO::Watcher {
+ public:
+  ChannelPosix(Delegate* delegate,
+               ScopedPlatformHandle handle,
+               scoped_refptr<base::TaskRunner> io_task_runner)
+      : Channel(delegate),
+        self_(this),
+        handle_(std::move(handle)),
+        io_task_runner_(io_task_runner)
+#if defined(OS_MACOSX)
+        ,
+        handles_to_close_(new PlatformHandleVector)
+#endif
+  {
+  }
+
+  void Start() override {
+    if (io_task_runner_->RunsTasksOnCurrentThread()) {
+      StartOnIOThread();
+    } else {
+      io_task_runner_->PostTask(
+          FROM_HERE, base::Bind(&ChannelPosix::StartOnIOThread, this));
+    }
+  }
+
+  void ShutDownImpl() override {
+    // Always shut down asynchronously when called through the public interface.
+    io_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&ChannelPosix::ShutDownOnIOThread, this));
+  }
+
+  void Write(MessagePtr message) override {
+    bool write_error = false;
+    {
+      base::AutoLock lock(write_lock_);
+      if (reject_writes_)
+        return;
+      if (outgoing_messages_.empty()) {
+        if (!WriteNoLock(MessageView(std::move(message), 0)))
+          reject_writes_ = write_error = true;
+      } else {
+        outgoing_messages_.emplace_back(std::move(message), 0);
+      }
+    }
+    if (write_error) {
+      // Do not synchronously invoke OnError(). Write() may have been called by
+      // the delegate and we don't want to re-enter it.
+      io_task_runner_->PostTask(FROM_HERE,
+                                base::Bind(&ChannelPosix::OnError, this));
+    }
+  }
+
+  void LeakHandle() override {
+    DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+    leak_handle_ = true;
+  }
+
+  bool GetReadPlatformHandles(
+      size_t num_handles,
+      const void* extra_header,
+      size_t extra_header_size,
+      ScopedPlatformHandleVectorPtr* handles) override {
+    if (num_handles > std::numeric_limits<uint16_t>::max())
+      return false;
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+    // On OSX, we can have mach ports which are located in the extra header
+    // section.
+    using MachPortsEntry = Channel::Message::MachPortsEntry;
+    using MachPortsExtraHeader = Channel::Message::MachPortsExtraHeader;
+    CHECK(extra_header_size >=
+          sizeof(MachPortsExtraHeader) + num_handles * sizeof(MachPortsEntry));
+    const MachPortsExtraHeader* mach_ports_header =
+        reinterpret_cast<const MachPortsExtraHeader*>(extra_header);
+    size_t num_mach_ports = mach_ports_header->num_ports;
+    CHECK(num_mach_ports <= num_handles);
+    if (incoming_platform_handles_.size() + num_mach_ports < num_handles) {
+      handles->reset();
+      return true;
+    }
+
+    handles->reset(new PlatformHandleVector(num_handles));
+    const MachPortsEntry* mach_ports = mach_ports_header->entries;
+    for (size_t i = 0, mach_port_index = 0; i < num_handles; ++i) {
+      if (mach_port_index < num_mach_ports &&
+          mach_ports[mach_port_index].index == i) {
+        (*handles)->at(i) = PlatformHandle(
+            static_cast<mach_port_t>(mach_ports[mach_port_index].mach_port));
+        CHECK((*handles)->at(i).type == PlatformHandle::Type::MACH);
+        // These are actually just Mach port names until they're resolved from
+        // the remote process.
+        (*handles)->at(i).type = PlatformHandle::Type::MACH_NAME;
+        mach_port_index++;
+      } else {
+        CHECK(!incoming_platform_handles_.empty());
+        (*handles)->at(i) = incoming_platform_handles_.front();
+        incoming_platform_handles_.pop_front();
+      }
+    }
+#else
+    if (incoming_platform_handles_.size() < num_handles) {
+      handles->reset();
+      return true;
+    }
+
+    handles->reset(new PlatformHandleVector(num_handles));
+    for (size_t i = 0; i < num_handles; ++i) {
+      (*handles)->at(i) = incoming_platform_handles_.front();
+      incoming_platform_handles_.pop_front();
+    }
+#endif
+
+    return true;
+  }
+
+ private:
+  ~ChannelPosix() override {
+    DCHECK(!read_watcher_);
+    DCHECK(!write_watcher_);
+    for (auto handle : incoming_platform_handles_)
+      handle.CloseIfNecessary();
+  }
+
+  void StartOnIOThread() {
+    DCHECK(!read_watcher_);
+    DCHECK(!write_watcher_);
+    read_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
+    write_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
+    base::MessageLoopForIO::current()->WatchFileDescriptor(
+        handle_.get().handle, true /* persistent */,
+        base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this);
+    base::MessageLoop::current()->AddDestructionObserver(this);
+  }
+
+  void WaitForWriteOnIOThread() {
+    base::AutoLock lock(write_lock_);
+    WaitForWriteOnIOThreadNoLock();
+  }
+
+  void WaitForWriteOnIOThreadNoLock() {
+    if (pending_write_)
+      return;
+    if (!write_watcher_)
+      return;
+    if (io_task_runner_->RunsTasksOnCurrentThread()) {
+      pending_write_ = true;
+      base::MessageLoopForIO::current()->WatchFileDescriptor(
+          handle_.get().handle, false /* persistent */,
+          base::MessageLoopForIO::WATCH_WRITE, write_watcher_.get(), this);
+    } else {
+      io_task_runner_->PostTask(
+          FROM_HERE, base::Bind(&ChannelPosix::WaitForWriteOnIOThread, this));
+    }
+  }
+
+  void ShutDownOnIOThread() {
+    base::MessageLoop::current()->RemoveDestructionObserver(this);
+
+    read_watcher_.reset();
+    write_watcher_.reset();
+    if (leak_handle_)
+      ignore_result(handle_.release());
+    handle_.reset();
+#if defined(OS_MACOSX)
+    handles_to_close_.reset();
+#endif
+
+    // May destroy the |this| if it was the last reference.
+    self_ = nullptr;
+  }
+
+  // base::MessageLoop::DestructionObserver:
+  void WillDestroyCurrentMessageLoop() override {
+    DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+    if (self_)
+      ShutDownOnIOThread();
+  }
+
+  // base::MessageLoopForIO::Watcher:
+  void OnFileCanReadWithoutBlocking(int fd) override {
+    CHECK_EQ(fd, handle_.get().handle);
+
+    bool read_error = false;
+    size_t next_read_size = 0;
+    size_t buffer_capacity = 0;
+    size_t total_bytes_read = 0;
+    size_t bytes_read = 0;
+    do {
+      buffer_capacity = next_read_size;
+      char* buffer = GetReadBuffer(&buffer_capacity);
+      DCHECK_GT(buffer_capacity, 0u);
+
+      ssize_t read_result = PlatformChannelRecvmsg(
+          handle_.get(),
+          buffer,
+          buffer_capacity,
+          &incoming_platform_handles_);
+
+      if (read_result > 0) {
+        bytes_read = static_cast<size_t>(read_result);
+        total_bytes_read += bytes_read;
+        if (!OnReadComplete(bytes_read, &next_read_size)) {
+          read_error = true;
+          break;
+        }
+      } else if (read_result == 0 ||
+                 (errno != EAGAIN && errno != EWOULDBLOCK)) {
+        read_error = true;
+        break;
+      }
+    } while (bytes_read == buffer_capacity &&
+             total_bytes_read < kMaxBatchReadCapacity &&
+             next_read_size > 0);
+    if (read_error) {
+      // Stop receiving read notifications.
+      read_watcher_.reset();
+
+      OnError();
+    }
+  }
+
+  void OnFileCanWriteWithoutBlocking(int fd) override {
+    bool write_error = false;
+    {
+      base::AutoLock lock(write_lock_);
+      pending_write_ = false;
+      if (!FlushOutgoingMessagesNoLock())
+        reject_writes_ = write_error = true;
+    }
+    if (write_error)
+      OnError();
+  }
+
+  // Attempts to write a message directly to the channel. If the full message
+  // cannot be written, it's queued and a wait is initiated to write the message
+  // ASAP on the I/O thread.
+  bool WriteNoLock(MessageView message_view) {
+    size_t bytes_written = 0;
+    do {
+      message_view.advance_data_offset(bytes_written);
+
+      ssize_t result;
+      ScopedPlatformHandleVectorPtr handles = message_view.TakeHandles();
+      if (handles && handles->size()) {
+        iovec iov = {
+          const_cast<void*>(message_view.data()),
+          message_view.data_num_bytes()
+        };
+        // TODO: Handle lots of handles.
+        result = PlatformChannelSendmsgWithHandles(
+            handle_.get(), &iov, 1, handles->data(), handles->size());
+        if (result >= 0) {
+#if defined(OS_MACOSX)
+          // There is a bug on OSX which makes it dangerous to close
+          // a file descriptor while it is in transit. So instead we
+          // store the file descriptor in a set and send a message to
+          // the recipient, which is queued AFTER the message that
+          // sent the FD. The recipient will reply to the message,
+          // letting us know that it is now safe to close the file
+          // descriptor. For more information, see:
+          // http://crbug.com/298276
+          std::vector<int> fds;
+          for (auto& handle : *handles)
+            fds.push_back(handle.handle);
+          {
+            base::AutoLock l(handles_to_close_lock_);
+            for (auto& handle : *handles)
+              handles_to_close_->push_back(handle);
+          }
+          MessagePtr fds_message(
+              new Channel::Message(sizeof(fds[0]) * fds.size(), 0,
+                                   Message::Header::MessageType::HANDLES_SENT));
+          memcpy(fds_message->mutable_payload(), fds.data(),
+                 sizeof(fds[0]) * fds.size());
+          outgoing_messages_.emplace_back(std::move(fds_message), 0);
+          handles->clear();
+#else
+          handles.reset();
+#endif  // defined(OS_MACOSX)
+        }
+      } else {
+        result = PlatformChannelWrite(handle_.get(), message_view.data(),
+                                      message_view.data_num_bytes());
+      }
+
+      if (result < 0) {
+        if (errno != EAGAIN && errno != EWOULDBLOCK)
+          return false;
+        message_view.SetHandles(std::move(handles));
+        outgoing_messages_.emplace_front(std::move(message_view));
+        WaitForWriteOnIOThreadNoLock();
+        return true;
+      }
+
+      bytes_written = static_cast<size_t>(result);
+    } while (bytes_written < message_view.data_num_bytes());
+
+    return FlushOutgoingMessagesNoLock();
+  }
+
+  bool FlushOutgoingMessagesNoLock() {
+    std::deque<MessageView> messages;
+    std::swap(outgoing_messages_, messages);
+
+    while (!messages.empty()) {
+      if (!WriteNoLock(std::move(messages.front())))
+        return false;
+
+      messages.pop_front();
+      if (!outgoing_messages_.empty()) {
+        // The message was requeued by WriteNoLock(), so we have to wait for
+        // pipe to become writable again. Repopulate the message queue and exit.
+        // If sending the message triggered any control messages, they may be
+        // in |outgoing_messages_| in addition to or instead of the message
+        // being sent.
+        std::swap(messages, outgoing_messages_);
+        while (!messages.empty()) {
+          outgoing_messages_.push_front(std::move(messages.back()));
+          messages.pop_back();
+        }
+        return true;
+      }
+    }
+
+    return true;
+  }
+
+#if defined(OS_MACOSX)
+  bool OnControlMessage(Message::Header::MessageType message_type,
+                        const void* payload,
+                        size_t payload_size,
+                        ScopedPlatformHandleVectorPtr handles) override {
+    switch (message_type) {
+      case Message::Header::MessageType::HANDLES_SENT: {
+        if (payload_size == 0)
+          break;
+        MessagePtr message(new Channel::Message(
+            payload_size, 0, Message::Header::MessageType::HANDLES_SENT_ACK));
+        memcpy(message->mutable_payload(), payload, payload_size);
+        Write(std::move(message));
+        return true;
+      }
+
+      case Message::Header::MessageType::HANDLES_SENT_ACK: {
+        size_t num_fds = payload_size / sizeof(int);
+        if (num_fds == 0 || payload_size % sizeof(int) != 0)
+          break;
+
+        const int* fds = reinterpret_cast<const int*>(payload);
+        if (!CloseHandles(fds, num_fds))
+          break;
+        return true;
+      }
+
+      default:
+        break;
+    }
+
+    return false;
+  }
+
+  // Closes handles referenced by |fds|. Returns false if |num_fds| is 0, or if
+  // |fds| does not match a sequence of handles in |handles_to_close_|.
+  bool CloseHandles(const int* fds, size_t num_fds) {
+    base::AutoLock l(handles_to_close_lock_);
+    if (!num_fds)
+      return false;
+
+    auto start =
+        std::find_if(handles_to_close_->begin(), handles_to_close_->end(),
+                     [&fds](const PlatformHandle& handle) {
+                       return handle.handle == fds[0];
+                     });
+    if (start == handles_to_close_->end())
+      return false;
+
+    auto it = start;
+    size_t i = 0;
+    // The FDs in the message should match a sequence of handles in
+    // |handles_to_close_|.
+    for (; i < num_fds && it != handles_to_close_->end(); i++, ++it) {
+      if (it->handle != fds[i])
+        return false;
+
+      it->CloseIfNecessary();
+    }
+    if (i != num_fds)
+      return false;
+
+    handles_to_close_->erase(start, it);
+    return true;
+  }
+#endif  // defined(OS_MACOSX)
+
+  // Keeps the Channel alive at least until explicit shutdown on the IO thread.
+  scoped_refptr<Channel> self_;
+
+  ScopedPlatformHandle handle_;
+  scoped_refptr<base::TaskRunner> io_task_runner_;
+
+  // These watchers must only be accessed on the IO thread.
+  std::unique_ptr<base::MessageLoopForIO::FileDescriptorWatcher> read_watcher_;
+  std::unique_ptr<base::MessageLoopForIO::FileDescriptorWatcher> write_watcher_;
+
+  std::deque<PlatformHandle> incoming_platform_handles_;
+
+  // Protects |pending_write_| and |outgoing_messages_|.
+  base::Lock write_lock_;
+  bool pending_write_ = false;
+  bool reject_writes_ = false;
+  std::deque<MessageView> outgoing_messages_;
+
+  bool leak_handle_ = false;
+
+#if defined(OS_MACOSX)
+  base::Lock handles_to_close_lock_;
+  ScopedPlatformHandleVectorPtr handles_to_close_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(ChannelPosix);
+};
+
+}  // namespace
+
+// static
+scoped_refptr<Channel> Channel::Create(
+    Delegate* delegate,
+    ScopedPlatformHandle platform_handle,
+    scoped_refptr<base::TaskRunner> io_task_runner) {
+  return new ChannelPosix(delegate, std::move(platform_handle), io_task_runner);
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/channel_win.cc b/mojo/edk/system/channel_win.cc
new file mode 100644
index 0000000..c989344
--- /dev/null
+++ b/mojo/edk/system/channel_win.cc
@@ -0,0 +1,358 @@
+// Copyright 2016 The Chromium Authors. All 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 <stdint.h>
+#include <windows.h>
+
+#include <algorithm>
+#include <deque>
+#include <limits>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+// A view over a Channel::Message object. The write queue uses these since
+// large messages may need to be sent in chunks.
+class MessageView {
+ public:
+  // Owns |message|. |offset| indexes the first unsent byte in the message.
+  MessageView(Channel::MessagePtr message, size_t offset)
+      : message_(std::move(message)),
+        offset_(offset) {
+    DCHECK_GT(message_->data_num_bytes(), offset_);
+  }
+
+  MessageView(MessageView&& other) { *this = std::move(other); }
+
+  MessageView& operator=(MessageView&& other) {
+    message_ = std::move(other.message_);
+    offset_ = other.offset_;
+    return *this;
+  }
+
+  ~MessageView() {}
+
+  const void* data() const {
+    return static_cast<const char*>(message_->data()) + offset_;
+  }
+
+  size_t data_num_bytes() const { return message_->data_num_bytes() - offset_; }
+
+  size_t data_offset() const { return offset_; }
+  void advance_data_offset(size_t num_bytes) {
+    DCHECK_GE(message_->data_num_bytes(), offset_ + num_bytes);
+    offset_ += num_bytes;
+  }
+
+  Channel::MessagePtr TakeChannelMessage() { return std::move(message_); }
+
+ private:
+  Channel::MessagePtr message_;
+  size_t offset_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageView);
+};
+
+class ChannelWin : public Channel,
+                   public base::MessageLoop::DestructionObserver,
+                   public base::MessageLoopForIO::IOHandler {
+ public:
+  ChannelWin(Delegate* delegate,
+             ScopedPlatformHandle handle,
+             scoped_refptr<base::TaskRunner> io_task_runner)
+      : Channel(delegate),
+        self_(this),
+        handle_(std::move(handle)),
+        io_task_runner_(io_task_runner) {
+    CHECK(handle_.is_valid());
+
+    wait_for_connect_ = handle_.get().needs_connection;
+  }
+
+  void Start() override {
+    io_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&ChannelWin::StartOnIOThread, this));
+  }
+
+  void ShutDownImpl() override {
+    // Always shut down asynchronously when called through the public interface.
+    io_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&ChannelWin::ShutDownOnIOThread, this));
+  }
+
+  void Write(MessagePtr message) override {
+    bool write_error = false;
+    {
+      base::AutoLock lock(write_lock_);
+      if (reject_writes_)
+        return;
+
+      bool write_now = !delay_writes_ && outgoing_messages_.empty();
+      outgoing_messages_.emplace_back(std::move(message), 0);
+
+      if (write_now && !WriteNoLock(outgoing_messages_.front()))
+        reject_writes_ = write_error = true;
+    }
+    if (write_error) {
+      // Do not synchronously invoke OnError(). Write() may have been called by
+      // the delegate and we don't want to re-enter it.
+      io_task_runner_->PostTask(FROM_HERE,
+                                base::Bind(&ChannelWin::OnError, this));
+    }
+  }
+
+  void LeakHandle() override {
+    DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+    leak_handle_ = true;
+  }
+
+  bool GetReadPlatformHandles(
+      size_t num_handles,
+      const void* extra_header,
+      size_t extra_header_size,
+      ScopedPlatformHandleVectorPtr* handles) override {
+    if (num_handles > std::numeric_limits<uint16_t>::max())
+      return false;
+    using HandleEntry = Channel::Message::HandleEntry;
+    size_t handles_size = sizeof(HandleEntry) * num_handles;
+    if (handles_size > extra_header_size)
+      return false;
+    DCHECK(extra_header);
+    handles->reset(new PlatformHandleVector(num_handles));
+    const HandleEntry* extra_header_handles =
+        reinterpret_cast<const HandleEntry*>(extra_header);
+    for (size_t i = 0; i < num_handles; i++) {
+      (*handles)->at(i).handle = reinterpret_cast<HANDLE>(
+          static_cast<uintptr_t>(extra_header_handles[i].handle));
+    }
+    return true;
+  }
+
+ private:
+  // May run on any thread.
+  ~ChannelWin() override {}
+
+  void StartOnIOThread() {
+    base::MessageLoop::current()->AddDestructionObserver(this);
+    base::MessageLoopForIO::current()->RegisterIOHandler(
+        handle_.get().handle, this);
+
+    if (wait_for_connect_) {
+      BOOL ok = ConnectNamedPipe(handle_.get().handle,
+                                 &connect_context_.overlapped);
+      if (ok) {
+        PLOG(ERROR) << "Unexpected success while waiting for pipe connection";
+        OnError();
+        return;
+      }
+
+      const DWORD err = GetLastError();
+      switch (err) {
+        case ERROR_PIPE_CONNECTED:
+          wait_for_connect_ = false;
+          break;
+        case ERROR_IO_PENDING:
+          AddRef();
+          return;
+        case ERROR_NO_DATA:
+          OnError();
+          return;
+      }
+    }
+
+    // Now that we have registered our IOHandler, we can start writing.
+    {
+      base::AutoLock lock(write_lock_);
+      if (delay_writes_) {
+        delay_writes_ = false;
+        WriteNextNoLock();
+      }
+    }
+
+    // Keep this alive in case we synchronously run shutdown.
+    scoped_refptr<ChannelWin> keep_alive(this);
+    ReadMore(0);
+  }
+
+  void ShutDownOnIOThread() {
+    base::MessageLoop::current()->RemoveDestructionObserver(this);
+
+    // BUG(crbug.com/583525): This function is expected to be called once, and
+    // |handle_| should be valid at this point.
+    CHECK(handle_.is_valid());
+    CancelIo(handle_.get().handle);
+    if (leak_handle_)
+      ignore_result(handle_.release());
+    handle_.reset();
+
+    // May destroy the |this| if it was the last reference.
+    self_ = nullptr;
+  }
+
+  // base::MessageLoop::DestructionObserver:
+  void WillDestroyCurrentMessageLoop() override {
+    DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+    if (self_)
+      ShutDownOnIOThread();
+  }
+
+  // base::MessageLoop::IOHandler:
+  void OnIOCompleted(base::MessageLoopForIO::IOContext* context,
+                     DWORD bytes_transfered,
+                     DWORD error) override {
+    if (error != ERROR_SUCCESS) {
+      OnError();
+    } else if (context == &connect_context_) {
+      DCHECK(wait_for_connect_);
+      wait_for_connect_ = false;
+      ReadMore(0);
+
+      base::AutoLock lock(write_lock_);
+      if (delay_writes_) {
+        delay_writes_ = false;
+        WriteNextNoLock();
+      }
+    } else if (context == &read_context_) {
+      OnReadDone(static_cast<size_t>(bytes_transfered));
+    } else {
+      CHECK(context == &write_context_);
+      OnWriteDone(static_cast<size_t>(bytes_transfered));
+    }
+    Release();  // Balancing reference taken after ReadFile / WriteFile.
+  }
+
+  void OnReadDone(size_t bytes_read) {
+    if (bytes_read > 0) {
+      size_t next_read_size = 0;
+      if (OnReadComplete(bytes_read, &next_read_size)) {
+        ReadMore(next_read_size);
+      } else {
+        OnError();
+      }
+    } else if (bytes_read == 0) {
+      OnError();
+    }
+  }
+
+  void OnWriteDone(size_t bytes_written) {
+    if (bytes_written == 0)
+      return;
+
+    bool write_error = false;
+    {
+      base::AutoLock lock(write_lock_);
+
+      DCHECK(!outgoing_messages_.empty());
+
+      MessageView& message_view = outgoing_messages_.front();
+      message_view.advance_data_offset(bytes_written);
+      if (message_view.data_num_bytes() == 0) {
+        Channel::MessagePtr message = message_view.TakeChannelMessage();
+        outgoing_messages_.pop_front();
+
+        // Clear any handles so they don't get closed on destruction.
+        ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+        if (handles)
+          handles->clear();
+      }
+
+      if (!WriteNextNoLock())
+        reject_writes_ = write_error = true;
+    }
+    if (write_error)
+      OnError();
+  }
+
+  void ReadMore(size_t next_read_size_hint) {
+    size_t buffer_capacity = next_read_size_hint;
+    char* buffer = GetReadBuffer(&buffer_capacity);
+    DCHECK_GT(buffer_capacity, 0u);
+
+    BOOL ok = ReadFile(handle_.get().handle,
+                       buffer,
+                       static_cast<DWORD>(buffer_capacity),
+                       NULL,
+                       &read_context_.overlapped);
+
+    if (ok || GetLastError() == ERROR_IO_PENDING) {
+      AddRef();  // Will be balanced in OnIOCompleted
+    } else {
+      OnError();
+    }
+  }
+
+  // Attempts to write a message directly to the channel. If the full message
+  // cannot be written, it's queued and a wait is initiated to write the message
+  // ASAP on the I/O thread.
+  bool WriteNoLock(const MessageView& message_view) {
+    BOOL ok = WriteFile(handle_.get().handle,
+                        message_view.data(),
+                        static_cast<DWORD>(message_view.data_num_bytes()),
+                        NULL,
+                        &write_context_.overlapped);
+
+    if (ok || GetLastError() == ERROR_IO_PENDING) {
+      AddRef();  // Will be balanced in OnIOCompleted.
+      return true;
+    }
+    return false;
+  }
+
+  bool WriteNextNoLock() {
+    if (outgoing_messages_.empty())
+      return true;
+    return WriteNoLock(outgoing_messages_.front());
+  }
+
+  // Keeps the Channel alive at least until explicit shutdown on the IO thread.
+  scoped_refptr<Channel> self_;
+
+  ScopedPlatformHandle handle_;
+  scoped_refptr<base::TaskRunner> io_task_runner_;
+
+  base::MessageLoopForIO::IOContext connect_context_;
+  base::MessageLoopForIO::IOContext read_context_;
+  base::MessageLoopForIO::IOContext write_context_;
+
+  // Protects |reject_writes_| and |outgoing_messages_|.
+  base::Lock write_lock_;
+
+  bool delay_writes_ = true;
+
+  bool reject_writes_ = false;
+  std::deque<MessageView> outgoing_messages_;
+
+  bool wait_for_connect_;
+
+  bool leak_handle_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(ChannelWin);
+};
+
+}  // namespace
+
+// static
+scoped_refptr<Channel> Channel::Create(
+    Delegate* delegate,
+    ScopedPlatformHandle platform_handle,
+    scoped_refptr<base::TaskRunner> io_task_runner) {
+  return new ChannelWin(delegate, std::move(platform_handle), io_task_runner);
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/configuration.cc b/mojo/edk/system/configuration.cc
new file mode 100644
index 0000000..9aaed31
--- /dev/null
+++ b/mojo/edk/system/configuration.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/edk/system/configuration.h"
+
+namespace mojo {
+namespace edk {
+namespace internal {
+
+// These default values should be synced with the documentation in
+// mojo/edk/embedder/configuration.h.
+Configuration g_configuration = {
+    1000000,              // max_handle_table_size
+    1000000,              // max_mapping_table_sze
+    1000000,              // max_wait_many_num_handles
+    4 * 1024 * 1024,      // max_message_num_bytes
+    10000,                // max_message_num_handles
+    256 * 1024 * 1024,    // max_data_pipe_capacity_bytes
+    1024 * 1024,          // default_data_pipe_capacity_bytes
+    16,                   // data_pipe_buffer_alignment_bytes
+    1024 * 1024 * 1024};  // max_shared_memory_num_bytes
+
+}  // namespace internal
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/configuration.h b/mojo/edk/system/configuration.h
new file mode 100644
index 0000000..038835f
--- /dev/null
+++ b/mojo/edk/system/configuration.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_SYSTEM_CONFIGURATION_H_
+#define MOJO_EDK_SYSTEM_CONFIGURATION_H_
+
+#include "mojo/edk/embedder/configuration.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+namespace internal {
+MOJO_SYSTEM_IMPL_EXPORT extern Configuration g_configuration;
+}  // namespace internal
+
+MOJO_SYSTEM_IMPL_EXPORT inline const Configuration& GetConfiguration() {
+  return internal::g_configuration;
+}
+
+MOJO_SYSTEM_IMPL_EXPORT inline Configuration* GetMutableConfiguration() {
+  return &internal::g_configuration;
+}
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_CONFIGURATION_H_
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
new file mode 100644
index 0000000..b2abf0f
--- /dev/null
+++ b/mojo/edk/system/core.cc
@@ -0,0 +1,1181 @@
+// 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 <string.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/containers/stack_container.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/configuration.h"
+#include "mojo/edk/system/data_pipe_consumer_dispatcher.h"
+#include "mojo/edk/system/data_pipe_producer_dispatcher.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/message_for_transit.h"
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+#include "mojo/edk/system/platform_handle_dispatcher.h"
+#include "mojo/edk/system/ports/name.h"
+#include "mojo/edk/system/ports/node.h"
+#include "mojo/edk/system/remote_message_pipe_bootstrap.h"
+#include "mojo/edk/system/request_context.h"
+#include "mojo/edk/system/shared_buffer_dispatcher.h"
+#include "mojo/edk/system/wait_set_dispatcher.h"
+#include "mojo/edk/system/waiter.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+// This is an unnecessarily large limit that is relatively easy to enforce.
+const uint32_t kMaxHandlesPerMessage = 1024 * 1024;
+
+// TODO: Maybe we could negotiate a debugging pipe ID for cross-process pipes
+// too; for now we just use a constant. This only affects bootstrap pipes.
+const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL;
+
+void CallWatchCallback(MojoWatchCallback callback,
+                       uintptr_t context,
+                       MojoResult result,
+                       const HandleSignalsState& signals_state,
+                       MojoWatchNotificationFlags flags) {
+  callback(context, result, static_cast<MojoHandleSignalsState>(signals_state),
+      flags);
+}
+
+MojoResult MojoPlatformHandleToScopedPlatformHandle(
+    const MojoPlatformHandle* platform_handle,
+    ScopedPlatformHandle* out_handle) {
+  if (platform_handle->struct_size != sizeof(MojoPlatformHandle))
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (platform_handle->type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) {
+    out_handle->reset();
+    return MOJO_RESULT_OK;
+  }
+
+  PlatformHandle handle;
+  switch (platform_handle->type) {
+#if defined(OS_POSIX)
+    case MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR:
+      handle.handle = static_cast<int>(platform_handle->value);
+      break;
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+    case MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT:
+      handle.type = PlatformHandle::Type::MACH;
+      handle.port = static_cast<mach_port_t>(platform_handle->value);
+      break;
+#endif
+
+#if defined(OS_WIN)
+    case MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE:
+      handle.handle = reinterpret_cast<HANDLE>(platform_handle->value);
+      break;
+#endif
+
+    default:
+      return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  out_handle->reset(handle);
+  return MOJO_RESULT_OK;
+}
+
+MojoResult ScopedPlatformHandleToMojoPlatformHandle(
+    ScopedPlatformHandle handle,
+    MojoPlatformHandle* platform_handle) {
+  if (platform_handle->struct_size != sizeof(MojoPlatformHandle))
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (!handle.is_valid()) {
+    platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_INVALID;
+    return MOJO_RESULT_OK;
+  }
+
+#if defined(OS_POSIX)
+  switch (handle.get().type) {
+    case PlatformHandle::Type::POSIX:
+      platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
+      platform_handle->value = static_cast<uint64_t>(handle.release().handle);
+      break;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+    case PlatformHandle::Type::MACH:
+      platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
+      platform_handle->value = static_cast<uint64_t>(handle.release().port);
+      break;
+#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
+
+    default:
+      return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+#elif defined(OS_WIN)
+  platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
+  platform_handle->value = reinterpret_cast<uint64_t>(handle.release().handle);
+#endif  // defined(OS_WIN)
+
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace
+
+Core::Core() {}
+
+Core::~Core() {
+  if (node_controller_ && node_controller_->io_task_runner()) {
+    // If this races with IO thread shutdown the callback will be dropped and
+    // the NodeController will be shutdown on this thread anyway, which is also
+    // just fine.
+    scoped_refptr<base::TaskRunner> io_task_runner =
+        node_controller_->io_task_runner();
+    io_task_runner->PostTask(FROM_HERE,
+                             base::Bind(&Core::PassNodeControllerToIOThread,
+                                        base::Passed(&node_controller_)));
+  }
+}
+
+void Core::SetIOTaskRunner(scoped_refptr<base::TaskRunner> io_task_runner) {
+  GetNodeController()->SetIOTaskRunner(io_task_runner);
+}
+
+NodeController* Core::GetNodeController() {
+  base::AutoLock lock(node_controller_lock_);
+  if (!node_controller_)
+    node_controller_.reset(new NodeController(this));
+  return node_controller_.get();
+}
+
+scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) {
+  base::AutoLock lock(handles_lock_);
+  return handles_.GetDispatcher(handle);
+}
+
+void Core::AddChild(base::ProcessHandle process_handle,
+                    ScopedPlatformHandle platform_handle,
+                    const std::string& child_token,
+                    const ProcessErrorCallback& process_error_callback) {
+  GetNodeController()->ConnectToChild(process_handle,
+                                      std::move(platform_handle),
+                                      child_token,
+                                      process_error_callback);
+}
+
+void Core::ChildLaunchFailed(const std::string& child_token) {
+  RequestContext request_context;
+  GetNodeController()->CloseChildPorts(child_token);
+}
+
+void Core::InitChild(ScopedPlatformHandle platform_handle) {
+  GetNodeController()->ConnectToParent(std::move(platform_handle));
+}
+
+void Core::SetMachPortProvider(base::PortProvider* port_provider) {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  GetNodeController()->CreateMachPortRelay(port_provider);
+#endif
+}
+
+MojoHandle Core::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) {
+  base::AutoLock lock(handles_lock_);
+  return handles_.AddDispatcher(dispatcher);
+}
+
+bool Core::AddDispatchersFromTransit(
+    const std::vector<Dispatcher::DispatcherInTransit>& dispatchers,
+    MojoHandle* handles) {
+  bool failed = false;
+  {
+    base::AutoLock lock(handles_lock_);
+    if (!handles_.AddDispatchersFromTransit(dispatchers, handles))
+      failed = true;
+  }
+  if (failed) {
+    for (auto d : dispatchers)
+      d.dispatcher->Close();
+    return false;
+  }
+  return true;
+}
+
+MojoResult Core::CreatePlatformHandleWrapper(
+    ScopedPlatformHandle platform_handle,
+    MojoHandle* wrapper_handle) {
+  MojoHandle h = AddDispatcher(
+      PlatformHandleDispatcher::Create(std::move(platform_handle)));
+  if (h == MOJO_HANDLE_INVALID)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  *wrapper_handle = h;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::PassWrappedPlatformHandle(
+    MojoHandle wrapper_handle,
+    ScopedPlatformHandle* platform_handle) {
+  base::AutoLock lock(handles_lock_);
+  scoped_refptr<Dispatcher> d;
+  MojoResult result = handles_.GetAndRemoveDispatcher(wrapper_handle, &d);
+  if (result != MOJO_RESULT_OK)
+    return result;
+  if (d->GetType() == Dispatcher::Type::PLATFORM_HANDLE) {
+    PlatformHandleDispatcher* phd =
+        static_cast<PlatformHandleDispatcher*>(d.get());
+    *platform_handle = phd->PassPlatformHandle();
+  } else {
+    result = MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  d->Close();
+  return result;
+}
+
+MojoResult Core::CreateSharedBufferWrapper(
+    base::SharedMemoryHandle shared_memory_handle,
+    size_t num_bytes,
+    bool read_only,
+    MojoHandle* mojo_wrapper_handle) {
+  DCHECK(num_bytes);
+  scoped_refptr<PlatformSharedBuffer> platform_buffer =
+      PlatformSharedBuffer::CreateFromSharedMemoryHandle(num_bytes, read_only,
+                                                         shared_memory_handle);
+  if (!platform_buffer)
+    return MOJO_RESULT_UNKNOWN;
+
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  MojoResult result = SharedBufferDispatcher::CreateFromPlatformSharedBuffer(
+      platform_buffer, &dispatcher);
+  if (result != MOJO_RESULT_OK)
+    return result;
+  MojoHandle h = AddDispatcher(dispatcher);
+  if (h == MOJO_HANDLE_INVALID)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  *mojo_wrapper_handle = h;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::PassSharedMemoryHandle(
+    MojoHandle mojo_handle,
+    base::SharedMemoryHandle* shared_memory_handle,
+    size_t* num_bytes,
+    bool* read_only) {
+  if (!shared_memory_handle)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_refptr<Dispatcher> dispatcher;
+  MojoResult result = MOJO_RESULT_OK;
+  {
+    base::AutoLock lock(handles_lock_);
+    // Get the dispatcher and check it before removing it from the handle table
+    // to ensure that the dispatcher is of the correct type. This ensures we
+    // don't close and remove the wrong type of dispatcher.
+    dispatcher = handles_.GetDispatcher(mojo_handle);
+    if (!dispatcher || dispatcher->GetType() != Dispatcher::Type::SHARED_BUFFER)
+      return MOJO_RESULT_INVALID_ARGUMENT;
+
+    result = handles_.GetAndRemoveDispatcher(mojo_handle, &dispatcher);
+    if (result != MOJO_RESULT_OK)
+      return result;
+  }
+
+  SharedBufferDispatcher* shm_dispatcher =
+      static_cast<SharedBufferDispatcher*>(dispatcher.get());
+  scoped_refptr<PlatformSharedBuffer> platform_shared_buffer =
+      shm_dispatcher->PassPlatformSharedBuffer();
+
+  if (!platform_shared_buffer)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (num_bytes)
+    *num_bytes = platform_shared_buffer->GetNumBytes();
+  if (read_only)
+    *read_only = platform_shared_buffer->IsReadOnly();
+  *shared_memory_handle = platform_shared_buffer->DuplicateSharedMemoryHandle();
+
+  shm_dispatcher->Close();
+  return result;
+}
+
+void Core::RequestShutdown(const base::Closure& callback) {
+  base::Closure on_shutdown;
+  if (base::ThreadTaskRunnerHandle::IsSet()) {
+    on_shutdown = base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
+                             base::ThreadTaskRunnerHandle::Get(),
+                             FROM_HERE, callback);
+  } else {
+    on_shutdown = callback;
+  }
+  GetNodeController()->RequestShutdown(on_shutdown);
+}
+
+ScopedMessagePipeHandle Core::CreateMessagePipe(
+    ScopedPlatformHandle platform_handle) {
+#if defined(OS_NACL)
+  NOTREACHED();
+  return ScopedMessagePipeHandle();
+#else
+  ports::PortRef port0, port1;
+  GetNodeController()->node()->CreatePortPair(&port0, &port1);
+  MojoHandle handle = AddDispatcher(
+    new MessagePipeDispatcher(GetNodeController(), port0,
+                              kUnknownPipeIdForDebug, 0));
+  RemoteMessagePipeBootstrap::Create(
+      GetNodeController(), std::move(platform_handle), port1);
+  return ScopedMessagePipeHandle(MessagePipeHandle(handle));
+#endif
+}
+
+ScopedMessagePipeHandle Core::CreateParentMessagePipe(
+    const std::string& token, const std::string& child_token) {
+  RequestContext request_context;
+  ports::PortRef port0, port1;
+  GetNodeController()->node()->CreatePortPair(&port0, &port1);
+  MojoHandle handle = AddDispatcher(
+      new MessagePipeDispatcher(GetNodeController(), port0,
+                                kUnknownPipeIdForDebug, 0));
+  GetNodeController()->ReservePort(token, port1, child_token);
+  return ScopedMessagePipeHandle(MessagePipeHandle(handle));
+}
+
+ScopedMessagePipeHandle Core::CreateChildMessagePipe(const std::string& token) {
+  RequestContext request_context;
+  ports::PortRef port0, port1;
+  GetNodeController()->node()->CreatePortPair(&port0, &port1);
+  MojoHandle handle = AddDispatcher(
+      new MessagePipeDispatcher(GetNodeController(), port0,
+                                kUnknownPipeIdForDebug, 1));
+  GetNodeController()->MergePortIntoParent(token, port1);
+  return ScopedMessagePipeHandle(MessagePipeHandle(handle));
+}
+
+MojoResult Core::SetProperty(MojoPropertyType type, const void* value) {
+  base::AutoLock locker(property_lock_);
+  switch (type) {
+    case MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED:
+      property_sync_call_allowed_ = *static_cast<const bool*>(value);
+      return MOJO_RESULT_OK;
+    default:
+      return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+}
+
+MojoTimeTicks Core::GetTimeTicksNow() {
+  return base::TimeTicks::Now().ToInternalValue();
+}
+
+MojoResult Core::Close(MojoHandle handle) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher;
+  {
+    base::AutoLock lock(handles_lock_);
+    MojoResult rv = handles_.GetAndRemoveDispatcher(handle, &dispatcher);
+    if (rv != MOJO_RESULT_OK)
+      return rv;
+  }
+  dispatcher->Close();
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::Wait(MojoHandle handle,
+                      MojoHandleSignals signals,
+                      MojoDeadline deadline,
+                      MojoHandleSignalsState* signals_state) {
+  RequestContext request_context;
+  uint32_t unused = static_cast<uint32_t>(-1);
+  HandleSignalsState hss;
+  MojoResult rv = WaitManyInternal(&handle, &signals, 1, deadline, &unused,
+                                   signals_state ? &hss : nullptr);
+  if (rv != MOJO_RESULT_INVALID_ARGUMENT && signals_state)
+    *signals_state = hss;
+  return rv;
+}
+
+MojoResult Core::WaitMany(const MojoHandle* handles,
+                          const MojoHandleSignals* signals,
+                          uint32_t num_handles,
+                          MojoDeadline deadline,
+                          uint32_t* result_index,
+                          MojoHandleSignalsState* signals_state) {
+  RequestContext request_context;
+  if (num_handles < 1)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (num_handles > GetConfiguration().max_wait_many_num_handles)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  uint32_t index = static_cast<uint32_t>(-1);
+  MojoResult rv;
+  if (!signals_state) {
+    rv = WaitManyInternal(handles, signals, num_handles, deadline, &index,
+                          nullptr);
+  } else {
+    // Note: The |reinterpret_cast| is safe, since |HandleSignalsState| is a
+    // subclass of |MojoHandleSignalsState| that doesn't add any data members.
+    rv = WaitManyInternal(handles, signals, num_handles, deadline, &index,
+                          reinterpret_cast<HandleSignalsState*>(signals_state));
+  }
+  if (index != static_cast<uint32_t>(-1) && result_index)
+    *result_index = index;
+  return rv;
+}
+
+MojoResult Core::Watch(MojoHandle handle,
+                       MojoHandleSignals signals,
+                       MojoWatchCallback callback,
+                       uintptr_t context) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle);
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return dispatcher->Watch(
+      signals, base::Bind(&CallWatchCallback, callback, context), context);
+}
+
+MojoResult Core::CancelWatch(MojoHandle handle, uintptr_t context) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle);
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  return dispatcher->CancelWatch(context);
+}
+
+MojoResult Core::AllocMessage(uint32_t num_bytes,
+                              const MojoHandle* handles,
+                              uint32_t num_handles,
+                              MojoAllocMessageFlags flags,
+                              MojoMessageHandle* message) {
+  if (!message)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (num_handles == 0) {  // Fast path: no handles.
+    std::unique_ptr<MessageForTransit> msg;
+    MojoResult rv = MessageForTransit::Create(&msg, num_bytes, nullptr, 0);
+    if (rv != MOJO_RESULT_OK)
+      return rv;
+
+    *message = reinterpret_cast<MojoMessageHandle>(msg.release());
+    return MOJO_RESULT_OK;
+  }
+
+  if (!handles)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (num_handles > kMaxHandlesPerMessage)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  std::vector<Dispatcher::DispatcherInTransit> dispatchers;
+  {
+    base::AutoLock lock(handles_lock_);
+    MojoResult rv = handles_.BeginTransit(handles, num_handles, &dispatchers);
+    if (rv != MOJO_RESULT_OK) {
+      handles_.CancelTransit(dispatchers);
+      return rv;
+    }
+  }
+  DCHECK_EQ(num_handles, dispatchers.size());
+
+  std::unique_ptr<MessageForTransit> msg;
+  MojoResult rv = MessageForTransit::Create(
+      &msg, num_bytes, dispatchers.data(), num_handles);
+
+  {
+    base::AutoLock lock(handles_lock_);
+    if (rv == MOJO_RESULT_OK) {
+      handles_.CompleteTransitAndClose(dispatchers);
+      *message = reinterpret_cast<MojoMessageHandle>(msg.release());
+    } else {
+      handles_.CancelTransit(dispatchers);
+    }
+  }
+
+  return rv;
+}
+
+MojoResult Core::FreeMessage(MojoMessageHandle message) {
+  if (!message)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  delete reinterpret_cast<MessageForTransit*>(message);
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::GetMessageBuffer(MojoMessageHandle message, void** buffer) {
+  if (!message)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  *buffer = reinterpret_cast<MessageForTransit*>(message)->mutable_bytes();
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::GetProperty(MojoPropertyType type, void* value) {
+  base::AutoLock locker(property_lock_);
+  switch (type) {
+    case MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED:
+      *static_cast<bool*>(value) = property_sync_call_allowed_;
+      return MOJO_RESULT_OK;
+    default:
+      return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+}
+
+MojoResult Core::CreateWaitSet(MojoHandle* wait_set_handle) {
+  RequestContext request_context;
+  if (!wait_set_handle)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_refptr<WaitSetDispatcher> dispatcher = new WaitSetDispatcher();
+  MojoHandle h = AddDispatcher(dispatcher);
+  if (h == MOJO_HANDLE_INVALID) {
+    LOG(ERROR) << "Handle table full";
+    dispatcher->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  *wait_set_handle = h;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::AddHandle(MojoHandle wait_set_handle,
+                           MojoHandle handle,
+                           MojoHandleSignals signals) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> wait_set_dispatcher(GetDispatcher(wait_set_handle));
+  if (!wait_set_dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_refptr<Dispatcher> dispatcher(GetDispatcher(handle));
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return wait_set_dispatcher->AddWaitingDispatcher(dispatcher, signals, handle);
+}
+
+MojoResult Core::RemoveHandle(MojoHandle wait_set_handle,
+                              MojoHandle handle) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> wait_set_dispatcher(GetDispatcher(wait_set_handle));
+  if (!wait_set_dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_refptr<Dispatcher> dispatcher(GetDispatcher(handle));
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return wait_set_dispatcher->RemoveWaitingDispatcher(dispatcher);
+}
+
+MojoResult Core::GetReadyHandles(MojoHandle wait_set_handle,
+                                 uint32_t* count,
+                                 MojoHandle* handles,
+                                 MojoResult* results,
+                                 MojoHandleSignalsState* signals_states) {
+  RequestContext request_context;
+  if (!handles || !count || !(*count) || !results)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  scoped_refptr<Dispatcher> wait_set_dispatcher(GetDispatcher(wait_set_handle));
+  if (!wait_set_dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  DispatcherVector awoken_dispatchers;
+  base::StackVector<uintptr_t, 16> contexts;
+  contexts->assign(*count, MOJO_HANDLE_INVALID);
+
+  MojoResult result = wait_set_dispatcher->GetReadyDispatchers(
+      count, &awoken_dispatchers, results, contexts->data());
+
+  if (result == MOJO_RESULT_OK) {
+    for (size_t i = 0; i < *count; i++) {
+      handles[i] = static_cast<MojoHandle>(contexts[i]);
+      if (signals_states)
+        signals_states[i] = awoken_dispatchers[i]->GetHandleSignalsState();
+    }
+  }
+
+  return result;
+}
+
+MojoResult Core::CreateMessagePipe(
+    const MojoCreateMessagePipeOptions* options,
+    MojoHandle* message_pipe_handle0,
+    MojoHandle* message_pipe_handle1) {
+  RequestContext request_context;
+  ports::PortRef port0, port1;
+  GetNodeController()->node()->CreatePortPair(&port0, &port1);
+
+  CHECK(message_pipe_handle0);
+  CHECK(message_pipe_handle1);
+
+  uint64_t pipe_id = base::RandUint64();
+
+  *message_pipe_handle0 = AddDispatcher(
+      new MessagePipeDispatcher(GetNodeController(), port0, pipe_id, 0));
+  if (*message_pipe_handle0 == MOJO_HANDLE_INVALID)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  *message_pipe_handle1 = AddDispatcher(
+      new MessagePipeDispatcher(GetNodeController(), port1, pipe_id, 1));
+  if (*message_pipe_handle1 == MOJO_HANDLE_INVALID) {
+    scoped_refptr<Dispatcher> unused;
+    unused->Close();
+
+    base::AutoLock lock(handles_lock_);
+    handles_.GetAndRemoveDispatcher(*message_pipe_handle0, &unused);
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::WriteMessage(MojoHandle message_pipe_handle,
+                              const void* bytes,
+                              uint32_t num_bytes,
+                              const MojoHandle* handles,
+                              uint32_t num_handles,
+                              MojoWriteMessageFlags flags) {
+  if (num_bytes && !bytes)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  MojoMessageHandle message;
+  MojoResult rv = AllocMessage(num_bytes, handles, num_handles,
+                               MOJO_ALLOC_MESSAGE_FLAG_NONE, &message);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  if (num_bytes) {
+    void* buffer = nullptr;
+    rv = GetMessageBuffer(message, &buffer);
+    DCHECK_EQ(rv, MOJO_RESULT_OK);
+    memcpy(buffer, bytes, num_bytes);
+  }
+
+  return WriteMessageNew(message_pipe_handle, message, flags);
+}
+
+MojoResult Core::WriteMessageNew(MojoHandle message_pipe_handle,
+                                 MojoMessageHandle message,
+                                 MojoWriteMessageFlags flags) {
+  RequestContext request_context;
+  std::unique_ptr<MessageForTransit> message_for_transit(
+      reinterpret_cast<MessageForTransit*>(message));
+  auto dispatcher = GetDispatcher(message_pipe_handle);
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->WriteMessage(std::move(message_for_transit), flags);
+}
+
+MojoResult Core::ReadMessage(MojoHandle message_pipe_handle,
+                             void* bytes,
+                             uint32_t* num_bytes,
+                             MojoHandle* handles,
+                             uint32_t* num_handles,
+                             MojoReadMessageFlags flags) {
+  CHECK((!num_handles || !*num_handles || handles) &&
+        (!num_bytes || !*num_bytes || bytes));
+  RequestContext request_context;
+  auto dispatcher = GetDispatcher(message_pipe_handle);
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  std::unique_ptr<MessageForTransit> message;
+  MojoResult rv =
+      dispatcher->ReadMessage(&message, num_bytes, handles, num_handles, flags,
+                              false /* ignore_num_bytes */);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  if (message && message->num_bytes())
+    memcpy(bytes, message->bytes(), message->num_bytes());
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::ReadMessageNew(MojoHandle message_pipe_handle,
+                                MojoMessageHandle* message,
+                                uint32_t* num_bytes,
+                                MojoHandle* handles,
+                                uint32_t* num_handles,
+                                MojoReadMessageFlags flags) {
+  CHECK(message);
+  CHECK(!num_handles || !*num_handles || handles);
+  RequestContext request_context;
+  auto dispatcher = GetDispatcher(message_pipe_handle);
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  std::unique_ptr<MessageForTransit> msg;
+  MojoResult rv =
+      dispatcher->ReadMessage(&msg, num_bytes, handles, num_handles, flags,
+                              true /* ignore_num_bytes */);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+  *message = reinterpret_cast<MojoMessageHandle>(msg.release());
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::FuseMessagePipes(MojoHandle handle0, MojoHandle handle1) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher0;
+  scoped_refptr<Dispatcher> dispatcher1;
+
+  bool valid_handles = true;
+  {
+    base::AutoLock lock(handles_lock_);
+    MojoResult result0 = handles_.GetAndRemoveDispatcher(handle0, &dispatcher0);
+    MojoResult result1 = handles_.GetAndRemoveDispatcher(handle1, &dispatcher1);
+    if (result0 != MOJO_RESULT_OK || result1 != MOJO_RESULT_OK ||
+        dispatcher0->GetType() != Dispatcher::Type::MESSAGE_PIPE ||
+        dispatcher1->GetType() != Dispatcher::Type::MESSAGE_PIPE)
+      valid_handles = false;
+  }
+
+  if (!valid_handles) {
+    if (dispatcher0)
+      dispatcher0->Close();
+    if (dispatcher1)
+      dispatcher1->Close();
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  MessagePipeDispatcher* mpd0 =
+      static_cast<MessagePipeDispatcher*>(dispatcher0.get());
+  MessagePipeDispatcher* mpd1 =
+      static_cast<MessagePipeDispatcher*>(dispatcher1.get());
+
+  if (!mpd0->Fuse(mpd1))
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::NotifyBadMessage(MojoMessageHandle message,
+                                  const char* error,
+                                  size_t error_num_bytes) {
+  if (!message)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  const PortsMessage& ports_message =
+      reinterpret_cast<MessageForTransit*>(message)->ports_message();
+  if (ports_message.source_node() == ports::kInvalidNodeName) {
+    DVLOG(1) << "Received invalid message from unknown node.";
+    return MOJO_RESULT_OK;
+  }
+
+  GetNodeController()->NotifyBadMessageFrom(
+      ports_message.source_node(), std::string(error, error_num_bytes));
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::CreateDataPipe(
+    const MojoCreateDataPipeOptions* options,
+    MojoHandle* data_pipe_producer_handle,
+    MojoHandle* data_pipe_consumer_handle) {
+  RequestContext request_context;
+  if (options && options->struct_size != sizeof(MojoCreateDataPipeOptions))
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  MojoCreateDataPipeOptions create_options;
+  create_options.struct_size = sizeof(MojoCreateDataPipeOptions);
+  create_options.flags = options ? options->flags : 0;
+  create_options.element_num_bytes = options ? options->element_num_bytes : 1;
+  // TODO: Use Configuration to get default data pipe capacity.
+  create_options.capacity_num_bytes =
+      options && options->capacity_num_bytes ? options->capacity_num_bytes
+                                             : 64 * 1024;
+
+  // TODO: Broker through the parent when necessary.
+  scoped_refptr<PlatformSharedBuffer> ring_buffer =
+      GetNodeController()->CreateSharedBuffer(
+          create_options.capacity_num_bytes);
+  if (!ring_buffer)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  ports::PortRef port0, port1;
+  GetNodeController()->node()->CreatePortPair(&port0, &port1);
+
+  CHECK(data_pipe_producer_handle);
+  CHECK(data_pipe_consumer_handle);
+
+  uint64_t pipe_id = base::RandUint64();
+
+  scoped_refptr<Dispatcher> producer = new DataPipeProducerDispatcher(
+      GetNodeController(), port0, ring_buffer, create_options,
+      true /* initialized */, pipe_id);
+  scoped_refptr<Dispatcher> consumer = new DataPipeConsumerDispatcher(
+      GetNodeController(), port1, ring_buffer, create_options,
+      true /* initialized */, pipe_id);
+
+  *data_pipe_producer_handle = AddDispatcher(producer);
+  *data_pipe_consumer_handle = AddDispatcher(consumer);
+  if (*data_pipe_producer_handle == MOJO_HANDLE_INVALID ||
+      *data_pipe_consumer_handle == MOJO_HANDLE_INVALID) {
+    if (*data_pipe_producer_handle != MOJO_HANDLE_INVALID) {
+      scoped_refptr<Dispatcher> unused;
+      base::AutoLock lock(handles_lock_);
+      handles_.GetAndRemoveDispatcher(*data_pipe_producer_handle, &unused);
+    }
+    producer->Close();
+    consumer->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::WriteData(MojoHandle data_pipe_producer_handle,
+                           const void* elements,
+                           uint32_t* num_bytes,
+                           MojoWriteDataFlags flags) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_producer_handle));
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->WriteData(elements, num_bytes, flags);
+}
+
+MojoResult Core::BeginWriteData(MojoHandle data_pipe_producer_handle,
+                                void** buffer,
+                                uint32_t* buffer_num_bytes,
+                                MojoWriteDataFlags flags) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_producer_handle));
+  if (!dispatcher)
+    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) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_producer_handle));
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->EndWriteData(num_bytes_written);
+}
+
+MojoResult Core::ReadData(MojoHandle data_pipe_consumer_handle,
+                          void* elements,
+                          uint32_t* num_bytes,
+                          MojoReadDataFlags flags) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_consumer_handle));
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->ReadData(elements, num_bytes, flags);
+}
+
+MojoResult Core::BeginReadData(MojoHandle data_pipe_consumer_handle,
+                               const void** buffer,
+                               uint32_t* buffer_num_bytes,
+                               MojoReadDataFlags flags) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_consumer_handle));
+  if (!dispatcher)
+    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) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher(
+      GetDispatcher(data_pipe_consumer_handle));
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return dispatcher->EndReadData(num_bytes_read);
+}
+
+MojoResult Core::CreateSharedBuffer(
+    const MojoCreateSharedBufferOptions* options,
+    uint64_t num_bytes,
+    MojoHandle* shared_buffer_handle) {
+  RequestContext request_context;
+  MojoCreateSharedBufferOptions validated_options = {};
+  MojoResult result = SharedBufferDispatcher::ValidateCreateOptions(
+      options, &validated_options);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  result = SharedBufferDispatcher::Create(
+      validated_options, GetNodeController(), num_bytes, &dispatcher);
+  if (result != MOJO_RESULT_OK) {
+    DCHECK(!dispatcher);
+    return result;
+  }
+
+  *shared_buffer_handle = AddDispatcher(dispatcher);
+  if (*shared_buffer_handle == MOJO_HANDLE_INVALID) {
+    LOG(ERROR) << "Handle table full";
+    dispatcher->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::DuplicateBufferHandle(
+    MojoHandle buffer_handle,
+    const MojoDuplicateBufferHandleOptions* options,
+    MojoHandle* new_buffer_handle) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
+  if (!dispatcher)
+    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;
+
+  *new_buffer_handle = AddDispatcher(new_dispatcher);
+  if (*new_buffer_handle == MOJO_HANDLE_INVALID) {
+    LOG(ERROR) << "Handle table full";
+    dispatcher->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::MapBuffer(MojoHandle buffer_handle,
+                           uint64_t offset,
+                           uint64_t num_bytes,
+                           void** buffer,
+                           MojoMapBufferFlags flags) {
+  RequestContext request_context;
+  scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
+  if (!dispatcher)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  std::unique_ptr<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(std::move(mapping));
+  }
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  *buffer = address;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::UnmapBuffer(void* buffer) {
+  RequestContext request_context;
+  base::AutoLock lock(mapping_table_lock_);
+  return mapping_table_.RemoveMapping(buffer);
+}
+
+MojoResult Core::WrapPlatformHandle(const MojoPlatformHandle* platform_handle,
+                                    MojoHandle* mojo_handle) {
+  ScopedPlatformHandle handle;
+  MojoResult result = MojoPlatformHandleToScopedPlatformHandle(platform_handle,
+                                                               &handle);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  return CreatePlatformHandleWrapper(std::move(handle), mojo_handle);
+}
+
+MojoResult Core::UnwrapPlatformHandle(MojoHandle mojo_handle,
+                                      MojoPlatformHandle* platform_handle) {
+  ScopedPlatformHandle handle;
+  MojoResult result = PassWrappedPlatformHandle(mojo_handle, &handle);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  return ScopedPlatformHandleToMojoPlatformHandle(std::move(handle),
+                                                  platform_handle);
+}
+
+MojoResult Core::WrapPlatformSharedBufferHandle(
+    const MojoPlatformHandle* platform_handle,
+    size_t size,
+    MojoPlatformSharedBufferHandleFlags flags,
+    MojoHandle* mojo_handle) {
+  DCHECK(size);
+  ScopedPlatformHandle handle;
+  MojoResult result = MojoPlatformHandleToScopedPlatformHandle(platform_handle,
+                                                               &handle);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  bool read_only = flags & MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY;
+  scoped_refptr<PlatformSharedBuffer> platform_buffer =
+      PlatformSharedBuffer::CreateFromPlatformHandle(size, read_only,
+                                                     std::move(handle));
+  if (!platform_buffer)
+    return MOJO_RESULT_UNKNOWN;
+
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  result = SharedBufferDispatcher::CreateFromPlatformSharedBuffer(
+      platform_buffer, &dispatcher);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  MojoHandle h = AddDispatcher(dispatcher);
+  if (h == MOJO_HANDLE_INVALID) {
+    dispatcher->Close();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  *mojo_handle = h;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Core::UnwrapPlatformSharedBufferHandle(
+    MojoHandle mojo_handle,
+    MojoPlatformHandle* platform_handle,
+    size_t* size,
+    MojoPlatformSharedBufferHandleFlags* flags) {
+  scoped_refptr<Dispatcher> dispatcher;
+  MojoResult result = MOJO_RESULT_OK;
+  {
+    base::AutoLock lock(handles_lock_);
+    result = handles_.GetAndRemoveDispatcher(mojo_handle, &dispatcher);
+    if (result != MOJO_RESULT_OK)
+      return result;
+  }
+
+  if (dispatcher->GetType() != Dispatcher::Type::SHARED_BUFFER) {
+    dispatcher->Close();
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  SharedBufferDispatcher* shm_dispatcher =
+      static_cast<SharedBufferDispatcher*>(dispatcher.get());
+  scoped_refptr<PlatformSharedBuffer> platform_shared_buffer =
+      shm_dispatcher->PassPlatformSharedBuffer();
+  CHECK(platform_shared_buffer);
+
+  CHECK(size);
+  *size = platform_shared_buffer->GetNumBytes();
+
+  CHECK(flags);
+  *flags = MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE;
+  if (platform_shared_buffer->IsReadOnly())
+    *flags |= MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY;
+
+  ScopedPlatformHandle handle = platform_shared_buffer->PassPlatformHandle();
+  return ScopedPlatformHandleToMojoPlatformHandle(std::move(handle),
+                                                  platform_handle);
+}
+
+void Core::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) {
+  base::AutoLock lock(handles_lock_);
+  handles_.GetActiveHandlesForTest(handles);
+}
+
+MojoResult Core::WaitManyInternal(const MojoHandle* handles,
+                                  const MojoHandleSignals* signals,
+                                  uint32_t num_handles,
+                                  MojoDeadline deadline,
+                                  uint32_t *result_index,
+                                  HandleSignalsState* signals_states) {
+  CHECK(handles);
+  CHECK(signals);
+  DCHECK_GT(num_handles, 0u);
+  if (result_index) {
+    DCHECK_EQ(*result_index, static_cast<uint32_t>(-1));
+  }
+
+  // The primary caller of |WaitManyInternal()| is |Wait()|, which only waits on
+  // a single handle. In the common case of a single handle, this avoid a heap
+  // allocation.
+  base::StackVector<scoped_refptr<Dispatcher>, 1> dispatchers;
+  dispatchers->reserve(num_handles);
+  for (uint32_t i = 0; i < num_handles; i++) {
+    scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handles[i]);
+    if (!dispatcher) {
+      if (result_index)
+        *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]->AddAwakable(
+        &waiter, signals[i], i, signals_states ? &signals_states[i] : nullptr);
+    if (rv != MOJO_RESULT_OK) {
+      if (result_index)
+        *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) {
+    uintptr_t uintptr_result = *result_index;
+    rv = waiter.Wait(deadline, &uintptr_result);
+    *result_index = static_cast<uint32_t>(uintptr_result);
+  }
+
+  // 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]->RemoveAwakable(
+        &waiter, signals_states ? &signals_states[i] : nullptr);
+  }
+  if (signals_states) {
+    for (; i < num_handles; i++)
+      signals_states[i] = dispatchers[i]->GetHandleSignalsState();
+  }
+
+  return rv;
+}
+
+// static
+void Core::PassNodeControllerToIOThread(
+    std::unique_ptr<NodeController> node_controller) {
+  // It's OK to leak this reference. At this point we know the IO loop is still
+  // running, and we know the NodeController will observe its eventual
+  // destruction. This tells the NodeController to delete itself when that
+  // happens.
+  node_controller.release()->DestroyOnIOThreadShutdown();
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
new file mode 100644
index 0000000..2e3e5b5
--- /dev/null
+++ b/mojo/edk/system/core.h
@@ -0,0 +1,309 @@
+// 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 <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/handle_table.h"
+#include "mojo/edk/system/mapping_table.h"
+#include "mojo/edk/system/node_controller.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/platform_handle.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace base {
+class PortProvider;
+}
+
+namespace mojo {
+namespace edk {
+
+// |Core| is an object that implements the Mojo system calls. All public methods
+// are thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT Core {
+ public:
+  explicit Core();
+  virtual ~Core();
+
+  // Called exactly once, shortly after construction, and before any other
+  // methods are called on this object.
+  void SetIOTaskRunner(scoped_refptr<base::TaskRunner> io_task_runner);
+
+  // Retrieves the NodeController for the current process.
+  NodeController* GetNodeController();
+
+  scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle);
+
+  // Called in the parent process any time a new child is launched.
+  void AddChild(base::ProcessHandle process_handle,
+                ScopedPlatformHandle platform_handle,
+                const std::string& child_token,
+                const ProcessErrorCallback& process_error_callback);
+
+  // Called in the parent process when a child process fails to launch.
+  void ChildLaunchFailed(const std::string& child_token);
+
+  // Called in a child process exactly once during early initialization.
+  void InitChild(ScopedPlatformHandle platform_handle);
+
+  // Creates a message pipe endpoint connected to an endpoint in a remote
+  // embedder. |platform_handle| is used as a channel to negotiate the
+  // connection.
+  ScopedMessagePipeHandle CreateMessagePipe(
+      ScopedPlatformHandle platform_handle);
+
+  // Creates a message pipe endpoint associated with |token|, which a child
+  // holding the token can later locate and connect to.
+  ScopedMessagePipeHandle CreateParentMessagePipe(
+      const std::string& token, const std::string& child_token);
+
+  // Creates a message pipe endpoint and connects it to a pipe the parent has
+  // associated with |token|.
+  ScopedMessagePipeHandle CreateChildMessagePipe(const std::string& token);
+
+  // Sets the mach port provider for this process.
+  void SetMachPortProvider(base::PortProvider* port_provider);
+
+  MojoHandle AddDispatcher(scoped_refptr<Dispatcher> dispatcher);
+
+  // Adds new dispatchers for non-message-pipe handles received in a message.
+  // |dispatchers| and |handles| should be the same size.
+  bool AddDispatchersFromTransit(
+      const std::vector<Dispatcher::DispatcherInTransit>& dispatchers,
+      MojoHandle* handles);
+
+  // See "mojo/edk/embedder/embedder.h" for more information on these functions.
+  MojoResult CreatePlatformHandleWrapper(ScopedPlatformHandle platform_handle,
+                                         MojoHandle* wrapper_handle);
+
+  MojoResult PassWrappedPlatformHandle(MojoHandle wrapper_handle,
+                                       ScopedPlatformHandle* platform_handle);
+
+  MojoResult CreateSharedBufferWrapper(
+      base::SharedMemoryHandle shared_memory_handle,
+      size_t num_bytes,
+      bool read_only,
+      MojoHandle* mojo_wrapper_handle);
+
+  MojoResult PassSharedMemoryHandle(
+      MojoHandle mojo_handle,
+      base::SharedMemoryHandle* shared_memory_handle,
+      size_t* num_bytes,
+      bool* read_only);
+
+  // Requests that the EDK tear itself down. |callback| will be called once
+  // the shutdown process is complete. Note that |callback| is always called
+  // asynchronously on the calling thread if said thread is running a message
+  // loop, and the calling thread must continue running a MessageLoop at least
+  // until the callback is called. If there is no running loop, the |callback|
+  // may be called from any thread. Beware!
+  void RequestShutdown(const base::Closure& callback);
+
+  MojoResult SetProperty(MojoPropertyType type, const void* value);
+
+  // ---------------------------------------------------------------------------
+
+  // The following methods are essentially implementations of the Mojo Core
+  // functions of the Mojo API, with the C interface translated to C++ by
+  // "mojo/edk/embedder/entrypoints.cc". The best way to understand the contract
+  // of these methods is to look at the header files defining the corresponding
+  // API functions, referenced below.
+
+  // These methods correspond to the API functions defined in
+  // "mojo/public/c/system/functions.h":
+  MojoTimeTicks GetTimeTicksNow();
+  MojoResult Close(MojoHandle handle);
+  MojoResult Wait(MojoHandle handle,
+                  MojoHandleSignals signals,
+                  MojoDeadline deadline,
+                  MojoHandleSignalsState* signals_state);
+  MojoResult WaitMany(const MojoHandle* handles,
+                      const MojoHandleSignals* signals,
+                      uint32_t num_handles,
+                      MojoDeadline deadline,
+                      uint32_t* result_index,
+                      MojoHandleSignalsState* signals_states);
+  MojoResult Watch(MojoHandle handle,
+                   MojoHandleSignals signals,
+                   MojoWatchCallback callback,
+                   uintptr_t context);
+  MojoResult CancelWatch(MojoHandle handle, uintptr_t context);
+  MojoResult AllocMessage(uint32_t num_bytes,
+                          const MojoHandle* handles,
+                          uint32_t num_handles,
+                          MojoAllocMessageFlags flags,
+                          MojoMessageHandle* message);
+  MojoResult FreeMessage(MojoMessageHandle message);
+  MojoResult GetMessageBuffer(MojoMessageHandle message, void** buffer);
+  MojoResult GetProperty(MojoPropertyType type, void* value);
+
+  // These methods correspond to the API functions defined in
+  // "mojo/public/c/system/wait_set.h":
+  MojoResult CreateWaitSet(MojoHandle* wait_set_handle);
+  MojoResult AddHandle(MojoHandle wait_set_handle,
+                       MojoHandle handle,
+                       MojoHandleSignals signals);
+  MojoResult RemoveHandle(MojoHandle wait_set_handle,
+                          MojoHandle handle);
+  MojoResult GetReadyHandles(MojoHandle wait_set_handle,
+                             uint32_t* count,
+                             MojoHandle* handles,
+                             MojoResult* results,
+                             MojoHandleSignalsState* signals_states);
+
+  // These methods correspond to the API functions defined in
+  // "mojo/public/c/system/message_pipe.h":
+  MojoResult CreateMessagePipe(
+      const 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 WriteMessageNew(MojoHandle message_pipe_handle,
+                             MojoMessageHandle message,
+                             MojoWriteMessageFlags flags);
+  MojoResult ReadMessage(MojoHandle message_pipe_handle,
+                         void* bytes,
+                         uint32_t* num_bytes,
+                         MojoHandle* handles,
+                         uint32_t* num_handles,
+                         MojoReadMessageFlags flags);
+  MojoResult ReadMessageNew(MojoHandle message_pipe_handle,
+                            MojoMessageHandle* message,
+                            uint32_t* num_bytes,
+                            MojoHandle* handles,
+                            uint32_t* num_handles,
+                            MojoReadMessageFlags flags);
+  MojoResult FuseMessagePipes(MojoHandle handle0, MojoHandle handle1);
+  MojoResult NotifyBadMessage(MojoMessageHandle message,
+                              const char* error,
+                              size_t error_num_bytes);
+
+  // These methods correspond to the API functions defined in
+  // "mojo/public/c/system/data_pipe.h":
+  MojoResult CreateDataPipe(
+      const 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_bytes,
+                       MojoWriteDataFlags flags);
+  MojoResult BeginWriteData(MojoHandle data_pipe_producer_handle,
+                            void** buffer,
+                            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,
+                      void* elements,
+                      uint32_t* num_bytes,
+                      MojoReadDataFlags flags);
+  MojoResult BeginReadData(MojoHandle data_pipe_consumer_handle,
+                           const void** buffer,
+                           uint32_t* buffer_num_bytes,
+                           MojoReadDataFlags flags);
+  MojoResult EndReadData(MojoHandle data_pipe_consumer_handle,
+                         uint32_t num_bytes_read);
+
+  // These methods correspond to the API functions defined in
+  // "mojo/public/c/system/buffer.h":
+  MojoResult CreateSharedBuffer(
+      const MojoCreateSharedBufferOptions* options,
+      uint64_t num_bytes,
+      MojoHandle* shared_buffer_handle);
+  MojoResult DuplicateBufferHandle(
+      MojoHandle buffer_handle,
+      const 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);
+
+  // These methods correspond to the API functions defined in
+  // "mojo/public/c/system/platform_handle.h".
+  MojoResult WrapPlatformHandle(const MojoPlatformHandle* platform_handle,
+                                MojoHandle* mojo_handle);
+  MojoResult UnwrapPlatformHandle(MojoHandle mojo_handle,
+                                  MojoPlatformHandle* platform_handle);
+  MojoResult WrapPlatformSharedBufferHandle(
+      const MojoPlatformHandle* platform_handle,
+      size_t size,
+      MojoPlatformSharedBufferHandleFlags flags,
+      MojoHandle* mojo_handle);
+  MojoResult UnwrapPlatformSharedBufferHandle(
+      MojoHandle mojo_handle,
+      MojoPlatformHandle* platform_handle,
+      size_t* size,
+      MojoPlatformSharedBufferHandleFlags* flags);
+
+  void GetActiveHandlesForTest(std::vector<MojoHandle>* handles);
+
+ private:
+  MojoResult WaitManyInternal(const MojoHandle* handles,
+                              const MojoHandleSignals* signals,
+                              uint32_t num_handles,
+                              MojoDeadline deadline,
+                              uint32_t *result_index,
+                              HandleSignalsState* signals_states);
+
+  // Used to pass ownership of our NodeController over to the IO thread in the
+  // event that we're torn down before said thread.
+  static void PassNodeControllerToIOThread(
+      std::unique_ptr<NodeController> node_controller);
+
+  // Guards node_controller_.
+  //
+  // TODO(rockot): Consider removing this. It's only needed because we
+  // initialize node_controller_ lazily and that may happen on any thread.
+  // Otherwise it's effectively const and shouldn't need to be guarded.
+  //
+  // We can get rid of lazy initialization if we defer Mojo initialization far
+  // enough that zygotes don't do it. The zygote can't create a NodeController.
+  base::Lock node_controller_lock_;
+
+  // This is lazily initialized on first access. Always use GetNodeController()
+  // to access it.
+  std::unique_ptr<NodeController> node_controller_;
+
+  base::Lock handles_lock_;
+  HandleTable handles_;
+
+  base::Lock mapping_table_lock_;  // Protects |mapping_table_|.
+  MappingTable mapping_table_;
+
+  base::Lock property_lock_;
+  // Properties that can be read using the MojoGetProperty() API.
+  bool property_sync_call_allowed_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+}  // namespace edk
+}  // 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..e98a55d
--- /dev/null
+++ b/mojo/edk/system/core_test_base.cc
@@ -0,0 +1,343 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/system/configuration.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/message_for_transit.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+namespace {
+
+// MockDispatcher --------------------------------------------------------------
+
+class MockDispatcher : public Dispatcher {
+ public:
+  static scoped_refptr<MockDispatcher> Create(
+      CoreTestBase::MockHandleInfo* info) {
+    return make_scoped_refptr(new MockDispatcher(info));
+  }
+
+  // Dispatcher:
+  Type GetType() const override { return Type::UNKNOWN; }
+
+  MojoResult Close() override {
+    info_->IncrementCloseCallCount();
+    return MOJO_RESULT_OK;
+  }
+
+  MojoResult WriteMessage(
+      std::unique_ptr<MessageForTransit> message,
+      MojoWriteMessageFlags /*flags*/) override {
+    info_->IncrementWriteMessageCallCount();
+
+    if (message->num_bytes() > GetConfiguration().max_message_num_bytes)
+      return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+    if (message->num_handles())
+      return MOJO_RESULT_UNIMPLEMENTED;
+
+    return MOJO_RESULT_OK;
+  }
+
+  MojoResult ReadMessage(std::unique_ptr<MessageForTransit>* message,
+                         uint32_t* num_bytes,
+                         MojoHandle* handle,
+                         uint32_t* num_handles,
+                         MojoReadMessageFlags /*flags*/,
+                         bool ignore_num_bytes) override {
+    info_->IncrementReadMessageCallCount();
+
+    if (num_handles)
+      *num_handles = 1;
+
+    return MOJO_RESULT_OK;
+  }
+
+  MojoResult WriteData(const void* elements,
+                       uint32_t* num_bytes,
+                       MojoWriteDataFlags flags) override {
+    info_->IncrementWriteDataCallCount();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  MojoResult BeginWriteData(void** buffer,
+                            uint32_t* buffer_num_bytes,
+                            MojoWriteDataFlags flags) override {
+    info_->IncrementBeginWriteDataCallCount();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  MojoResult EndWriteData(uint32_t num_bytes_written) override {
+    info_->IncrementEndWriteDataCallCount();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  MojoResult ReadData(void* elements,
+                      uint32_t* num_bytes,
+                      MojoReadDataFlags flags) override {
+    info_->IncrementReadDataCallCount();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  MojoResult BeginReadData(const void** buffer,
+                           uint32_t* buffer_num_bytes,
+                           MojoReadDataFlags flags) override {
+    info_->IncrementBeginReadDataCallCount();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  MojoResult EndReadData(uint32_t num_bytes_read) override {
+    info_->IncrementEndReadDataCallCount();
+    return MOJO_RESULT_UNIMPLEMENTED;
+  }
+
+  MojoResult AddAwakable(Awakable* awakable,
+                         MojoHandleSignals /*signals*/,
+                         uintptr_t /*context*/,
+                         HandleSignalsState* signals_state) override {
+    info_->IncrementAddAwakableCallCount();
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+    if (info_->IsAddAwakableAllowed()) {
+      info_->AwakableWasAdded(awakable);
+      return MOJO_RESULT_OK;
+    }
+
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  void RemoveAwakable(Awakable* /*awakable*/,
+                      HandleSignalsState* signals_state) override {
+    info_->IncrementRemoveAwakableCallCount();
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+  }
+
+ private:
+  explicit MockDispatcher(CoreTestBase::MockHandleInfo* info) : info_(info) {
+    CHECK(info_);
+    info_->IncrementCtorCallCount();
+  }
+
+  ~MockDispatcher() override { info_->IncrementDtorCallCount(); }
+
+  CoreTestBase::MockHandleInfo* const info_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockDispatcher);
+};
+
+}  // namespace
+
+// CoreTestBase ----------------------------------------------------------------
+
+CoreTestBase::CoreTestBase() {
+}
+
+CoreTestBase::~CoreTestBase() {
+}
+
+MojoHandle CoreTestBase::CreateMockHandle(CoreTestBase::MockHandleInfo* info) {
+  scoped_refptr<MockDispatcher> dispatcher = MockDispatcher::Create(info);
+  return core()->AddDispatcher(dispatcher);
+}
+
+Core* CoreTestBase::core() {
+  return mojo::edk::internal::g_core;
+}
+
+// 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_awakable_call_count_(0),
+      remove_awakable_call_count_(0),
+      add_awakable_allowed_(false) {
+}
+
+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::GetAddAwakableCallCount() const {
+  base::AutoLock locker(lock_);
+  return add_awakable_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetRemoveAwakableCallCount() const {
+  base::AutoLock locker(lock_);
+  return remove_awakable_call_count_;
+}
+
+size_t CoreTestBase_MockHandleInfo::GetAddedAwakableSize() const {
+  base::AutoLock locker(lock_);
+  return added_awakables_.size();
+}
+
+Awakable* CoreTestBase_MockHandleInfo::GetAddedAwakableAt(unsigned i) const {
+  base::AutoLock locker(lock_);
+  return added_awakables_[i];
+}
+
+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::IncrementAddAwakableCallCount() {
+  base::AutoLock locker(lock_);
+  add_awakable_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementRemoveAwakableCallCount() {
+  base::AutoLock locker(lock_);
+  remove_awakable_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::AllowAddAwakable(bool alllow) {
+  base::AutoLock locker(lock_);
+  add_awakable_allowed_ = alllow;
+}
+
+bool CoreTestBase_MockHandleInfo::IsAddAwakableAllowed() const {
+  base::AutoLock locker(lock_);
+  return add_awakable_allowed_;
+}
+
+void CoreTestBase_MockHandleInfo::AwakableWasAdded(Awakable* awakable) {
+  base::AutoLock locker(lock_);
+  added_awakables_.push_back(awakable);
+}
+
+}  // namespace test
+}  // namespace edk
+}  // 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..3d2346a
--- /dev/null
+++ b/mojo/edk/system/core_test_base.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_CORE_TEST_BASE_H_
+#define MOJO_EDK_SYSTEM_CORE_TEST_BASE_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/public/c/system/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+
+class Core;
+class Awakable;
+
+namespace test {
+
+class CoreTestBase_MockHandleInfo;
+
+class CoreTestBase : public testing::Test {
+ public:
+  using MockHandleInfo = CoreTestBase_MockHandleInfo;
+
+  CoreTestBase();
+  ~CoreTestBase() override;
+
+ protected:
+  // |info| must remain alive until the returned handle is closed.
+  MojoHandle CreateMockHandle(MockHandleInfo* info);
+
+  Core* core();
+
+ private:
+  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 GetAddAwakableCallCount() const;
+  unsigned GetRemoveAwakableCallCount() const;
+
+  size_t GetAddedAwakableSize() const;
+  Awakable* GetAddedAwakableAt(unsigned i) 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 IncrementAddAwakableCallCount();
+  void IncrementRemoveAwakableCallCount();
+
+  void AllowAddAwakable(bool alllow);
+  bool IsAddAwakableAllowed() const;
+  void AwakableWasAdded(Awakable*);
+
+ 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_awakable_call_count_;
+  unsigned remove_awakable_call_count_;
+
+  bool add_awakable_allowed_;
+  std::vector<Awakable*> added_awakables_;
+
+  DISALLOW_COPY_AND_ASSIGN(CoreTestBase_MockHandleInfo);
+};
+
+}  // namespace test
+}  // namespace edk
+}  // 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..33a7068
--- /dev/null
+++ b/mojo/edk/system/core_unittest.cc
@@ -0,0 +1,1250 @@
+// 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/bind.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/system/awakable.h"
+#include "mojo/edk/system/core_test_base.h"
+#include "mojo/edk/system/test_utils.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+namespace mojo {
+namespace edk {
+namespace {
+
+const MojoHandleSignalsState kEmptyMojoHandleSignalsState = {0u, 0u};
+const MojoHandleSignalsState kFullMojoHandleSignalsState = {~0u, ~0u};
+const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE |
+                                      MOJO_HANDLE_SIGNAL_WRITABLE |
+                                      MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+
+using CoreTest = test::CoreTestBase;
+
+TEST_F(CoreTest, GetTimeTicksNow) {
+  const MojoTimeTicks start = core()->GetTimeTicksNow();
+  ASSERT_NE(static_cast<MojoTimeTicks>(0), start)
+      << "GetTimeTicksNow should return nonzero value";
+  test::Sleep(test::DeadlineFromMilliseconds(15));
+  const MojoTimeTicks finish = core()->GetTimeTicksNow();
+  // Allow for some fuzz in sleep.
+  ASSERT_GE((finish - start), static_cast<MojoTimeTicks>(8000))
+      << "Sleeping should result in increasing time ticks";
+}
+
+TEST_F(CoreTest, Basic) {
+  MockHandleInfo info;
+
+  ASSERT_EQ(0u, info.GetCtorCallCount());
+  MojoHandle h = CreateMockHandle(&info);
+  ASSERT_EQ(1u, info.GetCtorCallCount());
+  ASSERT_NE(h, MOJO_HANDLE_INVALID);
+
+  ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h, nullptr, 0, nullptr, 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(1u, info.GetWriteMessageCallCount());
+
+  ASSERT_EQ(0u, info.GetReadMessageCallCount());
+  uint32_t num_bytes = 0;
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      core()->ReadMessage(h, nullptr, &num_bytes, nullptr, nullptr,
+                          MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(1u, info.GetReadMessageCallCount());
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(h, nullptr, nullptr, nullptr, nullptr,
+                                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(2u, info.GetReadMessageCallCount());
+
+  ASSERT_EQ(0u, info.GetWriteDataCallCount());
+  ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+            core()->WriteData(h, nullptr, nullptr, MOJO_WRITE_DATA_FLAG_NONE));
+  ASSERT_EQ(1u, info.GetWriteDataCallCount());
+
+  ASSERT_EQ(0u, info.GetBeginWriteDataCallCount());
+  ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+            core()->BeginWriteData(h, nullptr, nullptr,
+                                   MOJO_WRITE_DATA_FLAG_NONE));
+  ASSERT_EQ(1u, info.GetBeginWriteDataCallCount());
+
+  ASSERT_EQ(0u, info.GetEndWriteDataCallCount());
+  ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED, core()->EndWriteData(h, 0));
+  ASSERT_EQ(1u, info.GetEndWriteDataCallCount());
+
+  ASSERT_EQ(0u, info.GetReadDataCallCount());
+  ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+            core()->ReadData(h, nullptr, nullptr, MOJO_READ_DATA_FLAG_NONE));
+  ASSERT_EQ(1u, info.GetReadDataCallCount());
+
+  ASSERT_EQ(0u, info.GetBeginReadDataCallCount());
+  ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+            core()->BeginReadData(h, nullptr, nullptr,
+                                  MOJO_READ_DATA_FLAG_NONE));
+  ASSERT_EQ(1u, info.GetBeginReadDataCallCount());
+
+  ASSERT_EQ(0u, info.GetEndReadDataCallCount());
+  ASSERT_EQ(MOJO_RESULT_UNIMPLEMENTED, core()->EndReadData(h, 0));
+  ASSERT_EQ(1u, info.GetEndReadDataCallCount());
+
+  ASSERT_EQ(0u, info.GetAddAwakableCallCount());
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE,
+                         nullptr));
+  ASSERT_EQ(1u, info.GetAddAwakableCallCount());
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 0, nullptr));
+  ASSERT_EQ(2u, info.GetAddAwakableCallCount());
+  MojoHandleSignalsState hss = kFullMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE,
+                         &hss));
+  ASSERT_EQ(3u, info.GetAddAwakableCallCount());
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(0u, hss.satisfiable_signals);
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000, nullptr));
+  ASSERT_EQ(4u, info.GetAddAwakableCallCount());
+  hss = kFullMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000, &hss));
+  ASSERT_EQ(5u, info.GetAddAwakableCallCount());
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(0u, hss.satisfiable_signals);
+
+  MojoHandleSignals handle_signals = ~MOJO_HANDLE_SIGNAL_NONE;
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->WaitMany(&h, &handle_signals, 1, MOJO_DEADLINE_INDEFINITE,
+                       nullptr, nullptr));
+  ASSERT_EQ(6u, info.GetAddAwakableCallCount());
+  uint32_t result_index = static_cast<uint32_t>(-1);
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->WaitMany(&h, &handle_signals, 1, MOJO_DEADLINE_INDEFINITE,
+                       &result_index, nullptr));
+  ASSERT_EQ(7u, info.GetAddAwakableCallCount());
+  ASSERT_EQ(0u, result_index);
+  hss = kFullMojoHandleSignalsState;
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->WaitMany(&h, &handle_signals, 1, MOJO_DEADLINE_INDEFINITE,
+                       nullptr, &hss));
+  ASSERT_EQ(8u, info.GetAddAwakableCallCount());
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(0u, hss.satisfiable_signals);
+  result_index = static_cast<uint32_t>(-1);
+  hss = kFullMojoHandleSignalsState;
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->WaitMany(&h, &handle_signals, 1, MOJO_DEADLINE_INDEFINITE,
+                       &result_index, &hss));
+  ASSERT_EQ(9u, info.GetAddAwakableCallCount());
+  ASSERT_EQ(0u, result_index);
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(0u, hss.satisfiable_signals);
+
+  ASSERT_EQ(0u, info.GetDtorCallCount());
+  ASSERT_EQ(0u, info.GetCloseCallCount());
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  ASSERT_EQ(1u, info.GetCloseCallCount());
+  ASSERT_EQ(1u, info.GetDtorCallCount());
+
+  // No awakables should ever have ever been added.
+  ASSERT_EQ(0u, info.GetRemoveAwakableCallCount());
+}
+
+TEST_F(CoreTest, InvalidArguments) {
+  // |Close()|:
+  {
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(MOJO_HANDLE_INVALID));
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(10));
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(1000000000));
+
+    // Test a double-close.
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+    ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h));
+    ASSERT_EQ(1u, info.GetCloseCallCount());
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h));
+    ASSERT_EQ(1u, info.GetCloseCallCount());
+  }
+
+  // |Wait()|:
+  {
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->Wait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE,
+                           MOJO_DEADLINE_INDEFINITE, nullptr));
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->Wait(10, ~MOJO_HANDLE_SIGNAL_NONE,
+                           MOJO_DEADLINE_INDEFINITE, nullptr));
+
+    MojoHandleSignalsState hss = kFullMojoHandleSignalsState;
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->Wait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE,
+                           MOJO_DEADLINE_INDEFINITE, &hss));
+    // On invalid argument, it shouldn't modify the handle signals state.
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+    hss = kFullMojoHandleSignalsState;
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->Wait(10, ~MOJO_HANDLE_SIGNAL_NONE,
+                           MOJO_DEADLINE_INDEFINITE, &hss));
+    // On invalid argument, it shouldn't modify the handle signals state.
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    ASSERT_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};
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->WaitMany(handles, signals, 0, MOJO_DEADLINE_INDEFINITE,
+                         nullptr, nullptr));
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(nullptr, signals, 0, MOJO_DEADLINE_INDEFINITE,
+                               nullptr, nullptr));
+    // 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;
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(nullptr, signals, 0, MOJO_DEADLINE_INDEFINITE,
+                               &result_index, &hss));
+    ASSERT_EQ(123u, result_index);
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(handles, nullptr, 0, MOJO_DEADLINE_INDEFINITE,
+                               nullptr, nullptr));
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->WaitMany(handles, signals, 1, MOJO_DEADLINE_INDEFINITE, nullptr,
+                         nullptr));
+    // 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;
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(
+                  handles, signals, 1, MOJO_DEADLINE_INDEFINITE, &result_index,
+                  &hss));
+    ASSERT_EQ(0u, result_index);
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+
+    MockHandleInfo info[2];
+    handles[0] = CreateMockHandle(&info[0]);
+
+    result_index = static_cast<uint32_t>(-1);
+    hss = kFullMojoHandleSignalsState;
+    ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              core()->WaitMany(
+                  handles, signals, 1, MOJO_DEADLINE_INDEFINITE, &result_index,
+                  &hss));
+    ASSERT_EQ(0u, result_index);
+    ASSERT_EQ(0u, hss.satisfied_signals);
+    ASSERT_EQ(0u, hss.satisfiable_signals);
+
+    // On invalid argument, it'll leave |signals_states| alone.
+    result_index = static_cast<uint32_t>(-1);
+    hss = kFullMojoHandleSignalsState;
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WaitMany(
+                  handles, signals, 2, MOJO_DEADLINE_INDEFINITE, &result_index,
+                  &hss));
+    ASSERT_EQ(1u, result_index);
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfied_signals,
+              hss.satisfied_signals);
+    ASSERT_EQ(kFullMojoHandleSignalsState.satisfiable_signals,
+              hss.satisfiable_signals);
+    handles[1] = handles[0] + 1;  // Invalid handle.
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->WaitMany(handles, signals, 2, MOJO_DEADLINE_INDEFINITE, nullptr,
+                         nullptr));
+    handles[1] = CreateMockHandle(&info[1]);
+    ASSERT_EQ(
+        MOJO_RESULT_FAILED_PRECONDITION,
+        core()->WaitMany(handles, signals, 2, MOJO_DEADLINE_INDEFINITE, nullptr,
+                         nullptr));
+
+    // TODO(vtl): Test one where we get "failed precondition" only for the
+    // second handle (and the first one is valid to wait on).
+
+    ASSERT_EQ(MOJO_RESULT_OK, core()->Close(handles[0]));
+    ASSERT_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|.
+  {
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WriteMessage(MOJO_HANDLE_INVALID, nullptr, 0,
+                                   nullptr, 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.
+    ASSERT_NE(
+        MOJO_RESULT_OK,
+        core()->WriteMessage(h, nullptr, 0, handles,
+                             std::numeric_limits<uint32_t>::max(),
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Null |bytes| with non-zero message size.
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WriteMessage(h, nullptr, 1, nullptr, 0,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Null |handles| with non-zero handle count.
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->WriteMessage(h, nullptr, 0, nullptr, 1,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Huge handle count (plausibly big).
+    ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+              core()->WriteMessage(
+                  h, nullptr, 0, handles,
+                  std::numeric_limits<uint32_t>::max() / sizeof(handles[0]),
+                  MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Invalid handle in |handles|.
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->WriteMessage(h, nullptr, 0, handles, 1,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Two invalid handles in |handles|.
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->WriteMessage(h, nullptr, 0, handles, 2,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+
+    // Can't send a handle over itself. Note that this will also cause |h| to be
+    // closed.
+    handles[0] = h;
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->WriteMessage(h, nullptr, 0, handles, 1,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+
+    h = CreateMockHandle(&info);
+
+    MockHandleInfo info2;
+
+    // This is "okay", but |MockDispatcher| doesn't implement it.
+    handles[0] = CreateMockHandle(&info2);
+    ASSERT_EQ(
+        MOJO_RESULT_UNIMPLEMENTED,
+        core()->WriteMessage(h, nullptr, 0, handles, 1,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(1u, info.GetWriteMessageCallCount());
+
+    // One of the |handles| is still invalid.
+    handles[0] = CreateMockHandle(&info2);
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->WriteMessage(h, nullptr, 0, handles, 2,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(1u, info.GetWriteMessageCallCount());
+
+    // One of the |handles| is the same as |h|. Both handles are closed.
+    handles[0] = CreateMockHandle(&info2);
+    handles[1] = h;
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->WriteMessage(h, nullptr, 0, handles, 2,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(1u, info.GetWriteMessageCallCount());
+
+    h = CreateMockHandle(&info);
+
+    // Can't send a handle twice in the same message.
+    handles[0] = CreateMockHandle(&info2);
+    handles[1] = handles[0];
+    ASSERT_EQ(
+        MOJO_RESULT_BUSY,
+        core()->WriteMessage(h, nullptr, 0, handles, 2,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(1u, info.GetWriteMessageCallCount());
+
+    ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  }
+
+  // |ReadMessage()|:
+  // Only check arguments checked by |Core|, namely |handle|, |handles|, and
+  // |num_handles|.
+  {
+    ASSERT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->ReadMessage(MOJO_HANDLE_INVALID, nullptr, nullptr, nullptr,
+                            nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
+
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+
+    // Okay.
+    uint32_t handle_count = 0;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              core()->ReadMessage(
+                  h, nullptr, nullptr, nullptr, &handle_count,
+                  MOJO_READ_MESSAGE_FLAG_NONE));
+    // Checked by |Core|, shouldn't go through to the dispatcher.
+    ASSERT_EQ(1u, info.GetReadMessageCallCount());
+
+    ASSERT_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) {
+#if defined(OFFICIAL_BUILD)
+  const char kMemoryCheckFailedRegex[] = "";
+#else
+  const char kMemoryCheckFailedRegex[] = "Check failed";
+#endif
+
+  // |WaitMany()|:
+  {
+    MojoHandle handle = MOJO_HANDLE_INVALID;
+    MojoHandleSignals signals = ~MOJO_HANDLE_SIGNAL_NONE;
+    ASSERT_DEATH_IF_SUPPORTED(
+        core()->WaitMany(nullptr, &signals, 1, MOJO_DEADLINE_INDEFINITE,
+                         nullptr, nullptr),
+        kMemoryCheckFailedRegex);
+    ASSERT_DEATH_IF_SUPPORTED(
+        core()->WaitMany(&handle, nullptr, 1, MOJO_DEADLINE_INDEFINITE, nullptr,
+                         nullptr),
+        kMemoryCheckFailedRegex);
+    // TODO(vtl): |result_index| and |signals_states| are optional. Test them
+    // with non-null invalid pointers?
+  }
+
+  // |CreateMessagePipe()|:
+  {
+    MojoHandle h;
+    ASSERT_DEATH_IF_SUPPORTED(
+        core()->CreateMessagePipe(nullptr, nullptr, nullptr),
+        kMemoryCheckFailedRegex);
+    ASSERT_DEATH_IF_SUPPORTED(
+        core()->CreateMessagePipe(nullptr, &h, nullptr),
+        kMemoryCheckFailedRegex);
+    ASSERT_DEATH_IF_SUPPORTED(
+        core()->CreateMessagePipe(nullptr, nullptr, &h),
+        kMemoryCheckFailedRegex);
+  }
+
+  // |ReadMessage()|:
+  // Only check arguments checked by |Core|, namely |handle|, |handles|, and
+  // |num_handles|.
+  {
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+
+    uint32_t handle_count = 1;
+    ASSERT_DEATH_IF_SUPPORTED(
+        core()->ReadMessage(h, nullptr, nullptr, nullptr, &handle_count,
+                            MOJO_READ_MESSAGE_FLAG_NONE),
+        kMemoryCheckFailedRegex);
+
+    ASSERT_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;
+
+  ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessagePipe(nullptr, &h[0], &h[1]));
+  // Should get two distinct, valid handles.
+  ASSERT_NE(h[0], MOJO_HANDLE_INVALID);
+  ASSERT_NE(h[1], MOJO_HANDLE_INVALID);
+  ASSERT_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;
+  ASSERT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      core()->WaitMany(h, signals, 2, 0, &result_index, hss));
+  ASSERT_EQ(static_cast<uint32_t>(-1), result_index);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss[1].satisfiable_signals);
+
+  // Try to read anyway.
+  char buffer[1] = {'a'};
+  uint32_t buffer_size = 1;
+  ASSERT_EQ(
+      MOJO_RESULT_SHOULD_WAIT,
+      core()->ReadMessage(h[0], buffer, &buffer_size, nullptr, nullptr,
+                          MOJO_READ_MESSAGE_FLAG_NONE));
+  // Check that it left its inputs alone.
+  ASSERT_EQ('a', buffer[0]);
+  ASSERT_EQ(1u, buffer_size);
+
+  // Both should be writable.
+  hss[0] = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(h[0], MOJO_HANDLE_SIGNAL_WRITABLE,
+                                         1000000000, &hss[0]));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals);
+  hss[0] = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(h[1], MOJO_HANDLE_SIGNAL_WRITABLE,
+                                         1000000000, &hss[0]));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  ASSERT_EQ(kAllSignals, 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;
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      core()->WaitMany(h, signals, 2, MOJO_DEADLINE_INDEFINITE, &result_index,
+                       hss));
+  ASSERT_EQ(1u, result_index);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss[1].satisfiable_signals);
+
+  // Write to |h[1]|.
+  buffer[0] = 'b';
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      core()->WriteMessage(h[1], buffer, 1, nullptr, 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;
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      core()->WaitMany(h, signals, 2, MOJO_DEADLINE_INDEFINITE, &result_index,
+                       hss));
+  ASSERT_EQ(0u, result_index);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss[0].satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss[1].satisfiable_signals);
+
+  // Read from |h[0]|.
+  // First, get only the size.
+  buffer_size = 0;
+  ASSERT_EQ(
+      MOJO_RESULT_RESOURCE_EXHAUSTED,
+      core()->ReadMessage(h[0], nullptr, &buffer_size, nullptr, nullptr,
+                          MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(1u, buffer_size);
+  // Then actually read it.
+  buffer[0] = 'c';
+  buffer_size = 1;
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      core()->ReadMessage(h[0], buffer, &buffer_size, nullptr, nullptr,
+                          MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ('b', buffer[0]);
+  ASSERT_EQ(1u, buffer_size);
+
+  // |h[0]| should no longer be readable.
+  hss[0] = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            core()->Wait(h[0], MOJO_HANDLE_SIGNAL_READABLE, 0, &hss[0]));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals);
+
+  // Write to |h[0]|.
+  buffer[0] = 'd';
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      core()->WriteMessage(h[0], buffer, 1, nullptr, 0,
+                           MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Close |h[0]|.
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h[0]));
+
+  // Wait for |h[1]| to learn about the other end's closure.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h[1], MOJO_HANDLE_SIGNAL_PEER_CLOSED, 1000000000,
+                         &hss[0]));
+
+  // Check that |h[1]| is no longer writable (and will never be).
+  hss[0] = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h[1], MOJO_HANDLE_SIGNAL_WRITABLE, 1000000000,
+                         &hss[0]));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss[0].satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss[0].satisfiable_signals);
+
+  // Check that |h[1]| is still readable (for the moment).
+  hss[0] = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(h[1], MOJO_HANDLE_SIGNAL_READABLE,
+                                         1000000000, &hss[0]));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss[0].satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss[0].satisfiable_signals);
+
+  // Discard a message from |h[1]|.
+  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            core()->ReadMessage(h[1], nullptr, nullptr, nullptr, nullptr,
+                                MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+  // |h[1]| is no longer readable (and will never be).
+  hss[0] = kFullMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            core()->Wait(h[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss[0]));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss[0].satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss[0].satisfiable_signals);
+
+  // Try writing to |h[1]|.
+  buffer[0] = 'e';
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->WriteMessage(h[1], buffer, 1, nullptr, 0,
+                           MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  ASSERT_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];
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1]));
+
+  // Make sure that |h_passing[]| work properly.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0], kHello, kHelloSize, nullptr, 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(
+                h_passing[1], buffer, &num_bytes, handles, &num_handles,
+                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kHelloSize, num_bytes);
+  ASSERT_STREQ(kHello, buffer);
+  ASSERT_EQ(0u, num_handles);
+
+  // Make sure that you can't pass either of the message pipe's handles over
+  // itself.
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            core()->WriteMessage(h_passing[0], kHello, kHelloSize,
+                                 &h_passing[0], 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1]));
+
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            core()->WriteMessage(h_passing[0], kHello, kHelloSize,
+                                 &h_passing[1], 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1]));
+
+  MojoHandle h_passed[2];
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessagePipe(nullptr, &h_passed[0], &h_passed[1]));
+
+  // Make sure that |h_passed[]| work properly.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passed[0], kHello, kHelloSize, nullptr, 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passed[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(
+                h_passed[1], buffer, &num_bytes, handles, &num_handles,
+                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kHelloSize, num_bytes);
+  ASSERT_STREQ(kHello, buffer);
+  ASSERT_EQ(0u, num_handles);
+
+  // Send |h_passed[1]| from |h_passing[0]| to |h_passing[1]|.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0], kWorld, kWorldSize,
+                                 &h_passed[1], 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(
+                h_passing[1], buffer, &num_bytes, handles, &num_handles,
+                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kWorldSize, num_bytes);
+  ASSERT_STREQ(kWorld, buffer);
+  ASSERT_EQ(1u, num_handles);
+  h_received = handles[0];
+  ASSERT_NE(h_received, MOJO_HANDLE_INVALID);
+  ASSERT_NE(h_received, h_passing[0]);
+  ASSERT_NE(h_received, h_passing[1]);
+  ASSERT_NE(h_received, h_passed[0]);
+
+  // Note: We rely on the Mojo system not re-using handle values very often.
+  ASSERT_NE(h_received, h_passed[1]);
+
+  // |h_passed[1]| should no longer be valid; check that trying to close it
+  // fails. See above note.
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h_passed[1]));
+
+  // Write to |h_passed[0]|. Should receive on |h_received|.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passed[0], kHello, kHelloSize, nullptr, 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(
+                h_received, buffer, &num_bytes, handles, &num_handles,
+                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kHelloSize, num_bytes);
+  ASSERT_STREQ(kHello, buffer);
+  ASSERT_EQ(0u, num_handles);
+
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1]));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passed[0]));
+  ASSERT_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;
+
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateDataPipe(nullptr, &ph, &ch));
+  // Should get two distinct, valid handles.
+  ASSERT_NE(ph, MOJO_HANDLE_INVALID);
+  ASSERT_NE(ch, MOJO_HANDLE_INVALID);
+  ASSERT_NE(ph, ch);
+
+  // Producer should be never-readable, but already writable.
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->Wait(ph, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ph, MOJO_HANDLE_SIGNAL_WRITABLE, 0,
+                                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Consumer should be never-writable, and not yet readable.
+  hss = kFullMojoHandleSignalsState;
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+  hss = kFullMojoHandleSignalsState;
+  ASSERT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Write.
+  signed char elements[2] = {'A', 'B'};
+  uint32_t num_bytes = 2u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteData(ph, elements, &num_bytes,
+                              MOJO_WRITE_DATA_FLAG_NONE));
+  ASSERT_EQ(2u, num_bytes);
+
+  // Wait for the data to arrive to the consumer.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, &hss));
+
+  // Consumer should now be readable.
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0,
+                                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Peek one character.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = 1u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(
+                ch, elements, &num_bytes,
+                MOJO_READ_DATA_FLAG_NONE | MOJO_READ_DATA_FLAG_PEEK));
+  ASSERT_EQ('A', elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Read one character.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = 1u;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->ReadData(ch, elements, &num_bytes,
+                                             MOJO_READ_DATA_FLAG_NONE));
+  ASSERT_EQ('A', elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Two-phase write.
+  void* write_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->BeginWriteData(ph, &write_ptr, &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;
+  ASSERT_EQ(MOJO_RESULT_BUSY,
+            core()->WriteData(ph, elements, &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';
+  ASSERT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 3u));
+
+  // Wait for the data to arrive to the consumer.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, &hss));
+
+  // Query how much data we have.
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(ch, nullptr, &num_bytes,
+                             MOJO_READ_DATA_FLAG_QUERY));
+  ASSERT_GE(num_bytes, 1u);
+
+  // Try to query with peek. Should fail.
+  num_bytes = 0;
+  ASSERT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      core()->ReadData(ch, nullptr, &num_bytes,
+                       MOJO_READ_DATA_FLAG_QUERY | MOJO_READ_DATA_FLAG_PEEK));
+  ASSERT_EQ(0u, num_bytes);
+
+  // Try to discard ten characters, in all-or-none mode. Should fail.
+  num_bytes = 10;
+  ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            core()->ReadData(
+                ch, nullptr, &num_bytes,
+                MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+  // Try to discard two characters, in peek mode. Should fail.
+  num_bytes = 2;
+  ASSERT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      core()->ReadData(ch, nullptr, &num_bytes,
+                       MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_PEEK));
+
+  // Discard a character.
+  num_bytes = 1;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(
+                ch, nullptr, &num_bytes,
+                MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+  // Ensure the 3 bytes were read.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, &hss));
+
+  // Try a two-phase read of the remaining three bytes with peek. Should fail.
+  const void* read_ptr = nullptr;
+  num_bytes = 3;
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            core()->BeginReadData(ch, &read_ptr, &num_bytes,
+                                  MOJO_READ_DATA_FLAG_PEEK));
+
+  // Read the remaining two characters, in two-phase mode (all-or-none).
+  num_bytes = 3;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->BeginReadData(ch, &read_ptr, &num_bytes,
+                                  MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+  // Note: Count on still being able to do the contiguous read here.
+  ASSERT_EQ(3u, num_bytes);
+
+  // Discarding right now should fail.
+  num_bytes = 1;
+  ASSERT_EQ(MOJO_RESULT_BUSY,
+            core()->ReadData(ch, nullptr, &num_bytes,
+                             MOJO_READ_DATA_FLAG_DISCARD));
+
+  // Actually check our data and end the two-phase read.
+  ASSERT_EQ('C', static_cast<const char*>(read_ptr)[0]);
+  ASSERT_EQ('D', static_cast<const char*>(read_ptr)[1]);
+  ASSERT_EQ('E', static_cast<const char*>(read_ptr)[2]);
+  ASSERT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 3u));
+
+  // Consumer should now be no longer readable.
+  hss = kFullMojoHandleSignalsState;
+  ASSERT_EQ(
+      MOJO_RESULT_DEADLINE_EXCEEDED,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // TODO(vtl): More.
+
+  // Close the producer.
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ph));
+
+  // Wait for this to get to the consumer.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(ch, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 1000000000, &hss));
+
+  // The consumer should now be never-readable.
+  hss = kFullMojoHandleSignalsState;
+  ASSERT_EQ(
+      MOJO_RESULT_FAILED_PRECONDITION,
+      core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+
+  ASSERT_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];
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1]));
+
+  MojoHandle ph, ch;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateDataPipe(nullptr, &ph, &ch));
+
+  // Send |ch| from |h_passing[0]| to |h_passing[1]|.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(
+                h_passing[1], buffer, &num_bytes, handles, &num_handles,
+                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kHelloSize, num_bytes);
+  ASSERT_STREQ(kHello, buffer);
+  ASSERT_EQ(1u, num_handles);
+  MojoHandle ch_received = handles[0];
+  ASSERT_NE(ch_received, MOJO_HANDLE_INVALID);
+  ASSERT_NE(ch_received, h_passing[0]);
+  ASSERT_NE(ch_received, h_passing[1]);
+  ASSERT_NE(ch_received, ph);
+
+  // Note: We rely on the Mojo system not re-using handle values very often.
+  ASSERT_NE(ch_received, ch);
+
+  // |ch| should no longer be valid; check that trying to close it fails. See
+  // above note.
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ch));
+
+  // Write to |ph|. Should receive on |ch_received|.
+  num_bytes = kWorldSize;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteData(ph, kWorld, &num_bytes,
+                              MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(ch_received, buffer, &num_bytes,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kWorldSize, num_bytes);
+  ASSERT_STREQ(kWorld, buffer);
+
+  // Now pass |ph| in the same direction.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0], kWorld, kWorldSize, &ph, 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(
+                h_passing[1], buffer, &num_bytes, handles, &num_handles,
+                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kWorldSize, num_bytes);
+  ASSERT_STREQ(kWorld, buffer);
+  ASSERT_EQ(1u, num_handles);
+  MojoHandle ph_received = handles[0];
+  ASSERT_NE(ph_received, MOJO_HANDLE_INVALID);
+  ASSERT_NE(ph_received, h_passing[0]);
+  ASSERT_NE(ph_received, h_passing[1]);
+  ASSERT_NE(ph_received, ch_received);
+
+  // Again, rely on the Mojo system not re-using handle values very often.
+  ASSERT_NE(ph_received, ph);
+
+  // |ph| should no longer be valid; check that trying to close it fails. See
+  // above note.
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ph));
+
+  // Write to |ph_received|. Should receive on |ch_received|.
+  num_bytes = kHelloSize;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteData(ph_received, kHello, &num_bytes,
+                              MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(ch_received, buffer, &num_bytes,
+                             MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kHelloSize, num_bytes);
+  ASSERT_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, &write_ptr, &num_bytes,
+                                   MOJO_WRITE_DATA_FLAG_NONE));
+  ASSERT_GE(num_bytes, 1u);
+  ASSERT_EQ(MOJO_RESULT_BUSY,
+            core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ph, 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // But |ch| can, even if |ph| is in a two-phase write.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ch = MOJO_HANDLE_INVALID;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         nullptr));
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(
+                h_passing[1], buffer, &num_bytes, handles, &num_handles,
+                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kHelloSize, num_bytes);
+  ASSERT_STREQ(kHello, buffer);
+  ASSERT_EQ(1u, num_handles);
+  ch = handles[0];
+  ASSERT_NE(ch, MOJO_HANDLE_INVALID);
+
+  // Complete the two-phase write.
+  static_cast<char*>(write_ptr)[0] = 'x';
+  ASSERT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1));
+
+  // Wait for |ch| to be readable.
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE,
+                                         1000000000, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            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, &read_ptr, &num_bytes,
+                                  MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+  ASSERT_EQ(MOJO_RESULT_BUSY,
+            core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // But |ph| can, even if |ch| is in a two-phase read.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessage(h_passing[0], kWorld, kWorldSize, &ph, 1,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ph = MOJO_HANDLE_INVALID;
+  hss = kEmptyMojoHandleSignalsState;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
+                         &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  num_bytes = kBufferSize;
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessage(
+                h_passing[1], buffer, &num_bytes, handles, &num_handles,
+                MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(kWorldSize, num_bytes);
+  ASSERT_STREQ(kWorld, buffer);
+  ASSERT_EQ(1u, num_handles);
+  ph = handles[0];
+  ASSERT_NE(ph, MOJO_HANDLE_INVALID);
+
+  // Complete the two-phase read.
+  ASSERT_EQ('x', static_cast<const char*>(read_ptr)[0]);
+  ASSERT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 1));
+
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1]));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ph));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ch));
+}
+
+struct TestAsyncWaiter {
+  TestAsyncWaiter() : result(MOJO_RESULT_UNKNOWN) {}
+
+  void Awake(MojoResult r) { result = r; }
+
+  MojoResult result;
+};
+
+// TODO(vtl): Test |DuplicateBufferHandle()| and |MapBuffer()|.
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
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..23cb2e0
--- /dev/null
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -0,0 +1,572 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/data_pipe_control_message.h"
+#include "mojo/edk/system/node_controller.h"
+#include "mojo/edk/system/ports_message.h"
+#include "mojo/edk/system/request_context.h"
+#include "mojo/public/c/system/data_pipe.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+const uint8_t kFlagPeerClosed = 0x01;
+
+#pragma pack(push, 1)
+
+struct SerializedState {
+  MojoCreateDataPipeOptions options;
+  uint64_t pipe_id;
+  uint32_t read_offset;
+  uint32_t bytes_available;
+  uint8_t flags;
+  char padding[7];
+};
+
+static_assert(sizeof(SerializedState) % 8 == 0,
+              "Invalid SerializedState size.");
+
+#pragma pack(pop)
+
+}  // namespace
+
+// A PortObserver which forwards to a DataPipeConsumerDispatcher. This owns a
+// reference to the dispatcher to ensure it lives as long as the observed port.
+class DataPipeConsumerDispatcher::PortObserverThunk
+    : public NodeController::PortObserver {
+ public:
+  explicit PortObserverThunk(
+      scoped_refptr<DataPipeConsumerDispatcher> dispatcher)
+      : dispatcher_(dispatcher) {}
+
+ private:
+  ~PortObserverThunk() override {}
+
+  // NodeController::PortObserver:
+  void OnPortStatusChanged() override { dispatcher_->OnPortStatusChanged(); }
+
+  scoped_refptr<DataPipeConsumerDispatcher> dispatcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(PortObserverThunk);
+};
+
+DataPipeConsumerDispatcher::DataPipeConsumerDispatcher(
+    NodeController* node_controller,
+    const ports::PortRef& control_port,
+    scoped_refptr<PlatformSharedBuffer> shared_ring_buffer,
+    const MojoCreateDataPipeOptions& options,
+    bool initialized,
+    uint64_t pipe_id)
+    : options_(options),
+      node_controller_(node_controller),
+      control_port_(control_port),
+      pipe_id_(pipe_id),
+      shared_ring_buffer_(shared_ring_buffer) {
+  if (initialized) {
+    base::AutoLock lock(lock_);
+    InitializeNoLock();
+  }
+}
+
+Dispatcher::Type DataPipeConsumerDispatcher::GetType() const {
+  return Type::DATA_PIPE_CONSUMER;
+}
+
+MojoResult DataPipeConsumerDispatcher::Close() {
+  base::AutoLock lock(lock_);
+  DVLOG(1) << "Closing data pipe consumer " << pipe_id_;
+  return CloseNoLock();
+}
+
+
+MojoResult DataPipeConsumerDispatcher::Watch(
+    MojoHandleSignals signals,
+    const Watcher::WatchCallback& callback,
+    uintptr_t context) {
+  base::AutoLock lock(lock_);
+
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return awakable_list_.AddWatcher(
+      signals, callback, context, GetHandleSignalsStateNoLock());
+}
+
+MojoResult DataPipeConsumerDispatcher::CancelWatch(uintptr_t context) {
+  base::AutoLock lock(lock_);
+
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return awakable_list_.RemoveWatcher(context);
+}
+
+MojoResult DataPipeConsumerDispatcher::ReadData(void* elements,
+                                                uint32_t* num_bytes,
+                                                MojoReadDataFlags flags) {
+  base::AutoLock lock(lock_);
+  if (!shared_ring_buffer_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (in_two_phase_read_)
+    return MOJO_RESULT_BUSY;
+
+  if ((flags & MOJO_READ_DATA_FLAG_QUERY)) {
+    if ((flags & MOJO_READ_DATA_FLAG_PEEK) ||
+        (flags & MOJO_READ_DATA_FLAG_DISCARD))
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    DCHECK(!(flags & MOJO_READ_DATA_FLAG_DISCARD));  // Handled above.
+    DVLOG_IF(2, elements)
+        << "Query mode: ignoring non-null |elements|";
+    *num_bytes = static_cast<uint32_t>(bytes_available_);
+    return MOJO_RESULT_OK;
+  }
+
+  bool discard = false;
+  if ((flags & MOJO_READ_DATA_FLAG_DISCARD)) {
+    // These flags are mutally exclusive.
+    if (flags & MOJO_READ_DATA_FLAG_PEEK)
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    DVLOG_IF(2, elements)
+        << "Discard mode: ignoring non-null |elements|";
+    discard = true;
+  }
+
+  uint32_t max_num_bytes_to_read = *num_bytes;
+  if (max_num_bytes_to_read % options_.element_num_bytes != 0)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  bool all_or_none = flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE;
+  uint32_t min_num_bytes_to_read =
+      all_or_none ? max_num_bytes_to_read : 0;
+
+  if (min_num_bytes_to_read > bytes_available_) {
+    return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION
+                        : MOJO_RESULT_OUT_OF_RANGE;
+  }
+
+  uint32_t bytes_to_read = std::min(max_num_bytes_to_read, bytes_available_);
+  if (bytes_to_read == 0) {
+    return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION
+                        : MOJO_RESULT_SHOULD_WAIT;
+  }
+
+  if (!discard) {
+    uint8_t* data = static_cast<uint8_t*>(ring_buffer_mapping_->GetBase());
+    CHECK(data);
+
+    uint8_t* destination = static_cast<uint8_t*>(elements);
+    CHECK(destination);
+
+    DCHECK_LE(read_offset_, options_.capacity_num_bytes);
+    uint32_t tail_bytes_to_copy =
+        std::min(options_.capacity_num_bytes - read_offset_, bytes_to_read);
+    uint32_t head_bytes_to_copy = bytes_to_read - tail_bytes_to_copy;
+    if (tail_bytes_to_copy > 0)
+      memcpy(destination, data + read_offset_, tail_bytes_to_copy);
+    if (head_bytes_to_copy > 0)
+      memcpy(destination + tail_bytes_to_copy, data, head_bytes_to_copy);
+  }
+  *num_bytes = bytes_to_read;
+
+  bool peek = !!(flags & MOJO_READ_DATA_FLAG_PEEK);
+  if (discard || !peek) {
+    read_offset_ = (read_offset_ + bytes_to_read) % options_.capacity_num_bytes;
+    bytes_available_ -= bytes_to_read;
+
+    base::AutoUnlock unlock(lock_);
+    NotifyRead(bytes_to_read);
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult DataPipeConsumerDispatcher::BeginReadData(const void** buffer,
+                                                     uint32_t* buffer_num_bytes,
+                                                     MojoReadDataFlags flags) {
+  base::AutoLock lock(lock_);
+  if (!shared_ring_buffer_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (in_two_phase_read_)
+    return MOJO_RESULT_BUSY;
+
+  // These flags may not be used in two-phase mode.
+  if ((flags & MOJO_READ_DATA_FLAG_DISCARD) ||
+      (flags & MOJO_READ_DATA_FLAG_QUERY) ||
+      (flags & MOJO_READ_DATA_FLAG_PEEK))
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (bytes_available_ == 0) {
+    return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION
+                        : MOJO_RESULT_SHOULD_WAIT;
+  }
+
+  DCHECK_LT(read_offset_, options_.capacity_num_bytes);
+  uint32_t bytes_to_read = std::min(bytes_available_,
+                                    options_.capacity_num_bytes - read_offset_);
+
+  CHECK(ring_buffer_mapping_);
+  uint8_t* data = static_cast<uint8_t*>(ring_buffer_mapping_->GetBase());
+  CHECK(data);
+
+  in_two_phase_read_ = true;
+  *buffer = data + read_offset_;
+  *buffer_num_bytes = bytes_to_read;
+  two_phase_max_bytes_read_ = bytes_to_read;
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult DataPipeConsumerDispatcher::EndReadData(uint32_t num_bytes_read) {
+  base::AutoLock lock(lock_);
+  if (!in_two_phase_read_)
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  if (in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  CHECK(shared_ring_buffer_);
+
+  HandleSignalsState old_state = GetHandleSignalsStateNoLock();
+  MojoResult rv;
+  if (num_bytes_read > two_phase_max_bytes_read_ ||
+      num_bytes_read % options_.element_num_bytes != 0) {
+    rv = MOJO_RESULT_INVALID_ARGUMENT;
+  } else {
+    rv = MOJO_RESULT_OK;
+    read_offset_ =
+        (read_offset_ + num_bytes_read) % options_.capacity_num_bytes;
+
+    DCHECK_GE(bytes_available_, num_bytes_read);
+    bytes_available_ -= num_bytes_read;
+
+    base::AutoUnlock unlock(lock_);
+    NotifyRead(num_bytes_read);
+  }
+
+  in_two_phase_read_ = false;
+  two_phase_max_bytes_read_ = 0;
+
+  HandleSignalsState new_state = GetHandleSignalsStateNoLock();
+  if (!new_state.equals(old_state))
+    awakable_list_.AwakeForStateChange(new_state);
+
+  return rv;
+}
+
+HandleSignalsState DataPipeConsumerDispatcher::GetHandleSignalsState() const {
+  base::AutoLock lock(lock_);
+  return GetHandleSignalsStateNoLock();
+}
+
+MojoResult DataPipeConsumerDispatcher::AddAwakable(
+    Awakable* awakable,
+    MojoHandleSignals signals,
+    uintptr_t context,
+    HandleSignalsState* signals_state) {
+  base::AutoLock lock(lock_);
+  if (!shared_ring_buffer_ || in_transit_) {
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  UpdateSignalsStateNoLock();
+  HandleSignalsState state = GetHandleSignalsStateNoLock();
+  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;
+  }
+
+  awakable_list_.Add(awakable, signals, context);
+  return MOJO_RESULT_OK;
+}
+
+void DataPipeConsumerDispatcher::RemoveAwakable(
+    Awakable* awakable,
+    HandleSignalsState* signals_state) {
+  base::AutoLock lock(lock_);
+  if ((!shared_ring_buffer_ || in_transit_) && signals_state)
+    *signals_state = HandleSignalsState();
+  else if (signals_state)
+    *signals_state = GetHandleSignalsStateNoLock();
+  awakable_list_.Remove(awakable);
+}
+
+void DataPipeConsumerDispatcher::StartSerialize(uint32_t* num_bytes,
+                                                uint32_t* num_ports,
+                                                uint32_t* num_handles) {
+  base::AutoLock lock(lock_);
+  DCHECK(in_transit_);
+  *num_bytes = static_cast<uint32_t>(sizeof(SerializedState));
+  *num_ports = 1;
+  *num_handles = 1;
+}
+
+bool DataPipeConsumerDispatcher::EndSerialize(
+    void* destination,
+    ports::PortName* ports,
+    PlatformHandle* platform_handles) {
+  SerializedState* state = static_cast<SerializedState*>(destination);
+  memcpy(&state->options, &options_, sizeof(MojoCreateDataPipeOptions));
+  memset(state->padding, 0, sizeof(state->padding));
+
+  base::AutoLock lock(lock_);
+  DCHECK(in_transit_);
+  state->pipe_id = pipe_id_;
+  state->read_offset = read_offset_;
+  state->bytes_available = bytes_available_;
+  state->flags = peer_closed_ ? kFlagPeerClosed : 0;
+
+  ports[0] = control_port_.name();
+
+  buffer_handle_for_transit_ = shared_ring_buffer_->DuplicatePlatformHandle();
+  platform_handles[0] = buffer_handle_for_transit_.get();
+
+  return true;
+}
+
+bool DataPipeConsumerDispatcher::BeginTransit() {
+  base::AutoLock lock(lock_);
+  if (in_transit_)
+    return false;
+  in_transit_ = !in_two_phase_read_;
+  return in_transit_;
+}
+
+void DataPipeConsumerDispatcher::CompleteTransitAndClose() {
+  node_controller_->SetPortObserver(control_port_, nullptr);
+
+  base::AutoLock lock(lock_);
+  DCHECK(in_transit_);
+  in_transit_ = false;
+  transferred_ = true;
+  ignore_result(buffer_handle_for_transit_.release());
+  CloseNoLock();
+}
+
+void DataPipeConsumerDispatcher::CancelTransit() {
+  base::AutoLock lock(lock_);
+  DCHECK(in_transit_);
+  in_transit_ = false;
+  buffer_handle_for_transit_.reset();
+  UpdateSignalsStateNoLock();
+}
+
+// static
+scoped_refptr<DataPipeConsumerDispatcher>
+DataPipeConsumerDispatcher::Deserialize(const void* data,
+                                        size_t num_bytes,
+                                        const ports::PortName* ports,
+                                        size_t num_ports,
+                                        PlatformHandle* handles,
+                                        size_t num_handles) {
+  if (num_ports != 1 || num_handles != 1 ||
+      num_bytes != sizeof(SerializedState)) {
+    return nullptr;
+  }
+
+  const SerializedState* state = static_cast<const SerializedState*>(data);
+
+  NodeController* node_controller = internal::g_core->GetNodeController();
+  ports::PortRef port;
+  if (node_controller->node()->GetPort(ports[0], &port) != ports::OK)
+    return nullptr;
+
+  PlatformHandle buffer_handle;
+  std::swap(buffer_handle, handles[0]);
+  scoped_refptr<PlatformSharedBuffer> ring_buffer =
+      PlatformSharedBuffer::CreateFromPlatformHandle(
+          state->options.capacity_num_bytes,
+          false /* read_only */,
+          ScopedPlatformHandle(buffer_handle));
+  if (!ring_buffer) {
+    DLOG(ERROR) << "Failed to deserialize shared buffer handle.";
+    return nullptr;
+  }
+
+  scoped_refptr<DataPipeConsumerDispatcher> dispatcher =
+      new DataPipeConsumerDispatcher(node_controller, port, ring_buffer,
+                                     state->options, false /* initialized */,
+                                     state->pipe_id);
+
+  {
+    base::AutoLock lock(dispatcher->lock_);
+    dispatcher->read_offset_ = state->read_offset;
+    dispatcher->bytes_available_ = state->bytes_available;
+    dispatcher->peer_closed_ = state->flags & kFlagPeerClosed;
+    dispatcher->InitializeNoLock();
+  }
+
+  return dispatcher;
+}
+
+DataPipeConsumerDispatcher::~DataPipeConsumerDispatcher() {
+  DCHECK(is_closed_ && !shared_ring_buffer_ && !ring_buffer_mapping_ &&
+         !in_transit_);
+}
+
+void DataPipeConsumerDispatcher::InitializeNoLock() {
+  lock_.AssertAcquired();
+
+  if (shared_ring_buffer_) {
+    DCHECK(!ring_buffer_mapping_);
+    ring_buffer_mapping_ =
+        shared_ring_buffer_->Map(0, options_.capacity_num_bytes);
+    if (!ring_buffer_mapping_) {
+      DLOG(ERROR) << "Failed to map shared buffer.";
+      shared_ring_buffer_ = nullptr;
+    }
+  }
+
+  base::AutoUnlock unlock(lock_);
+  node_controller_->SetPortObserver(
+      control_port_,
+      make_scoped_refptr(new PortObserverThunk(this)));
+}
+
+MojoResult DataPipeConsumerDispatcher::CloseNoLock() {
+  lock_.AssertAcquired();
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  is_closed_ = true;
+  ring_buffer_mapping_.reset();
+  shared_ring_buffer_ = nullptr;
+
+  awakable_list_.CancelAll();
+  if (!transferred_) {
+    base::AutoUnlock unlock(lock_);
+    node_controller_->ClosePort(control_port_);
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+HandleSignalsState
+DataPipeConsumerDispatcher::GetHandleSignalsStateNoLock() const {
+  lock_.AssertAcquired();
+
+  HandleSignalsState rv;
+  if (shared_ring_buffer_ && bytes_available_) {
+    if (!in_two_phase_read_)
+      rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  } else if (!peer_closed_ && shared_ring_buffer_) {
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  }
+
+  if (peer_closed_)
+    rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+  rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+  return rv;
+}
+
+void DataPipeConsumerDispatcher::NotifyRead(uint32_t num_bytes) {
+  DVLOG(1) << "Data pipe consumer " << pipe_id_ << " notifying peer: "
+           << num_bytes << " bytes read. [control_port="
+           << control_port_.name() << "]";
+
+  SendDataPipeControlMessage(node_controller_, control_port_,
+                             DataPipeCommand::DATA_WAS_READ, num_bytes);
+}
+
+void DataPipeConsumerDispatcher::OnPortStatusChanged() {
+  DCHECK(RequestContext::current());
+
+  base::AutoLock lock(lock_);
+
+  // We stop observing the control port as soon it's transferred, but this can
+  // race with events which are raised right before that happens. This is fine
+  // to ignore.
+  if (transferred_)
+    return;
+
+  DVLOG(1) << "Control port status changed for data pipe producer " << pipe_id_;
+
+  UpdateSignalsStateNoLock();
+}
+
+void DataPipeConsumerDispatcher::UpdateSignalsStateNoLock() {
+  lock_.AssertAcquired();
+
+  bool was_peer_closed = peer_closed_;
+  size_t previous_bytes_available = bytes_available_;
+
+  ports::PortStatus port_status;
+  int rv = node_controller_->node()->GetStatus(control_port_, &port_status);
+  if (rv != ports::OK || !port_status.receiving_messages) {
+    DVLOG(1) << "Data pipe consumer " << pipe_id_ << " is aware of peer closure"
+             << " [control_port=" << control_port_.name() << "]";
+    peer_closed_ = true;
+  } else if (rv == ports::OK && port_status.has_messages && !in_transit_) {
+    ports::ScopedMessage message;
+    do {
+      int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr,
+                                                      &message);
+      if (rv != ports::OK)
+        peer_closed_ = true;
+      if (message) {
+        if (message->num_payload_bytes() < sizeof(DataPipeControlMessage)) {
+          peer_closed_ = true;
+          break;
+        }
+
+        const DataPipeControlMessage* m =
+            static_cast<const DataPipeControlMessage*>(
+                message->payload_bytes());
+
+        if (m->command != DataPipeCommand::DATA_WAS_WRITTEN) {
+          DLOG(ERROR) << "Unexpected control message from producer.";
+          peer_closed_ = true;
+          break;
+        }
+
+        if (static_cast<size_t>(bytes_available_) + m->num_bytes >
+              options_.capacity_num_bytes) {
+          DLOG(ERROR) << "Producer claims to have written too many bytes.";
+          peer_closed_ = true;
+          break;
+        }
+
+        DVLOG(1) << "Data pipe consumer " << pipe_id_ << " is aware that "
+                 << m->num_bytes << " bytes were written. [control_port="
+                 << control_port_.name() << "]";
+
+        bytes_available_ += m->num_bytes;
+      }
+    } while (message);
+  }
+
+  if (peer_closed_ != was_peer_closed ||
+      bytes_available_ != previous_bytes_available) {
+    awakable_list_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+  }
+}
+
+}  // namespace edk
+}  // 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..6a7fb1c
--- /dev/null
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -0,0 +1,127 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/awakable_list.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/ports/port_ref.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+struct DataPipeControlMessage;
+class NodeController;
+
+// 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 final
+    : public Dispatcher {
+ public:
+  DataPipeConsumerDispatcher(
+      NodeController* node_controller,
+      const ports::PortRef& control_port,
+      scoped_refptr<PlatformSharedBuffer> shared_ring_buffer,
+      const MojoCreateDataPipeOptions& options,
+      bool initialized,
+      uint64_t pipe_id);
+
+  // Dispatcher:
+  Type GetType() const override;
+  MojoResult Close() override;
+  MojoResult Watch(MojoHandleSignals signals,
+                   const Watcher::WatchCallback& callback,
+                   uintptr_t context) override;
+  MojoResult CancelWatch(uintptr_t context) override;
+  MojoResult ReadData(void* elements,
+                      uint32_t* num_bytes,
+                      MojoReadDataFlags flags) override;
+  MojoResult BeginReadData(const void** buffer,
+                           uint32_t* buffer_num_bytes,
+                           MojoReadDataFlags flags) override;
+  MojoResult EndReadData(uint32_t num_bytes_read) override;
+  HandleSignalsState GetHandleSignalsState() const override;
+  MojoResult AddAwakable(Awakable* awakable,
+                         MojoHandleSignals signals,
+                         uintptr_t context,
+                         HandleSignalsState* signals_state) override;
+  void RemoveAwakable(Awakable* awakable,
+                      HandleSignalsState* signals_state) override;
+  void StartSerialize(uint32_t* num_bytes,
+                      uint32_t* num_ports,
+                      uint32_t* num_handles) override;
+  bool EndSerialize(void* destination,
+                    ports::PortName* ports,
+                    PlatformHandle* handles) override;
+  bool BeginTransit() override;
+  void CompleteTransitAndClose() override;
+  void CancelTransit() override;
+
+  static scoped_refptr<DataPipeConsumerDispatcher>
+  Deserialize(const void* data,
+              size_t num_bytes,
+              const ports::PortName* ports,
+              size_t num_ports,
+              PlatformHandle* handles,
+              size_t num_handles);
+
+ private:
+  class PortObserverThunk;
+  friend class PortObserverThunk;
+
+  ~DataPipeConsumerDispatcher() override;
+
+  void InitializeNoLock();
+  MojoResult CloseNoLock();
+  HandleSignalsState GetHandleSignalsStateNoLock() const;
+  void NotifyRead(uint32_t num_bytes);
+  void OnPortStatusChanged();
+  void UpdateSignalsStateNoLock();
+
+  const MojoCreateDataPipeOptions options_;
+  NodeController* const node_controller_;
+  const ports::PortRef control_port_;
+  const uint64_t pipe_id_;
+
+  // Guards access to the fields below.
+  mutable base::Lock lock_;
+
+  AwakableList awakable_list_;
+
+  scoped_refptr<PlatformSharedBuffer> shared_ring_buffer_;
+  std::unique_ptr<PlatformSharedBufferMapping> ring_buffer_mapping_;
+  ScopedPlatformHandle buffer_handle_for_transit_;
+
+  bool in_two_phase_read_ = false;
+  uint32_t two_phase_max_bytes_read_ = 0;
+
+  bool in_transit_ = false;
+  bool is_closed_ = false;
+  bool peer_closed_ = false;
+  bool transferred_ = false;
+
+  uint32_t read_offset_ = 0;
+  uint32_t bytes_available_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_
diff --git a/mojo/edk/system/data_pipe_control_message.cc b/mojo/edk/system/data_pipe_control_message.cc
new file mode 100644
index 0000000..23873b8
--- /dev/null
+++ b/mojo/edk/system/data_pipe_control_message.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Authors. All 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_control_message.h"
+
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/system/node_controller.h"
+#include "mojo/edk/system/ports_message.h"
+
+namespace mojo {
+namespace edk {
+
+void SendDataPipeControlMessage(NodeController* node_controller,
+                                const ports::PortRef& port,
+                                DataPipeCommand command,
+                                uint32_t num_bytes) {
+  std::unique_ptr<PortsMessage> message =
+      PortsMessage::NewUserMessage(sizeof(DataPipeControlMessage), 0, 0);
+  CHECK(message);
+
+  DataPipeControlMessage* data =
+      static_cast<DataPipeControlMessage*>(message->mutable_payload_bytes());
+  data->command = command;
+  data->num_bytes = num_bytes;
+
+  int rv = node_controller->SendMessage(port, std::move(message));
+  if (rv != ports::OK && rv != ports::ERROR_PORT_PEER_CLOSED) {
+    DLOG(ERROR) << "Unexpected failure sending data pipe control message: "
+                << rv;
+  }
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/data_pipe_control_message.h b/mojo/edk/system/data_pipe_control_message.h
new file mode 100644
index 0000000..82ee594
--- /dev/null
+++ b/mojo/edk/system/data_pipe_control_message.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All 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_CONTROL_MESSAGE_H_
+#define MOJO_EDK_SYSTEM_DATA_PIPE_CONTROL_MESSAGE_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/ports/port_ref.h"
+#include "mojo/public/c/system/macros.h"
+
+namespace mojo {
+namespace edk {
+
+class NodeController;
+class PortsMessage;
+
+enum DataPipeCommand : uint32_t {
+  // Signal to the consumer that new data is available.
+  DATA_WAS_WRITTEN,
+
+  // Signal to the producer that data has been consumed.
+  DATA_WAS_READ,
+};
+
+// Message header for messages sent over a data pipe control port.
+struct MOJO_ALIGNAS(8) DataPipeControlMessage {
+  DataPipeCommand command;
+  uint32_t num_bytes;
+};
+
+void SendDataPipeControlMessage(NodeController* node_controller,
+                                const ports::PortRef& port,
+                                DataPipeCommand command,
+                                uint32_t num_bytes);
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_DATA_PIPE_CONTROL_MESSAGE_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..d056e7d
--- /dev/null
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -0,0 +1,549 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/configuration.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/data_pipe_control_message.h"
+#include "mojo/edk/system/node_controller.h"
+#include "mojo/edk/system/ports_message.h"
+#include "mojo/edk/system/request_context.h"
+#include "mojo/public/c/system/data_pipe.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+const uint8_t kFlagPeerClosed = 0x01;
+
+#pragma pack(push, 1)
+
+struct SerializedState {
+  MojoCreateDataPipeOptions options;
+  uint64_t pipe_id;
+  uint32_t write_offset;
+  uint32_t available_capacity;
+  uint8_t flags;
+  char padding[7];
+};
+
+static_assert(sizeof(SerializedState) % 8 == 0,
+              "Invalid SerializedState size.");
+
+#pragma pack(pop)
+
+}  // namespace
+
+// A PortObserver which forwards to a DataPipeProducerDispatcher. This owns a
+// reference to the dispatcher to ensure it lives as long as the observed port.
+class DataPipeProducerDispatcher::PortObserverThunk
+    : public NodeController::PortObserver {
+ public:
+  explicit PortObserverThunk(
+      scoped_refptr<DataPipeProducerDispatcher> dispatcher)
+      : dispatcher_(dispatcher) {}
+
+ private:
+  ~PortObserverThunk() override {}
+
+  // NodeController::PortObserver:
+  void OnPortStatusChanged() override { dispatcher_->OnPortStatusChanged(); }
+
+  scoped_refptr<DataPipeProducerDispatcher> dispatcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(PortObserverThunk);
+};
+
+DataPipeProducerDispatcher::DataPipeProducerDispatcher(
+    NodeController* node_controller,
+    const ports::PortRef& control_port,
+    scoped_refptr<PlatformSharedBuffer> shared_ring_buffer,
+    const MojoCreateDataPipeOptions& options,
+    bool initialized,
+    uint64_t pipe_id)
+    : options_(options),
+      node_controller_(node_controller),
+      control_port_(control_port),
+      pipe_id_(pipe_id),
+      shared_ring_buffer_(shared_ring_buffer),
+      available_capacity_(options_.capacity_num_bytes) {
+  if (initialized) {
+    base::AutoLock lock(lock_);
+    InitializeNoLock();
+  }
+}
+
+Dispatcher::Type DataPipeProducerDispatcher::GetType() const {
+  return Type::DATA_PIPE_PRODUCER;
+}
+
+MojoResult DataPipeProducerDispatcher::Close() {
+  base::AutoLock lock(lock_);
+  DVLOG(1) << "Closing data pipe producer " << pipe_id_;
+  return CloseNoLock();
+}
+
+MojoResult DataPipeProducerDispatcher::Watch(
+    MojoHandleSignals signals,
+    const Watcher::WatchCallback& callback,
+    uintptr_t context) {
+  base::AutoLock lock(lock_);
+
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return awakable_list_.AddWatcher(
+      signals, callback, context, GetHandleSignalsStateNoLock());
+}
+
+MojoResult DataPipeProducerDispatcher::CancelWatch(uintptr_t context) {
+  base::AutoLock lock(lock_);
+
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return awakable_list_.RemoveWatcher(context);
+}
+
+MojoResult DataPipeProducerDispatcher::WriteData(const void* elements,
+                                                 uint32_t* num_bytes,
+                                                 MojoWriteDataFlags flags) {
+  base::AutoLock lock(lock_);
+  if (!shared_ring_buffer_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (in_two_phase_write_)
+    return MOJO_RESULT_BUSY;
+
+  if (peer_closed_)
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  if (*num_bytes % options_.element_num_bytes != 0)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (*num_bytes == 0)
+    return MOJO_RESULT_OK;  // Nothing to do.
+
+  bool all_or_none = flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE;
+  uint32_t min_num_bytes_to_write = all_or_none ? *num_bytes : 0;
+  if (min_num_bytes_to_write > options_.capacity_num_bytes) {
+    // Don't return "should wait" since you can't wait for a specified amount of
+    // data.
+    return MOJO_RESULT_OUT_OF_RANGE;
+  }
+
+  DCHECK_LE(available_capacity_, options_.capacity_num_bytes);
+  uint32_t num_bytes_to_write = std::min(*num_bytes, available_capacity_);
+  if (num_bytes_to_write == 0)
+    return MOJO_RESULT_SHOULD_WAIT;
+
+  HandleSignalsState old_state = GetHandleSignalsStateNoLock();
+
+  *num_bytes = num_bytes_to_write;
+
+  CHECK(ring_buffer_mapping_);
+  uint8_t* data = static_cast<uint8_t*>(ring_buffer_mapping_->GetBase());
+  CHECK(data);
+
+  const uint8_t* source = static_cast<const uint8_t*>(elements);
+  CHECK(source);
+
+  DCHECK_LE(write_offset_, options_.capacity_num_bytes);
+  uint32_t tail_bytes_to_write =
+      std::min(options_.capacity_num_bytes - write_offset_,
+               num_bytes_to_write);
+  uint32_t head_bytes_to_write = num_bytes_to_write - tail_bytes_to_write;
+
+  DCHECK_GT(tail_bytes_to_write, 0u);
+  memcpy(data + write_offset_, source, tail_bytes_to_write);
+  if (head_bytes_to_write > 0)
+    memcpy(data, source + tail_bytes_to_write, head_bytes_to_write);
+
+  DCHECK_LE(num_bytes_to_write, available_capacity_);
+  available_capacity_ -= num_bytes_to_write;
+  write_offset_ = (write_offset_ + num_bytes_to_write) %
+      options_.capacity_num_bytes;
+
+  HandleSignalsState new_state = GetHandleSignalsStateNoLock();
+  if (!new_state.equals(old_state))
+    awakable_list_.AwakeForStateChange(new_state);
+
+  base::AutoUnlock unlock(lock_);
+  NotifyWrite(num_bytes_to_write);
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult DataPipeProducerDispatcher::BeginWriteData(
+    void** buffer,
+    uint32_t* buffer_num_bytes,
+    MojoWriteDataFlags flags) {
+  base::AutoLock lock(lock_);
+  if (!shared_ring_buffer_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (in_two_phase_write_)
+    return MOJO_RESULT_BUSY;
+  if (peer_closed_)
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  if (available_capacity_ == 0) {
+    return peer_closed_ ? MOJO_RESULT_FAILED_PRECONDITION
+                        : MOJO_RESULT_SHOULD_WAIT;
+  }
+
+  in_two_phase_write_ = true;
+  *buffer_num_bytes = std::min(options_.capacity_num_bytes - write_offset_,
+                               available_capacity_);
+  DCHECK_GT(*buffer_num_bytes, 0u);
+
+  CHECK(ring_buffer_mapping_);
+  uint8_t* data = static_cast<uint8_t*>(ring_buffer_mapping_->GetBase());
+  *buffer = data + write_offset_;
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult DataPipeProducerDispatcher::EndWriteData(
+    uint32_t num_bytes_written) {
+  base::AutoLock lock(lock_);
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if (!in_two_phase_write_)
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  DCHECK(shared_ring_buffer_);
+  DCHECK(ring_buffer_mapping_);
+
+  // Note: Allow successful completion of the two-phase write even if the other
+  // side has been closed.
+  MojoResult rv = MOJO_RESULT_OK;
+  if (num_bytes_written > available_capacity_ ||
+      num_bytes_written % options_.element_num_bytes != 0 ||
+      write_offset_ + num_bytes_written > options_.capacity_num_bytes) {
+    rv = MOJO_RESULT_INVALID_ARGUMENT;
+  } else {
+    DCHECK_LE(num_bytes_written + write_offset_, options_.capacity_num_bytes);
+    available_capacity_ -= num_bytes_written;
+    write_offset_ = (write_offset_ + num_bytes_written) %
+        options_.capacity_num_bytes;
+
+    base::AutoUnlock unlock(lock_);
+    NotifyWrite(num_bytes_written);
+  }
+
+  in_two_phase_write_ = false;
+
+  // If we're now writable, we *became* writable (since we weren't writable
+  // during the two-phase write), so awake producer awakables.
+  HandleSignalsState new_state = GetHandleSignalsStateNoLock();
+  if (new_state.satisfies(MOJO_HANDLE_SIGNAL_WRITABLE))
+    awakable_list_.AwakeForStateChange(new_state);
+
+  return rv;
+}
+
+HandleSignalsState DataPipeProducerDispatcher::GetHandleSignalsState() const {
+  base::AutoLock lock(lock_);
+  return GetHandleSignalsStateNoLock();
+}
+
+MojoResult DataPipeProducerDispatcher::AddAwakable(
+    Awakable* awakable,
+    MojoHandleSignals signals,
+    uintptr_t context,
+    HandleSignalsState* signals_state) {
+  base::AutoLock lock(lock_);
+  if (!shared_ring_buffer_ || in_transit_) {
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  UpdateSignalsStateNoLock();
+  HandleSignalsState state = GetHandleSignalsStateNoLock();
+  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;
+  }
+
+  awakable_list_.Add(awakable, signals, context);
+  return MOJO_RESULT_OK;
+}
+
+void DataPipeProducerDispatcher::RemoveAwakable(
+    Awakable* awakable,
+    HandleSignalsState* signals_state) {
+  base::AutoLock lock(lock_);
+  if ((!shared_ring_buffer_ || in_transit_) && signals_state)
+    *signals_state = HandleSignalsState();
+  else if (signals_state)
+    *signals_state = GetHandleSignalsStateNoLock();
+  awakable_list_.Remove(awakable);
+}
+
+void DataPipeProducerDispatcher::StartSerialize(uint32_t* num_bytes,
+                                                uint32_t* num_ports,
+                                                uint32_t* num_handles) {
+  base::AutoLock lock(lock_);
+  DCHECK(in_transit_);
+  *num_bytes = sizeof(SerializedState);
+  *num_ports = 1;
+  *num_handles = 1;
+}
+
+bool DataPipeProducerDispatcher::EndSerialize(
+    void* destination,
+    ports::PortName* ports,
+    PlatformHandle* platform_handles) {
+  SerializedState* state = static_cast<SerializedState*>(destination);
+  memcpy(&state->options, &options_, sizeof(MojoCreateDataPipeOptions));
+  memset(state->padding, 0, sizeof(state->padding));
+
+  base::AutoLock lock(lock_);
+  DCHECK(in_transit_);
+  state->pipe_id = pipe_id_;
+  state->write_offset = write_offset_;
+  state->available_capacity = available_capacity_;
+  state->flags = peer_closed_ ? kFlagPeerClosed : 0;
+
+  ports[0] = control_port_.name();
+
+  buffer_handle_for_transit_ = shared_ring_buffer_->DuplicatePlatformHandle();
+  platform_handles[0] = buffer_handle_for_transit_.get();
+
+  return true;
+}
+
+bool DataPipeProducerDispatcher::BeginTransit() {
+  base::AutoLock lock(lock_);
+  if (in_transit_)
+    return false;
+  in_transit_ = !in_two_phase_write_;
+  return in_transit_;
+}
+
+void DataPipeProducerDispatcher::CompleteTransitAndClose() {
+  node_controller_->SetPortObserver(control_port_, nullptr);
+
+  base::AutoLock lock(lock_);
+  DCHECK(in_transit_);
+  transferred_ = true;
+  in_transit_ = false;
+  ignore_result(buffer_handle_for_transit_.release());
+  CloseNoLock();
+}
+
+void DataPipeProducerDispatcher::CancelTransit() {
+  base::AutoLock lock(lock_);
+  DCHECK(in_transit_);
+  in_transit_ = false;
+  buffer_handle_for_transit_.reset();
+  awakable_list_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+}
+
+// static
+scoped_refptr<DataPipeProducerDispatcher>
+DataPipeProducerDispatcher::Deserialize(const void* data,
+                                        size_t num_bytes,
+                                        const ports::PortName* ports,
+                                        size_t num_ports,
+                                        PlatformHandle* handles,
+                                        size_t num_handles) {
+  if (num_ports != 1 || num_handles != 1 ||
+      num_bytes != sizeof(SerializedState)) {
+    return nullptr;
+  }
+
+  const SerializedState* state = static_cast<const SerializedState*>(data);
+
+  NodeController* node_controller = internal::g_core->GetNodeController();
+  ports::PortRef port;
+  if (node_controller->node()->GetPort(ports[0], &port) != ports::OK)
+    return nullptr;
+
+  PlatformHandle buffer_handle;
+  std::swap(buffer_handle, handles[0]);
+  scoped_refptr<PlatformSharedBuffer> ring_buffer =
+      PlatformSharedBuffer::CreateFromPlatformHandle(
+          state->options.capacity_num_bytes,
+          false /* read_only */,
+          ScopedPlatformHandle(buffer_handle));
+  if (!ring_buffer) {
+    DLOG(ERROR) << "Failed to deserialize shared buffer handle.";
+    return nullptr;
+  }
+
+  scoped_refptr<DataPipeProducerDispatcher> dispatcher =
+      new DataPipeProducerDispatcher(node_controller, port, ring_buffer,
+                                     state->options, false /* initialized */,
+                                     state->pipe_id);
+
+  {
+    base::AutoLock lock(dispatcher->lock_);
+    dispatcher->write_offset_ = state->write_offset;
+    dispatcher->available_capacity_ = state->available_capacity;
+    dispatcher->peer_closed_ = state->flags & kFlagPeerClosed;
+    dispatcher->InitializeNoLock();
+  }
+
+  return dispatcher;
+}
+
+DataPipeProducerDispatcher::~DataPipeProducerDispatcher() {
+  DCHECK(is_closed_ && !in_transit_ && !shared_ring_buffer_ &&
+         !ring_buffer_mapping_);
+}
+
+void DataPipeProducerDispatcher::InitializeNoLock() {
+  lock_.AssertAcquired();
+
+  if (shared_ring_buffer_) {
+    ring_buffer_mapping_ =
+        shared_ring_buffer_->Map(0, options_.capacity_num_bytes);
+    if (!ring_buffer_mapping_) {
+      DLOG(ERROR) << "Failed to map shared buffer.";
+      shared_ring_buffer_ = nullptr;
+    }
+  }
+
+  base::AutoUnlock unlock(lock_);
+  node_controller_->SetPortObserver(
+      control_port_,
+      make_scoped_refptr(new PortObserverThunk(this)));
+}
+
+MojoResult DataPipeProducerDispatcher::CloseNoLock() {
+  lock_.AssertAcquired();
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  is_closed_ = true;
+  ring_buffer_mapping_.reset();
+  shared_ring_buffer_ = nullptr;
+
+  awakable_list_.CancelAll();
+  if (!transferred_) {
+    base::AutoUnlock unlock(lock_);
+    node_controller_->ClosePort(control_port_);
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+HandleSignalsState DataPipeProducerDispatcher::GetHandleSignalsStateNoLock()
+    const {
+  lock_.AssertAcquired();
+  HandleSignalsState rv;
+  if (!peer_closed_) {
+    if (!in_two_phase_write_ && shared_ring_buffer_ &&
+        available_capacity_ > 0)
+      rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+  } else {
+    rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+  }
+  rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+  return rv;
+}
+
+void DataPipeProducerDispatcher::NotifyWrite(uint32_t num_bytes) {
+  DVLOG(1) << "Data pipe producer " << pipe_id_ << " notifying peer: "
+           << num_bytes << " bytes written. [control_port="
+           << control_port_.name() << "]";
+
+  SendDataPipeControlMessage(node_controller_, control_port_,
+                             DataPipeCommand::DATA_WAS_WRITTEN, num_bytes);
+}
+
+void DataPipeProducerDispatcher::OnPortStatusChanged() {
+  DCHECK(RequestContext::current());
+
+  base::AutoLock lock(lock_);
+
+  // We stop observing the control port as soon it's transferred, but this can
+  // race with events which are raised right before that happens. This is fine
+  // to ignore.
+  if (transferred_)
+    return;
+
+  DVLOG(1) << "Control port status changed for data pipe producer " << pipe_id_;
+
+  UpdateSignalsStateNoLock();
+}
+
+void DataPipeProducerDispatcher::UpdateSignalsStateNoLock() {
+  lock_.AssertAcquired();
+
+  bool was_peer_closed = peer_closed_;
+  size_t previous_capacity = available_capacity_;
+
+  ports::PortStatus port_status;
+  int rv = node_controller_->node()->GetStatus(control_port_, &port_status);
+  if (rv != ports::OK || !port_status.receiving_messages) {
+    DVLOG(1) << "Data pipe producer " << pipe_id_ << " is aware of peer closure"
+             << " [control_port=" << control_port_.name() << "]";
+    peer_closed_ = true;
+  } else if (rv == ports::OK && port_status.has_messages && !in_transit_) {
+    ports::ScopedMessage message;
+    do {
+      int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr,
+                                                      &message);
+      if (rv != ports::OK)
+        peer_closed_ = true;
+      if (message) {
+        if (message->num_payload_bytes() < sizeof(DataPipeControlMessage)) {
+          peer_closed_ = true;
+          break;
+        }
+
+        const DataPipeControlMessage* m =
+            static_cast<const DataPipeControlMessage*>(
+                message->payload_bytes());
+
+        if (m->command != DataPipeCommand::DATA_WAS_READ) {
+          DLOG(ERROR) << "Unexpected message from consumer.";
+          peer_closed_ = true;
+          break;
+        }
+
+        if (static_cast<size_t>(available_capacity_) + m->num_bytes >
+              options_.capacity_num_bytes) {
+          DLOG(ERROR) << "Consumer claims to have read too many bytes.";
+          break;
+        }
+
+        DVLOG(1) << "Data pipe producer " << pipe_id_ << " is aware that "
+                 << m->num_bytes << " bytes were read. [control_port="
+                 << control_port_.name() << "]";
+
+        available_capacity_ += m->num_bytes;
+      }
+    } while (message);
+  }
+
+  if (peer_closed_ != was_peer_closed ||
+      available_capacity_ != previous_capacity) {
+    awakable_list_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+  }
+}
+
+}  // namespace edk
+}  // 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..a55234a
--- /dev/null
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.h
@@ -0,0 +1,129 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/awakable_list.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/ports/port_ref.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+struct DataPipeControlMessage;
+class NodeController;
+
+// 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 final
+    : public Dispatcher {
+ public:
+  DataPipeProducerDispatcher(
+      NodeController* node_controller,
+      const ports::PortRef& port,
+      scoped_refptr<PlatformSharedBuffer> shared_ring_buffer,
+      const MojoCreateDataPipeOptions& options,
+      bool initialized,
+      uint64_t pipe_id);
+
+  // Dispatcher:
+  Type GetType() const override;
+  MojoResult Close() override;
+  MojoResult Watch(MojoHandleSignals signals,
+                   const Watcher::WatchCallback& callback,
+                   uintptr_t context) override;
+  MojoResult CancelWatch(uintptr_t context) override;
+  MojoResult WriteData(const void* elements,
+                       uint32_t* num_bytes,
+                       MojoReadDataFlags flags) override;
+  MojoResult BeginWriteData(void** buffer,
+                            uint32_t* buffer_num_bytes,
+                            MojoWriteDataFlags flags) override;
+  MojoResult EndWriteData(uint32_t num_bytes_written) override;
+  HandleSignalsState GetHandleSignalsState() const override;
+  MojoResult AddAwakable(Awakable* awakable,
+                         MojoHandleSignals signals,
+                         uintptr_t context,
+                         HandleSignalsState* signals_state) override;
+  void RemoveAwakable(Awakable* awakable,
+                      HandleSignalsState* signals_state) override;
+  void StartSerialize(uint32_t* num_bytes,
+                      uint32_t* num_ports,
+                      uint32_t* num_handles) override;
+  bool EndSerialize(void* destination,
+                    ports::PortName* ports,
+                    PlatformHandle* handles) override;
+  bool BeginTransit() override;
+  void CompleteTransitAndClose() override;
+  void CancelTransit() override;
+
+  static scoped_refptr<DataPipeProducerDispatcher>
+  Deserialize(const void* data,
+              size_t num_bytes,
+              const ports::PortName* ports,
+              size_t num_ports,
+              PlatformHandle* handles,
+              size_t num_handles);
+
+ private:
+  class PortObserverThunk;
+  friend class PortObserverThunk;
+
+  ~DataPipeProducerDispatcher() override;
+
+  void OnSharedBufferCreated(const scoped_refptr<PlatformSharedBuffer>& buffer);
+  void InitializeNoLock();
+  MojoResult CloseNoLock();
+  HandleSignalsState GetHandleSignalsStateNoLock() const;
+  void NotifyWrite(uint32_t num_bytes);
+  void OnPortStatusChanged();
+  void UpdateSignalsStateNoLock();
+  bool ProcessMessageNoLock(const DataPipeControlMessage& message,
+                            ScopedPlatformHandleVectorPtr handles);
+
+  const MojoCreateDataPipeOptions options_;
+  NodeController* const node_controller_;
+  const ports::PortRef control_port_;
+  const uint64_t pipe_id_;
+
+  // Guards access to the fields below.
+  mutable base::Lock lock_;
+
+  AwakableList awakable_list_;
+
+  bool buffer_requested_ = false;
+
+  scoped_refptr<PlatformSharedBuffer> shared_ring_buffer_;
+  std::unique_ptr<PlatformSharedBufferMapping> ring_buffer_mapping_;
+  ScopedPlatformHandle buffer_handle_for_transit_;
+
+  bool in_transit_ = false;
+  bool is_closed_ = false;
+  bool peer_closed_ = false;
+  bool transferred_ = false;
+  bool in_two_phase_write_ = false;
+
+  uint32_t write_offset_ = 0;
+  uint32_t available_capacity_;
+
+  DISALLOW_COPY_AND_ASSIGN(DataPipeProducerDispatcher);
+};
+
+}  // namespace edk
+}  // 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..526444c
--- /dev/null
+++ b/mojo/edk/system/data_pipe_unittest.cc
@@ -0,0 +1,1905 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+const uint32_t kSizeOfOptions =
+    static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions));
+
+// In various places, we have to poll (since, e.g., we can't yet wait for a
+// certain amount of data to be available). This is the maximum number of
+// iterations (separated by a short sleep).
+// TODO(vtl): Get rid of this.
+const size_t kMaxPoll = 100;
+
+// Used in Multiprocess test.
+const size_t kMultiprocessCapacity = 37;
+const char kMultiprocessTestData[] = "hello i'm a string that is 36 bytes";
+const int kMultiprocessMaxIter = 5;
+
+class DataPipeTest : public test::MojoTestBase {
+ public:
+  DataPipeTest() : producer_(MOJO_HANDLE_INVALID),
+                   consumer_(MOJO_HANDLE_INVALID) {}
+
+  ~DataPipeTest() override {
+    if (producer_ != MOJO_HANDLE_INVALID)
+      CHECK_EQ(MOJO_RESULT_OK, MojoClose(producer_));
+    if (consumer_ != MOJO_HANDLE_INVALID)
+      CHECK_EQ(MOJO_RESULT_OK, MojoClose(consumer_));
+  }
+
+  MojoResult Create(const MojoCreateDataPipeOptions* options) {
+    return MojoCreateDataPipe(options, &producer_, &consumer_);
+  }
+
+  MojoResult WriteData(const void* elements,
+                       uint32_t* num_bytes,
+                       bool all_or_none = false) {
+    return MojoWriteData(producer_, elements, num_bytes,
+                         all_or_none ? MOJO_WRITE_DATA_FLAG_ALL_OR_NONE
+                                     : MOJO_WRITE_DATA_FLAG_NONE);
+  }
+
+  MojoResult ReadData(void* elements,
+                      uint32_t* num_bytes,
+                      bool all_or_none = false,
+                      bool peek = false) {
+    MojoReadDataFlags flags = MOJO_READ_DATA_FLAG_NONE;
+    if (all_or_none)
+      flags |= MOJO_READ_DATA_FLAG_ALL_OR_NONE;
+    if (peek)
+      flags |= MOJO_READ_DATA_FLAG_PEEK;
+    return MojoReadData(consumer_, elements, num_bytes, flags);
+  }
+
+  MojoResult QueryData(uint32_t* num_bytes) {
+    return MojoReadData(consumer_, nullptr, num_bytes,
+                        MOJO_READ_DATA_FLAG_QUERY);
+  }
+
+  MojoResult DiscardData(uint32_t* num_bytes, bool all_or_none = false) {
+    MojoReadDataFlags flags = MOJO_READ_DATA_FLAG_DISCARD;
+    if (all_or_none)
+      flags |= MOJO_READ_DATA_FLAG_ALL_OR_NONE;
+    return MojoReadData(consumer_, nullptr, num_bytes, flags);
+  }
+
+  MojoResult BeginReadData(const void** elements,
+                           uint32_t* num_bytes,
+                           bool all_or_none = false) {
+    MojoReadDataFlags flags = MOJO_READ_DATA_FLAG_NONE;
+    if (all_or_none)
+      flags |= MOJO_READ_DATA_FLAG_ALL_OR_NONE;
+    return MojoBeginReadData(consumer_, elements, num_bytes, flags);
+  }
+
+  MojoResult EndReadData(uint32_t num_bytes_read) {
+    return MojoEndReadData(consumer_, num_bytes_read);
+  }
+
+  MojoResult BeginWriteData(void** elements,
+                            uint32_t* num_bytes,
+                            bool all_or_none = false) {
+    MojoReadDataFlags flags = MOJO_WRITE_DATA_FLAG_NONE;
+    if (all_or_none)
+      flags |= MOJO_WRITE_DATA_FLAG_ALL_OR_NONE;
+    return MojoBeginWriteData(producer_, elements, num_bytes, flags);
+  }
+
+  MojoResult EndWriteData(uint32_t num_bytes_written) {
+    return MojoEndWriteData(producer_, num_bytes_written);
+  }
+
+  MojoResult CloseProducer() {
+    MojoResult rv = MojoClose(producer_);
+    producer_ = MOJO_HANDLE_INVALID;
+    return rv;
+  }
+
+  MojoResult CloseConsumer() {
+    MojoResult rv = MojoClose(consumer_);
+    consumer_ = MOJO_HANDLE_INVALID;
+    return rv;
+  }
+
+  MojoHandle producer_, consumer_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DataPipeTest);
+};
+
+TEST_F(DataPipeTest, Basic) {
+  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|.
+  };
+
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+
+  // We can write to a data pipe handle immediately.
+  int32_t elements[10] = {};
+  uint32_t num_bytes = 0;
+
+  num_bytes =
+      static_cast<uint32_t>(arraysize(elements) * sizeof(elements[0]));
+
+  elements[0] = 123;
+  elements[1] = 456;
+  num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(&elements[0], &num_bytes));
+
+  // Now wait for the other side to become readable.
+  MojoHandleSignalsState state;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, state.satisfied_signals);
+
+  elements[0] = -1;
+  elements[1] = -1;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(&elements[0], &num_bytes));
+  ASSERT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes);
+  ASSERT_EQ(elements[0], 123);
+  ASSERT_EQ(elements[1], 456);
+}
+
+// Tests creation of data pipes with various (valid) options.
+TEST_F(DataPipeTest, CreateAndMaybeTransfer) {
+  MojoCreateDataPipeOptions test_options[] = {
+      // Default options.
+      {},
+      // Trivial element size, non-default capacity.
+      {kSizeOfOptions,                           // |struct_size|.
+       MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+       1,                                        // |element_num_bytes|.
+       1000},                                    // |capacity_num_bytes|.
+      // Nontrivial element size, non-default capacity.
+      {kSizeOfOptions,                           // |struct_size|.
+       MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+       4,                                        // |element_num_bytes|.
+       4000},                                    // |capacity_num_bytes|.
+      // Nontrivial element size, default capacity.
+      {kSizeOfOptions,                           // |struct_size|.
+       MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+       100,                                      // |element_num_bytes|.
+       0}                                        // |capacity_num_bytes|.
+  };
+  for (size_t i = 0; i < arraysize(test_options); i++) {
+    MojoHandle producer_handle, consumer_handle;
+    MojoCreateDataPipeOptions* options =
+        i ? &test_options[i] : nullptr;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoCreateDataPipe(options, &producer_handle, &consumer_handle));
+    ASSERT_EQ(MOJO_RESULT_OK, MojoClose(producer_handle));
+    ASSERT_EQ(MOJO_RESULT_OK, MojoClose(consumer_handle));
+  }
+}
+
+TEST_F(DataPipeTest, 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|.
+  };
+
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  int32_t elements[10] = {};
+  uint32_t num_bytes = 0;
+
+  // Try reading; nothing there yet.
+  num_bytes =
+      static_cast<uint32_t>(arraysize(elements) * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadData(elements, &num_bytes));
+
+  // Query; nothing there yet.
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(0u, num_bytes);
+
+  // Discard; nothing there yet.
+  num_bytes = static_cast<uint32_t>(5u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, DiscardData(&num_bytes));
+
+  // Read with invalid |num_bytes|.
+  num_bytes = sizeof(elements[0]) + 1;
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, ReadData(elements, &num_bytes));
+
+  // Write two elements.
+  elements[0] = 123;
+  elements[1] = 456;
+  num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes));
+  // It should have written everything (even without "all or none").
+  ASSERT_EQ(2u * sizeof(elements[0]), num_bytes);
+
+  // Wait.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Query.
+  // TODO(vtl): It's theoretically possible (though not with the current
+  // implementation/configured limits) that not all the data has arrived yet.
+  // (The theoretically-correct assertion here is that |num_bytes| is |1 * ...|
+  // or |2 * ...|.)
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_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]));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes));
+  ASSERT_EQ(1u * sizeof(elements[0]), num_bytes);
+  ASSERT_EQ(123, elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Query.
+  // TODO(vtl): See previous TODO. (If we got 2 elements there, however, we
+  // should get 1 here.)
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(1 * sizeof(elements[0]), num_bytes);
+
+  // Peek one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, false, true));
+  ASSERT_EQ(1u * sizeof(elements[0]), num_bytes);
+  ASSERT_EQ(456, elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Query. Still has 1 element remaining.
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_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]));
+  ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+            ReadData(elements, &num_bytes, true, false));
+  ASSERT_EQ(-1, elements[0]);
+  ASSERT_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]));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, false, false));
+  ASSERT_EQ(1u * sizeof(elements[0]), num_bytes);
+  ASSERT_EQ(456, elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Query.
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(0u, num_bytes);
+}
+
+// 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_F(DataPipeTest, BasicProducerWaiting) {
+  // Note: We take advantage of the fact that current for current
+  // implementations 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|.
+  };
+  Create(&options);
+  MojoHandleSignalsState hss;
+
+  // Never readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Already writable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+
+  // Write two elements.
+  int32_t elements[2] = {123, 456};
+  uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
+  ASSERT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes);
+
+  // Wait for data to become available to the consumer.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Peek one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true, true));
+  ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+  ASSERT_EQ(123, elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Read one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true, false));
+  ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+  ASSERT_EQ(123, elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Try writing, using a two-phase write.
+  void* buffer = nullptr;
+  num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&buffer, &num_bytes));
+  EXPECT_TRUE(buffer);
+  ASSERT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(elements[0])));
+
+  static_cast<int32_t*>(buffer)[0] = 789;
+  ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(static_cast<uint32_t>(
+                                         1u * sizeof(elements[0]))));
+
+  // Read one element, using a two-phase read.
+  const void* read_buffer = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            BeginReadData(&read_buffer, &num_bytes, false));
+  EXPECT_TRUE(read_buffer);
+  // The two-phase read should be able to read at least one element.
+  ASSERT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(elements[0])));
+  ASSERT_EQ(456, static_cast<const int32_t*>(read_buffer)[0]);
+  ASSERT_EQ(MOJO_RESULT_OK, EndReadData(static_cast<uint32_t>(
+                                        1u * sizeof(elements[0]))));
+
+  // Write one element.
+  elements[0] = 123;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes));
+  ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+
+  // Close the consumer.
+  CloseConsumer();
+
+  // It should now be never-writable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+}
+
+TEST_F(DataPipeTest, PeerClosedProducerWaiting) {
+  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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Close the consumer.
+  CloseConsumer();
+
+  // It should be signaled.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+}
+
+TEST_F(DataPipeTest, PeerClosedConsumerWaiting) {
+  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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Close the producer.
+  CloseProducer();
+
+  // It should be signaled.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+}
+
+TEST_F(DataPipeTest, 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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Never writable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Write two elements.
+  int32_t elements[2] = {123, 456};
+  uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
+
+  // Wait for readability.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Discard one element.
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, DiscardData(&num_bytes, true));
+  ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+
+  // Should still be readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Peek one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true, true));
+  ASSERT_EQ(456, elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Should still be readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Read one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true));
+  ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+  ASSERT_EQ(456, elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Write one element.
+  elements[0] = 789;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
+
+  // Waiting should now succeed.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Close the producer.
+  CloseProducer();
+
+  // Should still be readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) != 0);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Wait for the peer closed signal.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED) != 0);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Read one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(elements, &num_bytes, true));
+  ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+  ASSERT_EQ(789, elements[0]);
+  ASSERT_EQ(-1, elements[1]);
+
+  // Should be never-readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+}
+
+// Test with two-phase APIs and also closing the producer with an active
+// consumer waiter.
+TEST_F(DataPipeTest, ConsumerWaitingTwoPhase) {
+  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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState 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]));
+  ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&buffer, &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;
+  ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(2u * sizeof(elements[0])));
+
+  // Wait for readability.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            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]));
+  ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer, &num_bytes, true));
+  EXPECT_TRUE(read_buffer);
+  ASSERT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes);
+  const int32_t* read_elements = static_cast<const int32_t*>(read_buffer);
+  ASSERT_EQ(123, read_elements[0]);
+  ASSERT_EQ(MOJO_RESULT_OK, EndReadData(1u * sizeof(elements[0])));
+
+  // Should still be readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            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]));
+  ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer, &num_bytes));
+  EXPECT_TRUE(read_buffer);
+  ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+  read_elements = static_cast<const int32_t*>(read_buffer);
+  ASSERT_EQ(456, read_elements[0]);
+  ASSERT_EQ(MOJO_RESULT_OK, EndReadData(1u * sizeof(elements[0])));
+
+  // Close the producer.
+  CloseProducer();
+
+  // Should be never-readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+}
+
+// Tests that data pipes aren't writable/readable during two-phase writes/reads.
+TEST_F(DataPipeTest, 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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // It should be writable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  uint32_t num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t));
+  void* write_ptr = nullptr;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_ptr, &num_bytes));
+  EXPECT_TRUE(write_ptr);
+  EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t)));
+
+  // At this point, it shouldn't be writable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // It shouldn't be readable yet either (we'll wait later).
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  static_cast<int32_t*>(write_ptr)[0] = 123;
+  ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(1u * sizeof(int32_t)));
+
+  // It should immediately be writable again.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // It should become readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            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;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_ptr, &num_bytes));
+  EXPECT_TRUE(write_ptr);
+  EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t)));
+
+  // It should be readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // End the two-phase write without writing anything.
+  ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(0u));
+
+  // Start a two-phase read.
+  num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t));
+  const void* read_ptr = nullptr;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_ptr, &num_bytes));
+  EXPECT_TRUE(read_ptr);
+  ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(int32_t)), num_bytes);
+
+  // At this point, it should still be writable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // But not readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // End the two-phase read without reading anything.
+  ASSERT_EQ(MOJO_RESULT_OK, EndReadData(0u));
+
+  // It should be readable again.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+}
+
+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_F(DataPipeTest, 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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Try writing way too much.
+  uint32_t num_bytes = 20u * sizeof(int32_t);
+  int32_t buffer[100];
+  Seq(0, arraysize(buffer), buffer);
+  ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, WriteData(buffer, &num_bytes, true));
+
+  // Should still be empty.
+  num_bytes = ~0u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(0u, num_bytes);
+
+  // Write some data.
+  num_bytes = 5u * sizeof(int32_t);
+  Seq(100, arraysize(buffer), buffer);
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(buffer, &num_bytes, true));
+  ASSERT_EQ(5u * sizeof(int32_t), num_bytes);
+
+  // Wait for data.
+  // TODO(vtl): There's no real guarantee that all the data will become
+  // available at once (except that in current implementations, with reasonable
+  // limits, it will). Eventually, we'll be able to wait for a specified amount
+  // of data to become available.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Half full.
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(5u * sizeof(int32_t), num_bytes);
+
+  /* TODO(jam): enable if we end up observing max capacity
+  // Too much.
+  num_bytes = 6u * sizeof(int32_t);
+  Seq(200, arraysize(buffer), buffer);
+  ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, WriteData(buffer, &num_bytes, true));
+  */
+
+  // Try reading too much.
+  num_bytes = 11u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, ReadData(buffer, &num_bytes, true));
+  int32_t expected_buffer[100];
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try discarding too much.
+  num_bytes = 11u * sizeof(int32_t);
+  ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, DiscardData(&num_bytes, true));
+
+  // Just a little.
+  num_bytes = 2u * sizeof(int32_t);
+  Seq(300, arraysize(buffer), buffer);
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(buffer, &num_bytes, true));
+  ASSERT_EQ(2u * sizeof(int32_t), num_bytes);
+
+  // Just right.
+  num_bytes = 3u * sizeof(int32_t);
+  Seq(400, arraysize(buffer), buffer);
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(buffer, &num_bytes, true));
+  ASSERT_EQ(3u * sizeof(int32_t), num_bytes);
+
+  // TODO(vtl): Hack (see also the TODO above): We can't currently wait for a
+  // specified amount of data to be available, so poll.
+  for (size_t i = 0; i < kMaxPoll; i++) {
+    num_bytes = 0u;
+    ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+    if (num_bytes >= 10u * sizeof(int32_t))
+      break;
+
+    test::Sleep(test::EpsilonDeadline());
+  }
+  ASSERT_EQ(10u * sizeof(int32_t), num_bytes);
+
+  // Read half.
+  num_bytes = 5u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(buffer, &num_bytes, true));
+  ASSERT_EQ(5u * sizeof(int32_t), num_bytes);
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  Seq(100, 5, expected_buffer);
+  ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try reading too much again.
+  num_bytes = 6u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, ReadData(buffer, &num_bytes, true));
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try discarding too much again.
+  num_bytes = 6u * sizeof(int32_t);
+  ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, DiscardData(&num_bytes, true));
+
+  // Discard a little.
+  num_bytes = 2u * sizeof(int32_t);
+  ASSERT_EQ(MOJO_RESULT_OK, DiscardData(&num_bytes, true));
+  ASSERT_EQ(2u * sizeof(int32_t), num_bytes);
+
+  // Three left.
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(3u * sizeof(int32_t), num_bytes);
+
+  // Close the producer, then test producer-closed cases.
+  CloseProducer();
+
+  // Wait.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Try reading too much; "failed precondition" since the producer is closed.
+  num_bytes = 4u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            ReadData(buffer, &num_bytes, true));
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Try discarding too much; "failed precondition" again.
+  num_bytes = 4u * sizeof(int32_t);
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, DiscardData(&num_bytes, true));
+
+  // Read a little.
+  num_bytes = 2u * sizeof(int32_t);
+  memset(buffer, 0xab, sizeof(buffer));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(buffer, &num_bytes, true));
+  ASSERT_EQ(2u * sizeof(int32_t), num_bytes);
+  memset(expected_buffer, 0xab, sizeof(expected_buffer));
+  Seq(400, 2, expected_buffer);
+  ASSERT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+  // Discard the remaining element.
+  num_bytes = 1u * sizeof(int32_t);
+  ASSERT_EQ(MOJO_RESULT_OK, DiscardData(&num_bytes, true));
+  ASSERT_EQ(1u * sizeof(int32_t), num_bytes);
+
+  // Empty again.
+  num_bytes = ~0u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(0u, num_bytes);
+}
+
+// Tests that |ProducerWriteData()| and |ConsumerReadData()| writes and reads,
+// respectively, as much as possible, even if it may have to "wrap around" the
+// internal circular buffer. (Note that the two-phase write and read need not do
+// this.)
+TEST_F(DataPipeTest, 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|.
+  };
+
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Write 20 bytes.
+  uint32_t num_bytes = 20u;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(&test_data[0], &num_bytes, true));
+  ASSERT_EQ(20u, num_bytes);
+
+  // Wait for data.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) != 0);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Read 10 bytes.
+  unsigned char read_buffer[1000] = {0};
+  num_bytes = 10u;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(read_buffer, &num_bytes, true));
+  ASSERT_EQ(10u, num_bytes);
+  ASSERT_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.)
+  void* write_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            BeginWriteData(&write_buffer_ptr, &num_bytes, false));
+  EXPECT_TRUE(write_buffer_ptr);
+  ASSERT_EQ(80u, num_bytes);
+  ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(0));
+
+  size_t total_num_bytes = 0;
+  while (total_num_bytes < 90) {
+    // Wait to write.
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE,
+                       MOJO_DEADLINE_INDEFINITE, &hss));
+    ASSERT_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_WRITABLE);
+    ASSERT_EQ(hss.satisfiable_signals,
+              MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+
+    // Write as much as we can.
+    num_bytes = 100;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              WriteData(&test_data[20 + total_num_bytes], &num_bytes, false));
+    total_num_bytes += num_bytes;
+  }
+
+  ASSERT_EQ(90u, total_num_bytes);
+
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(100u, 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.)
+  const void* read_buffer_ptr = nullptr;
+  num_bytes = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer_ptr, &num_bytes, false));
+  EXPECT_TRUE(read_buffer_ptr);
+  ASSERT_EQ(90u, num_bytes);
+  ASSERT_EQ(MOJO_RESULT_OK, EndReadData(0));
+
+  // Read as much as possible. We should read 100 bytes.
+  num_bytes = static_cast<uint32_t>(arraysize(read_buffer) *
+                                    sizeof(read_buffer[0]));
+  memset(read_buffer, 0, num_bytes);
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(read_buffer, &num_bytes));
+  ASSERT_EQ(100u, num_bytes);
+  ASSERT_EQ(0, memcmp(read_buffer, &test_data[10], 100u));
+}
+
+// Tests the behavior of writing (simple and two-phase), closing the producer,
+// then reading (simple and two-phase).
+TEST_F(DataPipeTest, WriteCloseProducerRead) {
+  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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+
+  // Write some data, so we'll have something to read.
+  uint32_t num_bytes = kTestDataSize;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes, false));
+  ASSERT_EQ(kTestDataSize, num_bytes);
+
+  // Write it again, so we'll have something left over.
+  num_bytes = kTestDataSize;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes, false));
+  ASSERT_EQ(kTestDataSize, num_bytes);
+
+  // Start two-phase write.
+  void* write_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            BeginWriteData(&write_buffer_ptr, &num_bytes, false));
+  EXPECT_TRUE(write_buffer_ptr);
+  EXPECT_GT(num_bytes, 0u);
+
+  // TODO(vtl): (See corresponding TODO in TwoPhaseAllOrNone.)
+  for (size_t i = 0; i < kMaxPoll; i++) {
+    num_bytes = 0u;
+    ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+    if (num_bytes >= 2u * kTestDataSize)
+      break;
+
+    test::Sleep(test::EpsilonDeadline());
+  }
+  ASSERT_EQ(2u * kTestDataSize, num_bytes);
+
+  // Start two-phase read.
+  const void* read_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            BeginReadData(&read_buffer_ptr, &num_bytes));
+  EXPECT_TRUE(read_buffer_ptr);
+  ASSERT_EQ(2u * kTestDataSize, num_bytes);
+
+  // Close the producer.
+  CloseProducer();
+
+  // The consumer can finish its two-phase read.
+  ASSERT_EQ(0, memcmp(read_buffer_ptr, kTestData, kTestDataSize));
+  ASSERT_EQ(MOJO_RESULT_OK, EndReadData(kTestDataSize));
+
+  // And start another.
+  read_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            BeginReadData(&read_buffer_ptr, &num_bytes));
+  EXPECT_TRUE(read_buffer_ptr);
+  ASSERT_EQ(kTestDataSize, num_bytes);
+}
+
+
+// Tests the behavior of interrupting a two-phase read and write by closing the
+// consumer.
+TEST_F(DataPipeTest, TwoPhaseWriteReadCloseConsumer) {
+  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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Write some data, so we'll have something to read.
+  uint32_t num_bytes = kTestDataSize;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes));
+  ASSERT_EQ(kTestDataSize, num_bytes);
+
+  // Start two-phase write.
+  void* write_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_buffer_ptr, &num_bytes));
+  EXPECT_TRUE(write_buffer_ptr);
+  ASSERT_GT(num_bytes, kTestDataSize);
+
+  // Wait for data.
+  // TODO(vtl): (See corresponding TODO in AllOrNone.)
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Start two-phase read.
+  const void* read_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer_ptr, &num_bytes));
+  EXPECT_TRUE(read_buffer_ptr);
+  ASSERT_EQ(kTestDataSize, num_bytes);
+
+  // Close the consumer.
+  CloseConsumer();
+
+  // Wait for producer to know that the consumer is closed.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+
+  // 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.
+  ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(kTestDataSize));
+
+  // But trying to write should result in failure.
+  num_bytes = kTestDataSize;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, WriteData(kTestData, &num_bytes));
+
+  // As will trying to start another two-phase write.
+  write_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            BeginWriteData(&write_buffer_ptr, &num_bytes));
+}
+
+// Tests the behavior of "interrupting" a two-phase write by closing both the
+// producer and the consumer.
+TEST_F(DataPipeTest, TwoPhaseWriteCloseBoth) {
+  const uint32_t kTestDataSize = 15u;
+
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      1u,                                       // |element_num_bytes|.
+      1000u                                     // |capacity_num_bytes|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+
+  // Start two-phase write.
+  void* write_buffer_ptr = nullptr;
+  uint32_t num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_buffer_ptr, &num_bytes));
+  EXPECT_TRUE(write_buffer_ptr);
+  ASSERT_GT(num_bytes, kTestDataSize);
+}
+
+// Tests the behavior of writing, closing the producer, and then reading (with
+// and without data remaining).
+TEST_F(DataPipeTest, WriteCloseProducerReadNoData) {
+  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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Write some data, so we'll have something to read.
+  uint32_t num_bytes = kTestDataSize;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes));
+  ASSERT_EQ(kTestDataSize, num_bytes);
+
+  // Close the producer.
+  CloseProducer();
+
+  // Wait. (Note that once the consumer knows that the producer is closed, it
+  // must also know about all the data that was sent.)
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Peek that data.
+  char buffer[1000];
+  num_bytes = static_cast<uint32_t>(sizeof(buffer));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(buffer, &num_bytes, false, true));
+  ASSERT_EQ(kTestDataSize, num_bytes);
+  ASSERT_EQ(0, memcmp(buffer, kTestData, kTestDataSize));
+
+  // Read that data.
+  memset(buffer, 0, 1000);
+  num_bytes = static_cast<uint32_t>(sizeof(buffer));
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(buffer, &num_bytes));
+  ASSERT_EQ(kTestDataSize, num_bytes);
+  ASSERT_EQ(0, memcmp(buffer, kTestData, kTestDataSize));
+
+  // A second read should fail.
+  num_bytes = static_cast<uint32_t>(sizeof(buffer));
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ReadData(buffer, &num_bytes));
+
+  // A two-phase read should also fail.
+  const void* read_buffer_ptr = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            BeginReadData(&read_buffer_ptr, &num_bytes));
+
+  // Ditto for discard.
+  num_bytes = 10u;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, DiscardData(&num_bytes));
+}
+
+// Test that during a two phase read the memory stays valid even if more data
+// comes in.
+TEST_F(DataPipeTest, TwoPhaseReadMemoryStable) {
+  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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Write some data.
+  uint32_t num_bytes = kTestDataSize;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes));
+  ASSERT_EQ(kTestDataSize, num_bytes);
+
+  // Wait for the data.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Begin a two-phase read.
+  const void* read_buffer_ptr = nullptr;
+  uint32_t read_buffer_size = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer_ptr, &read_buffer_size));
+
+  // Write more data.
+  const char kExtraData[] = "bye world";
+  const uint32_t kExtraDataSize = static_cast<uint32_t>(sizeof(kExtraData));
+  num_bytes = kExtraDataSize;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(kExtraData, &num_bytes));
+  ASSERT_EQ(kExtraDataSize, num_bytes);
+
+  // Close the producer.
+  CloseProducer();
+
+  // Wait. (Note that once the consumer knows that the producer is closed, it
+  // must also have received the extra data).
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Read the two phase memory to check it's still valid.
+  ASSERT_EQ(0, memcmp(read_buffer_ptr, kTestData, kTestDataSize));
+  EndReadData(read_buffer_size);
+}
+
+// Test that two-phase reads/writes behave correctly when given invalid
+// arguments.
+TEST_F(DataPipeTest, 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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // No data.
+  uint32_t num_bytes = 1000u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(0u, num_bytes);
+
+  // Try "ending" a two-phase write when one isn't active.
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            EndWriteData(1u * sizeof(int32_t)));
+
+  // Wait a bit, to make sure that if a signal were (incorrectly) sent, it'd
+  // have time to propagate.
+  test::Sleep(test::EpsilonDeadline());
+
+  // Still no data.
+  num_bytes = 1000u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(0u, num_bytes);
+
+  // Try ending a two-phase write with an invalid amount (too much).
+  num_bytes = 0u;
+  void* write_ptr = nullptr;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_ptr, &num_bytes));
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            EndWriteData(num_bytes + static_cast<uint32_t>(sizeof(int32_t))));
+
+  // But the two-phase write still ended.
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, EndWriteData(0u));
+
+  // Wait a bit (as above).
+  test::Sleep(test::EpsilonDeadline());
+
+  // Still no data.
+  num_bytes = 1000u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_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;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&write_ptr, &num_bytes));
+  EXPECT_GE(num_bytes, 1u);
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, EndWriteData(1u));
+
+  // But the two-phase write still ended.
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, EndWriteData(0u));
+
+  // Wait a bit (as above).
+  test::Sleep(test::EpsilonDeadline());
+
+  // Still no data.
+  num_bytes = 1000u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_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);
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(&element, &num_bytes));
+
+  // Wait for data.
+  // TODO(vtl): (See corresponding TODO in AllOrNone.)
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // One element available.
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(1u * sizeof(int32_t), num_bytes);
+
+  // Try "ending" a two-phase read when one isn't active.
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, EndReadData(1u * sizeof(int32_t)));
+
+  // Still one element available.
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_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;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_ptr, &num_bytes));
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            EndReadData(num_bytes + static_cast<uint32_t>(sizeof(int32_t))));
+
+  // Still one element available.
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_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;
+  ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_ptr, &num_bytes));
+  ASSERT_EQ(1u * sizeof(int32_t), num_bytes);
+  ASSERT_EQ(123, static_cast<const int32_t*>(read_ptr)[0]);
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, EndReadData(1u));
+
+  // Still one element available.
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
+  ASSERT_EQ(1u * sizeof(int32_t), num_bytes);
+}
+
+// Test that a producer can be sent over a MP.
+TEST_F(DataPipeTest, SendProducer) {
+  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|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+  MojoHandleSignalsState hss;
+
+  // Write some data.
+  uint32_t num_bytes = kTestDataSize;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(kTestData, &num_bytes));
+  ASSERT_EQ(kTestDataSize, num_bytes);
+
+  // Wait for the data.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Check the data.
+  const void* read_buffer = nullptr;
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            BeginReadData(&read_buffer, &num_bytes, false));
+  ASSERT_EQ(0, memcmp(read_buffer, kTestData, kTestDataSize));
+  EndReadData(num_bytes);
+
+  // Now send the producer over a MP so that it's serialized.
+  MojoHandle pipe0, pipe1;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoCreateMessagePipe(nullptr, &pipe0, &pipe1));
+
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWriteMessage(pipe0, nullptr, 0, &producer_, 1,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+  producer_ = MOJO_HANDLE_INVALID;
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  uint32_t num_handles = 1;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoReadMessage(pipe1, nullptr, 0, &producer_, &num_handles,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(num_handles, 1u);
+
+  // Write more data.
+  const char kExtraData[] = "bye world";
+  const uint32_t kExtraDataSize = static_cast<uint32_t>(sizeof(kExtraData));
+  num_bytes = kExtraDataSize;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(kExtraData, &num_bytes));
+  ASSERT_EQ(kExtraDataSize, num_bytes);
+
+  // Wait for it.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Check the second write.
+  num_bytes = 0u;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            BeginReadData(&read_buffer, &num_bytes, false));
+  ASSERT_EQ(0, memcmp(read_buffer, kExtraData, kExtraDataSize));
+  EndReadData(num_bytes);
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipe0));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipe1));
+}
+
+// Ensures that if a data pipe consumer whose producer has closed is passed over
+// a message pipe, the deserialized dispatcher is also marked as having a closed
+// peer.
+TEST_F(DataPipeTest, ConsumerWithClosedProducerSent) {
+  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|.
+  };
+
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+
+  // We can write to a data pipe handle immediately.
+  int32_t data = 123;
+  uint32_t num_bytes = sizeof(data);
+  ASSERT_EQ(MOJO_RESULT_OK, WriteData(&data, &num_bytes));
+  ASSERT_EQ(MOJO_RESULT_OK, CloseProducer());
+
+  // Now wait for the other side to become readable and to see the peer closed.
+  MojoHandleSignalsState state;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  // Now send the consumer over a MP so that it's serialized.
+  MojoHandle pipe0, pipe1;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoCreateMessagePipe(nullptr, &pipe0, &pipe1));
+
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWriteMessage(pipe0, nullptr, 0, &consumer_, 1,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+  consumer_ = MOJO_HANDLE_INVALID;
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+  uint32_t num_handles = 1;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoReadMessage(pipe1, nullptr, 0, &consumer_, &num_handles,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(num_handles, 1u);
+
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &state));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  int32_t read_data;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadData(&read_data, &num_bytes));
+  ASSERT_EQ(sizeof(read_data), num_bytes);
+  ASSERT_EQ(data, read_data);
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipe0));
+  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipe1));
+}
+
+bool WriteAllData(MojoHandle producer,
+                  const void* elements,
+                  uint32_t num_bytes) {
+  for (size_t i = 0; i < kMaxPoll; i++) {
+    // Write as much data as we can.
+    uint32_t write_bytes = num_bytes;
+    MojoResult result = MojoWriteData(producer, elements, &write_bytes,
+                                      MOJO_WRITE_DATA_FLAG_NONE);
+    if (result == MOJO_RESULT_OK) {
+      num_bytes -= write_bytes;
+      elements = static_cast<const uint8_t*>(elements) + write_bytes;
+      if (num_bytes == 0)
+        return true;
+    } else {
+      EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, result);
+    }
+
+    MojoHandleSignalsState hss = MojoHandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_OK, MojoWait(producer, MOJO_HANDLE_SIGNAL_WRITABLE,
+                                       MOJO_DEADLINE_INDEFINITE, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+              hss.satisfiable_signals);
+  }
+
+  return false;
+}
+
+// If |expect_empty| is true, expect |consumer| to be empty after reading.
+bool ReadAllData(MojoHandle consumer,
+                 void* elements,
+                 uint32_t num_bytes,
+                 bool expect_empty) {
+  for (size_t i = 0; i < kMaxPoll; i++) {
+    // Read as much data as we can.
+    uint32_t read_bytes = num_bytes;
+    MojoResult result =
+        MojoReadData(consumer, elements, &read_bytes, MOJO_READ_DATA_FLAG_NONE);
+    if (result == MOJO_RESULT_OK) {
+      num_bytes -= read_bytes;
+      elements = static_cast<uint8_t*>(elements) + read_bytes;
+      if (num_bytes == 0) {
+        if (expect_empty) {
+          // Expect no more data.
+          test::Sleep(test::TinyDeadline());
+          MojoReadData(consumer, nullptr, &num_bytes,
+                       MOJO_READ_DATA_FLAG_QUERY);
+          EXPECT_EQ(0u, num_bytes);
+        }
+        return true;
+      }
+    } else {
+      EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, result);
+    }
+
+    MojoHandleSignalsState hss = MojoHandleSignalsState();
+    EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer, MOJO_HANDLE_SIGNAL_READABLE,
+                                       MOJO_DEADLINE_INDEFINITE, &hss));
+    // Peer could have become closed while we're still waiting for data.
+    EXPECT_TRUE(MOJO_HANDLE_SIGNAL_READABLE & hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+              hss.satisfiable_signals);
+  }
+
+  return num_bytes == 0;
+}
+
+#if !defined(OS_IOS)
+
+TEST_F(DataPipeTest, Multiprocess) {
+  const uint32_t kTestDataSize =
+      static_cast<uint32_t>(sizeof(kMultiprocessTestData));
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      1,                                        // |element_num_bytes|.
+      kMultiprocessCapacity                     // |capacity_num_bytes|.
+  };
+  ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
+
+  RUN_CHILD_ON_PIPE(MultiprocessClient, server_mp)
+    // Send some data before serialising and sending the data pipe over.
+    // This is the first write so we don't need to use WriteAllData.
+    uint32_t num_bytes = kTestDataSize;
+    ASSERT_EQ(MOJO_RESULT_OK, WriteData(kMultiprocessTestData, &num_bytes,
+                                        MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+    ASSERT_EQ(kTestDataSize, num_bytes);
+
+    // Send child process the data pipe.
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(server_mp, nullptr, 0, &consumer_, 1,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Send a bunch of data of varying sizes.
+    uint8_t buffer[100];
+    int seq = 0;
+    for (int i = 0; i < kMultiprocessMaxIter; ++i) {
+      for (uint32_t size = 1; size <= kMultiprocessCapacity; size++) {
+        for (unsigned int j = 0; j < size; ++j)
+          buffer[j] = seq + j;
+        EXPECT_TRUE(WriteAllData(producer_, buffer, size));
+        seq += size;
+      }
+    }
+
+    // Write the test string in again.
+    ASSERT_TRUE(WriteAllData(producer_, kMultiprocessTestData, kTestDataSize));
+
+    // Swap ends.
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(server_mp, nullptr, 0, &producer_, 1,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Receive the consumer from the other side.
+    producer_ = MOJO_HANDLE_INVALID;
+    MojoHandleSignalsState hss = MojoHandleSignalsState();
+    ASSERT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_READABLE,
+                                       MOJO_DEADLINE_INDEFINITE, &hss));
+    MojoHandle handles[2];
+    uint32_t num_handles = arraysize(handles);
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(server_mp, nullptr, 0, handles, &num_handles,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(1u, num_handles);
+    consumer_ = handles[0];
+
+    // Read the test string twice. Once for when we sent it, and once for the
+    // other end sending it.
+    for (int i = 0; i < 2; ++i) {
+      EXPECT_TRUE(ReadAllData(consumer_, buffer, kTestDataSize, i == 1));
+      EXPECT_EQ(0, memcmp(buffer, kMultiprocessTestData, kTestDataSize));
+    }
+
+    WriteMessage(server_mp, "quit");
+
+    // Don't have to close the consumer here because it will be done for us.
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessClient, DataPipeTest, client_mp) {
+  const uint32_t kTestDataSize =
+      static_cast<uint32_t>(sizeof(kMultiprocessTestData));
+
+  // Receive the data pipe from the other side.
+  MojoHandle consumer = MOJO_HANDLE_INVALID;
+  MojoHandleSignalsState hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  MojoHandle handles[2];
+  uint32_t num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoReadMessage(client_mp, nullptr, 0, handles, &num_handles,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(1u, num_handles);
+  consumer = handles[0];
+
+  // Read the initial string that was sent.
+  int32_t buffer[100];
+  EXPECT_TRUE(ReadAllData(consumer, buffer, kTestDataSize, false));
+  EXPECT_EQ(0, memcmp(buffer, kMultiprocessTestData, kTestDataSize));
+
+  // Receive the main data and check it is correct.
+  int seq = 0;
+  uint8_t expected_buffer[100];
+  for (int i = 0; i < kMultiprocessMaxIter; ++i) {
+    for (uint32_t size = 1; size <= kMultiprocessCapacity; ++size) {
+      for (unsigned int j = 0; j < size; ++j)
+        expected_buffer[j] = seq + j;
+      EXPECT_TRUE(ReadAllData(consumer, buffer, size, false));
+      EXPECT_EQ(0, memcmp(buffer, expected_buffer, size));
+
+      seq += size;
+    }
+  }
+
+  // Swap ends.
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWriteMessage(client_mp, nullptr, 0, &consumer,
+                                             1, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // Receive the producer from the other side.
+  MojoHandle producer = MOJO_HANDLE_INVALID;
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  num_handles = arraysize(handles);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoReadMessage(client_mp, nullptr, 0, handles, &num_handles,
+                            MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(1u, num_handles);
+  producer = handles[0];
+
+  // Write the test string one more time.
+  EXPECT_TRUE(WriteAllData(producer, kMultiprocessTestData, kTestDataSize));
+
+  // We swapped ends, so close the producer.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
+
+  // Wait to receive a "quit" message before exiting.
+  EXPECT_EQ("quit", ReadMessage(client_mp));
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteAndCloseProducer, DataPipeTest, h) {
+  MojoHandle p;
+  std::string message = ReadMessageWithHandles(h, &p, 1);
+
+  // Write some data to the producer and close it.
+  uint32_t num_bytes = static_cast<uint32_t>(message.size());
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWriteData(p, message.data(), &num_bytes,
+                                          MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(num_bytes, static_cast<uint32_t>(message.size()));
+
+  // Close the producer before quitting.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(p));
+
+  // Wait for a quit message.
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadAndCloseConsumer, DataPipeTest, h) {
+  MojoHandle c;
+  std::string expected_message = ReadMessageWithHandles(h, &c, 1);
+
+  // Wait for the consumer to become readable.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+
+  // Drain the consumer and expect to find the given message.
+  uint32_t num_bytes = static_cast<uint32_t>(expected_message.size());
+  std::vector<char> bytes(expected_message.size());
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadData(c, bytes.data(), &num_bytes,
+                                         MOJO_READ_DATA_FLAG_NONE));
+  EXPECT_EQ(num_bytes, static_cast<uint32_t>(bytes.size()));
+
+  std::string message(bytes.data(), bytes.size());
+  EXPECT_EQ(expected_message, message);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
+
+  // Wait for a quit message.
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+TEST_F(DataPipeTest, SendConsumerAndCloseProducer) {
+  // Create a new data pipe.
+  MojoHandle p, c;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p ,&c));
+
+  RUN_CHILD_ON_PIPE(WriteAndCloseProducer, producer_client)
+    RUN_CHILD_ON_PIPE(ReadAndCloseConsumer, consumer_client)
+      const std::string kMessage = "Hello, world!";
+      WriteMessageWithHandles(producer_client, kMessage, &p, 1);
+      WriteMessageWithHandles(consumer_client, kMessage, &c, 1);
+
+      WriteMessage(consumer_client, "quit");
+    END_CHILD()
+
+    WriteMessage(producer_client, "quit");
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateAndWrite, DataPipeTest, h) {
+  const MojoCreateDataPipeOptions options = {
+      kSizeOfOptions,                           // |struct_size|.
+      MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,  // |flags|.
+      1,                                        // |element_num_bytes|.
+      kMultiprocessCapacity                     // |capacity_num_bytes|.
+  };
+
+  MojoHandle p, c;
+  ASSERT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(&options, &p, &c));
+
+  const std::string kMessage = "Hello, world!";
+  WriteMessageWithHandles(h, kMessage, &c, 1);
+
+  // Write some data to the producer and close it.
+  uint32_t num_bytes = static_cast<uint32_t>(kMessage.size());
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWriteData(p, kMessage.data(), &num_bytes,
+                                          MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(num_bytes, static_cast<uint32_t>(kMessage.size()));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(p));
+
+  // Wait for a quit message.
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+TEST_F(DataPipeTest, CreateInChild) {
+  RUN_CHILD_ON_PIPE(CreateAndWrite, child)
+    MojoHandle c;
+    std::string expected_message = ReadMessageWithHandles(child, &c, 1);
+
+    // Wait for the consumer to become readable.
+    EXPECT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE,
+                                       MOJO_DEADLINE_INDEFINITE, nullptr));
+
+    // Drain the consumer and expect to find the given message.
+    uint32_t num_bytes = static_cast<uint32_t>(expected_message.size());
+    std::vector<char> bytes(expected_message.size());
+    EXPECT_EQ(MOJO_RESULT_OK, MojoReadData(c, bytes.data(), &num_bytes,
+                                           MOJO_READ_DATA_FLAG_NONE));
+    EXPECT_EQ(num_bytes, static_cast<uint32_t>(bytes.size()));
+
+    std::string message(bytes.data(), bytes.size());
+    EXPECT_EQ(expected_message, message);
+
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
+    WriteMessage(child, "quit");
+  END_CHILD()
+}
+
+#endif  // !defined(OS_IOS)
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc
new file mode 100644
index 0000000..7d701b2
--- /dev/null
+++ b/mojo/edk/system/dispatcher.cc
@@ -0,0 +1,192 @@
+// 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/configuration.h"
+#include "mojo/edk/system/data_pipe_consumer_dispatcher.h"
+#include "mojo/edk/system/data_pipe_producer_dispatcher.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 edk {
+
+Dispatcher::DispatcherInTransit::DispatcherInTransit() {}
+
+Dispatcher::DispatcherInTransit::DispatcherInTransit(
+    const DispatcherInTransit& other) = default;
+
+Dispatcher::DispatcherInTransit::~DispatcherInTransit() {}
+
+MojoResult Dispatcher::Watch(MojoHandleSignals signals,
+                             const Watcher::WatchCallback& callback,
+                             uintptr_t context) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::CancelWatch(uintptr_t context) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::WriteMessage(std::unique_ptr<MessageForTransit> message,
+                                    MojoWriteMessageFlags flags) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::ReadMessage(std::unique_ptr<MessageForTransit>* message,
+                                   uint32_t* num_bytes,
+                                   MojoHandle* handles,
+                                   uint32_t* num_handles,
+                                   MojoReadMessageFlags flags,
+                                   bool read_any_size) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::DuplicateBufferHandle(
+    const MojoDuplicateBufferHandleOptions* options,
+    scoped_refptr<Dispatcher>* new_dispatcher) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::MapBuffer(
+    uint64_t offset,
+    uint64_t num_bytes,
+    MojoMapBufferFlags flags,
+    std::unique_ptr<PlatformSharedBufferMapping>* mapping) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::ReadData(void* elements,
+                                uint32_t* num_bytes,
+                                MojoReadDataFlags flags) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::BeginReadData(const void** buffer,
+                                     uint32_t* buffer_num_bytes,
+                                     MojoReadDataFlags flags) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::EndReadData(uint32_t num_bytes_read) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::WriteData(const void* elements,
+                                 uint32_t* num_bytes,
+                                 MojoWriteDataFlags flags) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::BeginWriteData(void** buffer,
+                                      uint32_t* buffer_num_bytes,
+                                      MojoWriteDataFlags flags) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::EndWriteData(uint32_t num_bytes_written) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::AddWaitingDispatcher(
+    const scoped_refptr<Dispatcher>& dispatcher,
+    MojoHandleSignals signals,
+    uintptr_t context) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::RemoveWaitingDispatcher(
+    const scoped_refptr<Dispatcher>& dispatcher) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::GetReadyDispatchers(uint32_t* count,
+                                           DispatcherVector* dispatchers,
+                                           MojoResult* results,
+                                           uintptr_t* contexts) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+HandleSignalsState Dispatcher::GetHandleSignalsState() const {
+  return HandleSignalsState();
+}
+
+MojoResult Dispatcher::AddAwakable(Awakable* awakable,
+                                   MojoHandleSignals signals,
+                                   uintptr_t context,
+                                   HandleSignalsState* signals_state) {
+  return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+void Dispatcher::RemoveAwakable(Awakable* awakable,
+                                HandleSignalsState* handle_signals_state) {
+  NOTREACHED();
+}
+
+void Dispatcher::StartSerialize(uint32_t* num_bytes,
+                                uint32_t* num_ports,
+                                uint32_t* num_platform_handles) {
+  *num_bytes = 0;
+  *num_ports = 0;
+  *num_platform_handles = 0;
+}
+
+bool Dispatcher::EndSerialize(void* destination,
+                              ports::PortName* ports,
+                              PlatformHandle* handles) {
+  LOG(ERROR) << "Attempting to serialize a non-transferrable dispatcher.";
+  return true;
+}
+
+bool Dispatcher::BeginTransit() { return true; }
+
+void Dispatcher::CompleteTransitAndClose() {}
+
+void Dispatcher::CancelTransit() {}
+
+// static
+scoped_refptr<Dispatcher> Dispatcher::Deserialize(
+    Type type,
+    const void* bytes,
+    size_t num_bytes,
+    const ports::PortName* ports,
+    size_t num_ports,
+    PlatformHandle* platform_handles,
+    size_t num_platform_handles) {
+  switch (type) {
+    case Type::MESSAGE_PIPE:
+      return MessagePipeDispatcher::Deserialize(
+          bytes, num_bytes, ports, num_ports, platform_handles,
+          num_platform_handles);
+    case Type::SHARED_BUFFER:
+      return SharedBufferDispatcher::Deserialize(
+          bytes, num_bytes, ports, num_ports, platform_handles,
+          num_platform_handles);
+    case Type::DATA_PIPE_CONSUMER:
+      return DataPipeConsumerDispatcher::Deserialize(
+          bytes, num_bytes, ports, num_ports, platform_handles,
+          num_platform_handles);
+    case Type::DATA_PIPE_PRODUCER:
+      return DataPipeProducerDispatcher::Deserialize(
+          bytes, num_bytes, ports, num_ports, platform_handles,
+          num_platform_handles);
+    case Type::PLATFORM_HANDLE:
+      return PlatformHandleDispatcher::Deserialize(
+          bytes, num_bytes, ports, num_ports, platform_handles,
+          num_platform_handles);
+    default:
+      LOG(ERROR) << "Deserializing invalid dispatcher type.";
+      return nullptr;
+  }
+}
+
+Dispatcher::Dispatcher() {}
+
+Dispatcher::~Dispatcher() {}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h
new file mode 100644
index 0000000..9dca67f
--- /dev/null
+++ b/mojo/edk/system/dispatcher.h
@@ -0,0 +1,258 @@
+// 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 <memory>
+#include <ostream>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/ports/name.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/edk/system/watcher.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 edk {
+
+class Awakable;
+class Dispatcher;
+class MessageForTransit;
+
+using DispatcherVector = std::vector<scoped_refptr<Dispatcher>>;
+
+// A |Dispatcher| implements Mojo EDK calls that are associated with a
+// particular MojoHandle, with the exception of MojoWait and MojoWaitMany (
+// which are implemented directly in Core.).
+class MOJO_SYSTEM_IMPL_EXPORT Dispatcher
+    : public base::RefCountedThreadSafe<Dispatcher> {
+ public:
+  struct DispatcherInTransit {
+    DispatcherInTransit();
+    DispatcherInTransit(const DispatcherInTransit& other);
+    ~DispatcherInTransit();
+
+    scoped_refptr<Dispatcher> dispatcher;
+    MojoHandle local_handle;
+  };
+
+  enum class Type {
+    UNKNOWN = 0,
+    MESSAGE_PIPE,
+    DATA_PIPE_PRODUCER,
+    DATA_PIPE_CONSUMER,
+    SHARED_BUFFER,
+    WAIT_SET,
+
+    // "Private" types (not exposed via the public interface):
+    PLATFORM_HANDLE = -1,
+  };
+
+  // All Dispatchers must minimally implement these methods.
+
+  virtual Type GetType() const = 0;
+  virtual MojoResult Close() = 0;
+
+  ///////////// Watch API ////////////////////
+
+  virtual MojoResult Watch(MojoHandleSignals signals,
+                           const Watcher::WatchCallback& callback,
+                           uintptr_t context);
+
+  virtual MojoResult CancelWatch(uintptr_t context);
+
+  ///////////// Message pipe API /////////////
+
+  virtual MojoResult WriteMessage(std::unique_ptr<MessageForTransit> message,
+                                  MojoWriteMessageFlags flags);
+
+  virtual MojoResult ReadMessage(std::unique_ptr<MessageForTransit>* message,
+                                 uint32_t* num_bytes,
+                                 MojoHandle* handles,
+                                 uint32_t* num_handles,
+                                 MojoReadMessageFlags flags,
+                                 bool read_any_size);
+
+  ///////////// Shared buffer API /////////////
+
+  // |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).
+  virtual MojoResult DuplicateBufferHandle(
+      const MojoDuplicateBufferHandleOptions* options,
+      scoped_refptr<Dispatcher>* new_dispatcher);
+
+  virtual MojoResult MapBuffer(
+      uint64_t offset,
+      uint64_t num_bytes,
+      MojoMapBufferFlags flags,
+      std::unique_ptr<PlatformSharedBufferMapping>* mapping);
+
+  ///////////// Data pipe consumer API /////////////
+
+  virtual MojoResult ReadData(void* elements,
+                              uint32_t* num_bytes,
+                              MojoReadDataFlags flags);
+
+  virtual MojoResult BeginReadData(const void** buffer,
+                                   uint32_t* buffer_num_bytes,
+                                   MojoReadDataFlags flags);
+
+  virtual MojoResult EndReadData(uint32_t num_bytes_read);
+
+  ///////////// Data pipe producer API /////////////
+
+  virtual MojoResult WriteData(const void* elements,
+                               uint32_t* num_bytes,
+                               MojoWriteDataFlags flags);
+
+  virtual MojoResult BeginWriteData(void** buffer,
+                                    uint32_t* buffer_num_bytes,
+                                    MojoWriteDataFlags flags);
+
+  virtual MojoResult EndWriteData(uint32_t num_bytes_written);
+
+  ///////////// Wait set API /////////////
+
+  // Adds a dispatcher to wait on. When the dispatcher satisfies |signals|, it
+  // will be returned in the next call to |GetReadyDispatchers()|. If
+  // |dispatcher| has been added, it must be removed before adding again,
+  // otherwise |MOJO_RESULT_ALREADY_EXISTS| will be returned.
+  virtual MojoResult AddWaitingDispatcher(
+      const scoped_refptr<Dispatcher>& dispatcher,
+      MojoHandleSignals signals,
+      uintptr_t context);
+
+  // Removes a dispatcher to wait on. If |dispatcher| has not been added,
+  // |MOJO_RESULT_NOT_FOUND| will be returned.
+  virtual MojoResult RemoveWaitingDispatcher(
+      const scoped_refptr<Dispatcher>& dispatcher);
+
+  // Returns a set of ready dispatchers. |*count| is the maximum number of
+  // dispatchers to return, and will contain the number of dispatchers returned
+  // in |dispatchers| on completion.
+  virtual MojoResult GetReadyDispatchers(uint32_t* count,
+                                         DispatcherVector* dispatchers,
+                                         MojoResult* results,
+                                         uintptr_t* contexts);
+
+  ///////////// General-purpose API for all handle types /////////
+
+  // 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.
+  virtual HandleSignalsState GetHandleSignalsState() const;
+
+  // Adds an awakable to this dispatcher, which 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 awakable 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.
+  virtual MojoResult AddAwakable(Awakable* awakable,
+                                 MojoHandleSignals signals,
+                                 uintptr_t context,
+                                 HandleSignalsState* signals_state);
+
+  // Removes an awakable from this dispatcher. (It is valid to call this
+  // multiple times for the same |awakable| on the same object, so long as
+  // |AddAwakable()| was called at most once.) If |signals_state| is non-null,
+  // |*signals_state| will be set to the current handle signals state.
+  virtual void RemoveAwakable(Awakable* awakable,
+                              HandleSignalsState* signals_state);
+
+  // Informs the caller of the total serialized size (in bytes) and the total
+  // number of platform handles and ports needed to transfer this dispatcher
+  // across a message pipe.
+  //
+  // Must eventually be followed by a call to EndSerializeAndClose(). Note that
+  // StartSerialize() and EndSerialize() are always called in sequence, and
+  // only between calls to BeginTransit() and either (but not both)
+  // CompleteTransitAndClose() or CancelTransit().
+  //
+  // For this reason it is IMPERATIVE that the implementation ensure a
+  // consistent serializable state between BeginTransit() and
+  // CompleteTransitAndClose()/CancelTransit().
+  virtual void StartSerialize(uint32_t* num_bytes,
+                              uint32_t* num_ports,
+                              uint32_t* num_platform_handles);
+
+  // Serializes this dispatcher into |destination|, |ports|, and |handles|.
+  // Returns true iff successful, false otherwise. In either case the dispatcher
+  // will close.
+  //
+  // NOTE: Transit MAY still fail after this call returns. Implementations
+  // should not assume PlatformHandle ownership has transferred until
+  // CompleteTransitAndClose() is called. In other words, if CancelTransit() is
+  // called, the implementation should retain its PlatformHandles in working
+  // condition.
+  virtual bool EndSerialize(void* destination,
+                            ports::PortName* ports,
+                            PlatformHandle* handles);
+
+  // Does whatever is necessary to begin transit of the dispatcher.  This
+  // should return |true| if transit is OK, or false if the underlying resource
+  // is deemed busy by the implementation.
+  virtual bool BeginTransit();
+
+  // Does whatever is necessary to complete transit of the dispatcher, including
+  // closure. This is only called upon successfully transmitting an outgoing
+  // message containing this serialized dispatcher.
+  virtual void CompleteTransitAndClose();
+
+  // Does whatever is necessary to cancel transit of the dispatcher. The
+  // dispatcher should remain in a working state and resume normal operation.
+  virtual void CancelTransit();
+
+  // Deserializes a specific dispatcher type from an incoming message.
+  static scoped_refptr<Dispatcher> Deserialize(
+      Type type,
+      const void* bytes,
+      size_t num_bytes,
+      const ports::PortName* ports,
+      size_t num_ports,
+      PlatformHandle* platform_handles,
+      size_t num_platform_handles);
+
+ protected:
+  friend class base::RefCountedThreadSafe<Dispatcher>;
+
+  Dispatcher();
+  virtual ~Dispatcher();
+
+  DISALLOW_COPY_AND_ASSIGN(Dispatcher);
+};
+
+// So logging macros and |DCHECK_EQ()|, etc. work.
+MOJO_SYSTEM_IMPL_EXPORT inline std::ostream& operator<<(std::ostream& out,
+                                                        Dispatcher::Type type) {
+  return out << static_cast<int>(type);
+}
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_DISPATCHER_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..1c47a28
--- /dev/null
+++ b/mojo/edk/system/handle_signals_state.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_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
+#define MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
+
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+// Just "add" some constructors and methods to the C struct
+// |MojoHandleSignalsState| (for convenience). This should add no overhead.
+struct MOJO_SYSTEM_IMPL_EXPORT HandleSignalsState final
+    : 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 edk
+}  // 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..e41817d
--- /dev/null
+++ b/mojo/edk/system/handle_table.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/edk/system/handle_table.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+namespace mojo {
+namespace edk {
+
+HandleTable::HandleTable() {}
+
+HandleTable::~HandleTable() {}
+
+MojoHandle HandleTable::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) {
+  // Oops, we're out of handles.
+  if (next_available_handle_ == MOJO_HANDLE_INVALID)
+    return MOJO_HANDLE_INVALID;
+
+  MojoHandle handle = next_available_handle_++;
+  auto result = handles_.insert(std::make_pair(handle, Entry(dispatcher)));
+  DCHECK(result.second);
+
+  return handle;
+}
+
+bool HandleTable::AddDispatchersFromTransit(
+    const std::vector<Dispatcher::DispatcherInTransit>& dispatchers,
+    MojoHandle* handles) {
+  // Oops, we're out of handles.
+  if (next_available_handle_ == MOJO_HANDLE_INVALID)
+    return false;
+
+  DCHECK_LE(dispatchers.size(), std::numeric_limits<uint32_t>::max());
+  // If this insertion would cause handle overflow, we're out of handles.
+  if (next_available_handle_ + dispatchers.size() < next_available_handle_)
+    return false;
+
+  for (size_t i = 0; i < dispatchers.size(); ++i) {
+    MojoHandle handle = next_available_handle_++;
+    auto result = handles_.insert(
+        std::make_pair(handle, Entry(dispatchers[i].dispatcher)));
+    DCHECK(result.second);
+    handles[i] = handle;
+  }
+
+  return true;
+}
+
+scoped_refptr<Dispatcher> HandleTable::GetDispatcher(MojoHandle handle) const {
+  auto it = handles_.find(handle);
+  if (it == handles_.end())
+    return nullptr;
+  return it->second.dispatcher;
+}
+
+MojoResult HandleTable::GetAndRemoveDispatcher(
+    MojoHandle handle,
+    scoped_refptr<Dispatcher>* dispatcher) {
+  auto it = handles_.find(handle);
+  if (it == handles_.end())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (it->second.busy)
+    return MOJO_RESULT_BUSY;
+
+  *dispatcher = it->second.dispatcher;
+  handles_.erase(it);
+  return MOJO_RESULT_OK;
+}
+
+MojoResult HandleTable::BeginTransit(
+    const MojoHandle* handles,
+    uint32_t num_handles,
+    std::vector<Dispatcher::DispatcherInTransit>* dispatchers) {
+  dispatchers->clear();
+  dispatchers->reserve(num_handles);
+  for (size_t i = 0; i < num_handles; ++i) {
+    auto it = handles_.find(handles[i]);
+    if (it == handles_.end())
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    if (it->second.busy)
+      return MOJO_RESULT_BUSY;
+
+    Dispatcher::DispatcherInTransit d;
+    d.local_handle = handles[i];
+    d.dispatcher = it->second.dispatcher;
+    if (!d.dispatcher->BeginTransit())
+      return MOJO_RESULT_BUSY;
+    it->second.busy = true;
+    dispatchers->push_back(d);
+  }
+  return MOJO_RESULT_OK;
+}
+
+void HandleTable::CompleteTransitAndClose(
+    const std::vector<Dispatcher::DispatcherInTransit>& dispatchers) {
+  for (const auto& dispatcher : dispatchers) {
+    auto it = handles_.find(dispatcher.local_handle);
+    DCHECK(it != handles_.end() && it->second.busy);
+    handles_.erase(it);
+    dispatcher.dispatcher->CompleteTransitAndClose();
+  }
+}
+
+void HandleTable::CancelTransit(
+    const std::vector<Dispatcher::DispatcherInTransit>& dispatchers) {
+  for (const auto& dispatcher : dispatchers) {
+    auto it = handles_.find(dispatcher.local_handle);
+    DCHECK(it != handles_.end() && it->second.busy);
+    it->second.busy = false;
+    dispatcher.dispatcher->CancelTransit();
+  }
+}
+
+void HandleTable::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) {
+  handles->clear();
+  for (const auto& entry : handles_)
+    handles->push_back(entry.first);
+}
+
+HandleTable::Entry::Entry() {}
+
+HandleTable::Entry::Entry(scoped_refptr<Dispatcher> dispatcher)
+    : dispatcher(dispatcher) {}
+
+HandleTable::Entry::Entry(const Entry& other) = default;
+
+HandleTable::Entry::~Entry() {}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/handle_table.h b/mojo/edk/system/handle_table.h
new file mode 100644
index 0000000..882d540
--- /dev/null
+++ b/mojo/edk/system/handle_table.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_EDK_SYSTEM_HANDLE_TABLE_H_
+#define MOJO_EDK_SYSTEM_HANDLE_TABLE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+class HandleTable {
+ public:
+  HandleTable();
+  ~HandleTable();
+
+  MojoHandle AddDispatcher(scoped_refptr<Dispatcher> dispatcher);
+
+  // Inserts multiple dispatchers received from message transit, populating
+  // |handles| with their newly allocated handles. Returns |true| on success.
+  bool AddDispatchersFromTransit(
+      const std::vector<Dispatcher::DispatcherInTransit>& dispatchers,
+      MojoHandle* handles);
+
+  scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle) const;
+  MojoResult GetAndRemoveDispatcher(MojoHandle,
+                                    scoped_refptr<Dispatcher>* dispatcher);
+
+  // Marks handles as busy and populates |dispatchers|. Returns MOJO_RESULT_BUSY
+  // if any of the handles are already in transit; MOJO_RESULT_INVALID_ARGUMENT
+  // if any of the handles are invalid; or MOJO_RESULT_OK if successful.
+  MojoResult BeginTransit(
+      const MojoHandle* handles,
+      uint32_t num_handles,
+      std::vector<Dispatcher::DispatcherInTransit>* dispatchers);
+
+  void CompleteTransitAndClose(
+      const std::vector<Dispatcher::DispatcherInTransit>& dispatchers);
+  void CancelTransit(
+      const std::vector<Dispatcher::DispatcherInTransit>& dispatchers);
+
+  void GetActiveHandlesForTest(std::vector<MojoHandle> *handles);
+
+ private:
+  struct Entry {
+   Entry();
+   explicit Entry(scoped_refptr<Dispatcher> dispatcher);
+   Entry(const Entry& other);
+   ~Entry();
+
+   scoped_refptr<Dispatcher> dispatcher;
+   bool busy = false;
+  };
+
+  using HandleMap = base::hash_map<MojoHandle, Entry>;
+
+  HandleMap handles_;
+
+  uint32_t next_available_handle_ = 1;
+
+  DISALLOW_COPY_AND_ASSIGN(HandleTable);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_HANDLE_TABLE_H_
diff --git a/mojo/edk/system/mapping_table.cc b/mojo/edk/system/mapping_table.cc
new file mode 100644
index 0000000..8509443
--- /dev/null
+++ b/mojo/edk/system/mapping_table.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/edk/system/mapping_table.h"
+
+#include "base/logging.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/configuration.h"
+
+namespace mojo {
+namespace edk {
+
+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(
+    std::unique_ptr<PlatformSharedBufferMapping> mapping) {
+  DCHECK(mapping);
+
+  if (address_to_mapping_map_.size() >=
+      GetConfiguration().max_mapping_table_sze)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  void* address = 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(void* address) {
+  AddressToMappingMap::iterator it = address_to_mapping_map_.find(address);
+  if (it == address_to_mapping_map_.end())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  PlatformSharedBufferMapping* mapping_to_delete = it->second;
+  address_to_mapping_map_.erase(it);
+  delete mapping_to_delete;
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/mapping_table.h b/mojo/edk/system/mapping_table.h
new file mode 100644
index 0000000..00167e3
--- /dev/null
+++ b/mojo/edk/system/mapping_table.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_SYSTEM_MAPPING_TABLE_H_
+#define MOJO_EDK_SYSTEM_MAPPING_TABLE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+
+namespace edk {
+class Core;
+class PlatformSharedBufferMapping;
+
+// 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(std::unique_ptr<PlatformSharedBufferMapping> mapping);
+  MojoResult RemoveMapping(void* address);
+
+ private:
+  friend bool internal::ShutdownCheckNoLeaks(Core*);
+
+  using AddressToMappingMap =
+      base::hash_map<void*, PlatformSharedBufferMapping*>;
+  AddressToMappingMap address_to_mapping_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(MappingTable);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MAPPING_TABLE_H_
diff --git a/mojo/edk/system/message_for_transit.cc b/mojo/edk/system/message_for_transit.cc
new file mode 100644
index 0000000..26658e1
--- /dev/null
+++ b/mojo/edk/system/message_for_transit.cc
@@ -0,0 +1,136 @@
+// Copyright 2016 The Chromium Authors. All 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_for_transit.h"
+
+#include <vector>
+
+#include "mojo/edk/embedder/platform_handle_vector.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+static_assert(sizeof(MessageForTransit::MessageHeader) % 8 == 0,
+              "Invalid MessageHeader size.");
+static_assert(sizeof(MessageForTransit::DispatcherHeader) % 8 == 0,
+              "Invalid DispatcherHeader size.");
+
+}  // namespace
+
+MessageForTransit::~MessageForTransit() {}
+
+// static
+MojoResult MessageForTransit::Create(
+    std::unique_ptr<MessageForTransit>* message,
+    uint32_t num_bytes,
+    const Dispatcher::DispatcherInTransit* dispatchers,
+    uint32_t num_dispatchers) {
+  // A structure for retaining information about every Dispatcher that will be
+  // sent with this message.
+  struct DispatcherInfo {
+    uint32_t num_bytes;
+    uint32_t num_ports;
+    uint32_t num_handles;
+  };
+
+  // This is only the base header size. It will grow as we accumulate the
+  // size of serialized state for each dispatcher.
+  size_t header_size = sizeof(MessageHeader) +
+      num_dispatchers * sizeof(DispatcherHeader);
+  size_t num_ports = 0;
+  size_t num_handles = 0;
+
+  std::vector<DispatcherInfo> dispatcher_info(num_dispatchers);
+  for (size_t i = 0; i < num_dispatchers; ++i) {
+    Dispatcher* d = dispatchers[i].dispatcher.get();
+    d->StartSerialize(&dispatcher_info[i].num_bytes,
+                      &dispatcher_info[i].num_ports,
+                      &dispatcher_info[i].num_handles);
+    header_size += dispatcher_info[i].num_bytes;
+    num_ports += dispatcher_info[i].num_ports;
+    num_handles += dispatcher_info[i].num_handles;
+  }
+
+  // We now have enough information to fully allocate the message storage.
+  std::unique_ptr<PortsMessage> msg = PortsMessage::NewUserMessage(
+      header_size + num_bytes, num_ports, num_handles);
+  if (!msg)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  // Populate the message header with information about serialized dispatchers.
+  //
+  // The front of the message is always a MessageHeader followed by a
+  // DispatcherHeader for each dispatcher to be sent.
+  MessageHeader* header =
+      static_cast<MessageHeader*>(msg->mutable_payload_bytes());
+  DispatcherHeader* dispatcher_headers =
+      reinterpret_cast<DispatcherHeader*>(header + 1);
+
+  // Serialized dispatcher state immediately follows the series of
+  // DispatcherHeaders.
+  char* dispatcher_data =
+      reinterpret_cast<char*>(dispatcher_headers + num_dispatchers);
+
+  header->num_dispatchers = num_dispatchers;
+
+  // |header_size| is the total number of bytes preceding the message payload,
+  // including all dispatcher headers and serialized dispatcher state.
+  DCHECK_LE(header_size, std::numeric_limits<uint32_t>::max());
+  header->header_size = static_cast<uint32_t>(header_size);
+
+  if (num_dispatchers > 0) {
+    ScopedPlatformHandleVectorPtr handles(
+        new PlatformHandleVector(num_handles));
+    size_t port_index = 0;
+    size_t handle_index = 0;
+    bool fail = false;
+    for (size_t i = 0; i < num_dispatchers; ++i) {
+      Dispatcher* d = dispatchers[i].dispatcher.get();
+      DispatcherHeader* dh = &dispatcher_headers[i];
+      const DispatcherInfo& info = dispatcher_info[i];
+
+      // Fill in the header for this dispatcher.
+      dh->type = static_cast<int32_t>(d->GetType());
+      dh->num_bytes = info.num_bytes;
+      dh->num_ports = info.num_ports;
+      dh->num_platform_handles = info.num_handles;
+
+      // Fill in serialized state, ports, and platform handles. We'll cancel
+      // the send if the dispatcher implementation rejects for some reason.
+      if (!d->EndSerialize(static_cast<void*>(dispatcher_data),
+                           msg->mutable_ports() + port_index,
+                           handles->data() + handle_index)) {
+        fail = true;
+        break;
+      }
+
+      dispatcher_data += info.num_bytes;
+      port_index += info.num_ports;
+      handle_index += info.num_handles;
+    }
+
+    if (fail) {
+      // Release any platform handles we've accumulated. Their dispatchers
+      // retain ownership when message creation fails, so these are not actually
+      // leaking.
+      handles->clear();
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    }
+
+    // Take ownership of all the handles and move them into message storage.
+    msg->SetHandles(std::move(handles));
+  }
+
+  message->reset(new MessageForTransit(std::move(msg)));
+  return MOJO_RESULT_OK;
+}
+
+MessageForTransit::MessageForTransit(std::unique_ptr<PortsMessage> message)
+    : message_(std::move(message)) {
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/message_for_transit.h b/mojo/edk/system/message_for_transit.h
new file mode 100644
index 0000000..6103a77
--- /dev/null
+++ b/mojo/edk/system/message_for_transit.h
@@ -0,0 +1,115 @@
+// Copyright 2016 The Chromium Authors. All 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_FOR_TRANSIT_H_
+#define MOJO_EDK_SYSTEM_MESSAGE_FOR_TRANSIT_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/ports_message.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+// MessageForTransit holds onto a PortsMessage which may be sent via
+// |MojoWriteMessage()| or which may have been received on a pipe endpoint.
+// Instances of this class are exposed to Mojo system API consumers via the
+// opaque pointers used with |MojoCreateMessage()|, |MojoDestroyMessage()|,
+// |MojoWriteMessageNew()|, and |MojoReadMessageNew()|.
+class MOJO_SYSTEM_IMPL_EXPORT MessageForTransit {
+ public:
+#pragma pack(push, 1)
+  // Header attached to every message.
+  struct MessageHeader {
+    // The number of serialized dispatchers included in this header.
+    uint32_t num_dispatchers;
+
+    // Total size of the header, including serialized dispatcher data.
+    uint32_t header_size;
+  };
+
+  // Header for each dispatcher in a message, immediately following the message
+  // header.
+  struct DispatcherHeader {
+    // The type of the dispatcher, correpsonding to the Dispatcher::Type enum.
+    int32_t type;
+
+    // The size of the serialized dispatcher, not including this header.
+    uint32_t num_bytes;
+
+    // The number of ports needed to deserialize this dispatcher.
+    uint32_t num_ports;
+
+    // The number of platform handles needed to deserialize this dispatcher.
+    uint32_t num_platform_handles;
+  };
+#pragma pack(pop)
+
+  ~MessageForTransit();
+
+  // A static constructor for building outbound messages.
+  static MojoResult Create(
+      std::unique_ptr<MessageForTransit>* message,
+      uint32_t num_bytes,
+      const Dispatcher::DispatcherInTransit* dispatchers,
+      uint32_t num_dispatchers);
+
+  // A static constructor for wrapping inbound messages.
+  static std::unique_ptr<MessageForTransit> WrapPortsMessage(
+      std::unique_ptr<PortsMessage> message) {
+    return base::WrapUnique(new MessageForTransit(std::move(message)));
+  }
+
+  const void* bytes() const {
+    DCHECK(message_);
+    return static_cast<const void*>(
+        static_cast<const char*>(message_->payload_bytes()) +
+            header()->header_size);
+  }
+
+  void* mutable_bytes() {
+    DCHECK(message_);
+    return static_cast<void*>(
+        static_cast<char*>(message_->mutable_payload_bytes()) +
+            header()->header_size);
+  }
+
+  size_t num_bytes() const {
+    size_t header_size = header()->header_size;
+    DCHECK_GE(message_->num_payload_bytes(), header_size);
+    return message_->num_payload_bytes() - header_size;
+  }
+
+  size_t num_handles() const { return header()->num_dispatchers; }
+
+  const PortsMessage& ports_message() const { return *message_; }
+
+  std::unique_ptr<PortsMessage> TakePortsMessage() {
+    return std::move(message_);
+  }
+
+ private:
+  explicit MessageForTransit(std::unique_ptr<PortsMessage> message);
+
+  const MessageForTransit::MessageHeader* header() const {
+    DCHECK(message_);
+    return static_cast<const MessageForTransit::MessageHeader*>(
+        message_->payload_bytes());
+  }
+
+  std::unique_ptr<PortsMessage> message_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageForTransit);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MESSAGE_FOR_TRANSIT_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..f06054d
--- /dev/null
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -0,0 +1,554 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+
+#include <limits>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/message_for_transit.h"
+#include "mojo/edk/system/node_controller.h"
+#include "mojo/edk/system/ports_message.h"
+#include "mojo/edk/system/request_context.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+using DispatcherHeader = MessageForTransit::DispatcherHeader;
+using MessageHeader = MessageForTransit::MessageHeader;
+
+#pragma pack(push, 1)
+
+struct SerializedState {
+  uint64_t pipe_id;
+  int8_t endpoint;
+  char padding[7];
+};
+
+static_assert(sizeof(SerializedState) % 8 == 0,
+              "Invalid SerializedState size.");
+
+#pragma pack(pop)
+
+}  // namespace
+
+// A PortObserver which forwards to a MessagePipeDispatcher. This owns a
+// reference to the MPD to ensure it lives as long as the observed port.
+class MessagePipeDispatcher::PortObserverThunk
+    : public NodeController::PortObserver {
+ public:
+  explicit PortObserverThunk(scoped_refptr<MessagePipeDispatcher> dispatcher)
+      : dispatcher_(dispatcher) {}
+
+ private:
+  ~PortObserverThunk() override {}
+
+  // NodeController::PortObserver:
+  void OnPortStatusChanged() override { dispatcher_->OnPortStatusChanged(); }
+
+  scoped_refptr<MessagePipeDispatcher> dispatcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(PortObserverThunk);
+};
+
+MessagePipeDispatcher::MessagePipeDispatcher(NodeController* node_controller,
+                                             const ports::PortRef& port,
+                                             uint64_t pipe_id,
+                                             int endpoint)
+    : node_controller_(node_controller),
+      port_(port),
+      pipe_id_(pipe_id),
+      endpoint_(endpoint) {
+  DVLOG(2) << "Creating new MessagePipeDispatcher for port " << port.name()
+           << " [pipe_id=" << pipe_id << "; endpoint=" << endpoint << "]";
+
+  node_controller_->SetPortObserver(
+      port_,
+      make_scoped_refptr(new PortObserverThunk(this)));
+}
+
+bool MessagePipeDispatcher::Fuse(MessagePipeDispatcher* other) {
+  node_controller_->SetPortObserver(port_, nullptr);
+  node_controller_->SetPortObserver(other->port_, nullptr);
+
+  ports::PortRef port0;
+  {
+    base::AutoLock lock(signal_lock_);
+    port0 = port_;
+    port_closed_.Set(true);
+    awakables_.CancelAll();
+  }
+
+  ports::PortRef port1;
+  {
+    base::AutoLock lock(other->signal_lock_);
+    port1 = other->port_;
+    other->port_closed_.Set(true);
+    other->awakables_.CancelAll();
+  }
+
+  // Both ports are always closed by this call.
+  int rv = node_controller_->MergeLocalPorts(port0, port1);
+  return rv == ports::OK;
+}
+
+Dispatcher::Type MessagePipeDispatcher::GetType() const {
+  return Type::MESSAGE_PIPE;
+}
+
+MojoResult MessagePipeDispatcher::Close() {
+  base::AutoLock lock(signal_lock_);
+  DVLOG(2) << "Closing message pipe " << pipe_id_ << " endpoint " << endpoint_
+           << " [port=" << port_.name() << "]";
+  return CloseNoLock();
+}
+
+MojoResult MessagePipeDispatcher::Watch(MojoHandleSignals signals,
+                                        const Watcher::WatchCallback& callback,
+                                        uintptr_t context) {
+  base::AutoLock lock(signal_lock_);
+
+  if (port_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return awakables_.AddWatcher(
+      signals, callback, context, GetHandleSignalsStateNoLock());
+}
+
+MojoResult MessagePipeDispatcher::CancelWatch(uintptr_t context) {
+  base::AutoLock lock(signal_lock_);
+
+  if (port_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  return awakables_.RemoveWatcher(context);
+}
+
+MojoResult MessagePipeDispatcher::WriteMessage(
+    std::unique_ptr<MessageForTransit> message,
+    MojoWriteMessageFlags flags) {
+  if (port_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  size_t num_bytes = message->num_bytes();
+  int rv = node_controller_->SendMessage(port_, message->TakePortsMessage());
+
+  DVLOG(2) << "Sent message on pipe " << pipe_id_ << " endpoint " << endpoint_
+           << " [port=" << port_.name() << "; rv=" << rv
+           << "; num_bytes=" << num_bytes << "]";
+
+  if (rv != ports::OK) {
+    if (rv == ports::ERROR_PORT_UNKNOWN ||
+        rv == ports::ERROR_PORT_STATE_UNEXPECTED ||
+        rv == ports::ERROR_PORT_CANNOT_SEND_PEER) {
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    } else if (rv == ports::ERROR_PORT_PEER_CLOSED) {
+      base::AutoLock lock(signal_lock_);
+      awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+      return MOJO_RESULT_FAILED_PRECONDITION;
+    }
+
+    NOTREACHED();
+    return MOJO_RESULT_UNKNOWN;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult MessagePipeDispatcher::ReadMessage(
+    std::unique_ptr<MessageForTransit>* message,
+    uint32_t* num_bytes,
+    MojoHandle* handles,
+    uint32_t* num_handles,
+    MojoReadMessageFlags flags,
+    bool read_any_size) {
+  // We can't read from a port that's closed or in transit!
+  if (port_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  bool no_space = false;
+  bool may_discard = flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD;
+  bool invalid_message = false;
+
+  // Grab a message if the provided handles buffer is large enough. If the input
+  // |num_bytes| is provided and |read_any_size| is false, we also ensure
+  // that it specifies a size at least as large as the next available payload.
+  //
+  // If |read_any_size| is true, the input value of |*num_bytes| is ignored.
+  // This flag exists to support both new and old API behavior.
+
+  ports::ScopedMessage ports_message;
+  int rv = node_controller_->node()->GetMessageIf(
+      port_,
+      [read_any_size, num_bytes, num_handles, &no_space, &may_discard,
+       &invalid_message](
+          const ports::Message& next_message) {
+        const PortsMessage& message =
+            static_cast<const PortsMessage&>(next_message);
+        if (message.num_payload_bytes() < sizeof(MessageHeader)) {
+          invalid_message = true;
+          return true;
+        }
+
+        const MessageHeader* header =
+            static_cast<const MessageHeader*>(message.payload_bytes());
+        if (header->header_size > message.num_payload_bytes()) {
+          invalid_message = true;
+          return true;
+        }
+
+        uint32_t bytes_to_read = 0;
+        uint32_t bytes_available =
+            static_cast<uint32_t>(message.num_payload_bytes()) -
+            header->header_size;
+        if (num_bytes) {
+          bytes_to_read = std::min(*num_bytes, bytes_available);
+          *num_bytes = bytes_available;
+        }
+
+        uint32_t handles_to_read = 0;
+        uint32_t handles_available = header->num_dispatchers;
+        if (num_handles) {
+          handles_to_read = std::min(*num_handles, handles_available);
+          *num_handles = handles_available;
+        }
+
+        if (handles_to_read < handles_available ||
+            (!read_any_size && bytes_to_read < bytes_available)) {
+          no_space = true;
+          return may_discard;
+        }
+
+        return true;
+      },
+      &ports_message);
+
+  if (invalid_message)
+    return MOJO_RESULT_UNKNOWN;
+
+  if (rv != ports::OK && rv != ports::ERROR_PORT_PEER_CLOSED) {
+    if (rv == ports::ERROR_PORT_UNKNOWN ||
+        rv == ports::ERROR_PORT_STATE_UNEXPECTED)
+      return MOJO_RESULT_INVALID_ARGUMENT;
+
+    NOTREACHED();
+    return MOJO_RESULT_UNKNOWN;  // TODO: Add a better error code here?
+  }
+
+  if (no_space) {
+    // |*num_handles| (and/or |*num_bytes| if |read_any_size| is false) wasn't
+    // sufficient to hold this message's data. The message will still be in
+    // queue unless MOJO_READ_MESSAGE_FLAG_MAY_DISCARD was set.
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  if (!ports_message) {
+    // No message was available in queue.
+
+    if (rv == ports::OK)
+      return MOJO_RESULT_SHOULD_WAIT;
+
+    // Peer is closed and there are no more messages to read.
+    DCHECK_EQ(rv, ports::ERROR_PORT_PEER_CLOSED);
+    base::AutoLock lock(signal_lock_);
+    awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  // Alright! We have a message and the caller has provided sufficient storage
+  // in which to receive it.
+
+  std::unique_ptr<PortsMessage> msg(
+      static_cast<PortsMessage*>(ports_message.release()));
+
+  const MessageHeader* header =
+      static_cast<const MessageHeader*>(msg->payload_bytes());
+  const DispatcherHeader* dispatcher_headers =
+      reinterpret_cast<const DispatcherHeader*>(header + 1);
+
+  if (header->num_dispatchers > std::numeric_limits<uint16_t>::max())
+    return MOJO_RESULT_UNKNOWN;
+
+  // Deserialize dispatchers.
+  if (header->num_dispatchers > 0) {
+    CHECK(handles);
+    std::vector<DispatcherInTransit> dispatchers(header->num_dispatchers);
+    size_t data_payload_index = sizeof(MessageHeader) +
+        header->num_dispatchers * sizeof(DispatcherHeader);
+    if (data_payload_index > header->header_size)
+      return MOJO_RESULT_UNKNOWN;
+    const char* dispatcher_data = reinterpret_cast<const char*>(
+        dispatcher_headers + header->num_dispatchers);
+    size_t port_index = 0;
+    size_t platform_handle_index = 0;
+    ScopedPlatformHandleVectorPtr msg_handles = msg->TakeHandles();
+    const size_t num_msg_handles = msg_handles ? msg_handles->size() : 0;
+    for (size_t i = 0; i < header->num_dispatchers; ++i) {
+      const DispatcherHeader& dh = dispatcher_headers[i];
+      Type type = static_cast<Type>(dh.type);
+
+      size_t next_payload_index = data_payload_index + dh.num_bytes;
+      if (msg->num_payload_bytes() < next_payload_index ||
+          next_payload_index < data_payload_index) {
+        return MOJO_RESULT_UNKNOWN;
+      }
+
+      size_t next_port_index = port_index + dh.num_ports;
+      if (msg->num_ports() < next_port_index || next_port_index < port_index)
+        return MOJO_RESULT_UNKNOWN;
+
+      size_t next_platform_handle_index =
+          platform_handle_index + dh.num_platform_handles;
+      if (num_msg_handles < next_platform_handle_index ||
+          next_platform_handle_index < platform_handle_index) {
+        return MOJO_RESULT_UNKNOWN;
+      }
+
+      PlatformHandle* out_handles =
+          num_msg_handles ? msg_handles->data() + platform_handle_index
+                          : nullptr;
+      dispatchers[i].dispatcher = Dispatcher::Deserialize(
+          type, dispatcher_data, dh.num_bytes, msg->ports() + port_index,
+          dh.num_ports, out_handles, dh.num_platform_handles);
+      if (!dispatchers[i].dispatcher)
+        return MOJO_RESULT_UNKNOWN;
+
+      dispatcher_data += dh.num_bytes;
+      data_payload_index = next_payload_index;
+      port_index = next_port_index;
+      platform_handle_index = next_platform_handle_index;
+    }
+
+    if (!node_controller_->core()->AddDispatchersFromTransit(dispatchers,
+                                                             handles))
+      return MOJO_RESULT_UNKNOWN;
+  }
+
+  CHECK(msg);
+  *message = MessageForTransit::WrapPortsMessage(std::move(msg));
+  return MOJO_RESULT_OK;
+}
+
+HandleSignalsState
+MessagePipeDispatcher::GetHandleSignalsState() const {
+  base::AutoLock lock(signal_lock_);
+  return GetHandleSignalsStateNoLock();
+}
+
+MojoResult MessagePipeDispatcher::AddAwakable(
+    Awakable* awakable,
+    MojoHandleSignals signals,
+    uintptr_t context,
+    HandleSignalsState* signals_state) {
+  base::AutoLock lock(signal_lock_);
+
+  if (port_closed_ || in_transit_) {
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  HandleSignalsState state = GetHandleSignalsStateNoLock();
+
+  DVLOG(2) << "Getting signal state for pipe " << pipe_id_ << " endpoint "
+           << endpoint_ << " [awakable=" << awakable << "; port="
+           << port_.name() << "; signals=" << signals << "; satisfied="
+           << state.satisfied_signals << "; satisfiable="
+           << state.satisfiable_signals << "]";
+
+  if (state.satisfies(signals)) {
+    if (signals_state)
+      *signals_state = state;
+    DVLOG(2) << "Signals already set for " << port_.name();
+    return MOJO_RESULT_ALREADY_EXISTS;
+  }
+  if (!state.can_satisfy(signals)) {
+    if (signals_state)
+      *signals_state = state;
+    DVLOG(2) << "Signals impossible to satisfy for " << port_.name();
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  DVLOG(2) << "Adding awakable to pipe " << pipe_id_ << " endpoint "
+           << endpoint_ << " [awakable=" << awakable << "; port="
+           << port_.name() << "; signals=" << signals << "]";
+
+  awakables_.Add(awakable, signals, context);
+  return MOJO_RESULT_OK;
+}
+
+void MessagePipeDispatcher::RemoveAwakable(Awakable* awakable,
+                                           HandleSignalsState* signals_state) {
+  base::AutoLock lock(signal_lock_);
+  if (port_closed_ || in_transit_) {
+    if (signals_state)
+      *signals_state = HandleSignalsState();
+  } else if (signals_state) {
+    *signals_state = GetHandleSignalsStateNoLock();
+  }
+
+  DVLOG(2) << "Removing awakable from pipe " << pipe_id_ << " endpoint "
+           << endpoint_ << " [awakable=" << awakable << "; port="
+           << port_.name() << "]";
+
+  awakables_.Remove(awakable);
+}
+
+void MessagePipeDispatcher::StartSerialize(uint32_t* num_bytes,
+                                           uint32_t* num_ports,
+                                           uint32_t* num_handles) {
+  *num_bytes = static_cast<uint32_t>(sizeof(SerializedState));
+  *num_ports = 1;
+  *num_handles = 0;
+}
+
+bool MessagePipeDispatcher::EndSerialize(void* destination,
+                                         ports::PortName* ports,
+                                         PlatformHandle* handles) {
+  SerializedState* state = static_cast<SerializedState*>(destination);
+  state->pipe_id = pipe_id_;
+  state->endpoint = static_cast<int8_t>(endpoint_);
+  memset(state->padding, 0, sizeof(state->padding));
+  ports[0] = port_.name();
+  return true;
+}
+
+bool MessagePipeDispatcher::BeginTransit() {
+  base::AutoLock lock(signal_lock_);
+  if (in_transit_ || port_closed_)
+    return false;
+  in_transit_.Set(true);
+  return in_transit_;
+}
+
+void MessagePipeDispatcher::CompleteTransitAndClose() {
+  node_controller_->SetPortObserver(port_, nullptr);
+
+  base::AutoLock lock(signal_lock_);
+  port_transferred_ = true;
+  in_transit_.Set(false);
+  CloseNoLock();
+}
+
+void MessagePipeDispatcher::CancelTransit() {
+  base::AutoLock lock(signal_lock_);
+  in_transit_.Set(false);
+
+  // Something may have happened while we were waiting for potential transit.
+  awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+}
+
+// static
+scoped_refptr<Dispatcher> MessagePipeDispatcher::Deserialize(
+    const void* data,
+    size_t num_bytes,
+    const ports::PortName* ports,
+    size_t num_ports,
+    PlatformHandle* handles,
+    size_t num_handles) {
+  if (num_ports != 1 || num_handles || num_bytes != sizeof(SerializedState))
+    return nullptr;
+
+  const SerializedState* state = static_cast<const SerializedState*>(data);
+
+  ports::PortRef port;
+  CHECK_EQ(
+      ports::OK,
+      internal::g_core->GetNodeController()->node()->GetPort(ports[0], &port));
+
+  return new MessagePipeDispatcher(internal::g_core->GetNodeController(), port,
+                                   state->pipe_id, state->endpoint);
+}
+
+MessagePipeDispatcher::~MessagePipeDispatcher() {
+  DCHECK(port_closed_ && !in_transit_);
+}
+
+MojoResult MessagePipeDispatcher::CloseNoLock() {
+  signal_lock_.AssertAcquired();
+  if (port_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  port_closed_.Set(true);
+  awakables_.CancelAll();
+
+  if (!port_transferred_) {
+    base::AutoUnlock unlock(signal_lock_);
+    node_controller_->ClosePort(port_);
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+HandleSignalsState MessagePipeDispatcher::GetHandleSignalsStateNoLock() const {
+  HandleSignalsState rv;
+
+  ports::PortStatus port_status;
+  if (node_controller_->node()->GetStatus(port_, &port_status) != ports::OK) {
+    CHECK(in_transit_ || port_transferred_ || port_closed_);
+    return HandleSignalsState();
+  }
+
+  if (port_status.has_messages) {
+    rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  }
+  if (port_status.receiving_messages)
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  if (!port_status.peer_closed) {
+    rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+    rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+  } else {
+    rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+  }
+  rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+  return rv;
+}
+
+void MessagePipeDispatcher::OnPortStatusChanged() {
+  DCHECK(RequestContext::current());
+
+  base::AutoLock lock(signal_lock_);
+
+  // We stop observing our port as soon as it's transferred, but this can race
+  // with events which are raised right before that happens. This is fine to
+  // ignore.
+  if (port_transferred_)
+    return;
+
+#if DCHECK_IS_ON()
+  ports::PortStatus port_status;
+  if (node_controller_->node()->GetStatus(port_, &port_status) == ports::OK) {
+    if (port_status.has_messages) {
+      ports::ScopedMessage unused;
+      size_t message_size = 0;
+      node_controller_->node()->GetMessageIf(
+          port_, [&message_size](const ports::Message& message) {
+            message_size = message.num_payload_bytes();
+            return false;
+          }, &unused);
+      DVLOG(2) << "New message detected on message pipe " << pipe_id_
+               << " endpoint " << endpoint_ << " [port=" << port_.name()
+               << "; size=" << message_size << "]";
+    }
+    if (port_status.peer_closed) {
+      DVLOG(2) << "Peer closure detected on message pipe " << pipe_id_
+               << " endpoint " << endpoint_ << " [port=" << port_.name() << "]";
+    }
+  }
+#endif
+
+  awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock());
+}
+
+}  // namespace edk
+}  // 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..fddd0fd
--- /dev/null
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -0,0 +1,122 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <queue>
+
+#include "base/macros.h"
+#include "mojo/edk/system/atomic_flag.h"
+#include "mojo/edk/system/awakable_list.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/message_for_transit.h"
+#include "mojo/edk/system/ports/port_ref.h"
+
+namespace mojo {
+namespace edk {
+
+class NodeController;
+class PortsMessage;
+
+class MessagePipeDispatcher : public Dispatcher {
+ public:
+  // Constructs a MessagePipeDispatcher permanently tied to a specific port.
+  // |connected| must indicate the state of the port at construction time; if
+  // the port is initialized with a peer, |connected| must be true. Otherwise it
+  // must be false.
+  //
+  // A MessagePipeDispatcher may not be transferred while in a disconnected
+  // state, and one can never return to a disconnected once connected.
+  //
+  // |pipe_id| is a unique identifier which can be used to track pipe endpoints
+  // as they're passed around. |endpoint| is either 0 or 1 and again is only
+  // used for tracking pipes (one side is always 0, the other is always 1.)
+  MessagePipeDispatcher(NodeController* node_controller,
+                        const ports::PortRef& port,
+                        uint64_t pipe_id,
+                        int endpoint);
+
+  // Fuses this pipe with |other|. Returns |true| on success or |false| on
+  // failure. Regardless of the return value, both dispatchers are closed by
+  // this call.
+  bool Fuse(MessagePipeDispatcher* other);
+
+  // Dispatcher:
+  Type GetType() const override;
+  MojoResult Close() override;
+  MojoResult Watch(MojoHandleSignals signals,
+                   const Watcher::WatchCallback& callback,
+                   uintptr_t context) override;
+  MojoResult CancelWatch(uintptr_t context) override;
+  MojoResult WriteMessage(std::unique_ptr<MessageForTransit> message,
+                          MojoWriteMessageFlags flags) override;
+  MojoResult ReadMessage(std::unique_ptr<MessageForTransit>* message,
+                         uint32_t* num_bytes,
+                         MojoHandle* handles,
+                         uint32_t* num_handles,
+                         MojoReadMessageFlags flags,
+                         bool read_any_size) override;
+  HandleSignalsState GetHandleSignalsState() const override;
+  MojoResult AddAwakable(Awakable* awakable,
+                         MojoHandleSignals signals,
+                         uintptr_t context,
+                         HandleSignalsState* signals_state) override;
+  void RemoveAwakable(Awakable* awakable,
+                      HandleSignalsState* signals_state) override;
+  void StartSerialize(uint32_t* num_bytes,
+                      uint32_t* num_ports,
+                      uint32_t* num_handles) override;
+  bool EndSerialize(void* destination,
+                    ports::PortName* ports,
+                    PlatformHandle* handles) override;
+  bool BeginTransit() override;
+  void CompleteTransitAndClose() override;
+  void CancelTransit() override;
+
+  static scoped_refptr<Dispatcher> Deserialize(
+      const void* data,
+      size_t num_bytes,
+      const ports::PortName* ports,
+      size_t num_ports,
+      PlatformHandle* handles,
+      size_t num_handles);
+
+ private:
+  class PortObserverThunk;
+  friend class PortObserverThunk;
+
+  ~MessagePipeDispatcher() override;
+
+  MojoResult CloseNoLock();
+  HandleSignalsState GetHandleSignalsStateNoLock() const;
+  void OnPortStatusChanged();
+
+  // These are safe to access from any thread without locking.
+  NodeController* const node_controller_;
+  const ports::PortRef port_;
+  const uint64_t pipe_id_;
+  const int endpoint_;
+
+  // Guards access to all the fields below.
+  mutable base::Lock signal_lock_;
+
+  // This is not the same is |port_transferred_|. It's only held true between
+  // BeginTransit() and Complete/CancelTransit().
+  AtomicFlag in_transit_;
+
+  bool port_transferred_ = false;
+  AtomicFlag port_closed_;
+  AwakableList awakables_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_MESSAGE_PIPE_DISPATCHER_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..a6ce370
--- /dev/null
+++ b/mojo/edk/system/message_pipe_perftest.cc
@@ -0,0 +1,170 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/perf_time_logger.h"
+#include "base/threading/thread.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/edk/test/test_utils.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+class MessagePipePerfTest : public test::MojoTestBase {
+ public:
+  MessagePipePerfTest() : message_count_(0), message_size_(0) {}
+
+  void SetUpMeasurement(int message_count, size_t message_size) {
+    message_count_ = message_count;
+    message_size_ = message_size;
+    payload_ = std::string(message_size, '*');
+    read_buffer_.resize(message_size * 2);
+  }
+
+ protected:
+  void WriteWaitThenRead(MojoHandle mp) {
+    CHECK_EQ(MojoWriteMessage(mp, payload_.data(),
+                              static_cast<uint32_t>(payload_.size()), nullptr,
+                              0, MOJO_WRITE_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    HandleSignalsState hss;
+    CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
+                      &hss),
+             MOJO_RESULT_OK);
+    uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer_.size());
+    CHECK_EQ(MojoReadMessage(mp, &read_buffer_[0], &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(MojoHandle mp) {
+    CHECK_EQ(MojoWriteMessage(mp, "", 0, nullptr, 0,
+                              MOJO_WRITE_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+  }
+
+  void Measure(MojoHandle 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();
+  }
+
+ protected:
+  void RunPingPongServer(MojoHandle mp) {
+    // 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);
+  }
+
+  static int RunPingPongClient(MojoHandle mp) {
+    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 =
+          MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE,
+                   MOJO_DEADLINE_INDEFINITE, &hss);
+      if (result != MOJO_RESULT_OK) {
+        rv = result;
+        break;
+      }
+
+      uint32_t read_size = static_cast<uint32_t>(buffer.size());
+      CHECK_EQ(MojoReadMessage(mp, &buffer[0],
+                               &read_size, nullptr,
+                               0, MOJO_READ_MESSAGE_FLAG_NONE),
+               MOJO_RESULT_OK);
+
+      // Empty message indicates quit.
+      if (read_size == 0)
+        break;
+
+      CHECK_EQ(MojoWriteMessage(mp, &buffer[0],
+                                read_size,
+                                nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE),
+               MOJO_RESULT_OK);
+    }
+
+    return rv;
+  }
+
+ private:
+  int message_count_;
+  size_t message_size_;
+  std::string payload_;
+  std::string read_buffer_;
+  std::unique_ptr<base::PerfTimeLogger> perf_logger_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePipePerfTest);
+};
+
+TEST_F(MessagePipePerfTest, PingPong) {
+  MojoHandle server_handle, client_handle;
+  CreateMessagePipe(&server_handle, &client_handle);
+
+  base::Thread client_thread("PingPongClient");
+  client_thread.Start();
+  client_thread.task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(base::IgnoreResult(&RunPingPongClient), client_handle));
+
+  RunPingPongServer(server_handle);
+}
+
+// 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.
+DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MessagePipePerfTest, h) {
+  return RunPingPongClient(h);
+}
+
+// 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(MessagePipePerfTest, MultiprocessPingPong) {
+  RUN_CHILD_ON_PIPE(PingPongClient, h)
+    RunPingPongServer(h);
+  END_CHILD()
+}
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc
new file mode 100644
index 0000000..fcfaeca
--- /dev/null
+++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -0,0 +1,711 @@
+// 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 <string.h>
+
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE |
+                                      MOJO_HANDLE_SIGNAL_WRITABLE |
+                                      MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+static const char kHelloWorld[] = "hello world";
+
+class MessagePipeTest : public test::MojoTestBase {
+ public:
+  MessagePipeTest() {
+    CHECK_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &pipe0_, &pipe1_));
+  }
+
+  ~MessagePipeTest() override {
+    if (pipe0_ != MOJO_HANDLE_INVALID)
+      CHECK_EQ(MOJO_RESULT_OK, MojoClose(pipe0_));
+    if (pipe1_ != MOJO_HANDLE_INVALID)
+      CHECK_EQ(MOJO_RESULT_OK, MojoClose(pipe1_));
+  }
+
+  MojoResult WriteMessage(MojoHandle message_pipe_handle,
+                          const void* bytes,
+                          uint32_t num_bytes) {
+    return MojoWriteMessage(message_pipe_handle, bytes, num_bytes, nullptr, 0,
+                            MOJO_WRITE_MESSAGE_FLAG_NONE);
+  }
+
+  MojoResult ReadMessage(MojoHandle message_pipe_handle,
+                         void* bytes,
+                         uint32_t* num_bytes,
+                         bool may_discard = false) {
+    return MojoReadMessage(message_pipe_handle, bytes, num_bytes, nullptr, 0,
+                           may_discard ? MOJO_READ_MESSAGE_FLAG_MAY_DISCARD :
+                                         MOJO_READ_MESSAGE_FLAG_NONE);
+  }
+
+  MojoHandle pipe0_, pipe1_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MessagePipeTest);
+};
+
+using FuseMessagePipeTest = test::MojoTestBase;
+
+TEST_F(MessagePipeTest, WriteData) {
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WriteMessage(pipe0_, kHelloWorld, sizeof(kHelloWorld)));
+}
+
+// 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_F(MessagePipeTest, Basic) {
+  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;
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe0_, buffer, &buffer_size));
+  ASSERT_EQ(kBufferSize, buffer_size);
+  ASSERT_EQ(123, buffer[0]);
+  ASSERT_EQ(456, buffer[1]);
+
+  // Ditto for port 1.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe1_, buffer, &buffer_size));
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 789012345;
+  buffer[1] = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
+
+  MojoHandleSignalsState state;
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // Read from port 0.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe0_, buffer, &buffer_size));
+  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  ASSERT_EQ(789012345, buffer[0]);
+  ASSERT_EQ(456, buffer[1]);
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe0_, buffer, &buffer_size));
+
+  // Write two messages from port 0 (to port 1).
+  buffer[0] = 123456789;
+  buffer[1] = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0])));
+  buffer[0] = 234567890;
+  buffer[1] = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0])));
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // 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;
+  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            ReadMessage(pipe1_, nullptr, &buffer_size));
+  ASSERT_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;
+  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            ReadMessage(pipe1_, buffer, &buffer_size));
+  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  ASSERT_EQ(123, buffer[0]);
+  ASSERT_EQ(456, buffer[1]);
+
+  // Read from port 1.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size));
+  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  ASSERT_EQ(123456789, buffer[0]);
+  ASSERT_EQ(456, buffer[1]);
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // Read again from port 1.
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size));
+  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  ASSERT_EQ(234567890, buffer[0]);
+  ASSERT_EQ(456, buffer[1]);
+
+  // Read again from port 1 -- it should be empty.
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe1_, buffer, &buffer_size));
+
+  // Write from port 0 (to port 1).
+  buffer[0] = 345678901;
+  buffer[1] = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0])));
+
+  // Close port 0.
+  MojoClose(pipe0_);
+  pipe0_ = MOJO_HANDLE_INVALID;
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // Try to write from port 1 (to port 0).
+  buffer[0] = 456789012;
+  buffer[1] = 0;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
+
+  // Read from port 1; should still get message (even though port 0 was closed).
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size));
+  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  ASSERT_EQ(345678901, buffer[0]);
+  ASSERT_EQ(456, buffer[1]);
+
+  // Read again from port 1 -- it should be empty (and port 0 is closed).
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            ReadMessage(pipe1_, buffer, &buffer_size));
+}
+
+TEST_F(MessagePipeTest, CloseWithQueuedIncomingMessages) {
+  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;
+    ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, kBufferSize));
+  }
+
+  MojoHandleSignalsState state;
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // Port 0 shouldn't be empty.
+  buffer_size = 0;
+  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            ReadMessage(pipe0_, nullptr, &buffer_size));
+  ASSERT_EQ(kBufferSize, buffer_size);
+
+  // Close port 0 first, which should have outstanding (incoming) messages.
+  MojoClose(pipe0_);
+  MojoClose(pipe1_);
+  pipe0_ = pipe1_ = MOJO_HANDLE_INVALID;
+}
+
+TEST_F(MessagePipeTest, DiscardMode) {
+  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;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
+
+  MojoHandleSignalsState state;
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // Read/discard from port 0 (no buffer); get size.
+  buffer_size = 0;
+  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            ReadMessage(pipe0_, nullptr, &buffer_size, true));
+  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            ReadMessage(pipe0_, buffer, &buffer_size, true));
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 890123456;
+  buffer[1] = 0;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // Read from port 0 (buffer big enough).
+  buffer[0] = 123;
+  buffer[1] = 456;
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe0_, buffer, &buffer_size, true));
+  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+  ASSERT_EQ(890123456, buffer[0]);
+  ASSERT_EQ(456, buffer[1]);
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            ReadMessage(pipe0_, buffer, &buffer_size, true));
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 901234567;
+  buffer[1] = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // Read/discard from port 0 (buffer too small); get size.
+  buffer_size = 1;
+  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            ReadMessage(pipe0_, buffer, &buffer_size, true));
+  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            ReadMessage(pipe0_, buffer, &buffer_size, true));
+
+  // Write from port 1 (to port 0).
+  buffer[0] = 123456789;
+  buffer[1] = 0;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
+
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // Discard from port 0.
+  buffer_size = 1;
+  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            ReadMessage(pipe0_, nullptr, 0, true));
+
+  // Read again from port 0 -- it should be empty.
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            ReadMessage(pipe0_, buffer, &buffer_size, true));
+}
+
+TEST_F(MessagePipeTest, BasicWaiting) {
+  MojoHandleSignalsState 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).
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_WRITABLE, 0,
+                                     &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  hss = MojoHandleSignalsState();
+
+  // Not yet readable.
+  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+
+  // The peer is not closed.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 0, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+
+  // Write from port 0 (to port 1), to make port 1 readable.
+  buffer[0] = 123456789;
+  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, kBufferSize));
+
+  // Port 1 should already be readable now.
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+  // ... and still writable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            hss.satisfied_signals);
+  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
+
+  // Close port 0.
+  MojoClose(pipe0_);
+  pipe0_ = MOJO_HANDLE_INVALID;
+
+  // Port 1 should be signaled with peer closed.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Port 1 should not be writable.
+  hss = MojoHandleSignalsState();
+
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // But it should still be readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            hss.satisfiable_signals);
+
+  // Read from port 1.
+  buffer[0] = 0;
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size));
+  ASSERT_EQ(123456789, buffer[0]);
+
+  // Now port 1 should no longer be readable.
+  hss = MojoHandleSignalsState();
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &hss));
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+}
+
+TEST_F(MessagePipeTest, InvalidMessageObjects) {
+  // null message
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoFreeMessage(MOJO_MESSAGE_HANDLE_INVALID));
+
+  // null message
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoGetMessageBuffer(MOJO_MESSAGE_HANDLE_INVALID, nullptr));
+
+  // Non-zero num_handles with null handles array.
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoAllocMessage(0, nullptr, 1, MOJO_ALLOC_MESSAGE_FLAG_NONE,
+                             nullptr));
+}
+
+TEST_F(MessagePipeTest, AllocAndFreeMessage) {
+  const std::string kMessage = "Hello, world.";
+  MojoMessageHandle message = MOJO_MESSAGE_HANDLE_INVALID;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoAllocMessage(static_cast<uint32_t>(kMessage.size()), nullptr, 0,
+                             MOJO_ALLOC_MESSAGE_FLAG_NONE, &message));
+  ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
+  ASSERT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
+}
+
+TEST_F(MessagePipeTest, WriteAndReadMessageObject) {
+  const std::string kMessage = "Hello, world.";
+  MojoMessageHandle message = MOJO_MESSAGE_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoAllocMessage(static_cast<uint32_t>(kMessage.size()), nullptr, 0,
+                             MOJO_ALLOC_MESSAGE_FLAG_NONE, &message));
+  ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
+
+  void* buffer = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageBuffer(message, &buffer));
+  ASSERT_TRUE(buffer);
+  memcpy(buffer, kMessage.data(), kMessage.size());
+
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWait(b, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
+                     nullptr));
+  uint32_t num_bytes = 0;
+  uint32_t num_handles = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoReadMessageNew(b, &message, &num_bytes, nullptr, &num_handles,
+                               MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
+  EXPECT_EQ(static_cast<uint32_t>(kMessage.size()), num_bytes);
+  EXPECT_EQ(0u, num_handles);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageBuffer(message, &buffer));
+  ASSERT_TRUE(buffer);
+
+  EXPECT_EQ(0, strncmp(static_cast<const char*>(buffer), kMessage.data(),
+                       num_bytes));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+}
+
+#if !defined(OS_IOS)
+
+const size_t kPingPongHandlesPerIteration = 50;
+const size_t kPingPongIterations = 500;
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(HandlePingPong, MessagePipeTest, h) {
+  // Waits for a handle to become readable and writes it back to the sender.
+  for (size_t i = 0; i < kPingPongIterations; i++) {
+    MojoHandle handles[kPingPongHandlesPerIteration];
+    ReadMessageWithHandles(h, handles, kPingPongHandlesPerIteration);
+    WriteMessageWithHandles(h, "", handles, kPingPongHandlesPerIteration);
+  }
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  char msg[4];
+  uint32_t num_bytes = 4;
+  EXPECT_EQ(MOJO_RESULT_OK, ReadMessage(h, msg, &num_bytes));
+}
+
+// This test is flaky: http://crbug.com/585784
+TEST_F(MessagePipeTest, DISABLED_DataPipeConsumerHandlePingPong) {
+  MojoHandle p, c[kPingPongHandlesPerIteration];
+  for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) {
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p, &c[i]));
+    MojoClose(p);
+  }
+
+  RUN_CHILD_ON_PIPE(HandlePingPong, h)
+    for (size_t i = 0; i < kPingPongIterations; i++) {
+      WriteMessageWithHandles(h, "", c, kPingPongHandlesPerIteration);
+      ReadMessageWithHandles(h, c, kPingPongHandlesPerIteration);
+    }
+    WriteMessage(h, "quit", 4);
+  END_CHILD()
+  for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i)
+    MojoClose(c[i]);
+}
+
+// This test is flaky: http://crbug.com/585784
+TEST_F(MessagePipeTest, DISABLED_DataPipeProducerHandlePingPong) {
+  MojoHandle p[kPingPongHandlesPerIteration], c;
+  for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) {
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p[i], &c));
+    MojoClose(c);
+  }
+
+  RUN_CHILD_ON_PIPE(HandlePingPong, h)
+    for (size_t i = 0; i < kPingPongIterations; i++) {
+      WriteMessageWithHandles(h, "", p, kPingPongHandlesPerIteration);
+      ReadMessageWithHandles(h, p, kPingPongHandlesPerIteration);
+    }
+    WriteMessage(h, "quit", 4);
+  END_CHILD()
+  for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i)
+    MojoClose(p[i]);
+}
+
+TEST_F(MessagePipeTest, SharedBufferHandlePingPong) {
+  MojoHandle buffers[kPingPongHandlesPerIteration];
+  for (size_t i = 0; i <kPingPongHandlesPerIteration; ++i)
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(nullptr, 1, &buffers[i]));
+
+  RUN_CHILD_ON_PIPE(HandlePingPong, h)
+    for (size_t i = 0; i < kPingPongIterations; i++) {
+      WriteMessageWithHandles(h, "", buffers, kPingPongHandlesPerIteration);
+      ReadMessageWithHandles(h, buffers, kPingPongHandlesPerIteration);
+    }
+    WriteMessage(h, "quit", 4);
+  END_CHILD()
+  for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i)
+    MojoClose(buffers[i]);
+}
+
+#endif  // !defined(OS_IOS)
+
+TEST_F(FuseMessagePipeTest, Basic) {
+  // Test that we can fuse pipes and they still work.
+
+  MojoHandle a, b, c, d;
+  CreateMessagePipe(&a, &b);
+  CreateMessagePipe(&c, &d);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c));
+
+  // Handles b and c should be closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c));
+
+  const std::string kTestMessage1 = "Hello, world!";
+  const std::string kTestMessage2 = "Goodbye, world!";
+
+  WriteMessage(a, kTestMessage1);
+  EXPECT_EQ(kTestMessage1, ReadMessage(d));
+
+  WriteMessage(d, kTestMessage2);
+  EXPECT_EQ(kTestMessage2, ReadMessage(a));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+TEST_F(FuseMessagePipeTest, FuseAfterPeerWrite) {
+  // Test that messages written before fusion are eventually delivered.
+
+  MojoHandle a, b, c, d;
+  CreateMessagePipe(&a, &b);
+  CreateMessagePipe(&c, &d);
+
+  const std::string kTestMessage1 = "Hello, world!";
+  const std::string kTestMessage2 = "Goodbye, world!";
+  WriteMessage(a, kTestMessage1);
+  WriteMessage(d, kTestMessage2);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c));
+
+  // Handles b and c should be closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c));
+
+  EXPECT_EQ(kTestMessage1, ReadMessage(d));
+  EXPECT_EQ(kTestMessage2, ReadMessage(a));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+TEST_F(FuseMessagePipeTest, NoFuseAfterWrite) {
+  // Test that a pipe endpoint which has been written to cannot be fused.
+
+  MojoHandle a, b, c, d;
+  CreateMessagePipe(&a, &b);
+  CreateMessagePipe(&c, &d);
+
+  WriteMessage(b, "shouldn't have done that!");
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoFuseMessagePipes(b, c));
+
+  // Handles b and c should be closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+TEST_F(FuseMessagePipeTest, NoFuseSelf) {
+  // Test that a pipe's own endpoints can't be fused together.
+
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoFuseMessagePipes(a, b));
+
+  // Handles a and b should be closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b));
+}
+
+TEST_F(FuseMessagePipeTest, FuseInvalidArguments) {
+  MojoHandle a, b, c, d;
+  CreateMessagePipe(&a, &b);
+  CreateMessagePipe(&c, &d);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+
+  // Can't fuse an invalid handle.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoFuseMessagePipes(b, c));
+
+  // Handle c should be closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c));
+
+  // Can't fuse a non-message pipe handle.
+  MojoHandle e, f;
+  CreateDataPipe(&e, &f, 16);
+
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoFuseMessagePipes(e, d));
+
+  // Handles d and e should be closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(d));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(e));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(f));
+}
+
+TEST_F(FuseMessagePipeTest, FuseAfterPeerClosure) {
+  // Test that peer closure prior to fusion can still be detected after fusion.
+
+  MojoHandle a, b, c, d;
+  CreateMessagePipe(&a, &b);
+  CreateMessagePipe(&c, &d);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c));
+
+  // Handles b and c should be closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+TEST_F(FuseMessagePipeTest, FuseAfterPeerWriteAndClosure) {
+  // Test that peer write and closure prior to fusion still results in the
+  // both message arrival and awareness of peer closure.
+
+  MojoHandle a, b, c, d;
+  CreateMessagePipe(&a, &b);
+  CreateMessagePipe(&c, &d);
+
+  const std::string kTestMessage = "ayyy lmao";
+  WriteMessage(a, kTestMessage);
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c));
+
+  // Handles b and c should be closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b));
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c));
+
+  EXPECT_EQ(kTestMessage, ReadMessage(d));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
+}
+
+}  // namespace
+}  // namespace edk
+}  // 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..6e2c6f1
--- /dev/null
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -0,0 +1,1351 @@
+// 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 <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/containers/hash_tables.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/logging.h"
+#include "base/strings/string_split.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/edk/test/test_utils.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace mojo {
+namespace edk {
+namespace {
+
+class MultiprocessMessagePipeTest : public test::MojoTestBase {
+ protected:
+  // Convenience class for tests which will control command-driven children.
+  // See the CommandDrivenClient definition below.
+  class CommandDrivenClientController {
+   public:
+    explicit CommandDrivenClientController(MojoHandle h) : h_(h) {}
+
+    void Send(const std::string& command) {
+      WriteMessage(h_, command);
+      EXPECT_EQ("ok", ReadMessage(h_));
+    }
+
+    void SendHandle(const std::string& name, MojoHandle p) {
+      WriteMessageWithHandles(h_, "take:" + name, &p, 1);
+      EXPECT_EQ("ok", ReadMessage(h_));
+    }
+
+    MojoHandle RetrieveHandle(const std::string& name) {
+      WriteMessage(h_, "return:" + name);
+      MojoHandle p;
+      EXPECT_EQ("ok", ReadMessageWithHandles(h_, &p, 1));
+      return p;
+    }
+
+    void Exit() { WriteMessage(h_, "exit"); }
+
+   private:
+    MojoHandle h_;
+  };
+};
+
+// 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.
+DEFINE_TEST_CLIENT_WITH_PIPE(EchoEcho, MultiprocessMessagePipeTest, h) {
+  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 =
+        MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                 MOJO_DEADLINE_INDEFINITE, &hss);
+    if (result != MOJO_RESULT_OK) {
+      // It was closed, probably.
+      CHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION);
+      CHECK_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+      CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+      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(MojoReadMessage(h, &read_buffer[0],
+                             &read_buffer_size, nullptr,
+                             0, 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(MojoWriteMessage(h, write_buffer.data(),
+                              static_cast<uint32_t>(write_buffer.size()),
+                              nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+  }
+
+  return rv;
+}
+
+TEST_F(MultiprocessMessagePipeTest, Basic) {
+  RUN_CHILD_ON_PIPE(EchoEcho, h)
+    std::string hello("hello");
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, hello.data(),
+                               static_cast<uint32_t>(hello.size()), nullptr, 0u,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    HandleSignalsState hss;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &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(MojoReadMessage(h, &read_buffer[0],
+                             &read_buffer_size, nullptr, 0,
+                             MOJO_READ_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    read_buffer.resize(read_buffer_size);
+    VLOG(2) << "Parent got: " << read_buffer;
+    ASSERT_EQ(hello + hello, read_buffer);
+
+    std::string quitquitquit("quitquitquit");
+    CHECK_EQ(MojoWriteMessage(h, quitquitquit.data(),
+                              static_cast<uint32_t>(quitquitquit.size()),
+                              nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+  END_CHILD_AND_EXPECT_EXIT_CODE(1 % 100);
+}
+
+TEST_F(MultiprocessMessagePipeTest, QueueMessages) {
+  static const size_t kNumMessages = 1001;
+  RUN_CHILD_ON_PIPE(EchoEcho, h)
+    for (size_t i = 0; i < kNumMessages; i++) {
+      std::string write_buffer(i, 'A' + (i % 26));
+      ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, write_buffer.data(),
+                               static_cast<uint32_t>(write_buffer.size()),
+                               nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE));
+    }
+
+    for (size_t i = 0; i < kNumMessages; i++) {
+      HandleSignalsState hss;
+      ASSERT_EQ(MOJO_RESULT_OK,
+                MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                         MOJO_DEADLINE_INDEFINITE, &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.
+      ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
+      ASSERT_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());
+      ASSERT_EQ(MojoReadMessage(h, &read_buffer[0],
+                                &read_buffer_size, nullptr, 0,
+                                MOJO_READ_MESSAGE_FLAG_NONE),
+               MOJO_RESULT_OK);
+      read_buffer.resize(read_buffer_size);
+
+      ASSERT_EQ(std::string(i * 2, 'A' + (i % 26)), read_buffer);
+    }
+
+    const std::string quitquitquit("quitquitquit");
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, quitquitquit.data(),
+                               static_cast<uint32_t>(quitquitquit.size()),
+                               nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for it to become readable, which should fail (since we sent
+    // "quitquitquit").
+    HandleSignalsState hss;
+    ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &hss));
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+  END_CHILD_AND_EXPECT_EXIT_CODE(static_cast<int>(kNumMessages % 100));
+}
+
+DEFINE_TEST_CLIENT_WITH_PIPE(CheckSharedBuffer, MultiprocessMessagePipeTest,
+                             h) {
+  // Wait for the first message from our parent.
+  HandleSignalsState hss;
+  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+               MOJO_DEADLINE_INDEFINITE, &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 |
+                                    MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+
+  // It should have a shared buffer.
+  std::string read_buffer(100, '\0');
+  uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
+  MojoHandle handles[10];
+  uint32_t num_handlers = arraysize(handles);  // Maximum number to receive
+  CHECK_EQ(MojoReadMessage(h, &read_buffer[0],
+                           &num_bytes, &handles[0],
+                           &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  read_buffer.resize(num_bytes);
+  CHECK_EQ(read_buffer, std::string("go 1"));
+  CHECK_EQ(num_handlers, 1u);
+
+  // Make a mapping.
+  void* buffer;
+  CHECK_EQ(MojoMapBuffer(handles[0], 0, 100, &buffer,
+                         MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE),
+           MOJO_RESULT_OK);
+
+  // Write some stuff to the shared buffer.
+  static const char kHello[] = "hello";
+  memcpy(buffer, kHello, sizeof(kHello));
+
+  // We should be able to close the dispatcher now.
+  MojoClose(handles[0]);
+
+  // And send a message to signal that we've written stuff.
+  const std::string go2("go 2");
+  CHECK_EQ(MojoWriteMessage(h, go2.data(),
+                            static_cast<uint32_t>(go2.size()), nullptr, 0u,
+                            MOJO_WRITE_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+
+  // Now wait for our parent to send us a message.
+  hss = HandleSignalsState();
+  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                    MOJO_DEADLINE_INDEFINITE, &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 |
+                                        MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+
+  read_buffer = std::string(100, '\0');
+  num_bytes = static_cast<uint32_t>(read_buffer.size());
+  CHECK_EQ(MojoReadMessage(h, &read_buffer[0], &num_bytes,
+                           nullptr, 0, 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(buffer, kWorld, sizeof(kWorld)), 0);
+
+  // And we're done.
+
+  return 0;
+}
+
+TEST_F(MultiprocessMessagePipeTest, SharedBufferPassing) {
+  RUN_CHILD_ON_PIPE(CheckSharedBuffer, h)
+    // Make a shared buffer.
+    MojoCreateSharedBufferOptions options;
+    options.struct_size = sizeof(options);
+    options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
+
+    MojoHandle shared_buffer;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoCreateSharedBuffer(&options, 100, &shared_buffer));
+
+    // Send the shared buffer.
+    const std::string go1("go 1");
+
+    MojoHandle duplicated_shared_buffer;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoDuplicateBufferHandle(
+                  shared_buffer,
+                  nullptr,
+                  &duplicated_shared_buffer));
+    MojoHandle handles[1];
+    handles[0] = duplicated_shared_buffer;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, &go1[0],
+                               static_cast<uint32_t>(go1.size()), &handles[0],
+                               arraysize(handles),
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for a message from the child.
+    HandleSignalsState hss;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &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());
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoReadMessage(h, &read_buffer[0],
+                              &num_bytes, nullptr, 0,
+                              MOJO_READ_MESSAGE_FLAG_NONE));
+    read_buffer.resize(num_bytes);
+    ASSERT_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";
+    void* buffer;
+    CHECK_EQ(MojoMapBuffer(shared_buffer, 0, 100, &buffer,
+                           MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE),
+             MOJO_RESULT_OK);
+    ASSERT_EQ(0, memcmp(buffer, kHello, sizeof(kHello)));
+
+    // Now we'll write some stuff to the shared buffer.
+    static const char kWorld[] = "world!!!";
+    memcpy(buffer, kWorld, sizeof(kWorld));
+
+    // And send a message to signal that we've written stuff.
+    const std::string go3("go 3");
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, &go3[0],
+                               static_cast<uint32_t>(go3.size()), nullptr, 0u,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for |h| to become readable, which should fail.
+    hss = HandleSignalsState();
+    ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &hss));
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_WITH_PIPE(CheckPlatformHandleFile,
+                             MultiprocessMessagePipeTest, h) {
+  HandleSignalsState hss;
+  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                    MOJO_DEADLINE_INDEFINITE, &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 |
+                                        MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+
+  std::string read_buffer(100, '\0');
+  uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
+  MojoHandle handles[255];  // Maximum number to receive.
+  uint32_t num_handlers = arraysize(handles);
+
+  CHECK_EQ(MojoReadMessage(h, &read_buffer[0],
+                           &num_bytes, &handles[0],
+                           &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+
+  read_buffer.resize(num_bytes);
+  char hello[32];
+  int num_handles = 0;
+  sscanf(read_buffer.c_str(), "%s %d", hello, &num_handles);
+  CHECK_EQ(std::string("hello"), std::string(hello));
+  CHECK_GT(num_handles, 0);
+
+  for (int i = 0; i < num_handles; ++i) {
+    ScopedPlatformHandle h;
+    CHECK_EQ(PassWrappedPlatformHandle(handles[i], &h), MOJO_RESULT_OK);
+    CHECK(h.is_valid());
+    MojoClose(handles[i]);
+
+    base::ScopedFILE fp(test::FILEFromPlatformHandle(std::move(h), "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;
+}
+
+class MultiprocessMessagePipeTestWithPipeCount
+    : public MultiprocessMessagePipeTest,
+      public testing::WithParamInterface<size_t> {};
+
+TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  RUN_CHILD_ON_PIPE(CheckPlatformHandleFile, h)
+    std::vector<MojoHandle> handles;
+
+    size_t pipe_count = GetParam();
+    for (size_t i = 0; i < pipe_count; ++i) {
+      base::FilePath unused;
+      base::ScopedFILE fp(
+          CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
+      const std::string world("world");
+      CHECK_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size());
+      fflush(fp.get());
+      rewind(fp.get());
+      MojoHandle handle;
+      ASSERT_EQ(
+          CreatePlatformHandleWrapper(
+              ScopedPlatformHandle(test::PlatformHandleFromFILE(std::move(fp))),
+              &handle),
+          MOJO_RESULT_OK);
+      handles.push_back(handle);
+    }
+
+    char message[128];
+    sprintf(message, "hello %d", static_cast<int>(pipe_count));
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, message,
+                               static_cast<uint32_t>(strlen(message)),
+                               &handles[0],
+                               static_cast<uint32_t>(handles.size()),
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for it to become readable, which should fail.
+    HandleSignalsState hss;
+    ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+              MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &hss));
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
+  END_CHILD()
+}
+
+// Android multi-process tests are not executing the new process. This is flaky.
+#if !defined(OS_ANDROID)
+INSTANTIATE_TEST_CASE_P(PipeCount,
+                        MultiprocessMessagePipeTestWithPipeCount,
+                        // TODO: Re-enable the 140-pipe case when ChannelPosix
+                        // has support for sending lots of handles.
+                        testing::Values(1u, 128u/*, 140u*/));
+#endif
+
+DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) {
+  // Wait for the first message from our parent.
+  HandleSignalsState hss;
+  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                    MOJO_DEADLINE_INDEFINITE, &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 |
+                                    MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+
+  // It should have a message pipe.
+  MojoHandle handles[10];
+  uint32_t num_handlers = arraysize(handles);
+  CHECK_EQ(MojoReadMessage(h, nullptr,
+                           nullptr, &handles[0],
+                           &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  CHECK_EQ(num_handlers, 1u);
+
+  // Read data from the received message pipe.
+  CHECK_EQ(MojoWait(handles[0], MOJO_HANDLE_SIGNAL_READABLE,
+               MOJO_DEADLINE_INDEFINITE, &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 |
+                                    MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+
+  std::string read_buffer(100, '\0');
+  uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+  CHECK_EQ(MojoReadMessage(handles[0], &read_buffer[0],
+                           &read_buffer_size, nullptr,
+                           0, MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  read_buffer.resize(read_buffer_size);
+  CHECK_EQ(read_buffer, std::string("hello"));
+
+  // Now write some data into the message pipe.
+  std::string write_buffer = "world";
+  CHECK_EQ(MojoWriteMessage(handles[0], write_buffer.data(),
+                            static_cast<uint32_t>(write_buffer.size()), nullptr,
+                            0u, MOJO_WRITE_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  MojoClose(handles[0]);
+  return 0;
+}
+
+TEST_F(MultiprocessMessagePipeTest, MessagePipePassing) {
+  RUN_CHILD_ON_PIPE(CheckMessagePipe, h)
+    MojoCreateSharedBufferOptions options;
+    options.struct_size = sizeof(options);
+    options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
+
+    MojoHandle mp1, mp2;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoCreateMessagePipe(nullptr, &mp1, &mp2));
+
+    // Write a string into one end of the new message pipe and send the other
+    // end.
+    const std::string hello("hello");
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(mp1, &hello[0],
+                               static_cast<uint32_t>(hello.size()), nullptr, 0,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, nullptr, 0, &mp2, 1,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for a message from the child.
+    HandleSignalsState hss;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &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 read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+    CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0],
+                             &read_buffer_size, nullptr,
+                             0, MOJO_READ_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    read_buffer.resize(read_buffer_size);
+    CHECK_EQ(read_buffer, std::string("world"));
+
+    MojoClose(mp1);
+  END_CHILD()
+}
+
+TEST_F(MultiprocessMessagePipeTest, MessagePipeTwoPassing) {
+  RUN_CHILD_ON_PIPE(CheckMessagePipe, h)
+    MojoHandle mp1, mp2;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoCreateMessagePipe(nullptr, &mp2, &mp1));
+
+    // Write a string into one end of the new message pipe and send the other
+    // end.
+    const std::string hello("hello");
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(mp1, &hello[0],
+                               static_cast<uint32_t>(hello.size()), nullptr, 0u,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, nullptr, 0u, &mp2, 1u,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for a message from the child.
+    HandleSignalsState hss;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &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 read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+    CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0],
+                             &read_buffer_size, nullptr,
+                             0, MOJO_READ_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    read_buffer.resize(read_buffer_size);
+    CHECK_EQ(read_buffer, std::string("world"));
+  END_CHILD();
+}
+
+DEFINE_TEST_CLIENT_WITH_PIPE(DataPipeConsumer, MultiprocessMessagePipeTest, h) {
+  // Wait for the first message from our parent.
+  HandleSignalsState hss;
+  CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE,
+                    MOJO_DEADLINE_INDEFINITE, &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 |
+                                    MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+
+  // It should have a message pipe.
+  MojoHandle handles[10];
+  uint32_t num_handlers = arraysize(handles);
+  CHECK_EQ(MojoReadMessage(h, nullptr,
+                           nullptr, &handles[0],
+                           &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  CHECK_EQ(num_handlers, 1u);
+
+  // Read data from the received message pipe.
+  CHECK_EQ(MojoWait(handles[0], MOJO_HANDLE_SIGNAL_READABLE,
+               MOJO_DEADLINE_INDEFINITE, &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 |
+                                    MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+
+  std::string read_buffer(100, '\0');
+  uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+  CHECK_EQ(MojoReadMessage(handles[0], &read_buffer[0],
+                           &read_buffer_size, nullptr,
+                           0, MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  read_buffer.resize(read_buffer_size);
+  CHECK_EQ(read_buffer, std::string("hello"));
+
+  // Now write some data into the message pipe.
+  std::string write_buffer = "world";
+  CHECK_EQ(MojoWriteMessage(handles[0], write_buffer.data(),
+                            static_cast<uint32_t>(write_buffer.size()),
+                            nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE),
+            MOJO_RESULT_OK);
+  MojoClose(handles[0]);
+  return 0;
+}
+
+TEST_F(MultiprocessMessagePipeTest, DataPipeConsumer) {
+  RUN_CHILD_ON_PIPE(DataPipeConsumer, h)
+    MojoCreateSharedBufferOptions options;
+    options.struct_size = sizeof(options);
+    options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
+
+    MojoHandle mp1, mp2;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoCreateMessagePipe(nullptr, &mp2, &mp1));
+
+    // Write a string into one end of the new message pipe and send the other
+    // end.
+    const std::string hello("hello");
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(mp1, &hello[0],
+                               static_cast<uint32_t>(hello.size()), nullptr, 0u,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWriteMessage(h, nullptr, 0, &mp2, 1u,
+                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Wait for a message from the child.
+    HandleSignalsState hss;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &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 read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+    CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0],
+                             &read_buffer_size, nullptr,
+                             0, MOJO_READ_MESSAGE_FLAG_NONE),
+             MOJO_RESULT_OK);
+    read_buffer.resize(read_buffer_size);
+    CHECK_EQ(read_buffer, std::string("world"));
+
+    MojoClose(mp1);
+  END_CHILD();
+}
+
+TEST_F(MultiprocessMessagePipeTest, CreateMessagePipe) {
+  MojoHandle p0, p1;
+  CreateMessagePipe(&p0, &p1);
+  VerifyTransmission(p0, p1, "hey man");
+  VerifyTransmission(p1, p0, "slow down");
+  VerifyTransmission(p0, p1, std::string(10 * 1024 * 1024, 'a'));
+  VerifyTransmission(p1, p0, std::string(10 * 1024 * 1024, 'e'));
+
+  CloseHandle(p0);
+  CloseHandle(p1);
+}
+
+TEST_F(MultiprocessMessagePipeTest, PassMessagePipeLocal) {
+  MojoHandle p0, p1;
+  CreateMessagePipe(&p0, &p1);
+  VerifyTransmission(p0, p1, "testing testing");
+  VerifyTransmission(p1, p0, "one two three");
+
+  MojoHandle p2, p3;
+
+  CreateMessagePipe(&p2, &p3);
+  VerifyTransmission(p2, p3, "testing testing");
+  VerifyTransmission(p3, p2, "one two three");
+
+  // Pass p2 over p0 to p1.
+  const std::string message = "ceci n'est pas une pipe";
+  WriteMessageWithHandles(p0, message, &p2, 1);
+  EXPECT_EQ(message, ReadMessageWithHandles(p1, &p2, 1));
+
+  CloseHandle(p0);
+  CloseHandle(p1);
+
+  // Verify that the received handle (now in p2) still works.
+  VerifyTransmission(p2, p3, "Easy come, easy go; will you let me go?");
+  VerifyTransmission(p3, p2, "Bismillah! NO! We will not let you go!");
+
+  CloseHandle(p2);
+  CloseHandle(p3);
+}
+
+// Echos the primordial channel until "exit".
+DEFINE_TEST_CLIENT_WITH_PIPE(ChannelEchoClient, MultiprocessMessagePipeTest,
+                             h) {
+  for (;;) {
+    std::string message = ReadMessage(h);
+    if (message == "exit")
+      break;
+    WriteMessage(h, message);
+  }
+  return 0;
+}
+
+TEST_F(MultiprocessMessagePipeTest, MultiprocessChannelPipe) {
+  RUN_CHILD_ON_PIPE(ChannelEchoClient, h)
+    VerifyEcho(h, "in an interstellar burst");
+    VerifyEcho(h, "i am back to save the universe");
+    VerifyEcho(h, std::string(10 * 1024 * 1024, 'o'));
+
+    WriteMessage(h, "exit");
+  END_CHILD()
+}
+
+// Receives a pipe handle from the primordial channel and echos on it until
+// "exit". Used to test simple pipe transfer across processes via channels.
+DEFINE_TEST_CLIENT_WITH_PIPE(EchoServiceClient, MultiprocessMessagePipeTest,
+                             h) {
+  MojoHandle p;
+  ReadMessageWithHandles(h, &p, 1);
+  for (;;) {
+    std::string message = ReadMessage(p);
+    if (message == "exit")
+      break;
+    WriteMessage(p, message);
+  }
+  return 0;
+}
+
+TEST_F(MultiprocessMessagePipeTest, PassMessagePipeCrossProcess) {
+  MojoHandle p0, p1;
+  CreateMessagePipe(&p0, &p1);
+  RUN_CHILD_ON_PIPE(EchoServiceClient, h)
+
+    // Pass one end of the pipe to the other process.
+    WriteMessageWithHandles(h, "here take this", &p1, 1);
+
+    VerifyEcho(p0, "and you may ask yourself");
+    VerifyEcho(p0, "where does that highway go?");
+    VerifyEcho(p0, std::string(20 * 1024 * 1024, 'i'));
+
+    WriteMessage(p0, "exit");
+  END_CHILD()
+  CloseHandle(p0);
+}
+
+// Receives a pipe handle from the primordial channel and reads new handles
+// from it. Each read handle establishes a new echo channel.
+DEFINE_TEST_CLIENT_WITH_PIPE(EchoServiceFactoryClient,
+                             MultiprocessMessagePipeTest, h) {
+  MojoHandle p;
+  ReadMessageWithHandles(h, &p, 1);
+
+  std::vector<MojoHandle> handles(2);
+  handles[0] = h;
+  handles[1] = p;
+  std::vector<MojoHandleSignals> signals(2, MOJO_HANDLE_SIGNAL_READABLE);
+  for (;;) {
+    uint32_t index;
+    CHECK_EQ(MojoWaitMany(handles.data(), signals.data(),
+                          static_cast<uint32_t>(handles.size()),
+                          MOJO_DEADLINE_INDEFINITE, &index, nullptr),
+             MOJO_RESULT_OK);
+    DCHECK_LE(index, handles.size());
+    if (index == 0) {
+      // If data is available on the first pipe, it should be an exit command.
+      EXPECT_EQ(std::string("exit"), ReadMessage(h));
+      break;
+    } else if (index == 1) {
+      // If the second pipe, it should be a new handle requesting echo service.
+      MojoHandle echo_request;
+      ReadMessageWithHandles(p, &echo_request, 1);
+      handles.push_back(echo_request);
+      signals.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+    } else {
+      // Otherwise it was one of our established echo pipes. Echo!
+      WriteMessage(handles[index], ReadMessage(handles[index]));
+    }
+  }
+
+  for (size_t i = 1; i < handles.size(); ++i)
+    CloseHandle(handles[i]);
+
+  return 0;
+}
+
+TEST_F(MultiprocessMessagePipeTest, PassMoarMessagePipesCrossProcess) {
+  MojoHandle echo_factory_proxy, echo_factory_request;
+  CreateMessagePipe(&echo_factory_proxy, &echo_factory_request);
+
+  MojoHandle echo_proxy_a, echo_request_a;
+  CreateMessagePipe(&echo_proxy_a, &echo_request_a);
+
+  MojoHandle echo_proxy_b, echo_request_b;
+  CreateMessagePipe(&echo_proxy_b, &echo_request_b);
+
+  MojoHandle echo_proxy_c, echo_request_c;
+  CreateMessagePipe(&echo_proxy_c, &echo_request_c);
+
+  RUN_CHILD_ON_PIPE(EchoServiceFactoryClient, h)
+    WriteMessageWithHandles(
+        h, "gief factory naow plz", &echo_factory_request, 1);
+
+    WriteMessageWithHandles(echo_factory_proxy, "give me an echo service plz!",
+                           &echo_request_a, 1);
+    WriteMessageWithHandles(echo_factory_proxy, "give me one too!",
+                           &echo_request_b, 1);
+
+    VerifyEcho(echo_proxy_a, "i came here for an argument");
+    VerifyEcho(echo_proxy_a, "shut your festering gob");
+    VerifyEcho(echo_proxy_a, "mumble mumble mumble");
+
+    VerifyEcho(echo_proxy_b, "wubalubadubdub");
+    VerifyEcho(echo_proxy_b, "wubalubadubdub");
+
+    WriteMessageWithHandles(echo_factory_proxy, "hook me up also thanks",
+                           &echo_request_c, 1);
+
+    VerifyEcho(echo_proxy_a, "the frobinators taste like frobinators");
+    VerifyEcho(echo_proxy_b, "beep bop boop");
+    VerifyEcho(echo_proxy_c, "zzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+    WriteMessage(h, "exit");
+  END_CHILD()
+
+  CloseHandle(echo_factory_proxy);
+  CloseHandle(echo_proxy_a);
+  CloseHandle(echo_proxy_b);
+  CloseHandle(echo_proxy_c);
+}
+
+TEST_F(MultiprocessMessagePipeTest, ChannelPipesWithMultipleChildren) {
+  RUN_CHILD_ON_PIPE(ChannelEchoClient, a)
+    RUN_CHILD_ON_PIPE(ChannelEchoClient, b)
+      VerifyEcho(a, "hello child 0");
+      VerifyEcho(b, "hello child 1");
+
+      WriteMessage(a, "exit");
+      WriteMessage(b, "exit");
+    END_CHILD()
+  END_CHILD()
+}
+
+// Reads and turns a pipe handle some number of times to create lots of
+// transient proxies.
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingPongPipeClient,
+                                  MultiprocessMessagePipeTest, h) {
+  const size_t kNumBounces = 50;
+  MojoHandle p0, p1;
+  ReadMessageWithHandles(h, &p0, 1);
+  ReadMessageWithHandles(h, &p1, 1);
+  for (size_t i = 0; i < kNumBounces; ++i) {
+    WriteMessageWithHandles(h, "", &p1, 1);
+    ReadMessageWithHandles(h, &p1, 1);
+  }
+  WriteMessageWithHandles(h, "", &p0, 1);
+  WriteMessage(p1, "bye");
+  MojoClose(p1);
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+TEST_F(MultiprocessMessagePipeTest, PingPongPipe) {
+  MojoHandle p0, p1;
+  CreateMessagePipe(&p0, &p1);
+
+  RUN_CHILD_ON_PIPE(PingPongPipeClient, h)
+    const size_t kNumBounces = 50;
+    WriteMessageWithHandles(h, "", &p0, 1);
+    WriteMessageWithHandles(h, "", &p1, 1);
+    for (size_t i = 0; i < kNumBounces; ++i) {
+      ReadMessageWithHandles(h, &p1, 1);
+      WriteMessageWithHandles(h, "", &p1, 1);
+    }
+    ReadMessageWithHandles(h, &p0, 1);
+    WriteMessage(h, "quit");
+  END_CHILD()
+
+  EXPECT_EQ("bye", ReadMessage(p0));
+
+  // We should still be able to observe peer closure from the other end.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWait(p0, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, nullptr));
+}
+
+// Parses commands from the parent pipe and does whatever it's asked to do.
+DEFINE_TEST_CLIENT_WITH_PIPE(CommandDrivenClient, MultiprocessMessagePipeTest,
+                             h) {
+  base::hash_map<std::string, MojoHandle> named_pipes;
+  for (;;) {
+    MojoHandle p;
+    auto parts = base::SplitString(ReadMessageWithOptionalHandle(h, &p), ":",
+                                   base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+    CHECK(!parts.empty());
+    std::string command = parts[0];
+    if (command == "take") {
+      // Take a pipe.
+      CHECK_EQ(parts.size(), 2u);
+      CHECK_NE(p, MOJO_HANDLE_INVALID);
+      named_pipes[parts[1]] = p;
+      WriteMessage(h, "ok");
+    } else if (command == "return") {
+      // Return a pipe.
+      CHECK_EQ(parts.size(), 2u);
+      CHECK_EQ(p, MOJO_HANDLE_INVALID);
+      p = named_pipes[parts[1]];
+      CHECK_NE(p, MOJO_HANDLE_INVALID);
+      named_pipes.erase(parts[1]);
+      WriteMessageWithHandles(h, "ok", &p, 1);
+    } else if (command == "say") {
+      // Say something to a named pipe.
+      CHECK_EQ(parts.size(), 3u);
+      CHECK_EQ(p, MOJO_HANDLE_INVALID);
+      p = named_pipes[parts[1]];
+      CHECK_NE(p, MOJO_HANDLE_INVALID);
+      CHECK(!parts[2].empty());
+      WriteMessage(p, parts[2]);
+      WriteMessage(h, "ok");
+    } else if (command == "hear") {
+      // Expect to read something from a named pipe.
+      CHECK_EQ(parts.size(), 3u);
+      CHECK_EQ(p, MOJO_HANDLE_INVALID);
+      p = named_pipes[parts[1]];
+      CHECK_NE(p, MOJO_HANDLE_INVALID);
+      CHECK(!parts[2].empty());
+      CHECK_EQ(parts[2], ReadMessage(p));
+      WriteMessage(h, "ok");
+    } else if (command == "pass") {
+      // Pass one named pipe over another named pipe.
+      CHECK_EQ(parts.size(), 3u);
+      CHECK_EQ(p, MOJO_HANDLE_INVALID);
+      p = named_pipes[parts[1]];
+      MojoHandle carrier = named_pipes[parts[2]];
+      CHECK_NE(p, MOJO_HANDLE_INVALID);
+      CHECK_NE(carrier, MOJO_HANDLE_INVALID);
+      named_pipes.erase(parts[1]);
+      WriteMessageWithHandles(carrier, "got a pipe for ya", &p, 1);
+      WriteMessage(h, "ok");
+    } else if (command == "catch") {
+      // Expect to receive one named pipe from another named pipe.
+      CHECK_EQ(parts.size(), 3u);
+      CHECK_EQ(p, MOJO_HANDLE_INVALID);
+      MojoHandle carrier = named_pipes[parts[2]];
+      CHECK_NE(carrier, MOJO_HANDLE_INVALID);
+      ReadMessageWithHandles(carrier, &p, 1);
+      CHECK_NE(p, MOJO_HANDLE_INVALID);
+      named_pipes[parts[1]] = p;
+      WriteMessage(h, "ok");
+    } else if (command == "exit") {
+      CHECK_EQ(parts.size(), 1u);
+      break;
+    }
+  }
+
+  for (auto& pipe: named_pipes)
+    CloseHandle(pipe.second);
+
+  return 0;
+}
+
+TEST_F(MultiprocessMessagePipeTest, ChildToChildPipes) {
+  RUN_CHILD_ON_PIPE(CommandDrivenClient, h0)
+    RUN_CHILD_ON_PIPE(CommandDrivenClient, h1)
+      CommandDrivenClientController a(h0);
+      CommandDrivenClientController b(h1);
+
+      // Create a pipe and pass each end to a different client.
+      MojoHandle p0, p1;
+      CreateMessagePipe(&p0, &p1);
+      a.SendHandle("x", p0);
+      b.SendHandle("y", p1);
+
+      // Make sure they can talk.
+      a.Send("say:x:hello sir");
+      b.Send("hear:y:hello sir");
+
+      b.Send("say:y:i love multiprocess pipes!");
+      a.Send("hear:x:i love multiprocess pipes!");
+
+      a.Exit();
+      b.Exit();
+    END_CHILD()
+  END_CHILD()
+}
+
+TEST_F(MultiprocessMessagePipeTest, MoreChildToChildPipes) {
+  RUN_CHILD_ON_PIPE(CommandDrivenClient, h0)
+    RUN_CHILD_ON_PIPE(CommandDrivenClient, h1)
+      RUN_CHILD_ON_PIPE(CommandDrivenClient, h2)
+        RUN_CHILD_ON_PIPE(CommandDrivenClient, h3)
+          CommandDrivenClientController a(h0), b(h1), c(h2), d(h3);
+
+          // Connect a to b and c to d
+
+          MojoHandle p0, p1;
+
+          CreateMessagePipe(&p0, &p1);
+          a.SendHandle("b_pipe", p0);
+          b.SendHandle("a_pipe", p1);
+
+          MojoHandle p2, p3;
+
+          CreateMessagePipe(&p2, &p3);
+          c.SendHandle("d_pipe", p2);
+          d.SendHandle("c_pipe", p3);
+
+          // Connect b to c via a and d
+          MojoHandle p4, p5;
+          CreateMessagePipe(&p4, &p5);
+          a.SendHandle("d_pipe", p4);
+          d.SendHandle("a_pipe", p5);
+
+          // Have |a| pass its new |d|-pipe to |b|. It will eventually connect
+          // to |c|.
+          a.Send("pass:d_pipe:b_pipe");
+          b.Send("catch:c_pipe:a_pipe");
+
+          // Have |d| pass its new |a|-pipe to |c|. It will now be connected to
+          // |b|.
+          d.Send("pass:a_pipe:c_pipe");
+          c.Send("catch:b_pipe:d_pipe");
+
+          // Make sure b and c and talk.
+          b.Send("say:c_pipe:it's a beautiful day");
+          c.Send("hear:b_pipe:it's a beautiful day");
+
+          // Create x and y and have b and c exchange them.
+          MojoHandle x, y;
+          CreateMessagePipe(&x, &y);
+          b.SendHandle("x", x);
+          c.SendHandle("y", y);
+          b.Send("pass:x:c_pipe");
+          c.Send("pass:y:b_pipe");
+          b.Send("catch:y:c_pipe");
+          c.Send("catch:x:b_pipe");
+
+          // Make sure the pipe still works in both directions.
+          b.Send("say:y:hello");
+          c.Send("hear:x:hello");
+          c.Send("say:x:goodbye");
+          b.Send("hear:y:goodbye");
+
+          // Take both pipes back.
+          y = c.RetrieveHandle("x");
+          x = b.RetrieveHandle("y");
+
+          VerifyTransmission(x, y, "still works");
+          VerifyTransmission(y, x, "in both directions");
+
+          CloseHandle(x);
+          CloseHandle(y);
+
+          a.Exit();
+          b.Exit();
+          c.Exit();
+          d.Exit();
+        END_CHILD()
+      END_CHILD()
+    END_CHILD()
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceivePipeWithClosedPeer,
+                                  MultiprocessMessagePipeTest, h) {
+  MojoHandle p;
+  EXPECT_EQ("foo", ReadMessageWithHandles(h, &p, 1));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+}
+
+TEST_F(MultiprocessMessagePipeTest, SendPipeThenClosePeer) {
+  RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeer, h)
+    MojoHandle a, b;
+    CreateMessagePipe(&a, &b);
+
+    // Send |a| and immediately close |b|. The child should observe closure.
+    WriteMessageWithHandles(h, "foo", &a, 1);
+    MojoClose(b);
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(SendOtherChildPipeWithClosedPeer,
+                                  MultiprocessMessagePipeTest, h) {
+  // Create a new pipe and send one end to the parent, who will connect it to
+  // a client running ReceivePipeWithClosedPeerFromOtherChild.
+  MojoHandle application_proxy, application_request;
+  CreateMessagePipe(&application_proxy, &application_request);
+  WriteMessageWithHandles(h, "c2a plz", &application_request, 1);
+
+  // Create another pipe and send one end to the remote "application".
+  MojoHandle service_proxy, service_request;
+  CreateMessagePipe(&service_proxy, &service_request);
+  WriteMessageWithHandles(application_proxy, "c2s lol", &service_request, 1);
+
+  // Immediately close the service proxy. The "application" should detect this.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(service_proxy));
+
+  // Wait for quit.
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceivePipeWithClosedPeerFromOtherChild,
+                                  MultiprocessMessagePipeTest, h) {
+  // Receive a pipe from the parent. This is akin to an "application request".
+  MojoHandle application_client;
+  EXPECT_EQ("c2a", ReadMessageWithHandles(h, &application_client, 1));
+
+  // Receive a pipe from the "application" "client".
+  MojoHandle service_client;
+  EXPECT_EQ("c2s lol",
+            ReadMessageWithHandles(application_client, &service_client, 1));
+
+  // Wait for the service client to signal closure.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(service_client,
+                                     MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(service_client));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(application_client));
+}
+
+#if defined(OS_ANDROID)
+// Android multi-process tests are not executing the new process. This is flaky.
+#define MAYBE_SendPipeWithClosedPeerBetweenChildren \
+    DISABLED_SendPipeWithClosedPeerBetweenChildren
+#else
+#define MAYBE_SendPipeWithClosedPeerBetweenChildren \
+    SendPipeWithClosedPeerBetweenChildren
+#endif
+TEST_F(MultiprocessMessagePipeTest,
+       MAYBE_SendPipeWithClosedPeerBetweenChildren) {
+  RUN_CHILD_ON_PIPE(SendOtherChildPipeWithClosedPeer, kid_a)
+    RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeerFromOtherChild, kid_b)
+      // Receive an "application request" from the first child and forward it
+      // to the second child.
+      MojoHandle application_request;
+      EXPECT_EQ("c2a plz",
+                ReadMessageWithHandles(kid_a, &application_request, 1));
+
+      WriteMessageWithHandles(kid_b, "c2a", &application_request, 1);
+    END_CHILD()
+
+    WriteMessage(kid_a, "quit");
+  END_CHILD()
+}
+
+
+TEST_F(MultiprocessMessagePipeTest, SendClosePeerSend) {
+  MojoHandle a, b;
+  CreateMessagePipe(&a, &b);
+
+  MojoHandle c, d;
+  CreateMessagePipe(&c, &d);
+
+  // Send |a| over |c|, immediately close |b|, then send |a| back over |d|.
+  WriteMessageWithHandles(c, "foo", &a, 1);
+  EXPECT_EQ("foo", ReadMessageWithHandles(d, &a, 1));
+  WriteMessageWithHandles(d, "bar", &a, 1);
+  EXPECT_EQ("bar", ReadMessageWithHandles(c, &a, 1));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+
+  // We should be able to detect peer closure on |a|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(a, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteCloseSendPeerClient,
+                                  MultiprocessMessagePipeTest, h) {
+  MojoHandle pipe[2];
+  EXPECT_EQ("foo", ReadMessageWithHandles(h, pipe, 2));
+
+  // Write some messages to the first endpoint and then close it.
+  WriteMessage(pipe[0], "baz");
+  WriteMessage(pipe[0], "qux");
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe[0]));
+
+  MojoHandle c, d;
+  CreateMessagePipe(&c, &d);
+
+  // Pass the orphaned endpoint over another pipe before passing it back to
+  // the parent, just for some extra proxying goodness.
+  WriteMessageWithHandles(c, "foo", &pipe[1], 1);
+  EXPECT_EQ("foo", ReadMessageWithHandles(d, &pipe[1], 1));
+
+  // And finally pass it back to the parent.
+  WriteMessageWithHandles(h, "bar", &pipe[1], 1);
+
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+TEST_F(MultiprocessMessagePipeTest, WriteCloseSendPeer) {
+  MojoHandle pipe[2];
+  CreateMessagePipe(&pipe[0], &pipe[1]);
+
+  RUN_CHILD_ON_PIPE(WriteCloseSendPeerClient, h)
+    // Pass the pipe to the child.
+    WriteMessageWithHandles(h, "foo", pipe, 2);
+
+    // Read back an endpoint which should have messages on it.
+    MojoHandle p;
+    EXPECT_EQ("bar", ReadMessageWithHandles(h, &p, 1));
+
+    EXPECT_EQ("baz", ReadMessage(p));
+    EXPECT_EQ("qux", ReadMessage(p));
+
+    // Expect to have peer closure signaled.
+    EXPECT_EQ(MOJO_RESULT_OK, MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                       MOJO_DEADLINE_INDEFINITE, nullptr));
+
+    WriteMessage(h, "quit");
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(BootstrapMessagePipeAsyncClient,
+                                  MultiprocessMessagePipeTest, parent) {
+  // Receive one end of a platform channel from the parent.
+  MojoHandle channel_handle;
+  EXPECT_EQ("hi", ReadMessageWithHandles(parent, &channel_handle, 1));
+  ScopedPlatformHandle channel;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            edk::PassWrappedPlatformHandle(channel_handle, &channel));
+  ASSERT_TRUE(channel.is_valid());
+
+  // Create a new pipe using our end of the channel.
+  ScopedMessagePipeHandle pipe = edk::CreateMessagePipe(std::move(channel));
+
+  // Ensure that we can read and write on the new pipe.
+  VerifyEcho(pipe.get().value(), "goodbye");
+}
+
+TEST_F(MultiprocessMessagePipeTest, BootstrapMessagePipeAsync) {
+  // Tests that new cross-process message pipes can be created synchronously
+  // using asynchronous negotiation over an arbitrary platform channel.
+  RUN_CHILD_ON_PIPE(BootstrapMessagePipeAsyncClient, child)
+    // Pass one end of a platform channel to the child.
+    PlatformChannelPair platform_channel;
+    MojoHandle client_channel_handle;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              CreatePlatformHandleWrapper(platform_channel.PassClientHandle(),
+                                          &client_channel_handle));
+    WriteMessageWithHandles(child, "hi", &client_channel_handle, 1);
+
+    // Create a new pipe using our end of the channel.
+    ScopedMessagePipeHandle pipe =
+        edk::CreateMessagePipe(platform_channel.PassServerHandle());
+
+    // Ensure that we can read and write on the new pipe.
+    VerifyEcho(pipe.get().value(), "goodbye");
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(BadMessageClient, MultiprocessMessagePipeTest,
+                                  parent) {
+  MojoHandle pipe;
+  EXPECT_EQ("hi", ReadMessageWithHandles(parent, &pipe, 1));
+  WriteMessage(pipe, "derp");
+  EXPECT_EQ("bye", ReadMessage(parent));
+}
+
+void OnProcessError(std::string* out_error, const std::string& error) {
+  *out_error = error;
+}
+
+TEST_F(MultiprocessMessagePipeTest, NotifyBadMessage) {
+  const std::string kFirstErrorMessage = "everything is terrible!";
+  const std::string kSecondErrorMessage = "not the bits you're looking for";
+
+  std::string first_process_error;
+  std::string second_process_error;
+
+  set_process_error_callback(base::Bind(&OnProcessError, &first_process_error));
+  RUN_CHILD_ON_PIPE(BadMessageClient, child1)
+    set_process_error_callback(base::Bind(&OnProcessError,
+                                          &second_process_error));
+    RUN_CHILD_ON_PIPE(BadMessageClient, child2)
+      MojoHandle a, b, c, d;
+      CreateMessagePipe(&a, &b);
+      CreateMessagePipe(&c, &d);
+      WriteMessageWithHandles(child1, "hi", &b, 1);
+      WriteMessageWithHandles(child2, "hi", &d, 1);
+
+      // Read a message from the pipe we sent to child1 and flag it as bad.
+      ASSERT_EQ(MOJO_RESULT_OK, MojoWait(a, MOJO_HANDLE_SIGNAL_READABLE,
+                                         MOJO_DEADLINE_INDEFINITE, nullptr));
+      uint32_t num_bytes = 0;
+      MojoMessageHandle message;
+      ASSERT_EQ(MOJO_RESULT_OK,
+                MojoReadMessageNew(a, &message, &num_bytes, nullptr, 0,
+                                   MOJO_READ_MESSAGE_FLAG_NONE));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                MojoNotifyBadMessage(message, kFirstErrorMessage.data(),
+                                     kFirstErrorMessage.size()));
+      EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
+
+      // Read a message from the pipe we sent to child2 and flag it as bad.
+      ASSERT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE,
+                                         MOJO_DEADLINE_INDEFINITE, nullptr));
+      ASSERT_EQ(MOJO_RESULT_OK,
+                MojoReadMessageNew(c, &message, &num_bytes, nullptr, 0,
+                                   MOJO_READ_MESSAGE_FLAG_NONE));
+      EXPECT_EQ(MOJO_RESULT_OK,
+                MojoNotifyBadMessage(message, kSecondErrorMessage.data(),
+                                     kSecondErrorMessage.size()));
+      EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
+
+      WriteMessage(child2, "bye");
+    END_CHILD();
+
+    WriteMessage(child1, "bye");
+  END_CHILD()
+
+  // The error messages should match the processes which triggered them.
+  EXPECT_NE(std::string::npos, first_process_error.find(kFirstErrorMessage));
+  EXPECT_NE(std::string::npos, second_process_error.find(kSecondErrorMessage));
+}
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/node_channel.cc b/mojo/edk/system/node_channel.cc
new file mode 100644
index 0000000..ce094c1
--- /dev/null
+++ b/mojo/edk/system/node_channel.cc
@@ -0,0 +1,876 @@
+// Copyright 2016 The Chromium Authors. All 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/node_channel.h"
+
+#include <cstring>
+#include <limits>
+#include <sstream>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/request_context.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "mojo/edk/system/mach_port_relay.h"
+#endif
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+template <typename T>
+T Align(T t) {
+  const auto k = kChannelMessageAlignment;
+  return t + (k - (t % k)) % k;
+}
+
+// NOTE: Please ONLY append messages to the end of this enum.
+enum class MessageType : uint32_t {
+  ACCEPT_CHILD,
+  ACCEPT_PARENT,
+  ADD_BROKER_CLIENT,
+  BROKER_CLIENT_ADDED,
+  ACCEPT_BROKER_CLIENT,
+  PORTS_MESSAGE,
+  REQUEST_PORT_MERGE,
+  REQUEST_INTRODUCTION,
+  INTRODUCE,
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+  RELAY_PORTS_MESSAGE,
+#endif
+  BROADCAST,
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+  PORTS_MESSAGE_FROM_RELAY,
+#endif
+};
+
+struct Header {
+  MessageType type;
+  uint32_t padding;
+};
+
+static_assert(sizeof(Header) % kChannelMessageAlignment == 0,
+    "Invalid header size.");
+
+struct AcceptChildData {
+  ports::NodeName parent_name;
+  ports::NodeName token;
+};
+
+struct AcceptParentData {
+  ports::NodeName token;
+  ports::NodeName child_name;
+};
+
+// This message may include a process handle on plaforms that require it.
+struct AddBrokerClientData {
+  ports::NodeName client_name;
+#if !defined(OS_WIN)
+  uint32_t process_handle;
+  uint32_t padding;
+#endif
+};
+
+#if !defined(OS_WIN)
+static_assert(sizeof(base::ProcessHandle) == sizeof(uint32_t),
+              "Unexpected pid size");
+static_assert(sizeof(AddBrokerClientData) % kChannelMessageAlignment == 0,
+              "Invalid AddBrokerClientData size.");
+#endif
+
+// This data is followed by a platform channel handle to the broker.
+struct BrokerClientAddedData {
+  ports::NodeName client_name;
+};
+
+// This data may be followed by a platform channel handle to the broker. If not,
+// then the parent is the broker and its channel should be used as such.
+struct AcceptBrokerClientData {
+  ports::NodeName broker_name;
+};
+
+// This is followed by arbitrary payload data which is interpreted as a token
+// string for port location.
+struct RequestPortMergeData {
+  ports::PortName connector_port_name;
+};
+
+// Used for both REQUEST_INTRODUCTION and INTRODUCE.
+//
+// For INTRODUCE the message also includes a valid platform handle for a channel
+// the receiver may use to communicate with the named node directly, or an
+// invalid platform handle if the node is unknown to the sender or otherwise
+// cannot be introduced.
+struct IntroductionData {
+  ports::NodeName name;
+};
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+// This struct is followed by the full payload of a message to be relayed.
+struct RelayPortsMessageData {
+  ports::NodeName destination;
+};
+
+// This struct is followed by the full payload of a relayed message.
+struct PortsMessageFromRelayData {
+  ports::NodeName source;
+};
+#endif
+
+template <typename DataType>
+Channel::MessagePtr CreateMessage(MessageType type,
+                                  size_t payload_size,
+                                  size_t num_handles,
+                                  DataType** out_data) {
+  Channel::MessagePtr message(
+      new Channel::Message(sizeof(Header) + payload_size, num_handles));
+  Header* header = reinterpret_cast<Header*>(message->mutable_payload());
+  header->type = type;
+  header->padding = 0;
+  *out_data = reinterpret_cast<DataType*>(&header[1]);
+  return message;
+}
+
+template <typename DataType>
+bool GetMessagePayload(const void* bytes,
+                       size_t num_bytes,
+                       DataType** out_data) {
+  static_assert(sizeof(DataType) > 0, "DataType must have non-zero size.");
+  if (num_bytes < sizeof(Header) + sizeof(DataType))
+    return false;
+  *out_data = reinterpret_cast<const DataType*>(
+      static_cast<const char*>(bytes) + sizeof(Header));
+  return true;
+}
+
+}  // namespace
+
+// static
+scoped_refptr<NodeChannel> NodeChannel::Create(
+    Delegate* delegate,
+    ScopedPlatformHandle platform_handle,
+    scoped_refptr<base::TaskRunner> io_task_runner,
+    const ProcessErrorCallback& process_error_callback) {
+#if defined(OS_NACL_SFI)
+  LOG(FATAL) << "Multi-process not yet supported on NaCl-SFI";
+  return nullptr;
+#else
+  return new NodeChannel(delegate, std::move(platform_handle), io_task_runner,
+                         process_error_callback);
+#endif
+}
+
+// static
+Channel::MessagePtr NodeChannel::CreatePortsMessage(size_t payload_size,
+                                                    void** payload,
+                                                    size_t num_handles) {
+  return CreateMessage(MessageType::PORTS_MESSAGE, payload_size, num_handles,
+                       payload);
+}
+
+// static
+void NodeChannel::GetPortsMessageData(Channel::Message* message,
+                                      void** data,
+                                      size_t* num_data_bytes) {
+  *data = reinterpret_cast<Header*>(message->mutable_payload()) + 1;
+  *num_data_bytes = message->payload_size() - sizeof(Header);
+}
+
+void NodeChannel::Start() {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  MachPortRelay* relay = delegate_->GetMachPortRelay();
+  if (relay)
+    relay->AddObserver(this);
+#endif
+
+  base::AutoLock lock(channel_lock_);
+  // ShutDown() may have already been called, in which case |channel_| is null.
+  if (channel_)
+    channel_->Start();
+}
+
+void NodeChannel::ShutDown() {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  MachPortRelay* relay = delegate_->GetMachPortRelay();
+  if (relay)
+    relay->RemoveObserver(this);
+#endif
+
+  base::AutoLock lock(channel_lock_);
+  if (channel_) {
+    channel_->ShutDown();
+    channel_ = nullptr;
+  }
+}
+
+void NodeChannel::LeakHandleOnShutdown() {
+  base::AutoLock lock(channel_lock_);
+  if (channel_) {
+    channel_->LeakHandle();
+  }
+}
+
+void NodeChannel::NotifyBadMessage(const std::string& error) {
+  if (!process_error_callback_.is_null())
+    process_error_callback_.Run("Received bad user message: " + error);
+}
+
+void NodeChannel::SetRemoteProcessHandle(base::ProcessHandle process_handle) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+  base::AutoLock lock(remote_process_handle_lock_);
+  DCHECK_EQ(base::kNullProcessHandle, remote_process_handle_);
+  CHECK_NE(remote_process_handle_, base::GetCurrentProcessHandle());
+  remote_process_handle_ = process_handle;
+#if defined(OS_WIN)
+  DCHECK(!scoped_remote_process_handle_.is_valid());
+  scoped_remote_process_handle_.reset(PlatformHandle(process_handle));
+#endif
+}
+
+bool NodeChannel::HasRemoteProcessHandle() {
+  base::AutoLock lock(remote_process_handle_lock_);
+  return remote_process_handle_ != base::kNullProcessHandle;
+}
+
+base::ProcessHandle NodeChannel::CopyRemoteProcessHandle() {
+  base::AutoLock lock(remote_process_handle_lock_);
+#if defined(OS_WIN)
+  if (remote_process_handle_ != base::kNullProcessHandle) {
+    // Privileged nodes use this to pass their childrens' process handles to the
+    // broker on launch.
+    HANDLE handle = remote_process_handle_;
+    BOOL result = DuplicateHandle(
+        base::GetCurrentProcessHandle(), remote_process_handle_,
+        base::GetCurrentProcessHandle(), &handle, 0, FALSE,
+        DUPLICATE_SAME_ACCESS);
+    DPCHECK(result);
+    return handle;
+  }
+  return base::kNullProcessHandle;
+#else
+  return remote_process_handle_;
+#endif
+}
+
+void NodeChannel::SetRemoteNodeName(const ports::NodeName& name) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+  remote_node_name_ = name;
+}
+
+void NodeChannel::AcceptChild(const ports::NodeName& parent_name,
+                              const ports::NodeName& token) {
+  AcceptChildData* data;
+  Channel::MessagePtr message = CreateMessage(
+      MessageType::ACCEPT_CHILD, sizeof(AcceptChildData), 0, &data);
+  data->parent_name = parent_name;
+  data->token = token;
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::AcceptParent(const ports::NodeName& token,
+                               const ports::NodeName& child_name) {
+  AcceptParentData* data;
+  Channel::MessagePtr message = CreateMessage(
+      MessageType::ACCEPT_PARENT, sizeof(AcceptParentData), 0, &data);
+  data->token = token;
+  data->child_name = child_name;
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::AddBrokerClient(const ports::NodeName& client_name,
+                                  base::ProcessHandle process_handle) {
+  AddBrokerClientData* data;
+  ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector());
+#if defined(OS_WIN)
+  handles->push_back(PlatformHandle(process_handle));
+#endif
+  Channel::MessagePtr message = CreateMessage(
+      MessageType::ADD_BROKER_CLIENT, sizeof(AddBrokerClientData),
+      handles->size(), &data);
+  message->SetHandles(std::move(handles));
+  data->client_name = client_name;
+#if !defined(OS_WIN)
+  data->process_handle = process_handle;
+  data->padding = 0;
+#endif
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::BrokerClientAdded(const ports::NodeName& client_name,
+                                    ScopedPlatformHandle broker_channel) {
+  BrokerClientAddedData* data;
+  ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector());
+  if (broker_channel.is_valid())
+    handles->push_back(broker_channel.release());
+  Channel::MessagePtr message = CreateMessage(
+      MessageType::BROKER_CLIENT_ADDED, sizeof(BrokerClientAddedData),
+      handles->size(), &data);
+  message->SetHandles(std::move(handles));
+  data->client_name = client_name;
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::AcceptBrokerClient(const ports::NodeName& broker_name,
+                                     ScopedPlatformHandle broker_channel) {
+  AcceptBrokerClientData* data;
+  ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector());
+  if (broker_channel.is_valid())
+    handles->push_back(broker_channel.release());
+  Channel::MessagePtr message = CreateMessage(
+      MessageType::ACCEPT_BROKER_CLIENT, sizeof(AcceptBrokerClientData),
+      handles->size(), &data);
+  message->SetHandles(std::move(handles));
+  data->broker_name = broker_name;
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::PortsMessage(Channel::MessagePtr message) {
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::RequestPortMerge(const ports::PortName& connector_port_name,
+                                   const std::string& token) {
+  RequestPortMergeData* data;
+  Channel::MessagePtr message = CreateMessage(
+      MessageType::REQUEST_PORT_MERGE,
+      sizeof(RequestPortMergeData) + token.size(), 0, &data);
+  data->connector_port_name = connector_port_name;
+  memcpy(data + 1, token.data(), token.size());
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::RequestIntroduction(const ports::NodeName& name) {
+  IntroductionData* data;
+  Channel::MessagePtr message = CreateMessage(
+      MessageType::REQUEST_INTRODUCTION, sizeof(IntroductionData), 0, &data);
+  data->name = name;
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::Introduce(const ports::NodeName& name,
+                            ScopedPlatformHandle channel_handle) {
+  IntroductionData* data;
+  ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector());
+  if (channel_handle.is_valid())
+    handles->push_back(channel_handle.release());
+  Channel::MessagePtr message = CreateMessage(
+      MessageType::INTRODUCE, sizeof(IntroductionData), handles->size(), &data);
+  message->SetHandles(std::move(handles));
+  data->name = name;
+  WriteChannelMessage(std::move(message));
+}
+
+void NodeChannel::Broadcast(Channel::MessagePtr message) {
+  DCHECK(!message->has_handles());
+  void* data;
+  Channel::MessagePtr broadcast_message = CreateMessage(
+      MessageType::BROADCAST, message->data_num_bytes(), 0, &data);
+  memcpy(data, message->data(), message->data_num_bytes());
+  WriteChannelMessage(std::move(broadcast_message));
+}
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+void NodeChannel::RelayPortsMessage(const ports::NodeName& destination,
+                                    Channel::MessagePtr message) {
+#if defined(OS_WIN)
+  DCHECK(message->has_handles());
+
+  // Note that this is only used on Windows, and on Windows all platform
+  // handles are included in the message data. We blindly copy all the data
+  // here and the relay node (the parent) will duplicate handles as needed.
+  size_t num_bytes = sizeof(RelayPortsMessageData) + message->data_num_bytes();
+  RelayPortsMessageData* data;
+  Channel::MessagePtr relay_message = CreateMessage(
+      MessageType::RELAY_PORTS_MESSAGE, num_bytes, 0, &data);
+  data->destination = destination;
+  memcpy(data + 1, message->data(), message->data_num_bytes());
+
+  // When the handles are duplicated in the parent, the source handles will
+  // be closed. If the parent never receives this message then these handles
+  // will leak, but that means something else has probably broken and the
+  // sending process won't likely be around much longer.
+  ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+  handles->clear();
+
+#else
+  DCHECK(message->has_mach_ports());
+
+  // On OSX, the handles are extracted from the relayed message and attached to
+  // the wrapper. The broker then takes the handles attached to the wrapper and
+  // moves them back to the relayed message. This is necessary because the
+  // message may contain fds which need to be attached to the outer message so
+  // that they can be transferred to the broker.
+  ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+  size_t num_bytes = sizeof(RelayPortsMessageData) + message->data_num_bytes();
+  RelayPortsMessageData* data;
+  Channel::MessagePtr relay_message = CreateMessage(
+      MessageType::RELAY_PORTS_MESSAGE, num_bytes, handles->size(), &data);
+  data->destination = destination;
+  memcpy(data + 1, message->data(), message->data_num_bytes());
+  relay_message->SetHandles(std::move(handles));
+#endif  // defined(OS_WIN)
+
+  WriteChannelMessage(std::move(relay_message));
+}
+
+void NodeChannel::PortsMessageFromRelay(const ports::NodeName& source,
+                                        Channel::MessagePtr message) {
+  size_t num_bytes = sizeof(PortsMessageFromRelayData) +
+      message->payload_size();
+  PortsMessageFromRelayData* data;
+  Channel::MessagePtr relayed_message = CreateMessage(
+      MessageType::PORTS_MESSAGE_FROM_RELAY, num_bytes, message->num_handles(),
+      &data);
+  data->source = source;
+  if (message->payload_size())
+    memcpy(data + 1, message->payload(), message->payload_size());
+  relayed_message->SetHandles(message->TakeHandles());
+  WriteChannelMessage(std::move(relayed_message));
+}
+#endif  // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+
+NodeChannel::NodeChannel(Delegate* delegate,
+                         ScopedPlatformHandle platform_handle,
+                         scoped_refptr<base::TaskRunner> io_task_runner,
+                         const ProcessErrorCallback& process_error_callback)
+    : delegate_(delegate),
+      io_task_runner_(io_task_runner),
+      process_error_callback_(process_error_callback)
+#if !defined(OS_NACL_SFI)
+      , channel_(
+          Channel::Create(this, std::move(platform_handle), io_task_runner_))
+#endif
+      {
+}
+
+NodeChannel::~NodeChannel() {
+  ShutDown();
+}
+
+void NodeChannel::OnChannelMessage(const void* payload,
+                                   size_t payload_size,
+                                   ScopedPlatformHandleVectorPtr handles) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  RequestContext request_context(RequestContext::Source::SYSTEM);
+
+  // Ensure this NodeChannel stays alive through the extent of this method. The
+  // delegate may have the only other reference to this object and it may choose
+  // to drop it here in response to, e.g., a malformed message.
+  scoped_refptr<NodeChannel> keepalive = this;
+
+#if defined(OS_WIN)
+  // If we receive handles from a known process, rewrite them to our own
+  // process. This can occur when a privileged node receives handles directly
+  // from a privileged descendant.
+  {
+    base::AutoLock lock(remote_process_handle_lock_);
+    if (handles && remote_process_handle_ != base::kNullProcessHandle) {
+      // Note that we explicitly mark the handles as being owned by the sending
+      // process before rewriting them, in order to accommodate RewriteHandles'
+      // internal sanity checks.
+      for (auto& handle : *handles)
+        handle.owning_process = remote_process_handle_;
+      if (!Channel::Message::RewriteHandles(remote_process_handle_,
+                                            base::GetCurrentProcessHandle(),
+                                            handles.get())) {
+        DLOG(ERROR) << "Received one or more invalid handles.";
+      }
+    } else if (handles) {
+      // Handles received by an unknown process must already be owned by us.
+      for (auto& handle : *handles)
+        handle.owning_process = base::GetCurrentProcessHandle();
+    }
+  }
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  // If we're not the root, receive any mach ports from the message. If we're
+  // the root, the only message containing mach ports should be a
+  // RELAY_PORTS_MESSAGE.
+  {
+    MachPortRelay* relay = delegate_->GetMachPortRelay();
+    if (handles && !relay) {
+      if (!MachPortRelay::ReceivePorts(handles.get())) {
+        LOG(ERROR) << "Error receiving mach ports.";
+      }
+    }
+  }
+#endif  // defined(OS_WIN)
+
+
+  if (payload_size <= sizeof(Header)) {
+    delegate_->OnChannelError(remote_node_name_, this);
+    return;
+  }
+
+  const Header* header = static_cast<const Header*>(payload);
+  switch (header->type) {
+    case MessageType::ACCEPT_CHILD: {
+      const AcceptChildData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        delegate_->OnAcceptChild(remote_node_name_, data->parent_name,
+                                 data->token);
+        return;
+      }
+      break;
+    }
+
+    case MessageType::ACCEPT_PARENT: {
+      const AcceptParentData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        delegate_->OnAcceptParent(remote_node_name_, data->token,
+                                  data->child_name);
+        return;
+      }
+      break;
+    }
+
+    case MessageType::ADD_BROKER_CLIENT: {
+      const AddBrokerClientData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        ScopedPlatformHandle process_handle;
+#if defined(OS_WIN)
+        if (!handles || handles->size() != 1) {
+          DLOG(ERROR) << "Dropping invalid AddBrokerClient message.";
+          break;
+        }
+        process_handle = ScopedPlatformHandle(handles->at(0));
+        handles->clear();
+        delegate_->OnAddBrokerClient(remote_node_name_, data->client_name,
+                                     process_handle.release().handle);
+#else
+        if (handles && handles->size() != 0) {
+          DLOG(ERROR) << "Dropping invalid AddBrokerClient message.";
+          break;
+        }
+        delegate_->OnAddBrokerClient(remote_node_name_, data->client_name,
+                                     data->process_handle);
+#endif
+        return;
+      }
+      break;
+    }
+
+    case MessageType::BROKER_CLIENT_ADDED: {
+      const BrokerClientAddedData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        ScopedPlatformHandle broker_channel;
+        if (!handles || handles->size() != 1) {
+          DLOG(ERROR) << "Dropping invalid BrokerClientAdded message.";
+          break;
+        }
+        broker_channel = ScopedPlatformHandle(handles->at(0));
+        handles->clear();
+        delegate_->OnBrokerClientAdded(remote_node_name_, data->client_name,
+                                       std::move(broker_channel));
+        return;
+      }
+      break;
+    }
+
+    case MessageType::ACCEPT_BROKER_CLIENT: {
+      const AcceptBrokerClientData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        ScopedPlatformHandle broker_channel;
+        if (handles && handles->size() > 1) {
+          DLOG(ERROR) << "Dropping invalid AcceptBrokerClient message.";
+          break;
+        }
+        if (handles && handles->size() == 1) {
+          broker_channel = ScopedPlatformHandle(handles->at(0));
+          handles->clear();
+        }
+        delegate_->OnAcceptBrokerClient(remote_node_name_, data->broker_name,
+                                        std::move(broker_channel));
+        return;
+      }
+      break;
+    }
+
+    case MessageType::PORTS_MESSAGE: {
+      size_t num_handles = handles ? handles->size() : 0;
+      Channel::MessagePtr message(
+          new Channel::Message(payload_size, num_handles));
+      message->SetHandles(std::move(handles));
+      memcpy(message->mutable_payload(), payload, payload_size);
+      delegate_->OnPortsMessage(remote_node_name_, std::move(message));
+      return;
+    }
+
+    case MessageType::REQUEST_PORT_MERGE: {
+      const RequestPortMergeData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        // Don't accept an empty token.
+        size_t token_size = payload_size - sizeof(*data) - sizeof(Header);
+        if (token_size == 0)
+          break;
+        std::string token(reinterpret_cast<const char*>(data + 1), token_size);
+        delegate_->OnRequestPortMerge(remote_node_name_,
+                                      data->connector_port_name, token);
+        return;
+      }
+      break;
+    }
+
+    case MessageType::REQUEST_INTRODUCTION: {
+      const IntroductionData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        delegate_->OnRequestIntroduction(remote_node_name_, data->name);
+        return;
+      }
+      break;
+    }
+
+    case MessageType::INTRODUCE: {
+      const IntroductionData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        if (handles && handles->size() > 1) {
+          DLOG(ERROR) << "Dropping invalid introduction message.";
+          break;
+        }
+        ScopedPlatformHandle channel_handle;
+        if (handles && handles->size() == 1) {
+          channel_handle = ScopedPlatformHandle(handles->at(0));
+          handles->clear();
+        }
+        delegate_->OnIntroduce(remote_node_name_, data->name,
+                               std::move(channel_handle));
+        return;
+      }
+      break;
+    }
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+    case MessageType::RELAY_PORTS_MESSAGE: {
+      base::ProcessHandle from_process;
+      {
+        base::AutoLock lock(remote_process_handle_lock_);
+        from_process = remote_process_handle_;
+      }
+      const RelayPortsMessageData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        // Don't try to relay an empty message.
+        if (payload_size <= sizeof(Header) + sizeof(RelayPortsMessageData))
+          break;
+
+        const void* message_start = data + 1;
+        Channel::MessagePtr message = Channel::Message::Deserialize(
+            message_start, payload_size - sizeof(Header) - sizeof(*data));
+        if (!message) {
+          DLOG(ERROR) << "Dropping invalid relay message.";
+          break;
+        }
+  #if defined(OS_MACOSX) && !defined(OS_IOS)
+        message->SetHandles(std::move(handles));
+        MachPortRelay* relay = delegate_->GetMachPortRelay();
+        if (!relay) {
+          LOG(ERROR) << "Receiving mach ports without a port relay from "
+                     << remote_node_name_ << ". Dropping message.";
+          break;
+        }
+        {
+          base::AutoLock lock(pending_mach_messages_lock_);
+          if (relay->port_provider()->TaskForPid(from_process) ==
+              MACH_PORT_NULL) {
+            pending_relay_messages_.push(
+                std::make_pair(data->destination, std::move(message)));
+            break;
+          }
+        }
+  #endif
+        delegate_->OnRelayPortsMessage(remote_node_name_, from_process,
+                                       data->destination, std::move(message));
+        return;
+      }
+      break;
+    }
+#endif
+
+    case MessageType::BROADCAST: {
+      if (payload_size <= sizeof(Header))
+        break;
+      const void* data = static_cast<const void*>(
+          reinterpret_cast<const Header*>(payload) + 1);
+      Channel::MessagePtr message =
+          Channel::Message::Deserialize(data, payload_size - sizeof(Header));
+      if (!message || message->has_handles()) {
+        DLOG(ERROR) << "Dropping invalid broadcast message.";
+        break;
+      }
+      delegate_->OnBroadcast(remote_node_name_, std::move(message));
+      return;
+    }
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+    case MessageType::PORTS_MESSAGE_FROM_RELAY:
+      const PortsMessageFromRelayData* data;
+      if (GetMessagePayload(payload, payload_size, &data)) {
+        size_t num_bytes = payload_size - sizeof(*data);
+        if (num_bytes < sizeof(Header))
+          break;
+        num_bytes -= sizeof(Header);
+
+        size_t num_handles = handles ? handles->size() : 0;
+        Channel::MessagePtr message(
+            new Channel::Message(num_bytes, num_handles));
+        message->SetHandles(std::move(handles));
+        if (num_bytes)
+          memcpy(message->mutable_payload(), data + 1, num_bytes);
+        delegate_->OnPortsMessageFromRelay(
+            remote_node_name_, data->source, std::move(message));
+        return;
+      }
+      break;
+
+#endif  // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+
+    default:
+      break;
+  }
+
+  DLOG(ERROR) << "Received invalid message. Closing channel.";
+  delegate_->OnChannelError(remote_node_name_, this);
+}
+
+void NodeChannel::OnChannelError() {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  RequestContext request_context(RequestContext::Source::SYSTEM);
+
+  ShutDown();
+  // |OnChannelError()| may cause |this| to be destroyed, but still need access
+  // to the name name after that destruction. So may a copy of
+  // |remote_node_name_| so it can be used if |this| becomes destroyed.
+  ports::NodeName node_name = remote_node_name_;
+  delegate_->OnChannelError(node_name, this);
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+void NodeChannel::OnProcessReady(base::ProcessHandle process) {
+  io_task_runner_->PostTask(FROM_HERE, base::Bind(
+      &NodeChannel::ProcessPendingMessagesWithMachPorts, this));
+}
+
+void NodeChannel::ProcessPendingMessagesWithMachPorts() {
+  MachPortRelay* relay = delegate_->GetMachPortRelay();
+  DCHECK(relay);
+
+  base::ProcessHandle remote_process_handle;
+  {
+    base::AutoLock lock(remote_process_handle_lock_);
+    remote_process_handle = remote_process_handle_;
+  }
+  PendingMessageQueue pending_writes;
+  PendingRelayMessageQueue pending_relays;
+  {
+    base::AutoLock lock(pending_mach_messages_lock_);
+    pending_writes.swap(pending_write_messages_);
+    pending_relays.swap(pending_relay_messages_);
+  }
+  DCHECK(pending_writes.empty() && pending_relays.empty());
+
+  while (!pending_writes.empty()) {
+    Channel::MessagePtr message = std::move(pending_writes.front());
+    pending_writes.pop();
+    if (!relay->SendPortsToProcess(message.get(), remote_process_handle)) {
+      LOG(ERROR) << "Error on sending mach ports. Remote process is likely "
+                 << "gone. Dropping message.";
+      return;
+    }
+
+    base::AutoLock lock(channel_lock_);
+    if (!channel_) {
+      DLOG(ERROR) << "Dropping message on closed channel.";
+      break;
+    } else {
+      channel_->Write(std::move(message));
+    }
+  }
+
+  // Ensure this NodeChannel stays alive while flushing relay messages.
+  scoped_refptr<NodeChannel> keepalive = this;
+
+  while (!pending_relays.empty()) {
+    ports::NodeName destination = pending_relays.front().first;
+    Channel::MessagePtr message = std::move(pending_relays.front().second);
+    pending_relays.pop();
+    delegate_->OnRelayPortsMessage(remote_node_name_, remote_process_handle,
+                                   destination, std::move(message));
+  }
+}
+#endif
+
+void NodeChannel::WriteChannelMessage(Channel::MessagePtr message) {
+#if defined(OS_WIN)
+  // Map handles to the destination process. Note: only messages from a
+  // privileged node should contain handles on Windows. If an unprivileged
+  // node needs to send handles, it should do so via RelayPortsMessage which
+  // stashes the handles in the message in such a way that they go undetected
+  // here (they'll be unpacked and duplicated by a privileged parent.)
+
+  if (message->has_handles()) {
+    base::ProcessHandle remote_process_handle;
+    {
+      base::AutoLock lock(remote_process_handle_lock_);
+      remote_process_handle = remote_process_handle_;
+    }
+
+    // Rewrite outgoing handles if we have a handle to the destination process.
+    if (remote_process_handle != base::kNullProcessHandle) {
+      ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+      if (!Channel::Message::RewriteHandles(base::GetCurrentProcessHandle(),
+                                            remote_process_handle,
+                                            handles.get())) {
+        DLOG(ERROR) << "Failed to duplicate one or more outgoing handles.";
+      }
+      message->SetHandles(std::move(handles));
+    }
+  }
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  // On OSX, we need to transfer mach ports to the destination process before
+  // transferring the message itself.
+  if (message->has_mach_ports()) {
+    MachPortRelay* relay = delegate_->GetMachPortRelay();
+    if (relay) {
+      base::ProcessHandle remote_process_handle;
+      {
+        base::AutoLock lock(remote_process_handle_lock_);
+        // Expect that the receiving node is a child.
+        DCHECK(remote_process_handle_ != base::kNullProcessHandle);
+        remote_process_handle = remote_process_handle_;
+      }
+      {
+        base::AutoLock lock(pending_mach_messages_lock_);
+        if (relay->port_provider()->TaskForPid(remote_process_handle) ==
+            MACH_PORT_NULL) {
+          // It is also possible for TaskForPid() to return MACH_PORT_NULL when
+          // the process has started, then died. In that case, the queued
+          // message will never be processed. But that's fine since we're about
+          // to die anyway.
+          pending_write_messages_.push(std::move(message));
+          return;
+        }
+      }
+
+      if (!relay->SendPortsToProcess(message.get(), remote_process_handle)) {
+        LOG(ERROR) << "Error on sending mach ports. Remote process is likely "
+                   << "gone. Dropping message.";
+        return;
+      }
+    }
+  }
+#endif
+
+  base::AutoLock lock(channel_lock_);
+  if (!channel_)
+    DLOG(ERROR) << "Dropping message on closed channel.";
+  else
+    channel_->Write(std::move(message));
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/node_channel.h b/mojo/edk/system/node_channel.h
new file mode 100644
index 0000000..5a5fc8a
--- /dev/null
+++ b/mojo/edk/system/node_channel.h
@@ -0,0 +1,212 @@
+// Copyright 2016 The Chromium Authors. All 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_NODE_CHANNEL_H_
+#define MOJO_EDK_SYSTEM_NODE_CHANNEL_H_
+
+#include <queue>
+#include <unordered_map>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/process/process_handle.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/ports/name.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "mojo/edk/system/mach_port_relay.h"
+#endif
+
+namespace mojo {
+namespace edk {
+
+// Wraps a Channel to send and receive Node control messages.
+class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
+                    public Channel::Delegate
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+                    , public MachPortRelay::Observer
+#endif
+  {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+    virtual void OnAcceptChild(const ports::NodeName& from_node,
+                               const ports::NodeName& parent_name,
+                               const ports::NodeName& token) = 0;
+    virtual void OnAcceptParent(const ports::NodeName& from_node,
+                                const ports::NodeName& token,
+                                const ports::NodeName& child_name) = 0;
+    virtual void OnAddBrokerClient(const ports::NodeName& from_node,
+                                   const ports::NodeName& client_name,
+                                   base::ProcessHandle process_handle) = 0;
+    virtual void OnBrokerClientAdded(const ports::NodeName& from_node,
+                                     const ports::NodeName& client_name,
+                                     ScopedPlatformHandle broker_channel) = 0;
+    virtual void OnAcceptBrokerClient(const ports::NodeName& from_node,
+                                      const ports::NodeName& broker_name,
+                                      ScopedPlatformHandle broker_channel) = 0;
+    virtual void OnPortsMessage(const ports::NodeName& from_node,
+                                Channel::MessagePtr message) = 0;
+    virtual void OnRequestPortMerge(const ports::NodeName& from_node,
+                                    const ports::PortName& connector_port_name,
+                                    const std::string& token) = 0;
+    virtual void OnRequestIntroduction(const ports::NodeName& from_node,
+                                       const ports::NodeName& name) = 0;
+    virtual void OnIntroduce(const ports::NodeName& from_node,
+                             const ports::NodeName& name,
+                             ScopedPlatformHandle channel_handle) = 0;
+    virtual void OnBroadcast(const ports::NodeName& from_node,
+                             Channel::MessagePtr message) = 0;
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+    virtual void OnRelayPortsMessage(const ports::NodeName& from_node,
+                                     base::ProcessHandle from_process,
+                                     const ports::NodeName& destination,
+                                     Channel::MessagePtr message) = 0;
+    virtual void OnPortsMessageFromRelay(const ports::NodeName& from_node,
+                                         const ports::NodeName& source_node,
+                                         Channel::MessagePtr message) = 0;
+#endif
+
+    virtual void OnChannelError(const ports::NodeName& node,
+                                NodeChannel* channel) = 0;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+    virtual MachPortRelay* GetMachPortRelay() = 0;
+#endif
+  };
+
+  static scoped_refptr<NodeChannel> Create(
+      Delegate* delegate,
+      ScopedPlatformHandle platform_handle,
+      scoped_refptr<base::TaskRunner> io_task_runner,
+      const ProcessErrorCallback& process_error_callback);
+
+  static Channel::MessagePtr CreatePortsMessage(size_t payload_size,
+                                                void** payload,
+                                                size_t num_handles);
+
+  static void GetPortsMessageData(Channel::Message* message, void** data,
+                                  size_t* num_data_bytes);
+
+  // Start receiving messages.
+  void Start();
+
+  // Permanently stop the channel from sending or receiving messages.
+  void ShutDown();
+
+  // Leaks the pipe handle instead of closing it on shutdown.
+  void LeakHandleOnShutdown();
+
+  // Invokes the bad message callback for this channel, if any.
+  void NotifyBadMessage(const std::string& error);
+
+  // Note: On Windows, we take ownership of the remote process handle.
+  void SetRemoteProcessHandle(base::ProcessHandle process_handle);
+  bool HasRemoteProcessHandle();
+  // Note: The returned |ProcessHandle| is owned by the caller and should be
+  // freed if necessary.
+  base::ProcessHandle CopyRemoteProcessHandle();
+
+  // Used for context in Delegate calls (via |from_node| arguments.)
+  void SetRemoteNodeName(const ports::NodeName& name);
+
+  void AcceptChild(const ports::NodeName& parent_name,
+                   const ports::NodeName& token);
+  void AcceptParent(const ports::NodeName& token,
+                    const ports::NodeName& child_name);
+  void AddBrokerClient(const ports::NodeName& client_name,
+                       base::ProcessHandle process_handle);
+  void BrokerClientAdded(const ports::NodeName& client_name,
+                         ScopedPlatformHandle broker_channel);
+  void AcceptBrokerClient(const ports::NodeName& broker_name,
+                          ScopedPlatformHandle broker_channel);
+  void PortsMessage(Channel::MessagePtr message);
+  void RequestPortMerge(const ports::PortName& connector_port_name,
+                        const std::string& token);
+  void RequestIntroduction(const ports::NodeName& name);
+  void Introduce(const ports::NodeName& name,
+                 ScopedPlatformHandle channel_handle);
+  void Broadcast(Channel::MessagePtr message);
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+  // Relay the message to the specified node via this channel.  This is used to
+  // pass windows handles between two processes that do not have permission to
+  // duplicate handles into the other's address space. The relay process is
+  // assumed to have that permission.
+  void RelayPortsMessage(const ports::NodeName& destination,
+                         Channel::MessagePtr message);
+
+  // Sends a message to its destination from a relay. This is interpreted by the
+  // receiver similarly to PortsMessage, but the original source node is
+  // provided as additional message metadata from the (trusted) relay node.
+  void PortsMessageFromRelay(const ports::NodeName& source,
+                             Channel::MessagePtr message);
+#endif
+
+ private:
+  friend class base::RefCountedThreadSafe<NodeChannel>;
+
+  using PendingMessageQueue = std::queue<Channel::MessagePtr>;
+  using PendingRelayMessageQueue =
+      std::queue<std::pair<ports::NodeName, Channel::MessagePtr>>;
+
+  NodeChannel(Delegate* delegate,
+              ScopedPlatformHandle platform_handle,
+              scoped_refptr<base::TaskRunner> io_task_runner,
+              const ProcessErrorCallback& process_error_callback);
+  ~NodeChannel() override;
+
+  // Channel::Delegate:
+  void OnChannelMessage(const void* payload,
+                        size_t payload_size,
+                        ScopedPlatformHandleVectorPtr handles) override;
+  void OnChannelError() override;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  // MachPortRelay::Observer:
+  void OnProcessReady(base::ProcessHandle process) override;
+
+  void ProcessPendingMessagesWithMachPorts();
+#endif
+
+  void WriteChannelMessage(Channel::MessagePtr message);
+
+  Delegate* const delegate_;
+  const scoped_refptr<base::TaskRunner> io_task_runner_;
+  const ProcessErrorCallback process_error_callback_;
+
+  base::Lock channel_lock_;
+  scoped_refptr<Channel> channel_;
+
+  // Must only be accessed from |io_task_runner_|'s thread.
+  ports::NodeName remote_node_name_;
+
+  base::Lock remote_process_handle_lock_;
+  base::ProcessHandle remote_process_handle_ = base::kNullProcessHandle;
+#if defined(OS_WIN)
+  ScopedPlatformHandle scoped_remote_process_handle_;
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  base::Lock pending_mach_messages_lock_;
+  PendingMessageQueue pending_write_messages_;
+  PendingRelayMessageQueue pending_relay_messages_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(NodeChannel);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_NODE_CHANNEL_H_
diff --git a/mojo/edk/system/node_controller.cc b/mojo/edk/system/node_controller.cc
new file mode 100644
index 0000000..91599d8
--- /dev/null
+++ b/mojo/edk/system/node_controller.cc
@@ -0,0 +1,1297 @@
+// Copyright 2016 The Chromium Authors. All 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/node_controller.h"
+
+#include <algorithm>
+#include <limits>
+
+#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/metrics/histogram_macros.h"
+#include "base/process/process_handle.h"
+#include "base/rand_util.h"
+#include "base/time/time.h"
+#include "base/timer/elapsed_timer.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/system/broker.h"
+#include "mojo/edk/system/broker_host.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/ports_message.h"
+#include "mojo/edk/system/request_context.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "mojo/edk/system/mach_port_relay.h"
+#endif
+
+#if !defined(OS_NACL)
+#include "crypto/random.h"
+#endif
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+#if defined(OS_NACL)
+template <typename T>
+void GenerateRandomName(T* out) { base::RandBytes(out, sizeof(T)); }
+#else
+template <typename T>
+void GenerateRandomName(T* out) { crypto::RandBytes(out, sizeof(T)); }
+#endif
+
+ports::NodeName GetRandomNodeName() {
+  ports::NodeName name;
+  GenerateRandomName(&name);
+  return name;
+}
+
+void RecordPeerCount(size_t count) {
+  DCHECK_LE(count, static_cast<size_t>(std::numeric_limits<int32_t>::max()));
+
+  // 8k is the maximum number of file descriptors allowed in Chrome.
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.ConnectedPeers",
+                              static_cast<int32_t>(count),
+                              0 /* min */,
+                              8000 /* max */,
+                              50 /* bucket count */);
+}
+
+void RecordPendingChildCount(size_t count) {
+  DCHECK_LE(count, static_cast<size_t>(std::numeric_limits<int32_t>::max()));
+
+  // 8k is the maximum number of file descriptors allowed in Chrome.
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.PendingChildren",
+                              static_cast<int32_t>(count),
+                              0 /* min */,
+                              8000 /* max */,
+                              50 /* bucket count */);
+}
+
+bool ParsePortsMessage(Channel::Message* message,
+                       void** data,
+                       size_t* num_data_bytes,
+                       size_t* num_header_bytes,
+                       size_t* num_payload_bytes,
+                       size_t* num_ports_bytes) {
+  DCHECK(data && num_data_bytes && num_header_bytes && num_payload_bytes &&
+         num_ports_bytes);
+
+  NodeChannel::GetPortsMessageData(message, data, num_data_bytes);
+  if (!*num_data_bytes)
+    return false;
+
+  if (!ports::Message::Parse(*data, *num_data_bytes, num_header_bytes,
+                             num_payload_bytes, num_ports_bytes)) {
+    return false;
+  }
+
+  return true;
+}
+
+// Used by NodeController to watch for shutdown. Since no IO can happen once
+// the IO thread is killed, the NodeController can cleanly drop all its peers
+// at that time.
+class ThreadDestructionObserver :
+    public base::MessageLoop::DestructionObserver {
+ public:
+  static void Create(scoped_refptr<base::TaskRunner> task_runner,
+                     const base::Closure& callback) {
+    if (task_runner->RunsTasksOnCurrentThread()) {
+      // Owns itself.
+      new ThreadDestructionObserver(callback);
+    } else {
+      task_runner->PostTask(FROM_HERE,
+                            base::Bind(&Create, task_runner, callback));
+    }
+  }
+
+ private:
+  explicit ThreadDestructionObserver(const base::Closure& callback)
+      : callback_(callback) {
+    base::MessageLoop::current()->AddDestructionObserver(this);
+  }
+
+  ~ThreadDestructionObserver() override {
+    base::MessageLoop::current()->RemoveDestructionObserver(this);
+  }
+
+  // base::MessageLoop::DestructionObserver:
+  void WillDestroyCurrentMessageLoop() override {
+    callback_.Run();
+    delete this;
+  }
+
+  const base::Closure callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadDestructionObserver);
+};
+
+}  // namespace
+
+NodeController::~NodeController() {}
+
+NodeController::NodeController(Core* core)
+    : core_(core),
+      name_(GetRandomNodeName()),
+      node_(new ports::Node(name_, this)) {
+  DVLOG(1) << "Initializing node " << name_;
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+void NodeController::CreateMachPortRelay(
+    base::PortProvider* port_provider) {
+  base::AutoLock lock(mach_port_relay_lock_);
+  DCHECK(!mach_port_relay_);
+  mach_port_relay_.reset(new MachPortRelay(port_provider));
+}
+#endif
+
+void NodeController::SetIOTaskRunner(
+    scoped_refptr<base::TaskRunner> task_runner) {
+  io_task_runner_ = task_runner;
+  ThreadDestructionObserver::Create(
+      io_task_runner_,
+      base::Bind(&NodeController::DropAllPeers, base::Unretained(this)));
+}
+
+void NodeController::ConnectToChild(
+    base::ProcessHandle process_handle,
+    ScopedPlatformHandle platform_handle,
+    const std::string& child_token,
+    const ProcessErrorCallback& process_error_callback) {
+  // Generate the temporary remote node name here so that it can be associated
+  // with the embedder's child_token. If an error occurs in the child process
+  // after it is launched, but before any reserved ports are connected, this can
+  // be used to clean up any dangling ports.
+  ports::NodeName node_name;
+  GenerateRandomName(&node_name);
+
+  {
+    base::AutoLock lock(reserved_ports_lock_);
+    bool inserted = pending_child_tokens_.insert(
+        std::make_pair(node_name, child_token)).second;
+    DCHECK(inserted);
+  }
+
+#if defined(OS_WIN)
+  // On Windows, we need to duplicate the process handle because we have no
+  // control over its lifetime and it may become invalid by the time the posted
+  // task runs.
+  HANDLE dup_handle = INVALID_HANDLE_VALUE;
+  BOOL ok = ::DuplicateHandle(
+      base::GetCurrentProcessHandle(), process_handle,
+      base::GetCurrentProcessHandle(), &dup_handle,
+      0, FALSE, DUPLICATE_SAME_ACCESS);
+  DPCHECK(ok);
+  process_handle = dup_handle;
+#endif
+
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&NodeController::ConnectToChildOnIOThread,
+                 base::Unretained(this),
+                 process_handle,
+                 base::Passed(&platform_handle),
+                 node_name,
+                 process_error_callback));
+}
+
+void NodeController::CloseChildPorts(const std::string& child_token) {
+  std::vector<ports::PortRef> ports_to_close;
+  {
+    std::vector<std::string> port_tokens;
+    base::AutoLock lock(reserved_ports_lock_);
+    for (const auto& port : reserved_ports_) {
+      if (port.second.child_token == child_token) {
+        DVLOG(1) << "Closing reserved port " << port.second.port.name();
+        ports_to_close.push_back(port.second.port);
+        port_tokens.push_back(port.first);
+      }
+    }
+
+    for (const auto& token : port_tokens)
+      reserved_ports_.erase(token);
+  }
+
+  for (const auto& port : ports_to_close)
+    node_->ClosePort(port);
+
+  // Ensure local port closure messages are processed.
+  AcceptIncomingMessages();
+}
+
+void NodeController::ConnectToParent(ScopedPlatformHandle platform_handle) {
+// TODO(amistry): Consider the need for a broker on Windows.
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
+  // On posix, use the bootstrap channel for the broker and receive the node's
+  // channel synchronously as the first message from the broker.
+  base::ElapsedTimer timer;
+  broker_.reset(new Broker(std::move(platform_handle)));
+  platform_handle = broker_->GetParentPlatformHandle();
+  UMA_HISTOGRAM_TIMES("Mojo.System.GetParentPlatformHandleSyncTime",
+                      timer.Elapsed());
+
+  if (!platform_handle.is_valid()) {
+    // Most likely the browser side of the channel has already been closed and
+    // the broker was unable to negotiate a NodeChannel pipe. In this case we
+    // can cancel parent connection.
+    DVLOG(1) << "Cannot connect to invalid parent channel.";
+    return;
+  }
+#endif
+
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&NodeController::ConnectToParentOnIOThread,
+                 base::Unretained(this),
+                 base::Passed(&platform_handle)));
+}
+
+void NodeController::SetPortObserver(
+    const ports::PortRef& port,
+    const scoped_refptr<PortObserver>& observer) {
+  node_->SetUserData(port, observer);
+}
+
+void NodeController::ClosePort(const ports::PortRef& port) {
+  SetPortObserver(port, nullptr);
+  int rv = node_->ClosePort(port);
+  DCHECK_EQ(rv, ports::OK) << " Failed to close port: " << port.name();
+
+  AcceptIncomingMessages();
+}
+
+int NodeController::SendMessage(const ports::PortRef& port,
+                                std::unique_ptr<PortsMessage> message) {
+  ports::ScopedMessage ports_message(message.release());
+  int rv = node_->SendMessage(port, std::move(ports_message));
+
+  AcceptIncomingMessages();
+  return rv;
+}
+
+void NodeController::ReservePort(const std::string& token,
+                                 const ports::PortRef& port,
+                                 const std::string& child_token) {
+  DVLOG(2) << "Reserving port " << port.name() << "@" << name_ << " for token "
+           << token;
+
+  base::AutoLock lock(reserved_ports_lock_);
+  auto result = reserved_ports_.insert(
+      std::make_pair(token, ReservedPort{port, child_token}));
+  DCHECK(result.second);
+}
+
+void NodeController::MergePortIntoParent(const std::string& token,
+                                         const ports::PortRef& port) {
+  bool was_merged = false;
+  {
+    // This request may be coming from within the process that reserved the
+    // "parent" side (e.g. for Chrome single-process mode), so if this token is
+    // reserved locally, merge locally instead.
+    base::AutoLock lock(reserved_ports_lock_);
+    auto it = reserved_ports_.find(token);
+    if (it != reserved_ports_.end()) {
+      node_->MergePorts(port, name_, it->second.port.name());
+      reserved_ports_.erase(it);
+      was_merged = true;
+    }
+  }
+  if (was_merged) {
+    AcceptIncomingMessages();
+    return;
+  }
+
+  scoped_refptr<NodeChannel> parent;
+  bool reject_merge = false;
+  {
+    // Hold |pending_port_merges_lock_| while getting |parent|. Otherwise,
+    // there is a race where the parent can be set, and |pending_port_merges_|
+    // be processed between retrieving |parent| and adding the merge to
+    // |pending_port_merges_|.
+    base::AutoLock lock(pending_port_merges_lock_);
+    parent = GetParentChannel();
+    if (reject_pending_merges_) {
+      reject_merge = true;
+    } else if (!parent) {
+      pending_port_merges_.push_back(std::make_pair(token, port));
+      return;
+    }
+  }
+  if (reject_merge) {
+    node_->ClosePort(port);
+    DVLOG(2) << "Rejecting port merge for token " << token
+             << " due to closed parent channel.";
+    AcceptIncomingMessages();
+    return;
+  }
+
+  parent->RequestPortMerge(port.name(), token);
+}
+
+int NodeController::MergeLocalPorts(const ports::PortRef& port0,
+                                    const ports::PortRef& port1) {
+  int rv = node_->MergeLocalPorts(port0, port1);
+  AcceptIncomingMessages();
+  return rv;
+}
+
+scoped_refptr<PlatformSharedBuffer> NodeController::CreateSharedBuffer(
+    size_t num_bytes) {
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
+  // Shared buffer creation failure is fatal, so always use the broker when we
+  // have one. This does mean that a non-root process that has children will use
+  // the broker for shared buffer creation even though that process is
+  // privileged.
+  if (broker_) {
+    return broker_->GetSharedBuffer(num_bytes);
+  }
+#endif
+  return PlatformSharedBuffer::Create(num_bytes);
+}
+
+void NodeController::RequestShutdown(const base::Closure& callback) {
+  {
+    base::AutoLock lock(shutdown_lock_);
+    shutdown_callback_ = callback;
+    shutdown_callback_flag_.Set(true);
+  }
+
+  AttemptShutdownIfRequested();
+}
+
+void NodeController::NotifyBadMessageFrom(const ports::NodeName& source_node,
+                                          const std::string& error) {
+  scoped_refptr<NodeChannel> peer = GetPeerChannel(source_node);
+  if (peer)
+    peer->NotifyBadMessage(error);
+}
+
+void NodeController::ConnectToChildOnIOThread(
+    base::ProcessHandle process_handle,
+    ScopedPlatformHandle platform_handle,
+    ports::NodeName token,
+    const ProcessErrorCallback& process_error_callback) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL)
+  PlatformChannelPair node_channel;
+  // BrokerHost owns itself.
+  BrokerHost* broker_host = new BrokerHost(std::move(platform_handle));
+  broker_host->SendChannel(node_channel.PassClientHandle());
+  scoped_refptr<NodeChannel> channel = NodeChannel::Create(
+      this, node_channel.PassServerHandle(), io_task_runner_,
+      process_error_callback);
+#else
+  scoped_refptr<NodeChannel> channel =
+      NodeChannel::Create(this, std::move(platform_handle), io_task_runner_,
+                          process_error_callback);
+#endif
+
+  // We set up the child channel with a temporary name so it can be identified
+  // as a pending child if it writes any messages to the channel. We may start
+  // receiving messages from it (though we shouldn't) as soon as Start() is
+  // called below.
+
+  pending_children_.insert(std::make_pair(token, channel));
+  RecordPendingChildCount(pending_children_.size());
+
+  channel->SetRemoteNodeName(token);
+  channel->SetRemoteProcessHandle(process_handle);
+  channel->Start();
+
+  channel->AcceptChild(name_, token);
+}
+
+void NodeController::ConnectToParentOnIOThread(
+    ScopedPlatformHandle platform_handle) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  {
+    base::AutoLock lock(parent_lock_);
+    DCHECK(parent_name_ == ports::kInvalidNodeName);
+
+    // At this point we don't know the parent's name, so we can't yet insert it
+    // into our |peers_| map. That will happen as soon as we receive an
+    // AcceptChild message from them.
+    bootstrap_parent_channel_ =
+        NodeChannel::Create(this, std::move(platform_handle), io_task_runner_,
+                            ProcessErrorCallback());
+    // Prevent the parent pipe handle from being closed on shutdown. Pipe
+    // closure is used by the parent to detect the child process has exited.
+    // Relying on message pipes to be closed is not enough because the parent
+    // may see the message pipe closure before the child is dead, causing the
+    // child process to be unexpectedly SIGKILL'd.
+    bootstrap_parent_channel_->LeakHandleOnShutdown();
+  }
+  bootstrap_parent_channel_->Start();
+}
+
+scoped_refptr<NodeChannel> NodeController::GetPeerChannel(
+    const ports::NodeName& name) {
+  base::AutoLock lock(peers_lock_);
+  auto it = peers_.find(name);
+  if (it == peers_.end())
+    return nullptr;
+  return it->second;
+}
+
+scoped_refptr<NodeChannel> NodeController::GetParentChannel() {
+  ports::NodeName parent_name;
+  {
+    base::AutoLock lock(parent_lock_);
+    parent_name = parent_name_;
+  }
+  return GetPeerChannel(parent_name);
+}
+
+scoped_refptr<NodeChannel> NodeController::GetBrokerChannel() {
+  ports::NodeName broker_name;
+  {
+    base::AutoLock lock(broker_lock_);
+    broker_name = broker_name_;
+  }
+  return GetPeerChannel(broker_name);
+}
+
+void NodeController::AddPeer(const ports::NodeName& name,
+                             scoped_refptr<NodeChannel> channel,
+                             bool start_channel) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  DCHECK(name != ports::kInvalidNodeName);
+  DCHECK(channel);
+
+  channel->SetRemoteNodeName(name);
+
+  OutgoingMessageQueue pending_messages;
+  {
+    base::AutoLock lock(peers_lock_);
+    if (peers_.find(name) != peers_.end()) {
+      // This can happen normally if two nodes race to be introduced to each
+      // other. The losing pipe will be silently closed and introduction should
+      // not be affected.
+      DVLOG(1) << "Ignoring duplicate peer name " << name;
+      return;
+    }
+
+    auto result = peers_.insert(std::make_pair(name, channel));
+    DCHECK(result.second);
+
+    DVLOG(2) << "Accepting new peer " << name << " on node " << name_;
+
+    RecordPeerCount(peers_.size());
+
+    auto it = pending_peer_messages_.find(name);
+    if (it != pending_peer_messages_.end()) {
+      std::swap(pending_messages, it->second);
+      pending_peer_messages_.erase(it);
+    }
+  }
+
+  if (start_channel)
+    channel->Start();
+
+  // Flush any queued message we need to deliver to this node.
+  while (!pending_messages.empty()) {
+    channel->PortsMessage(std::move(pending_messages.front()));
+    pending_messages.pop();
+  }
+}
+
+void NodeController::DropPeer(const ports::NodeName& name,
+                              NodeChannel* channel) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  {
+    base::AutoLock lock(peers_lock_);
+    auto it = peers_.find(name);
+
+    if (it != peers_.end()) {
+      ports::NodeName peer = it->first;
+      peers_.erase(it);
+      DVLOG(1) << "Dropped peer " << peer;
+    }
+
+    pending_peer_messages_.erase(name);
+    pending_children_.erase(name);
+
+    RecordPeerCount(peers_.size());
+    RecordPendingChildCount(pending_children_.size());
+  }
+
+  std::vector<ports::PortRef> ports_to_close;
+  {
+    // Clean up any reserved ports.
+    base::AutoLock lock(reserved_ports_lock_);
+    auto it = pending_child_tokens_.find(name);
+    if (it != pending_child_tokens_.end()) {
+      const std::string& child_token = it->second;
+
+      std::vector<std::string> port_tokens;
+      for (const auto& port : reserved_ports_) {
+        if (port.second.child_token == child_token) {
+          DVLOG(1) << "Closing reserved port: " << port.second.port.name();
+          ports_to_close.push_back(port.second.port);
+          port_tokens.push_back(port.first);
+        }
+      }
+
+      // We have to erase reserved ports in a two-step manner because the usual
+      // manner of using the returned iterator from map::erase isn't technically
+      // valid in C++11 (although it is in C++14).
+      for (const auto& token : port_tokens)
+        reserved_ports_.erase(token);
+
+      pending_child_tokens_.erase(it);
+    }
+  }
+
+  bool is_parent;
+  {
+    base::AutoLock lock(parent_lock_);
+    is_parent = (name == parent_name_ || channel == bootstrap_parent_channel_);
+  }
+  // If the error comes from the parent channel, we also need to cancel any
+  // port merge requests, so that errors can be propagated to the message
+  // pipes.
+  if (is_parent) {
+    base::AutoLock lock(pending_port_merges_lock_);
+    reject_pending_merges_ = true;
+
+    for (const auto& port : pending_port_merges_)
+      ports_to_close.push_back(port.second);
+    pending_port_merges_.clear();
+  }
+
+  for (const auto& port : ports_to_close)
+    node_->ClosePort(port);
+
+  node_->LostConnectionToNode(name);
+
+  AcceptIncomingMessages();
+}
+
+void NodeController::SendPeerMessage(const ports::NodeName& name,
+                                     ports::ScopedMessage message) {
+  Channel::MessagePtr channel_message =
+      static_cast<PortsMessage*>(message.get())->TakeChannelMessage();
+
+  scoped_refptr<NodeChannel> peer = GetPeerChannel(name);
+#if defined(OS_WIN)
+  if (channel_message->has_handles()) {
+    // If we're sending a message with handles we aren't the destination
+    // node's parent or broker (i.e. we don't know its process handle), ask
+    // the broker to relay for us.
+    scoped_refptr<NodeChannel> broker = GetBrokerChannel();
+    if (!peer || !peer->HasRemoteProcessHandle()) {
+      if (broker) {
+        broker->RelayPortsMessage(name, std::move(channel_message));
+      } else {
+        base::AutoLock lock(broker_lock_);
+        pending_relay_messages_[name].emplace(std::move(channel_message));
+      }
+      return;
+    }
+  }
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  if (channel_message->has_mach_ports()) {
+    // Messages containing Mach ports are always routed through the broker, even
+    // if the broker process is the intended recipient.
+    bool use_broker = false;
+    {
+      base::AutoLock lock(parent_lock_);
+      use_broker = (bootstrap_parent_channel_ ||
+                    parent_name_ != ports::kInvalidNodeName);
+    }
+    if (use_broker) {
+      scoped_refptr<NodeChannel> broker = GetBrokerChannel();
+      if (broker) {
+        broker->RelayPortsMessage(name, std::move(channel_message));
+      } else {
+        base::AutoLock lock(broker_lock_);
+        pending_relay_messages_[name].emplace(std::move(channel_message));
+      }
+      return;
+    }
+  }
+#endif  // defined(OS_WIN)
+
+  if (peer) {
+    peer->PortsMessage(std::move(channel_message));
+    return;
+  }
+
+  // If we don't know who the peer is, queue the message for delivery. If this
+  // is the first message queued for the peer, we also ask the broker to
+  // introduce us to them.
+
+  bool needs_introduction = false;
+  {
+    base::AutoLock lock(peers_lock_);
+    auto& queue = pending_peer_messages_[name];
+    needs_introduction = queue.empty();
+    queue.emplace(std::move(channel_message));
+  }
+
+  if (needs_introduction) {
+    scoped_refptr<NodeChannel> broker = GetBrokerChannel();
+    if (!broker) {
+      DVLOG(1) << "Dropping message for unknown peer: " << name;
+      return;
+    }
+
+    // HACK: On ARC++ we never really need this codepath since it is always hit
+    // when RemoteMessagePipeBootstrap races against us in delivering the
+    // three-way broker handshake. If we do send the RequestIntroduction message,
+    // the broker (Chrome) won't yet know our peer, and it will cause us to drop
+    // that peer's connection, causing it to go in loops requesting for a
+    // re-introduction. Instead, let's assume that the node will eventually be
+    // introduced to us and just stick it in the queue. Note that this is only
+    // safe since ARC++ processes strictly follow a star topology, and we never
+    // pass handles between children.
+    //
+    // We need to revert this eventually when a long-term fix is ready. See
+    // b/33453258 for more details.
+    LOG(ERROR) << "Averted b/33453258 by dropping the introduction request for "
+               << name;
+    return;
+
+    broker->RequestIntroduction(name);
+  }
+}
+
+void NodeController::AcceptIncomingMessages() {
+  {
+    base::AutoLock lock(messages_lock_);
+    if (!incoming_messages_.empty()) {
+      // libstdc++'s deque creates an internal buffer on construction, even when
+      // the size is 0. So avoid creating it until it is necessary.
+      std::queue<ports::ScopedMessage> messages;
+      std::swap(messages, incoming_messages_);
+      base::AutoUnlock unlock(messages_lock_);
+
+      while (!messages.empty()) {
+        node_->AcceptMessage(std::move(messages.front()));
+        messages.pop();
+      }
+    }
+  }
+
+  AttemptShutdownIfRequested();
+}
+
+void NodeController::ProcessIncomingMessages() {
+  RequestContext request_context(RequestContext::Source::SYSTEM);
+
+  {
+    base::AutoLock lock(messages_lock_);
+    // Allow a new incoming messages processing task to be posted. This can't be
+    // done after AcceptIncomingMessages() otherwise a message might be missed.
+    // Doing it here may result in at most two tasks existing at the same time;
+    // this running one, and one pending in the task runner.
+    incoming_messages_task_posted_ = false;
+  }
+
+  AcceptIncomingMessages();
+}
+
+void NodeController::DropAllPeers() {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  std::vector<scoped_refptr<NodeChannel>> all_peers;
+  {
+    base::AutoLock lock(parent_lock_);
+    if (bootstrap_parent_channel_) {
+      // |bootstrap_parent_channel_| isn't null'd here becuase we rely on its
+      // existence to determine whether or not this is the root node. Once
+      // bootstrap_parent_channel_->ShutDown() has been called,
+      // |bootstrap_parent_channel_| is essentially a dead object and it doesn't
+      // matter if it's deleted now or when |this| is deleted.
+      // Note: |bootstrap_parent_channel_| is only modified on the IO thread.
+      all_peers.push_back(bootstrap_parent_channel_);
+    }
+  }
+
+  {
+    base::AutoLock lock(peers_lock_);
+    for (const auto& peer : peers_)
+      all_peers.push_back(peer.second);
+    for (const auto& peer : pending_children_)
+      all_peers.push_back(peer.second);
+    peers_.clear();
+    pending_children_.clear();
+    pending_peer_messages_.clear();
+  }
+
+  for (const auto& peer : all_peers)
+    peer->ShutDown();
+
+  if (destroy_on_io_thread_shutdown_)
+    delete this;
+}
+
+void NodeController::GenerateRandomPortName(ports::PortName* port_name) {
+  GenerateRandomName(port_name);
+}
+
+void NodeController::AllocMessage(size_t num_header_bytes,
+                                  ports::ScopedMessage* message) {
+  message->reset(new PortsMessage(num_header_bytes, 0, 0, nullptr));
+}
+
+void NodeController::ForwardMessage(const ports::NodeName& node,
+                                    ports::ScopedMessage message) {
+  DCHECK(message);
+  bool schedule_pump_task = false;
+  if (node == name_) {
+    // NOTE: We need to avoid re-entering the Node instance within
+    // ForwardMessage. Because ForwardMessage is only ever called
+    // (synchronously) in response to Node's ClosePort, SendMessage, or
+    // AcceptMessage, we flush the queue after calling any of those methods.
+    base::AutoLock lock(messages_lock_);
+    // |io_task_runner_| may be null in tests or processes that don't require
+    // multi-process Mojo.
+    schedule_pump_task = incoming_messages_.empty() && io_task_runner_ &&
+        !incoming_messages_task_posted_;
+    incoming_messages_task_posted_ |= schedule_pump_task;
+    incoming_messages_.emplace(std::move(message));
+  } else {
+    SendPeerMessage(node, std::move(message));
+  }
+
+  if (schedule_pump_task) {
+    // Normally, the queue is processed after the action that added the local
+    // message is done (i.e. SendMessage, ClosePort, etc). However, it's also
+    // possible for a local message to be added as a result of a remote message,
+    // and OnChannelMessage() doesn't process this queue (although
+    // OnPortsMessage() does). There may also be other code paths, now or added
+    // in the future, which cause local messages to be added but don't process
+    // this message queue.
+    //
+    // Instead of adding a call to AcceptIncomingMessages() on every possible
+    // code path, post a task to the IO thread to process the queue. If the
+    // current call stack processes the queue, this may end up doing nothing.
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&NodeController::ProcessIncomingMessages,
+                   base::Unretained(this)));
+  }
+}
+
+void NodeController::BroadcastMessage(ports::ScopedMessage message) {
+  CHECK_EQ(message->num_ports(), 0u);
+  Channel::MessagePtr channel_message =
+      static_cast<PortsMessage*>(message.get())->TakeChannelMessage();
+  CHECK(!channel_message->has_handles());
+
+  scoped_refptr<NodeChannel> broker = GetBrokerChannel();
+  if (broker)
+    broker->Broadcast(std::move(channel_message));
+  else
+    OnBroadcast(name_, std::move(channel_message));
+}
+
+void NodeController::PortStatusChanged(const ports::PortRef& port) {
+  scoped_refptr<ports::UserData> user_data;
+  node_->GetUserData(port, &user_data);
+
+  PortObserver* observer = static_cast<PortObserver*>(user_data.get());
+  if (observer) {
+    observer->OnPortStatusChanged();
+  } else {
+    DVLOG(2) << "Ignoring status change for " << port.name() << " because it "
+             << "doesn't have an observer.";
+  }
+}
+
+void NodeController::OnAcceptChild(const ports::NodeName& from_node,
+                                   const ports::NodeName& parent_name,
+                                   const ports::NodeName& token) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  scoped_refptr<NodeChannel> parent;
+  {
+    base::AutoLock lock(parent_lock_);
+    if (bootstrap_parent_channel_ && parent_name_ == ports::kInvalidNodeName) {
+      parent_name_ = parent_name;
+      parent = bootstrap_parent_channel_;
+    }
+  }
+
+  if (!parent) {
+    DLOG(ERROR) << "Unexpected AcceptChild message from " << from_node;
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  parent->SetRemoteNodeName(parent_name);
+  parent->AcceptParent(token, name_);
+
+  // NOTE: The child does not actually add its parent as a peer until
+  // receiving an AcceptBrokerClient message from the broker. The parent
+  // will request that said message be sent upon receiving AcceptParent.
+
+  DVLOG(1) << "Child " << name_ << " accepting parent " << parent_name;
+}
+
+void NodeController::OnAcceptParent(const ports::NodeName& from_node,
+                                    const ports::NodeName& token,
+                                    const ports::NodeName& child_name) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  auto it = pending_children_.find(from_node);
+  if (it == pending_children_.end() || token != from_node) {
+    DLOG(ERROR) << "Received unexpected AcceptParent message from "
+                << from_node;
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  scoped_refptr<NodeChannel> channel = it->second;
+  pending_children_.erase(it);
+
+  DCHECK(channel);
+
+  DVLOG(1) << "Parent " << name_ << " accepted child " << child_name;
+
+  AddPeer(child_name, channel, false /* start_channel */);
+
+  // TODO(rockot/amistry): We could simplify child initialization if we could
+  // synchronously get a new async broker channel from the broker. For now we do
+  // it asynchronously since it's only used to facilitate handle passing, not
+  // handle creation.
+  scoped_refptr<NodeChannel> broker = GetBrokerChannel();
+  if (broker) {
+    // Inform the broker of this new child.
+    broker->AddBrokerClient(child_name, channel->CopyRemoteProcessHandle());
+  } else {
+    // If we have no broker, either we need to wait for one, or we *are* the
+    // broker.
+    scoped_refptr<NodeChannel> parent = GetParentChannel();
+    if (!parent) {
+      base::AutoLock lock(parent_lock_);
+      parent = bootstrap_parent_channel_;
+    }
+
+    if (!parent) {
+      // Yes, we're the broker. We can initialize the child directly.
+      channel->AcceptBrokerClient(name_, ScopedPlatformHandle());
+    } else {
+      // We aren't the broker, so wait for a broker connection.
+      base::AutoLock lock(broker_lock_);
+      pending_broker_clients_.push(child_name);
+    }
+  }
+}
+
+void NodeController::OnAddBrokerClient(const ports::NodeName& from_node,
+                                       const ports::NodeName& client_name,
+                                       base::ProcessHandle process_handle) {
+#if defined(OS_WIN)
+  // Scoped handle to avoid leaks on error.
+  ScopedPlatformHandle scoped_process_handle =
+      ScopedPlatformHandle(PlatformHandle(process_handle));
+#endif
+  scoped_refptr<NodeChannel> sender = GetPeerChannel(from_node);
+  if (!sender) {
+    DLOG(ERROR) << "Ignoring AddBrokerClient from unknown sender.";
+    return;
+  }
+
+  if (GetPeerChannel(client_name)) {
+    DLOG(ERROR) << "Ignoring AddBrokerClient for known client.";
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  PlatformChannelPair broker_channel;
+  scoped_refptr<NodeChannel> client = NodeChannel::Create(
+      this, broker_channel.PassServerHandle(), io_task_runner_,
+      ProcessErrorCallback());
+
+#if defined(OS_WIN)
+  // The broker must have a working handle to the client process in order to
+  // properly copy other handles to and from the client.
+  if (!scoped_process_handle.is_valid()) {
+    DLOG(ERROR) << "Broker rejecting client with invalid process handle.";
+    return;
+  }
+  client->SetRemoteProcessHandle(scoped_process_handle.release().handle);
+#else
+  client->SetRemoteProcessHandle(process_handle);
+#endif
+
+  AddPeer(client_name, client, true /* start_channel */);
+
+  DVLOG(1) << "Broker " << name_ << " accepting client " << client_name
+           << " from peer " << from_node;
+
+  sender->BrokerClientAdded(client_name, broker_channel.PassClientHandle());
+}
+
+void NodeController::OnBrokerClientAdded(const ports::NodeName& from_node,
+                                         const ports::NodeName& client_name,
+                                         ScopedPlatformHandle broker_channel) {
+  scoped_refptr<NodeChannel> client = GetPeerChannel(client_name);
+  if (!client) {
+    DLOG(ERROR) << "BrokerClientAdded for unknown child " << client_name;
+    return;
+  }
+
+  // This should have come from our own broker.
+  if (GetBrokerChannel() != GetPeerChannel(from_node)) {
+    DLOG(ERROR) << "BrokerClientAdded from non-broker node " << from_node;
+    return;
+  }
+
+  DVLOG(1) << "Child " << client_name << " accepted by broker " << from_node;
+
+  client->AcceptBrokerClient(from_node, std::move(broker_channel));
+}
+
+void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node,
+                                          const ports::NodeName& broker_name,
+                                          ScopedPlatformHandle broker_channel) {
+  // This node should already have a parent in bootstrap mode.
+  ports::NodeName parent_name;
+  scoped_refptr<NodeChannel> parent;
+  {
+    base::AutoLock lock(parent_lock_);
+    parent_name = parent_name_;
+    parent = bootstrap_parent_channel_;
+    bootstrap_parent_channel_ = nullptr;
+  }
+  DCHECK(parent_name == from_node);
+  DCHECK(parent);
+
+  std::queue<ports::NodeName> pending_broker_clients;
+  std::unordered_map<ports::NodeName, OutgoingMessageQueue>
+      pending_relay_messages;
+  {
+    base::AutoLock lock(broker_lock_);
+    broker_name_ = broker_name;
+    std::swap(pending_broker_clients, pending_broker_clients_);
+    std::swap(pending_relay_messages, pending_relay_messages_);
+  }
+  DCHECK(broker_name != ports::kInvalidNodeName);
+
+  // It's now possible to add both the broker and the parent as peers.
+  // Note that the broker and parent may be the same node.
+  scoped_refptr<NodeChannel> broker;
+  if (broker_name == parent_name) {
+    DCHECK(!broker_channel.is_valid());
+    broker = parent;
+  } else {
+    DCHECK(broker_channel.is_valid());
+    broker = NodeChannel::Create(this, std::move(broker_channel),
+                                 io_task_runner_, ProcessErrorCallback());
+    AddPeer(broker_name, broker, true /* start_channel */);
+  }
+
+  AddPeer(parent_name, parent, false /* start_channel */);
+
+  {
+    // Complete any port merge requests we have waiting for the parent.
+    base::AutoLock lock(pending_port_merges_lock_);
+    for (const auto& request : pending_port_merges_)
+      parent->RequestPortMerge(request.second.name(), request.first);
+    pending_port_merges_.clear();
+  }
+
+  // Feed the broker any pending children of our own.
+  while (!pending_broker_clients.empty()) {
+    const ports::NodeName& child_name = pending_broker_clients.front();
+    auto it = pending_children_.find(child_name);
+    DCHECK(it != pending_children_.end());
+    broker->AddBrokerClient(child_name, it->second->CopyRemoteProcessHandle());
+    pending_broker_clients.pop();
+  }
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+  // Have the broker relay any messages we have waiting.
+  for (auto& entry : pending_relay_messages) {
+    const ports::NodeName& destination = entry.first;
+    auto& message_queue = entry.second;
+    while (!message_queue.empty()) {
+      broker->RelayPortsMessage(destination, std::move(message_queue.front()));
+      message_queue.pop();
+    }
+  }
+#endif
+
+  DVLOG(1) << "Child " << name_ << " accepted by broker " << broker_name;
+}
+
+void NodeController::OnPortsMessage(const ports::NodeName& from_node,
+                                    Channel::MessagePtr channel_message) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  void* data;
+  size_t num_data_bytes, num_header_bytes, num_payload_bytes, num_ports_bytes;
+  if (!ParsePortsMessage(channel_message.get(), &data, &num_data_bytes,
+                         &num_header_bytes, &num_payload_bytes,
+                         &num_ports_bytes)) {
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  CHECK(channel_message);
+  std::unique_ptr<PortsMessage> ports_message(
+      new PortsMessage(num_header_bytes,
+                       num_payload_bytes,
+                       num_ports_bytes,
+                       std::move(channel_message)));
+  ports_message->set_source_node(from_node);
+  node_->AcceptMessage(ports::ScopedMessage(ports_message.release()));
+  AcceptIncomingMessages();
+}
+
+void NodeController::OnRequestPortMerge(
+    const ports::NodeName& from_node,
+    const ports::PortName& connector_port_name,
+    const std::string& token) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  DVLOG(2) << "Node " << name_ << " received RequestPortMerge for token "
+           << token << " and port " << connector_port_name << "@" << from_node;
+
+  ports::PortRef local_port;
+  {
+    base::AutoLock lock(reserved_ports_lock_);
+    auto it = reserved_ports_.find(token);
+    if (it == reserved_ports_.end()) {
+      DVLOG(1) << "Ignoring request to connect to port for unknown token "
+               << token;
+      return;
+    }
+    local_port = it->second.port;
+  }
+
+  int rv = node_->MergePorts(local_port, from_node, connector_port_name);
+  if (rv != ports::OK)
+    DLOG(ERROR) << "MergePorts failed: " << rv;
+
+  AcceptIncomingMessages();
+}
+
+void NodeController::OnRequestIntroduction(const ports::NodeName& from_node,
+                                           const ports::NodeName& name) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  scoped_refptr<NodeChannel> requestor = GetPeerChannel(from_node);
+  if (from_node == name || name == ports::kInvalidNodeName || !requestor) {
+    DLOG(ERROR) << "Rejecting invalid OnRequestIntroduction message from "
+                << from_node;
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  scoped_refptr<NodeChannel> new_friend = GetPeerChannel(name);
+  if (!new_friend) {
+    // We don't know who they're talking about!
+    requestor->Introduce(name, ScopedPlatformHandle());
+  } else {
+    PlatformChannelPair new_channel;
+    requestor->Introduce(name, new_channel.PassServerHandle());
+    new_friend->Introduce(from_node, new_channel.PassClientHandle());
+  }
+}
+
+void NodeController::OnIntroduce(const ports::NodeName& from_node,
+                                 const ports::NodeName& name,
+                                 ScopedPlatformHandle channel_handle) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  if (!channel_handle.is_valid()) {
+    node_->LostConnectionToNode(name);
+
+    DLOG(ERROR) << "Could not be introduced to peer " << name;
+    base::AutoLock lock(peers_lock_);
+    pending_peer_messages_.erase(name);
+    return;
+  }
+
+  scoped_refptr<NodeChannel> channel =
+      NodeChannel::Create(this, std::move(channel_handle), io_task_runner_,
+                          ProcessErrorCallback());
+
+  DVLOG(1) << "Adding new peer " << name << " via parent introduction.";
+  AddPeer(name, channel, true /* start_channel */);
+}
+
+void NodeController::OnBroadcast(const ports::NodeName& from_node,
+                                 Channel::MessagePtr message) {
+  DCHECK(!message->has_handles());
+
+  void* data;
+  size_t num_data_bytes, num_header_bytes, num_payload_bytes, num_ports_bytes;
+  if (!ParsePortsMessage(message.get(), &data, &num_data_bytes,
+                         &num_header_bytes, &num_payload_bytes,
+                         &num_ports_bytes)) {
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  // Broadcast messages must not contain ports.
+  if (num_ports_bytes > 0) {
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  base::AutoLock lock(peers_lock_);
+  for (auto& iter : peers_) {
+    // Copy and send the message to each known peer.
+    Channel::MessagePtr peer_message(
+        new Channel::Message(message->payload_size(), 0));
+    memcpy(peer_message->mutable_payload(), message->payload(),
+           message->payload_size());
+    iter.second->PortsMessage(std::move(peer_message));
+  }
+}
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+void NodeController::OnRelayPortsMessage(const ports::NodeName& from_node,
+                                         base::ProcessHandle from_process,
+                                         const ports::NodeName& destination,
+                                         Channel::MessagePtr message) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  if (GetBrokerChannel()) {
+    // Only the broker should be asked to relay a message.
+    LOG(ERROR) << "Non-broker refusing to relay message.";
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  // The parent should always know which process this came from.
+  DCHECK(from_process != base::kNullProcessHandle);
+
+#if defined(OS_WIN)
+  // Rewrite the handles to this (the parent) process. If the message is
+  // destined for another child process, the handles will be rewritten to that
+  // process before going out (see NodeChannel::WriteChannelMessage).
+  //
+  // TODO: We could avoid double-duplication.
+  //
+  // Note that we explicitly mark the handles as being owned by the sending
+  // process before rewriting them, in order to accommodate RewriteHandles'
+  // internal sanity checks.
+  ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+  for (size_t i = 0; i < handles->size(); ++i)
+    (*handles)[i].owning_process = from_process;
+  if (!Channel::Message::RewriteHandles(from_process,
+                                        base::GetCurrentProcessHandle(),
+                                        handles.get())) {
+    DLOG(ERROR) << "Failed to relay one or more handles.";
+  }
+  message->SetHandles(std::move(handles));
+#else
+  MachPortRelay* relay = GetMachPortRelay();
+  if (!relay) {
+    LOG(ERROR) << "Receiving Mach ports without a port relay from "
+               << from_node << ". Dropping message.";
+    return;
+  }
+  if (!relay->ExtractPortRights(message.get(), from_process)) {
+    // NodeChannel should ensure that MachPortRelay is ready for the remote
+    // process. At this point, if the port extraction failed, either something
+    // went wrong in the mach stuff, or the remote process died.
+    LOG(ERROR) << "Error on receiving Mach ports " << from_node
+               << ". Dropping message.";
+    return;
+  }
+#endif  // defined(OS_WIN)
+
+  if (destination == name_) {
+    // Great, we can deliver this message locally.
+    OnPortsMessage(from_node, std::move(message));
+    return;
+  }
+
+  scoped_refptr<NodeChannel> peer = GetPeerChannel(destination);
+  if (peer)
+    peer->PortsMessageFromRelay(from_node, std::move(message));
+  else
+    DLOG(ERROR) << "Dropping relay message for unknown node " << destination;
+}
+
+void NodeController::OnPortsMessageFromRelay(const ports::NodeName& from_node,
+                                             const ports::NodeName& source_node,
+                                             Channel::MessagePtr message) {
+  if (GetPeerChannel(from_node) != GetBrokerChannel()) {
+    LOG(ERROR) << "Refusing relayed message from non-broker node.";
+    DropPeer(from_node, nullptr);
+    return;
+  }
+
+  OnPortsMessage(source_node, std::move(message));
+}
+#endif
+
+void NodeController::OnChannelError(const ports::NodeName& from_node,
+                                    NodeChannel* channel) {
+  if (io_task_runner_->RunsTasksOnCurrentThread()) {
+    DropPeer(from_node, channel);
+    // DropPeer may have caused local port closures, so be sure to process any
+    // pending local messages.
+    AcceptIncomingMessages();
+  } else {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&NodeController::OnChannelError, base::Unretained(this),
+                   from_node, channel));
+  }
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+MachPortRelay* NodeController::GetMachPortRelay() {
+  {
+    base::AutoLock lock(parent_lock_);
+    // Return null if we're not the root.
+    if (bootstrap_parent_channel_ || parent_name_ != ports::kInvalidNodeName)
+      return nullptr;
+  }
+
+  base::AutoLock lock(mach_port_relay_lock_);
+  return mach_port_relay_.get();
+}
+#endif
+
+void NodeController::DestroyOnIOThreadShutdown() {
+  destroy_on_io_thread_shutdown_ = true;
+}
+
+void NodeController::AttemptShutdownIfRequested() {
+  if (!shutdown_callback_flag_)
+    return;
+
+  base::Closure callback;
+  {
+    base::AutoLock lock(shutdown_lock_);
+    if (shutdown_callback_.is_null())
+      return;
+    if (!node_->CanShutdownCleanly(true /* allow_local_ports */)) {
+      DVLOG(2) << "Unable to cleanly shut down node " << name_;
+      return;
+    }
+
+    callback = shutdown_callback_;
+    shutdown_callback_.Reset();
+    shutdown_callback_flag_.Set(false);
+  }
+
+  DCHECK(!callback.is_null());
+
+  callback.Run();
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/node_controller.h b/mojo/edk/system/node_controller.h
new file mode 100644
index 0000000..11d5f19
--- /dev/null
+++ b/mojo/edk/system/node_controller.h
@@ -0,0 +1,327 @@
+// Copyright 2016 The Chromium Authors. All 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_NODE_CONTROLLER_H_
+#define MOJO_EDK_SYSTEM_NODE_CONTROLLER_H_
+
+#include <memory>
+#include <queue>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/atomic_flag.h"
+#include "mojo/edk/system/node_channel.h"
+#include "mojo/edk/system/ports/name.h"
+#include "mojo/edk/system/ports/node.h"
+#include "mojo/edk/system/ports/node_delegate.h"
+
+namespace base {
+class PortProvider;
+}
+
+namespace mojo {
+namespace edk {
+
+class Broker;
+class Core;
+class MachPortRelay;
+class PortsMessage;
+
+// The owner of ports::Node which facilitates core EDK implementation. All
+// public interface methods are safe to call from any thread.
+class NodeController : public ports::NodeDelegate,
+                       public NodeChannel::Delegate {
+ public:
+  class PortObserver : public ports::UserData {
+   public:
+    virtual void OnPortStatusChanged() = 0;
+
+   protected:
+    ~PortObserver() override {}
+  };
+
+  // |core| owns and out-lives us.
+  explicit NodeController(Core* core);
+  ~NodeController() override;
+
+  const ports::NodeName& name() const { return name_; }
+  Core* core() const { return core_; }
+  ports::Node* node() const { return node_.get(); }
+  scoped_refptr<base::TaskRunner> io_task_runner() const {
+    return io_task_runner_;
+  }
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  // Create the relay used to transfer mach ports between processes.
+  void CreateMachPortRelay(base::PortProvider* port_provider);
+#endif
+
+  // Called exactly once, shortly after construction, and before any other
+  // methods are called on this object.
+  void SetIOTaskRunner(scoped_refptr<base::TaskRunner> io_task_runner);
+
+  // Connects this node to a child node. This node will initiate a handshake.
+  void ConnectToChild(base::ProcessHandle process_handle,
+                      ScopedPlatformHandle platform_handle,
+                      const std::string& child_token,
+                      const ProcessErrorCallback& process_error_callback);
+
+  // Closes all reserved ports which associated with the child process
+  // |child_token|.
+  void CloseChildPorts(const std::string& child_token);
+
+  // Connects this node to a parent node. The parent node will initiate a
+  // handshake.
+  void ConnectToParent(ScopedPlatformHandle platform_handle);
+
+  // Sets a port's observer. If |observer| is null the port's current observer
+  // is removed.
+  void SetPortObserver(const ports::PortRef& port,
+                       const scoped_refptr<PortObserver>& observer);
+
+  // Closes a port. Use this in lieu of calling Node::ClosePort() directly, as
+  // it ensures the port's observer has also been removed.
+  void ClosePort(const ports::PortRef& port);
+
+  // Sends a message on a port to its peer.
+  int SendMessage(const ports::PortRef& port_ref,
+                  std::unique_ptr<PortsMessage> message);
+
+  // Reserves a local port |port| associated with |token|. A peer holding a copy
+  // of |token| can merge one of its own ports into this one.
+  void ReservePort(const std::string& token, const ports::PortRef& port,
+                   const std::string& child_token);
+
+  // Merges a local port |port| into a port reserved by |token| in the parent.
+  void MergePortIntoParent(const std::string& token,
+                           const ports::PortRef& port);
+
+  // Merges two local ports together.
+  int MergeLocalPorts(const ports::PortRef& port0, const ports::PortRef& port1);
+
+  // Creates a new shared buffer for use in the current process.
+  scoped_refptr<PlatformSharedBuffer> CreateSharedBuffer(size_t num_bytes);
+
+  // Request that the Node be shut down cleanly. This may take an arbitrarily
+  // long time to complete, at which point |callback| will be called.
+  //
+  // Note that while it is safe to continue using the NodeController's public
+  // interface after requesting shutdown, you do so at your own risk and there
+  // is NO guarantee that new messages will be sent or ports will complete
+  // transfer.
+  void RequestShutdown(const base::Closure& callback);
+
+  // Notifies the NodeController that we received a bad message from the given
+  // node.
+  void NotifyBadMessageFrom(const ports::NodeName& source_node,
+                            const std::string& error);
+
+ private:
+  friend Core;
+
+  using NodeMap = std::unordered_map<ports::NodeName,
+                                     scoped_refptr<NodeChannel>>;
+  using OutgoingMessageQueue = std::queue<Channel::MessagePtr>;
+
+  struct ReservedPort {
+    ports::PortRef port;
+    const std::string child_token;
+  };
+
+  void ConnectToChildOnIOThread(
+      base::ProcessHandle process_handle,
+      ScopedPlatformHandle platform_handle,
+      ports::NodeName token,
+      const ProcessErrorCallback& process_error_callback);
+  void ConnectToParentOnIOThread(ScopedPlatformHandle platform_handle);
+
+  scoped_refptr<NodeChannel> GetPeerChannel(const ports::NodeName& name);
+  scoped_refptr<NodeChannel> GetParentChannel();
+  scoped_refptr<NodeChannel> GetBrokerChannel();
+
+  void AddPeer(const ports::NodeName& name,
+               scoped_refptr<NodeChannel> channel,
+               bool start_channel);
+  void DropPeer(const ports::NodeName& name, NodeChannel* channel);
+  void SendPeerMessage(const ports::NodeName& name,
+                       ports::ScopedMessage message);
+  void AcceptIncomingMessages();
+  void ProcessIncomingMessages();
+  void DropAllPeers();
+
+  // ports::NodeDelegate:
+  void GenerateRandomPortName(ports::PortName* port_name) override;
+  void AllocMessage(size_t num_header_bytes,
+                    ports::ScopedMessage* message) override;
+  void ForwardMessage(const ports::NodeName& node,
+                      ports::ScopedMessage message) override;
+  void BroadcastMessage(ports::ScopedMessage message) override;
+  void PortStatusChanged(const ports::PortRef& port) override;
+
+  // NodeChannel::Delegate:
+  void OnAcceptChild(const ports::NodeName& from_node,
+                     const ports::NodeName& parent_name,
+                     const ports::NodeName& token) override;
+  void OnAcceptParent(const ports::NodeName& from_node,
+                      const ports::NodeName& token,
+                      const ports::NodeName& child_name) override;
+  void OnAddBrokerClient(const ports::NodeName& from_node,
+                         const ports::NodeName& client_name,
+                         base::ProcessHandle process_handle) override;
+  void OnBrokerClientAdded(const ports::NodeName& from_node,
+                           const ports::NodeName& client_name,
+                           ScopedPlatformHandle broker_channel) override;
+  void OnAcceptBrokerClient(const ports::NodeName& from_node,
+                            const ports::NodeName& broker_name,
+                            ScopedPlatformHandle broker_channel) override;
+  void OnPortsMessage(const ports::NodeName& from_node,
+                      Channel::MessagePtr message) override;
+  void OnRequestPortMerge(const ports::NodeName& from_node,
+                          const ports::PortName& connector_port_name,
+                          const std::string& token) override;
+  void OnRequestIntroduction(const ports::NodeName& from_node,
+                             const ports::NodeName& name) override;
+  void OnIntroduce(const ports::NodeName& from_node,
+                   const ports::NodeName& name,
+                   ScopedPlatformHandle channel_handle) override;
+  void OnBroadcast(const ports::NodeName& from_node,
+                   Channel::MessagePtr message) override;
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+  void OnRelayPortsMessage(const ports::NodeName& from_node,
+                           base::ProcessHandle from_process,
+                           const ports::NodeName& destination,
+                           Channel::MessagePtr message) override;
+  void OnPortsMessageFromRelay(const ports::NodeName& from_node,
+                               const ports::NodeName& source_node,
+                               Channel::MessagePtr message) override;
+#endif
+  void OnChannelError(const ports::NodeName& from_node,
+                      NodeChannel* channel) override;
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  MachPortRelay* GetMachPortRelay() override;
+#endif
+
+  // Marks this NodeController for destruction when the IO thread shuts down.
+  // This is used in case Core is torn down before the IO thread. Must only be
+  // called on the IO thread.
+  void DestroyOnIOThreadShutdown();
+
+  // If there is a registered shutdown callback (meaning shutdown has been
+  // requested, this checks the Node's status to see if clean shutdown is
+  // possible. If so, shutdown is performed and the shutdown callback is run.
+  void AttemptShutdownIfRequested();
+
+  // These are safe to access from any thread as long as the Node is alive.
+  Core* const core_;
+  const ports::NodeName name_;
+  const std::unique_ptr<ports::Node> node_;
+  scoped_refptr<base::TaskRunner> io_task_runner_;
+
+  // Guards |peers_| and |pending_peer_messages_|.
+  base::Lock peers_lock_;
+
+  // Channels to known peers, including parent and children, if any.
+  NodeMap peers_;
+
+  // Outgoing message queues for peers we've heard of but can't yet talk to.
+  std::unordered_map<ports::NodeName, OutgoingMessageQueue>
+      pending_peer_messages_;
+
+  // Guards |reserved_ports_| and |pending_child_tokens_|.
+  base::Lock reserved_ports_lock_;
+
+  // Ports reserved by token. Key is the port token.
+  base::hash_map<std::string, ReservedPort> reserved_ports_;
+  // TODO(amistry): This _really_ needs to be a bimap. Unfortunately, we don't
+  // have one yet :(
+  std::unordered_map<ports::NodeName, std::string> pending_child_tokens_;
+
+  // Guards |pending_port_merges_| and |reject_pending_merges_|.
+  base::Lock pending_port_merges_lock_;
+
+  // A set of port merge requests awaiting parent connection.
+  std::vector<std::pair<std::string, ports::PortRef>> pending_port_merges_;
+
+  // Indicates that new merge requests should be rejected because the parent has
+  // disconnected.
+  bool reject_pending_merges_ = false;
+
+  // Guards |parent_name_| and |bootstrap_parent_channel_|.
+  base::Lock parent_lock_;
+
+  // The name of our parent node, if any.
+  ports::NodeName parent_name_;
+
+  // A temporary reference to the parent channel before we know their name.
+  scoped_refptr<NodeChannel> bootstrap_parent_channel_;
+
+  // Guards |broker_name_|, |pending_broker_clients_|, and
+  // |pending_relay_messages_|.
+  base::Lock broker_lock_;
+
+  // The name of our broker node, if any.
+  ports::NodeName broker_name_;
+
+  // A queue of pending child names waiting to be connected to a broker.
+  std::queue<ports::NodeName> pending_broker_clients_;
+
+  // Messages waiting to be relayed by the broker once it's known.
+  std::unordered_map<ports::NodeName, OutgoingMessageQueue>
+      pending_relay_messages_;
+
+  // Guards |incoming_messages_| and |incoming_messages_task_posted_|.
+  base::Lock messages_lock_;
+  std::queue<ports::ScopedMessage> incoming_messages_;
+  // Ensures that there is only one incoming messages task posted to the IO
+  // thread.
+  bool incoming_messages_task_posted_ = false;
+
+  // Guards |shutdown_callback_|.
+  base::Lock shutdown_lock_;
+
+  // Set by RequestShutdown(). If this is non-null, the controller will
+  // begin polling the Node to see if clean shutdown is possible any time the
+  // Node's state is modified by the controller.
+  base::Closure shutdown_callback_;
+  // Flag to fast-path checking |shutdown_callback_|.
+  AtomicFlag shutdown_callback_flag_;
+
+  // All other fields below must only be accessed on the I/O thread, i.e., the
+  // thread on which core_->io_task_runner() runs tasks.
+
+  // Channels to children during handshake.
+  NodeMap pending_children_;
+
+  // Indicates whether this object should delete itself on IO thread shutdown.
+  // Must only be accessed from the IO thread.
+  bool destroy_on_io_thread_shutdown_ = false;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
+  // Broker for sync shared buffer creation (non-Mac posix-only) in children.
+  std::unique_ptr<Broker> broker_;
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  base::Lock mach_port_relay_lock_;
+  // Relay for transferring mach ports to/from children.
+  std::unique_ptr<MachPortRelay> mach_port_relay_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(NodeController);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_NODE_CONTROLLER_H_
diff --git a/mojo/edk/system/options_validation.h b/mojo/edk/system/options_validation.h
new file mode 100644
index 0000000..e1b337d
--- /dev/null
+++ b/mojo/edk/system/options_validation.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.
+
+// 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/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+template <class Options>
+class UserOptionsReader {
+ public:
+  // Constructor from a |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(const Options* options) {
+    CHECK(options && IsAligned<MOJO_ALIGNOF(Options)>(options));
+    options_ = GetSizeForReader(options) == 0 ? nullptr : 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_; }
+
+  const Options& options() const {
+    DCHECK(is_valid());
+    return *options_;
+  }
+
+  // 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(const Options* options) {
+    uint32_t struct_size = *reinterpret_cast<const uint32_t*>(options);
+    if (struct_size < sizeof(uint32_t))
+      return 0;
+
+    return std::min(static_cast<size_t>(struct_size), sizeof(Options));
+  }
+
+  template <size_t alignment>
+  static bool IsAligned(const void* pointer) {
+    return reinterpret_cast<uintptr_t>(pointer) % alignment == 0;
+  }
+
+  const Options* options_;
+
+  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 edk
+}  // 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..a01a92c
--- /dev/null
+++ b/mojo/edk/system/options_validation_unittest.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/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 edk {
+namespace {
+
+// Declare a test options struct just as we do in actual public headers.
+
+using TestOptionsFlags = uint32_t;
+
+static_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;
+};
+static_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(&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(&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(&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(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(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(&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) {
+#if defined(OFFICIAL_BUILD)
+  const char kMemoryCheckFailedRegex[] = "";
+#else
+  const char kMemoryCheckFailedRegex[] = "Check failed";
+#endif
+
+  // Null:
+  EXPECT_DEATH_IF_SUPPORTED(
+      { UserOptionsReader<TestOptions> reader((nullptr)); },
+      kMemoryCheckFailedRegex);
+
+  // Unaligned:
+  EXPECT_DEATH_IF_SUPPORTED(
+      {
+        UserOptionsReader<TestOptions> reader(
+            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(options);
+      },
+      kMemoryCheckFailedRegex);
+}
+
+}  // namespace
+}  // namespace edk
+}  // 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..3e708c2
--- /dev/null
+++ b/mojo/edk/system/platform_handle_dispatcher.cc
@@ -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.
+
+#include "mojo/edk/system/platform_handle_dispatcher.h"
+
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+
+namespace mojo {
+namespace edk {
+
+// static
+scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Create(
+    ScopedPlatformHandle platform_handle) {
+  return new PlatformHandleDispatcher(std::move(platform_handle));
+}
+
+ScopedPlatformHandle PlatformHandleDispatcher::PassPlatformHandle() {
+  return std::move(platform_handle_);
+}
+
+Dispatcher::Type PlatformHandleDispatcher::GetType() const {
+  return Type::PLATFORM_HANDLE;
+}
+
+MojoResult PlatformHandleDispatcher::Close() {
+  base::AutoLock lock(lock_);
+  if (is_closed_ || in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  is_closed_ = true;
+  platform_handle_.reset();
+  return MOJO_RESULT_OK;
+}
+
+void PlatformHandleDispatcher::StartSerialize(uint32_t* num_bytes,
+                                              uint32_t* num_ports,
+                                              uint32_t* num_handles) {
+  *num_bytes = 0;
+  *num_ports = 0;
+  *num_handles = 1;
+}
+
+bool PlatformHandleDispatcher::EndSerialize(void* destination,
+                                            ports::PortName* ports,
+                                            PlatformHandle* handles) {
+  base::AutoLock lock(lock_);
+  if (is_closed_)
+    return false;
+  handles[0] = platform_handle_.get();
+  return true;
+}
+
+bool PlatformHandleDispatcher::BeginTransit() {
+  base::AutoLock lock(lock_);
+  if (in_transit_)
+    return false;
+  in_transit_ = !is_closed_;
+  return in_transit_;
+}
+
+void PlatformHandleDispatcher::CompleteTransitAndClose() {
+  base::AutoLock lock(lock_);
+
+  in_transit_ = false;
+  is_closed_ = true;
+
+  // The system has taken ownership of our handle.
+  ignore_result(platform_handle_.release());
+}
+
+void PlatformHandleDispatcher::CancelTransit() {
+  base::AutoLock lock(lock_);
+  in_transit_ = false;
+}
+
+// static
+scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Deserialize(
+    const void* bytes,
+    size_t num_bytes,
+    const ports::PortName* ports,
+    size_t num_ports,
+    PlatformHandle* handles,
+    size_t num_handles) {
+  if (num_bytes || num_ports || num_handles != 1)
+    return nullptr;
+
+  PlatformHandle handle;
+  std::swap(handle, handles[0]);
+
+  return PlatformHandleDispatcher::Create(ScopedPlatformHandle(handle));
+}
+
+PlatformHandleDispatcher::PlatformHandleDispatcher(
+    ScopedPlatformHandle platform_handle)
+    : platform_handle_(std::move(platform_handle)) {}
+
+PlatformHandleDispatcher::~PlatformHandleDispatcher() {
+  DCHECK(is_closed_ && !in_transit_);
+  DCHECK(!platform_handle_.is_valid());
+}
+
+}  // namespace edk
+}  // 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..a36c7a0
--- /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 "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher : public Dispatcher {
+ public:
+  static scoped_refptr<PlatformHandleDispatcher> Create(
+      ScopedPlatformHandle platform_handle);
+
+  ScopedPlatformHandle PassPlatformHandle();
+
+  // Dispatcher:
+  Type GetType() const override;
+  MojoResult Close() override;
+  void StartSerialize(uint32_t* num_bytes,
+                      uint32_t* num_ports,
+                      uint32_t* num_handles) override;
+  bool EndSerialize(void* destination,
+                    ports::PortName* ports,
+                    PlatformHandle* handles) override;
+  bool BeginTransit() override;
+  void CompleteTransitAndClose() override;
+  void CancelTransit() override;
+
+  static scoped_refptr<PlatformHandleDispatcher> Deserialize(
+      const void* bytes,
+      size_t num_bytes,
+      const ports::PortName* ports,
+      size_t num_ports,
+      PlatformHandle* handles,
+      size_t num_handles);
+
+ private:
+  PlatformHandleDispatcher(ScopedPlatformHandle platform_handle);
+  ~PlatformHandleDispatcher() override;
+
+  base::Lock lock_;
+  bool in_transit_ = false;
+  bool is_closed_ = false;
+  ScopedPlatformHandle platform_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformHandleDispatcher);
+};
+
+}  // namespace edk
+}  // 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..ad6dc38
--- /dev/null
+++ b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
@@ -0,0 +1,123 @@
+// Copyright 2014 The Chromium Authors. All 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 <utility>
+
+#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/embedder/platform_handle_vector.h"
+#include "mojo/edk/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+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()));
+
+  ScopedPlatformHandle h(test::PlatformHandleFromFILE(std::move(fp)));
+  EXPECT_FALSE(fp);
+  ASSERT_TRUE(h.is_valid());
+
+  scoped_refptr<PlatformHandleDispatcher> dispatcher =
+      PlatformHandleDispatcher::Create(std::move(h));
+  EXPECT_FALSE(h.is_valid());
+  EXPECT_EQ(Dispatcher::Type::PLATFORM_HANDLE, dispatcher->GetType());
+
+  h = dispatcher->PassPlatformHandle();
+  EXPECT_TRUE(h.is_valid());
+
+  fp = test::FILEFromPlatformHandle(std::move(h), "rb");
+  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();
+  EXPECT_FALSE(h.is_valid());
+
+  EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+TEST(PlatformHandleDispatcherTest, Serialization) {
+  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 =
+      PlatformHandleDispatcher::Create(
+          test::PlatformHandleFromFILE(std::move(fp)));
+
+  uint32_t num_bytes = 0;
+  uint32_t num_ports = 0;
+  uint32_t num_handles = 0;
+  EXPECT_TRUE(dispatcher->BeginTransit());
+  dispatcher->StartSerialize(&num_bytes, &num_ports, &num_handles);
+
+  EXPECT_EQ(0u, num_bytes);
+  EXPECT_EQ(0u, num_ports);
+  EXPECT_EQ(1u, num_handles);
+
+  ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector(1));
+  EXPECT_TRUE(dispatcher->EndSerialize(nullptr, nullptr, handles->data()));
+  dispatcher->CompleteTransitAndClose();
+
+  EXPECT_TRUE(handles->at(0).is_valid());
+
+  ScopedPlatformHandle handle = dispatcher->PassPlatformHandle();
+  EXPECT_FALSE(handle.is_valid());
+
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher->Close());
+
+  dispatcher = static_cast<PlatformHandleDispatcher*>(
+      Dispatcher::Deserialize(Dispatcher::Type::PLATFORM_HANDLE, nullptr,
+                              num_bytes, nullptr, num_ports, handles->data(),
+                              1).get());
+
+  EXPECT_FALSE(handles->at(0).is_valid());
+  EXPECT_TRUE(dispatcher->GetType() == Dispatcher::Type::PLATFORM_HANDLE);
+
+  fp = test::FILEFromPlatformHandle(dispatcher->PassPlatformHandle(), "rb");
+  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 edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/BUILD.gn b/mojo/edk/system/ports/BUILD.gn
new file mode 100644
index 0000000..239b3a4
--- /dev/null
+++ b/mojo/edk/system/ports/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright 2016 The Chromium 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("//testing/test.gni")
+
+source_set("ports") {
+  sources = [
+    "event.cc",
+    "event.h",
+    "message.cc",
+    "message.h",
+    "message_queue.cc",
+    "message_queue.h",
+    "name.cc",
+    "name.h",
+    "node.cc",
+    "node.h",
+    "node_delegate.h",
+    "port.cc",
+    "port.h",
+    "port_ref.cc",
+    "user_data.h",
+  ]
+
+  public_deps = [
+    "//base",
+  ]
+}
+
+source_set("tests") {
+  testonly = true
+
+  sources = [
+    "ports_unittest.cc",
+  ]
+
+  deps = [
+    ":ports",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/mojo/edk/system/ports/event.cc b/mojo/edk/system/ports/event.cc
new file mode 100644
index 0000000..2e22086
--- /dev/null
+++ b/mojo/edk/system/ports/event.cc
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All 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/ports/event.h"
+
+#include <string.h>
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+namespace {
+
+const size_t kPortsMessageAlignment = 8;
+
+static_assert(sizeof(PortDescriptor) % kPortsMessageAlignment == 0,
+              "Invalid PortDescriptor size.");
+
+static_assert(sizeof(EventHeader) % kPortsMessageAlignment == 0,
+              "Invalid EventHeader size.");
+
+static_assert(sizeof(UserEventData) % kPortsMessageAlignment == 0,
+              "Invalid UserEventData size.");
+
+static_assert(sizeof(ObserveProxyEventData) % kPortsMessageAlignment == 0,
+              "Invalid ObserveProxyEventData size.");
+
+static_assert(sizeof(ObserveProxyAckEventData) % kPortsMessageAlignment == 0,
+              "Invalid ObserveProxyAckEventData size.");
+
+static_assert(sizeof(ObserveClosureEventData) % kPortsMessageAlignment == 0,
+              "Invalid ObserveClosureEventData size.");
+
+static_assert(sizeof(MergePortEventData) % kPortsMessageAlignment == 0,
+              "Invalid MergePortEventData size.");
+
+}  // namespace
+
+PortDescriptor::PortDescriptor() {
+  memset(padding, 0, sizeof(padding));
+}
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/event.h b/mojo/edk/system/ports/event.h
new file mode 100644
index 0000000..a66dfc1
--- /dev/null
+++ b/mojo/edk/system/ports/event.h
@@ -0,0 +1,111 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_EVENT_H_
+#define MOJO_EDK_SYSTEM_PORTS_EVENT_H_
+
+#include <stdint.h>
+
+#include "mojo/edk/system/ports/message.h"
+#include "mojo/edk/system/ports/name.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+#pragma pack(push, 1)
+
+// TODO: Add static assertions of alignment.
+
+struct PortDescriptor {
+  PortDescriptor();
+
+  NodeName peer_node_name;
+  PortName peer_port_name;
+  NodeName referring_node_name;
+  PortName referring_port_name;
+  uint64_t next_sequence_num_to_send;
+  uint64_t next_sequence_num_to_receive;
+  uint64_t last_sequence_num_to_receive;
+  bool peer_closed;
+  char padding[7];
+};
+
+enum struct EventType : uint32_t {
+  kUser,
+  kPortAccepted,
+  kObserveProxy,
+  kObserveProxyAck,
+  kObserveClosure,
+  kMergePort,
+};
+
+struct EventHeader {
+  EventType type;
+  uint32_t padding;
+  PortName port_name;
+};
+
+struct UserEventData {
+  uint64_t sequence_num;
+  uint32_t num_ports;
+  uint32_t padding;
+};
+
+struct ObserveProxyEventData {
+  NodeName proxy_node_name;
+  PortName proxy_port_name;
+  NodeName proxy_to_node_name;
+  PortName proxy_to_port_name;
+};
+
+struct ObserveProxyAckEventData {
+  uint64_t last_sequence_num;
+};
+
+struct ObserveClosureEventData {
+  uint64_t last_sequence_num;
+};
+
+struct MergePortEventData {
+  PortName new_port_name;
+  PortDescriptor new_port_descriptor;
+};
+
+#pragma pack(pop)
+
+inline const EventHeader* GetEventHeader(const Message& message) {
+  return static_cast<const EventHeader*>(message.header_bytes());
+}
+
+inline EventHeader* GetMutableEventHeader(Message* message) {
+  return static_cast<EventHeader*>(message->mutable_header_bytes());
+}
+
+template <typename EventData>
+inline const EventData* GetEventData(const Message& message) {
+  return reinterpret_cast<const EventData*>(
+      reinterpret_cast<const char*>(GetEventHeader(message) + 1));
+}
+
+template <typename EventData>
+inline EventData* GetMutableEventData(Message* message) {
+  return reinterpret_cast<EventData*>(
+      reinterpret_cast<char*>(GetMutableEventHeader(message) + 1));
+}
+
+inline const PortDescriptor* GetPortDescriptors(const UserEventData* event) {
+  return reinterpret_cast<const PortDescriptor*>(
+      reinterpret_cast<const char*>(event + 1));
+}
+
+inline PortDescriptor* GetMutablePortDescriptors(UserEventData* event) {
+  return reinterpret_cast<PortDescriptor*>(reinterpret_cast<char*>(event + 1));
+}
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_EVENT_H_
diff --git a/mojo/edk/system/ports/message.cc b/mojo/edk/system/ports/message.cc
new file mode 100644
index 0000000..5d3c000
--- /dev/null
+++ b/mojo/edk/system/ports/message.cc
@@ -0,0 +1,100 @@
+// Copyright 2016 The Chromium Authors. 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 <limits>
+
+#include "base/logging.h"
+#include "mojo/edk/system/ports/event.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+// static
+bool Message::Parse(const void* bytes,
+                    size_t num_bytes,
+                    size_t* num_header_bytes,
+                    size_t* num_payload_bytes,
+                    size_t* num_ports_bytes) {
+  if (num_bytes < sizeof(EventHeader))
+    return false;
+  const EventHeader* header = static_cast<const EventHeader*>(bytes);
+  switch (header->type) {
+    case EventType::kUser:
+      // See below.
+      break;
+    case EventType::kPortAccepted:
+      *num_header_bytes = sizeof(EventHeader);
+      break;
+    case EventType::kObserveProxy:
+      *num_header_bytes = sizeof(EventHeader) + sizeof(ObserveProxyEventData);
+      break;
+    case EventType::kObserveProxyAck:
+      *num_header_bytes =
+          sizeof(EventHeader) + sizeof(ObserveProxyAckEventData);
+      break;
+    case EventType::kObserveClosure:
+      *num_header_bytes = sizeof(EventHeader) + sizeof(ObserveClosureEventData);
+      break;
+    case EventType::kMergePort:
+      *num_header_bytes = sizeof(EventHeader) + sizeof(MergePortEventData);
+      break;
+    default:
+      return false;
+  }
+
+  if (header->type == EventType::kUser) {
+    if (num_bytes < sizeof(EventHeader) + sizeof(UserEventData))
+      return false;
+    const UserEventData* event_data =
+        reinterpret_cast<const UserEventData*>(
+            reinterpret_cast<const char*>(header + 1));
+    if (event_data->num_ports > std::numeric_limits<uint16_t>::max())
+      return false;
+    *num_header_bytes = sizeof(EventHeader) +
+                        sizeof(UserEventData) +
+                        event_data->num_ports * sizeof(PortDescriptor);
+    *num_ports_bytes = event_data->num_ports * sizeof(PortName);
+    if (num_bytes < *num_header_bytes + *num_ports_bytes)
+      return false;
+    *num_payload_bytes = num_bytes - *num_header_bytes - *num_ports_bytes;
+  } else {
+    if (*num_header_bytes != num_bytes)
+      return false;
+    *num_payload_bytes = 0;
+    *num_ports_bytes = 0;
+  }
+
+  return true;
+}
+
+Message::Message(size_t num_payload_bytes, size_t num_ports)
+    : Message(sizeof(EventHeader) + sizeof(UserEventData) +
+                  num_ports * sizeof(PortDescriptor),
+              num_payload_bytes, num_ports * sizeof(PortName)) {
+  num_ports_ = num_ports;
+}
+
+Message::Message(size_t num_header_bytes,
+                 size_t num_payload_bytes,
+                 size_t num_ports_bytes)
+    : start_(nullptr),
+      num_header_bytes_(num_header_bytes),
+      num_ports_bytes_(num_ports_bytes),
+      num_payload_bytes_(num_payload_bytes) {
+}
+
+void Message::InitializeUserMessageHeader(void* start) {
+  start_ = static_cast<char*>(start);
+  memset(start_, 0, num_header_bytes_);
+  GetMutableEventHeader(this)->type = EventType::kUser;
+  GetMutableEventData<UserEventData>(this)->num_ports =
+      static_cast<uint32_t>(num_ports_);
+}
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/message.h b/mojo/edk/system/ports/message.h
new file mode 100644
index 0000000..95fa046
--- /dev/null
+++ b/mojo/edk/system/ports/message.h
@@ -0,0 +1,93 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_MESSAGE_H_
+#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "mojo/edk/system/ports/name.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+// A message consists of a header (array of bytes), payload (array of bytes)
+// and an array of ports. The header is used by the Node implementation.
+//
+// This class is designed to be subclassed, and the subclass is responsible for
+// providing the underlying storage. The header size will be aligned, and it
+// should be followed in memory by the array of ports and finally the payload.
+//
+// NOTE: This class does not manage the lifetime of the ports it references.
+class Message {
+ public:
+  virtual ~Message() {}
+
+  // Inspect the message at |bytes| and return the size of each section. Returns
+  // |false| if the message is malformed and |true| otherwise.
+  static bool Parse(const void* bytes,
+                    size_t num_bytes,
+                    size_t* num_header_bytes,
+                    size_t* num_payload_bytes,
+                    size_t* num_ports_bytes);
+
+  void* mutable_header_bytes() { return start_; }
+  const void* header_bytes() const { return start_; }
+  size_t num_header_bytes() const { return num_header_bytes_; }
+
+  void* mutable_payload_bytes() {
+    return start_ + num_header_bytes_ + num_ports_bytes_;
+  }
+  const void* payload_bytes() const {
+    return const_cast<Message*>(this)->mutable_payload_bytes();
+  }
+  size_t num_payload_bytes() const { return num_payload_bytes_; }
+
+  PortName* mutable_ports() {
+    return reinterpret_cast<PortName*>(start_ + num_header_bytes_);
+  }
+  const PortName* ports() const {
+    return const_cast<Message*>(this)->mutable_ports();
+  }
+  size_t num_ports_bytes() const { return num_ports_bytes_; }
+  size_t num_ports() const { return num_ports_bytes_ / sizeof(PortName); }
+
+ protected:
+  // Constructs a new Message base for a user message.
+  //
+  // Note: You MUST call InitializeUserMessageHeader() before this Message is
+  // ready for transmission.
+  Message(size_t num_payload_bytes, size_t num_ports);
+
+  // Constructs a new Message base for an internal message. Do NOT call
+  // InitializeUserMessageHeader() when using this constructor.
+  Message(size_t num_header_bytes,
+          size_t num_payload_bytes,
+          size_t num_ports_bytes);
+
+  Message(const Message& other) = delete;
+  void operator=(const Message& other) = delete;
+
+  // Initializes the header in a newly allocated message buffer to carry a
+  // user message.
+  void InitializeUserMessageHeader(void* start);
+
+  // Note: storage is [header][ports][payload].
+  char* start_ = nullptr;
+  size_t num_ports_ = 0;
+  size_t num_header_bytes_ = 0;
+  size_t num_ports_bytes_ = 0;
+  size_t num_payload_bytes_ = 0;
+};
+
+using ScopedMessage = std::unique_ptr<Message>;
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_MESSAGE_H_
diff --git a/mojo/edk/system/ports/message_queue.cc b/mojo/edk/system/ports/message_queue.cc
new file mode 100644
index 0000000..ef2e940
--- /dev/null
+++ b/mojo/edk/system/ports/message_queue.cc
@@ -0,0 +1,87 @@
+// Copyright 2016 The Chromium Authors. All 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/ports/message_queue.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "mojo/edk/system/ports/event.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+inline uint64_t GetSequenceNum(const ScopedMessage& message) {
+  return GetEventData<UserEventData>(*message)->sequence_num;
+}
+
+// Used by std::{push,pop}_heap functions
+inline bool operator<(const ScopedMessage& a, const ScopedMessage& b) {
+  return GetSequenceNum(a) > GetSequenceNum(b);
+}
+
+MessageQueue::MessageQueue() : MessageQueue(kInitialSequenceNum) {}
+
+MessageQueue::MessageQueue(uint64_t next_sequence_num)
+    : next_sequence_num_(next_sequence_num) {
+  // The message queue is blocked waiting for a message with sequence number
+  // equal to |next_sequence_num|.
+}
+
+MessageQueue::~MessageQueue() {
+#if DCHECK_IS_ON()
+  size_t num_leaked_ports = 0;
+  for (const auto& message : heap_)
+    num_leaked_ports += message->num_ports();
+  DVLOG_IF(1, num_leaked_ports > 0)
+      << "Leaking " << num_leaked_ports << " ports in unreceived messages";
+#endif
+}
+
+bool MessageQueue::HasNextMessage() const {
+  return !heap_.empty() && GetSequenceNum(heap_[0]) == next_sequence_num_;
+}
+
+void MessageQueue::GetNextMessageIf(
+    std::function<bool(const Message&)> selector,
+    ScopedMessage* message) {
+  if (!HasNextMessage() || (selector && !selector(*heap_[0].get()))) {
+    message->reset();
+    return;
+  }
+
+  std::pop_heap(heap_.begin(), heap_.end());
+  *message = std::move(heap_.back());
+  heap_.pop_back();
+
+  next_sequence_num_++;
+}
+
+void MessageQueue::AcceptMessage(ScopedMessage message,
+                                 bool* has_next_message) {
+  DCHECK(GetEventHeader(*message)->type == EventType::kUser);
+
+  // TODO: Handle sequence number roll-over.
+
+  heap_.emplace_back(std::move(message));
+  std::push_heap(heap_.begin(), heap_.end());
+
+  if (!signalable_) {
+    *has_next_message = false;
+  } else {
+    *has_next_message = (GetSequenceNum(heap_[0]) == next_sequence_num_);
+  }
+}
+
+void MessageQueue::GetReferencedPorts(std::deque<PortName>* port_names) {
+  for (const auto& message : heap_) {
+    for (size_t i = 0; i < message->num_ports(); ++i)
+      port_names->push_back(message->ports()[i]);
+  }
+}
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/message_queue.h b/mojo/edk/system/ports/message_queue.h
new file mode 100644
index 0000000..d90ac1a
--- /dev/null
+++ b/mojo/edk/system/ports/message_queue.h
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_MESSAGE_QUEUE_H_
+#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_QUEUE_H_
+
+#include <stdint.h>
+
+#include <deque>
+#include <functional>
+#include <limits>
+#include <vector>
+
+#include "base/macros.h"
+#include "mojo/edk/system/ports/message.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+const uint64_t kInitialSequenceNum = 1;
+const uint64_t kInvalidSequenceNum = std::numeric_limits<uint64_t>::max();
+
+// An incoming message queue for a port. MessageQueue keeps track of the highest
+// known sequence number and can indicate whether the next sequential message is
+// available. Thus the queue enforces message ordering for the consumer without
+// enforcing it for the producer (see AcceptMessage() below.)
+class MessageQueue {
+ public:
+  explicit MessageQueue();
+  explicit MessageQueue(uint64_t next_sequence_num);
+  ~MessageQueue();
+
+  void set_signalable(bool value) { signalable_ = value; }
+
+  uint64_t next_sequence_num() const { return next_sequence_num_; }
+
+  bool HasNextMessage() const;
+
+  // Gives ownership of the message. The selector may be null.
+  void GetNextMessageIf(std::function<bool(const Message&)> selector,
+                        ScopedMessage* message);
+
+  // Takes ownership of the message. Note: Messages are ordered, so while we
+  // have added a message to the queue, we may still be waiting on a message
+  // ahead of this one before we can let any of the messages be returned by
+  // GetNextMessage.
+  //
+  // Furthermore, once has_next_message is set to true, it will remain false
+  // until GetNextMessage is called enough times to return a null message.
+  // In other words, has_next_message acts like an edge trigger.
+  //
+  void AcceptMessage(ScopedMessage message, bool* has_next_message);
+
+  // Returns all of the ports referenced by messages in this message queue.
+  void GetReferencedPorts(std::deque<PortName>* ports);
+
+ private:
+  std::vector<ScopedMessage> heap_;
+  uint64_t next_sequence_num_;
+  bool signalable_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageQueue);
+};
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_MESSAGE_QUEUE_H_
diff --git a/mojo/edk/system/ports/name.cc b/mojo/edk/system/ports/name.cc
new file mode 100644
index 0000000..7088cbf
--- /dev/null
+++ b/mojo/edk/system/ports/name.cc
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All 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/ports/name.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+std::ostream& operator<<(std::ostream& stream, const Name& name) {
+  std::ios::fmtflags flags(stream.flags());
+  stream << std::hex << std::uppercase << name.v1;
+  if (name.v2 != 0)
+    stream << '.' << name.v2;
+  stream.flags(flags);
+  return stream;
+}
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/name.h b/mojo/edk/system/ports/name.h
new file mode 100644
index 0000000..1082719
--- /dev/null
+++ b/mojo/edk/system/ports/name.h
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_NAME_H_
+#define MOJO_EDK_SYSTEM_PORTS_NAME_H_
+
+#include <stdint.h>
+
+#include <ostream>
+#include <tuple>
+
+#include "base/hash.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+struct Name {
+  Name(uint64_t v1, uint64_t v2) : v1(v1), v2(v2) {}
+  uint64_t v1, v2;
+};
+
+inline bool operator==(const Name& a, const Name& b) {
+  return a.v1 == b.v1 && a.v2 == b.v2;
+}
+
+inline bool operator!=(const Name& a, const Name& b) {
+  return !(a == b);
+}
+
+inline bool operator<(const Name& a, const Name& b) {
+  return std::tie(a.v1, a.v2) < std::tie(b.v1, b.v2);
+}
+
+std::ostream& operator<<(std::ostream& stream, const Name& name);
+
+struct PortName : Name {
+  PortName() : Name(0, 0) {}
+  PortName(uint64_t v1, uint64_t v2) : Name(v1, v2) {}
+};
+
+const PortName kInvalidPortName = {0, 0};
+
+struct NodeName : Name {
+  NodeName() : Name(0, 0) {}
+  NodeName(uint64_t v1, uint64_t v2) : Name(v1, v2) {}
+};
+
+const NodeName kInvalidNodeName = {0, 0};
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+namespace std {
+
+template <>
+struct hash<mojo::edk::ports::PortName> {
+  std::size_t operator()(const mojo::edk::ports::PortName& name) const {
+    return base::HashInts64(name.v1, name.v2);
+  }
+};
+
+template <>
+struct hash<mojo::edk::ports::NodeName> {
+  std::size_t operator()(const mojo::edk::ports::NodeName& name) const {
+    return base::HashInts64(name.v1, name.v2);
+  }
+};
+
+}  // namespace std
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_NAME_H_
diff --git a/mojo/edk/system/ports/node.cc b/mojo/edk/system/ports/node.cc
new file mode 100644
index 0000000..128ecdf
--- /dev/null
+++ b/mojo/edk/system/ports/node.cc
@@ -0,0 +1,1394 @@
+// Copyright 2016 The Chromium Authors. All 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/ports/node.h"
+
+#include <string.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/ports/node_delegate.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+namespace {
+
+int DebugError(const char* message, int error_code) {
+  CHECK(false) << "Oops: " << message;
+  return error_code;
+}
+
+#define OOPS(x) DebugError(#x, x)
+
+bool CanAcceptMoreMessages(const Port* port) {
+  // Have we already doled out the last message (i.e., do we expect to NOT
+  // receive further messages)?
+  uint64_t next_sequence_num = port->message_queue.next_sequence_num();
+  if (port->state == Port::kClosed)
+    return false;
+  if (port->peer_closed || port->remove_proxy_on_last_message) {
+    if (port->last_sequence_num_to_receive == next_sequence_num - 1)
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+class Node::LockedPort {
+ public:
+  explicit LockedPort(Port* port) : port_(port) {
+    port_->lock.AssertAcquired();
+  }
+
+  Port* get() const { return port_; }
+  Port* operator->() const { return port_; }
+
+ private:
+  Port* const port_;
+};
+
+Node::Node(const NodeName& name, NodeDelegate* delegate)
+    : name_(name),
+      delegate_(delegate) {
+}
+
+Node::~Node() {
+  if (!ports_.empty())
+    DLOG(WARNING) << "Unclean shutdown for node " << name_;
+}
+
+bool Node::CanShutdownCleanly(bool allow_local_ports) {
+  base::AutoLock ports_lock(ports_lock_);
+
+  if (!allow_local_ports) {
+#if DCHECK_IS_ON()
+    for (auto entry : ports_) {
+      DVLOG(2) << "Port " << entry.first << " referencing node "
+               << entry.second->peer_node_name << " is blocking shutdown of "
+               << "node " << name_ << " (state=" << entry.second->state << ")";
+    }
+#endif
+    return ports_.empty();
+  }
+
+  // NOTE: This is not efficient, though it probably doesn't need to be since
+  // relatively few ports should be open during shutdown and shutdown doesn't
+  // need to be blazingly fast.
+  bool can_shutdown = true;
+  for (auto entry : ports_) {
+    base::AutoLock lock(entry.second->lock);
+    if (entry.second->peer_node_name != name_ &&
+        entry.second->state != Port::kReceiving) {
+      can_shutdown = false;
+#if DCHECK_IS_ON()
+      DVLOG(2) << "Port " << entry.first << " referencing node "
+               << entry.second->peer_node_name << " is blocking shutdown of "
+               << "node " << name_ << " (state=" << entry.second->state << ")";
+#else
+      // Exit early when not debugging.
+      break;
+#endif
+    }
+  }
+
+  return can_shutdown;
+}
+
+int Node::GetPort(const PortName& port_name, PortRef* port_ref) {
+  scoped_refptr<Port> port = GetPort(port_name);
+  if (!port)
+    return ERROR_PORT_UNKNOWN;
+
+  *port_ref = PortRef(port_name, std::move(port));
+  return OK;
+}
+
+int Node::CreateUninitializedPort(PortRef* port_ref) {
+  PortName port_name;
+  delegate_->GenerateRandomPortName(&port_name);
+
+  scoped_refptr<Port> port = make_scoped_refptr(new Port(kInitialSequenceNum,
+                                                         kInitialSequenceNum));
+  int rv = AddPortWithName(port_name, port);
+  if (rv != OK)
+    return rv;
+
+  *port_ref = PortRef(port_name, std::move(port));
+  return OK;
+}
+
+int Node::InitializePort(const PortRef& port_ref,
+                         const NodeName& peer_node_name,
+                         const PortName& peer_port_name) {
+  Port* port = port_ref.port();
+
+  {
+    base::AutoLock lock(port->lock);
+    if (port->state != Port::kUninitialized)
+      return ERROR_PORT_STATE_UNEXPECTED;
+
+    port->state = Port::kReceiving;
+    port->peer_node_name = peer_node_name;
+    port->peer_port_name = peer_port_name;
+  }
+
+  delegate_->PortStatusChanged(port_ref);
+
+  return OK;
+}
+
+int Node::CreatePortPair(PortRef* port0_ref, PortRef* port1_ref) {
+  int rv;
+
+  rv = CreateUninitializedPort(port0_ref);
+  if (rv != OK)
+    return rv;
+
+  rv = CreateUninitializedPort(port1_ref);
+  if (rv != OK)
+    return rv;
+
+  rv = InitializePort(*port0_ref, name_, port1_ref->name());
+  if (rv != OK)
+    return rv;
+
+  rv = InitializePort(*port1_ref, name_, port0_ref->name());
+  if (rv != OK)
+    return rv;
+
+  return OK;
+}
+
+int Node::SetUserData(const PortRef& port_ref,
+                      const scoped_refptr<UserData>& user_data) {
+  Port* port = port_ref.port();
+
+  base::AutoLock lock(port->lock);
+  if (port->state == Port::kClosed)
+    return ERROR_PORT_STATE_UNEXPECTED;
+
+  port->user_data = std::move(user_data);
+
+  return OK;
+}
+
+int Node::GetUserData(const PortRef& port_ref,
+                      scoped_refptr<UserData>* user_data) {
+  Port* port = port_ref.port();
+
+  base::AutoLock lock(port->lock);
+  if (port->state == Port::kClosed)
+    return ERROR_PORT_STATE_UNEXPECTED;
+
+  *user_data = port->user_data;
+
+  return OK;
+}
+
+int Node::ClosePort(const PortRef& port_ref) {
+  std::deque<PortName> referenced_port_names;
+
+  ObserveClosureEventData data;
+
+  NodeName peer_node_name;
+  PortName peer_port_name;
+  Port* port = port_ref.port();
+  {
+    // We may need to erase the port, which requires ports_lock_ to be held,
+    // but ports_lock_ must be acquired before any individual port locks.
+    base::AutoLock ports_lock(ports_lock_);
+
+    base::AutoLock lock(port->lock);
+    if (port->state == Port::kUninitialized) {
+      // If the port was not yet initialized, there's nothing interesting to do.
+      ErasePort_Locked(port_ref.name());
+      return OK;
+    }
+
+    if (port->state != Port::kReceiving)
+      return ERROR_PORT_STATE_UNEXPECTED;
+
+    port->state = Port::kClosed;
+
+    // We pass along the sequence number of the last message sent from this
+    // port to allow the peer to have the opportunity to consume all inbound
+    // messages before notifying the embedder that this port is closed.
+    data.last_sequence_num = port->next_sequence_num_to_send - 1;
+
+    peer_node_name = port->peer_node_name;
+    peer_port_name = port->peer_port_name;
+
+    // If the port being closed still has unread messages, then we need to take
+    // care to close those ports so as to avoid leaking memory.
+    port->message_queue.GetReferencedPorts(&referenced_port_names);
+
+    ErasePort_Locked(port_ref.name());
+  }
+
+  DVLOG(2) << "Sending ObserveClosure from " << port_ref.name() << "@" << name_
+           << " to " << peer_port_name << "@" << peer_node_name;
+
+  delegate_->ForwardMessage(
+      peer_node_name,
+      NewInternalMessage(peer_port_name, EventType::kObserveClosure, data));
+
+  for (const auto& name : referenced_port_names) {
+    PortRef ref;
+    if (GetPort(name, &ref) == OK)
+      ClosePort(ref);
+  }
+  return OK;
+}
+
+int Node::GetStatus(const PortRef& port_ref, PortStatus* port_status) {
+  Port* port = port_ref.port();
+
+  base::AutoLock lock(port->lock);
+
+  if (port->state != Port::kReceiving)
+    return ERROR_PORT_STATE_UNEXPECTED;
+
+  port_status->has_messages = port->message_queue.HasNextMessage();
+  port_status->receiving_messages = CanAcceptMoreMessages(port);
+  port_status->peer_closed = port->peer_closed;
+  return OK;
+}
+
+int Node::GetMessage(const PortRef& port_ref, ScopedMessage* message) {
+  return GetMessageIf(port_ref, nullptr, message);
+}
+
+int Node::GetMessageIf(const PortRef& port_ref,
+                       std::function<bool(const Message&)> selector,
+                       ScopedMessage* message) {
+  *message = nullptr;
+
+  DVLOG(2) << "GetMessageIf for " << port_ref.name() << "@" << name_;
+
+  Port* port = port_ref.port();
+  {
+    base::AutoLock lock(port->lock);
+
+    // This could also be treated like the port being unknown since the
+    // embedder should no longer be referring to a port that has been sent.
+    if (port->state != Port::kReceiving)
+      return ERROR_PORT_STATE_UNEXPECTED;
+
+    // Let the embedder get messages until there are no more before reporting
+    // that the peer closed its end.
+    if (!CanAcceptMoreMessages(port))
+      return ERROR_PORT_PEER_CLOSED;
+
+    port->message_queue.GetNextMessageIf(std::move(selector), message);
+  }
+
+  // Allow referenced ports to trigger PortStatusChanged calls.
+  if (*message) {
+    for (size_t i = 0; i < (*message)->num_ports(); ++i) {
+      const PortName& new_port_name = (*message)->ports()[i];
+      scoped_refptr<Port> new_port = GetPort(new_port_name);
+
+      DCHECK(new_port) << "Port " << new_port_name << "@" << name_
+                       << " does not exist!";
+
+      base::AutoLock lock(new_port->lock);
+
+      DCHECK(new_port->state == Port::kReceiving);
+      new_port->message_queue.set_signalable(true);
+    }
+  }
+
+  return OK;
+}
+
+int Node::SendMessage(const PortRef& port_ref, ScopedMessage message) {
+  int rv = SendMessageInternal(port_ref, &message);
+  if (rv != OK) {
+    // If send failed, close all carried ports. Note that we're careful not to
+    // close the sending port itself if it happened to be one of the encoded
+    // ports (an invalid but possible condition.)
+    for (size_t i = 0; i < message->num_ports(); ++i) {
+      if (message->ports()[i] == port_ref.name())
+        continue;
+
+      PortRef port;
+      if (GetPort(message->ports()[i], &port) == OK)
+        ClosePort(port);
+    }
+  }
+  return rv;
+}
+
+int Node::AcceptMessage(ScopedMessage message) {
+  const EventHeader* header = GetEventHeader(*message);
+  switch (header->type) {
+    case EventType::kUser:
+      return OnUserMessage(std::move(message));
+    case EventType::kPortAccepted:
+      return OnPortAccepted(header->port_name);
+    case EventType::kObserveProxy:
+      return OnObserveProxy(
+          header->port_name,
+          *GetEventData<ObserveProxyEventData>(*message));
+    case EventType::kObserveProxyAck:
+      return OnObserveProxyAck(
+          header->port_name,
+          GetEventData<ObserveProxyAckEventData>(*message)->last_sequence_num);
+    case EventType::kObserveClosure:
+      return OnObserveClosure(
+          header->port_name,
+          GetEventData<ObserveClosureEventData>(*message)->last_sequence_num);
+    case EventType::kMergePort:
+      return OnMergePort(header->port_name,
+                         *GetEventData<MergePortEventData>(*message));
+  }
+  return OOPS(ERROR_NOT_IMPLEMENTED);
+}
+
+int Node::MergePorts(const PortRef& port_ref,
+                     const NodeName& destination_node_name,
+                     const PortName& destination_port_name) {
+  Port* port = port_ref.port();
+  MergePortEventData data;
+  {
+    base::AutoLock lock(port->lock);
+
+    DVLOG(1) << "Sending MergePort from " << port_ref.name() << "@" << name_
+             << " to " << destination_port_name << "@" << destination_node_name;
+
+    // Send the port-to-merge over to the destination node so it can be merged
+    // into the port cycle atomically there.
+    data.new_port_name = port_ref.name();
+    WillSendPort(LockedPort(port), destination_node_name, &data.new_port_name,
+                 &data.new_port_descriptor);
+  }
+  delegate_->ForwardMessage(
+      destination_node_name,
+      NewInternalMessage(destination_port_name,
+                         EventType::kMergePort, data));
+  return OK;
+}
+
+int Node::MergeLocalPorts(const PortRef& port0_ref, const PortRef& port1_ref) {
+  Port* port0 = port0_ref.port();
+  Port* port1 = port1_ref.port();
+  int rv;
+  {
+    // |ports_lock_| must be held when acquiring overlapping port locks.
+    base::AutoLock ports_lock(ports_lock_);
+    base::AutoLock port0_lock(port0->lock);
+    base::AutoLock port1_lock(port1->lock);
+
+    DVLOG(1) << "Merging local ports " << port0_ref.name() << "@" << name_
+             << " and " << port1_ref.name() << "@" << name_;
+
+    if (port0->state != Port::kReceiving || port1->state != Port::kReceiving)
+      rv = ERROR_PORT_STATE_UNEXPECTED;
+    else
+      rv = MergePorts_Locked(port0_ref, port1_ref);
+  }
+
+  if (rv != OK) {
+    ClosePort(port0_ref);
+    ClosePort(port1_ref);
+  }
+
+  return rv;
+}
+
+int Node::LostConnectionToNode(const NodeName& node_name) {
+  // We can no longer send events to the given node. We also can't expect any
+  // PortAccepted events.
+
+  DVLOG(1) << "Observing lost connection from node " << name_
+           << " to node " << node_name;
+
+  DestroyAllPortsWithPeer(node_name, kInvalidPortName);
+  return OK;
+}
+
+int Node::OnUserMessage(ScopedMessage message) {
+  PortName port_name = GetEventHeader(*message)->port_name;
+  const auto* event = GetEventData<UserEventData>(*message);
+
+#if DCHECK_IS_ON()
+  std::ostringstream ports_buf;
+  for (size_t i = 0; i < message->num_ports(); ++i) {
+    if (i > 0)
+      ports_buf << ",";
+    ports_buf << message->ports()[i];
+  }
+
+  DVLOG(2) << "AcceptMessage " << event->sequence_num
+             << " [ports=" << ports_buf.str() << "] at "
+             << port_name << "@" << name_;
+#endif
+
+  scoped_refptr<Port> port = GetPort(port_name);
+
+  // Even if this port does not exist, cannot receive anymore messages or is
+  // buffering or proxying messages, we still need these ports to be bound to
+  // this node. When the message is forwarded, these ports will get transferred
+  // following the usual method. If the message cannot be accepted, then the
+  // newly bound ports will simply be closed.
+
+  for (size_t i = 0; i < message->num_ports(); ++i) {
+    int rv = AcceptPort(message->ports()[i], GetPortDescriptors(event)[i]);
+    if (rv != OK)
+      return rv;
+  }
+
+  bool has_next_message = false;
+  bool message_accepted = false;
+
+  if (port) {
+    // We may want to forward messages once the port lock is held, so we must
+    // acquire |ports_lock_| first.
+    base::AutoLock ports_lock(ports_lock_);
+    base::AutoLock lock(port->lock);
+
+    // Reject spurious messages if we've already received the last expected
+    // message.
+    if (CanAcceptMoreMessages(port.get())) {
+      message_accepted = true;
+      port->message_queue.AcceptMessage(std::move(message), &has_next_message);
+
+      if (port->state == Port::kBuffering) {
+        has_next_message = false;
+      } else if (port->state == Port::kProxying) {
+        has_next_message = false;
+
+        // Forward messages. We forward messages in sequential order here so
+        // that we maintain the message queue's notion of next sequence number.
+        // That's useful for the proxy removal process as we can tell when this
+        // port has seen all of the messages it is expected to see.
+        int rv = ForwardMessages_Locked(LockedPort(port.get()), port_name);
+        if (rv != OK)
+          return rv;
+
+        MaybeRemoveProxy_Locked(LockedPort(port.get()), port_name);
+      }
+    }
+  }
+
+  if (!message_accepted) {
+    DVLOG(2) << "Message not accepted!\n";
+    // Close all newly accepted ports as they are effectively orphaned.
+    for (size_t i = 0; i < message->num_ports(); ++i) {
+      PortRef port_ref;
+      if (GetPort(message->ports()[i], &port_ref) == OK) {
+        ClosePort(port_ref);
+      } else {
+        DLOG(WARNING) << "Cannot close non-existent port!\n";
+      }
+    }
+  } else if (has_next_message) {
+    PortRef port_ref(port_name, port);
+    delegate_->PortStatusChanged(port_ref);
+  }
+
+  return OK;
+}
+
+int Node::OnPortAccepted(const PortName& port_name) {
+  scoped_refptr<Port> port = GetPort(port_name);
+  if (!port)
+    return ERROR_PORT_UNKNOWN;
+
+  DVLOG(2) << "PortAccepted at " << port_name << "@" << name_
+           << " pointing to "
+           << port->peer_port_name << "@" << port->peer_node_name;
+
+  return BeginProxying(PortRef(port_name, port));
+}
+
+int Node::OnObserveProxy(const PortName& port_name,
+                         const ObserveProxyEventData& event) {
+  if (port_name == kInvalidPortName) {
+    // An ObserveProxy with an invalid target port name is a broadcast used to
+    // inform ports when their peer (which was itself a proxy) has become
+    // defunct due to unexpected node disconnection.
+    //
+    // Receiving ports affected by this treat it as equivalent to peer closure.
+    // Proxies affected by this can be removed and will in turn broadcast their
+    // own death with a similar message.
+    CHECK_EQ(event.proxy_to_node_name, kInvalidNodeName);
+    CHECK_EQ(event.proxy_to_port_name, kInvalidPortName);
+    DestroyAllPortsWithPeer(event.proxy_node_name, event.proxy_port_name);
+    return OK;
+  }
+
+  // The port may have already been closed locally, in which case the
+  // ObserveClosure message will contain the last_sequence_num field.
+  // We can then silently ignore this message.
+  scoped_refptr<Port> port = GetPort(port_name);
+  if (!port) {
+    DVLOG(1) << "ObserveProxy: " << port_name << "@" << name_ << " not found";
+
+    if (port_name != event.proxy_port_name &&
+        port_name != event.proxy_to_port_name) {
+      // The receiving port may have been removed while this message was in
+      // transit.  In this case, we restart the ObserveProxy circulation from
+      // the referenced proxy port to avoid leaking the proxy.
+      delegate_->ForwardMessage(
+          event.proxy_node_name,
+          NewInternalMessage(
+              event.proxy_port_name, EventType::kObserveProxy, event));
+    }
+    return OK;
+  }
+
+  DVLOG(2) << "ObserveProxy at " << port_name << "@" << name_ << ", proxy at "
+           << event.proxy_port_name << "@"
+           << event.proxy_node_name << " pointing to "
+           << event.proxy_to_port_name << "@"
+           << event.proxy_to_node_name;
+
+  {
+    base::AutoLock lock(port->lock);
+
+    if (port->peer_node_name == event.proxy_node_name &&
+        port->peer_port_name == event.proxy_port_name) {
+      if (port->state == Port::kReceiving) {
+        port->peer_node_name = event.proxy_to_node_name;
+        port->peer_port_name = event.proxy_to_port_name;
+
+        ObserveProxyAckEventData ack;
+        ack.last_sequence_num = port->next_sequence_num_to_send - 1;
+
+        delegate_->ForwardMessage(
+            event.proxy_node_name,
+            NewInternalMessage(event.proxy_port_name,
+                               EventType::kObserveProxyAck,
+                               ack));
+      } else {
+        // As a proxy ourselves, we don't know how to honor the ObserveProxy
+        // event or to populate the last_sequence_num field of ObserveProxyAck.
+        // Afterall, another port could be sending messages to our peer now
+        // that we've sent out our own ObserveProxy event.  Instead, we will
+        // send an ObserveProxyAck indicating that the ObserveProxy event
+        // should be re-sent (last_sequence_num set to kInvalidSequenceNum).
+        // However, this has to be done after we are removed as a proxy.
+        // Otherwise, we might just find ourselves back here again, which
+        // would be akin to a busy loop.
+
+        DVLOG(2) << "Delaying ObserveProxyAck to "
+                 << event.proxy_port_name << "@" << event.proxy_node_name;
+
+        ObserveProxyAckEventData ack;
+        ack.last_sequence_num = kInvalidSequenceNum;
+
+        port->send_on_proxy_removal.reset(
+            new std::pair<NodeName, ScopedMessage>(
+                event.proxy_node_name,
+                NewInternalMessage(event.proxy_port_name,
+                                   EventType::kObserveProxyAck,
+                                   ack)));
+      }
+    } else {
+      // Forward this event along to our peer. Eventually, it should find the
+      // port referring to the proxy.
+      delegate_->ForwardMessage(
+          port->peer_node_name,
+          NewInternalMessage(port->peer_port_name,
+                             EventType::kObserveProxy,
+                             event));
+    }
+  }
+  return OK;
+}
+
+int Node::OnObserveProxyAck(const PortName& port_name,
+                            uint64_t last_sequence_num) {
+  DVLOG(2) << "ObserveProxyAck at " << port_name << "@" << name_
+           << " (last_sequence_num=" << last_sequence_num << ")";
+
+  scoped_refptr<Port> port = GetPort(port_name);
+  if (!port)
+    return ERROR_PORT_UNKNOWN;  // The port may have observed closure first, so
+                                // this is not an "Oops".
+
+  {
+    base::AutoLock lock(port->lock);
+
+    if (port->state != Port::kProxying)
+      return OOPS(ERROR_PORT_STATE_UNEXPECTED);
+
+    if (last_sequence_num == kInvalidSequenceNum) {
+      // Send again.
+      InitiateProxyRemoval(LockedPort(port.get()), port_name);
+      return OK;
+    }
+
+    // We can now remove this port once we have received and forwarded the last
+    // message addressed to this port.
+    port->remove_proxy_on_last_message = true;
+    port->last_sequence_num_to_receive = last_sequence_num;
+  }
+  TryRemoveProxy(PortRef(port_name, port));
+  return OK;
+}
+
+int Node::OnObserveClosure(const PortName& port_name,
+                           uint64_t last_sequence_num) {
+  // OK if the port doesn't exist, as it may have been closed already.
+  scoped_refptr<Port> port = GetPort(port_name);
+  if (!port)
+    return OK;
+
+  // This message tells the port that it should no longer expect more messages
+  // beyond last_sequence_num. This message is forwarded along until we reach
+  // the receiving end, and this message serves as an equivalent to
+  // ObserveProxyAck.
+
+  bool notify_delegate = false;
+  ObserveClosureEventData forwarded_data;
+  NodeName peer_node_name;
+  PortName peer_port_name;
+  bool try_remove_proxy = false;
+  {
+    base::AutoLock lock(port->lock);
+
+    port->peer_closed = true;
+    port->last_sequence_num_to_receive = last_sequence_num;
+
+    DVLOG(2) << "ObserveClosure at " << port_name << "@" << name_
+             << " (state=" << port->state << ") pointing to "
+             << port->peer_port_name << "@" << port->peer_node_name
+             << " (last_sequence_num=" << last_sequence_num << ")";
+
+    // We always forward ObserveClosure, even beyond the receiving port which
+    // cares about it. This ensures that any dead-end proxies beyond that port
+    // are notified to remove themselves.
+
+    if (port->state == Port::kReceiving) {
+      notify_delegate = true;
+
+      // When forwarding along the other half of the port cycle, this will only
+      // reach dead-end proxies. Tell them we've sent our last message so they
+      // can go away.
+      //
+      // TODO: Repurposing ObserveClosure for this has the desired result but
+      // may be semantically confusing since the forwarding port is not actually
+      // closed. Consider replacing this with a new event type.
+      forwarded_data.last_sequence_num = port->next_sequence_num_to_send - 1;
+    } else {
+      // We haven't yet reached the receiving peer of the closed port, so
+      // forward the message along as-is.
+      forwarded_data.last_sequence_num = last_sequence_num;
+
+      // See about removing the port if it is a proxy as our peer won't be able
+      // to participate in proxy removal.
+      port->remove_proxy_on_last_message = true;
+      if (port->state == Port::kProxying)
+        try_remove_proxy = true;
+    }
+
+    DVLOG(2) << "Forwarding ObserveClosure from "
+             << port_name << "@" << name_ << " to peer "
+             << port->peer_port_name << "@" << port->peer_node_name
+             << " (last_sequence_num=" << forwarded_data.last_sequence_num
+             << ")";
+
+    peer_node_name = port->peer_node_name;
+    peer_port_name = port->peer_port_name;
+  }
+  if (try_remove_proxy)
+    TryRemoveProxy(PortRef(port_name, port));
+
+  delegate_->ForwardMessage(
+      peer_node_name,
+      NewInternalMessage(peer_port_name, EventType::kObserveClosure,
+                         forwarded_data));
+
+  if (notify_delegate) {
+    PortRef port_ref(port_name, port);
+    delegate_->PortStatusChanged(port_ref);
+  }
+  return OK;
+}
+
+int Node::OnMergePort(const PortName& port_name,
+                      const MergePortEventData& event) {
+  scoped_refptr<Port> port = GetPort(port_name);
+
+  DVLOG(1) << "MergePort at " << port_name << "@" << name_ << " (state="
+           << (port ? port->state : -1) << ") merging with proxy "
+           << event.new_port_name
+           << "@" << name_ << " pointing to "
+           << event.new_port_descriptor.peer_port_name << "@"
+           << event.new_port_descriptor.peer_node_name << " referred by "
+           << event.new_port_descriptor.referring_port_name << "@"
+           << event.new_port_descriptor.referring_node_name;
+
+  bool close_target_port = false;
+  bool close_new_port = false;
+
+  // Accept the new port. This is now the receiving end of the other port cycle
+  // to be merged with ours.
+  int rv = AcceptPort(event.new_port_name, event.new_port_descriptor);
+  if (rv != OK) {
+    close_target_port = true;
+  } else if (port) {
+    // BeginProxying_Locked may call MaybeRemoveProxy_Locked, which in turn
+    // needs to hold |ports_lock_|. We also acquire multiple port locks within.
+    base::AutoLock ports_lock(ports_lock_);
+    base::AutoLock lock(port->lock);
+
+    if (port->state != Port::kReceiving) {
+      close_new_port = true;
+    } else {
+      scoped_refptr<Port> new_port = GetPort_Locked(event.new_port_name);
+      base::AutoLock new_port_lock(new_port->lock);
+      DCHECK(new_port->state == Port::kReceiving);
+
+      // Both ports are locked. Now all we have to do is swap their peer
+      // information and set them up as proxies.
+
+      PortRef port0_ref(port_name, port);
+      PortRef port1_ref(event.new_port_name, new_port);
+      int rv = MergePorts_Locked(port0_ref, port1_ref);
+      if (rv == OK)
+        return rv;
+
+      close_new_port = true;
+      close_target_port = true;
+    }
+  } else {
+    close_new_port = true;
+  }
+
+  if (close_target_port) {
+    PortRef target_port;
+    rv = GetPort(port_name, &target_port);
+    DCHECK(rv == OK);
+
+    ClosePort(target_port);
+  }
+
+  if (close_new_port) {
+    PortRef new_port;
+    rv = GetPort(event.new_port_name, &new_port);
+    DCHECK(rv == OK);
+
+    ClosePort(new_port);
+  }
+
+  return ERROR_PORT_STATE_UNEXPECTED;
+}
+
+int Node::AddPortWithName(const PortName& port_name,
+                          const scoped_refptr<Port>& port) {
+  base::AutoLock lock(ports_lock_);
+
+  if (!ports_.insert(std::make_pair(port_name, port)).second)
+    return OOPS(ERROR_PORT_EXISTS);  // Suggests a bad UUID generator.
+
+  DVLOG(2) << "Created port " << port_name << "@" << name_;
+  return OK;
+}
+
+void Node::ErasePort(const PortName& port_name) {
+  base::AutoLock lock(ports_lock_);
+  ErasePort_Locked(port_name);
+}
+
+void Node::ErasePort_Locked(const PortName& port_name) {
+  ports_lock_.AssertAcquired();
+  ports_.erase(port_name);
+  DVLOG(2) << "Deleted port " << port_name << "@" << name_;
+}
+
+scoped_refptr<Port> Node::GetPort(const PortName& port_name) {
+  base::AutoLock lock(ports_lock_);
+  return GetPort_Locked(port_name);
+}
+
+scoped_refptr<Port> Node::GetPort_Locked(const PortName& port_name) {
+  ports_lock_.AssertAcquired();
+  auto iter = ports_.find(port_name);
+  if (iter == ports_.end())
+    return nullptr;
+
+  return iter->second;
+}
+
+int Node::SendMessageInternal(const PortRef& port_ref, ScopedMessage* message) {
+  ScopedMessage& m = *message;
+  for (size_t i = 0; i < m->num_ports(); ++i) {
+    if (m->ports()[i] == port_ref.name())
+      return ERROR_PORT_CANNOT_SEND_SELF;
+  }
+
+  Port* port = port_ref.port();
+  NodeName peer_node_name;
+  {
+    // We must acquire |ports_lock_| before grabbing any port locks, because
+    // WillSendMessage_Locked may need to lock multiple ports out of order.
+    base::AutoLock ports_lock(ports_lock_);
+    base::AutoLock lock(port->lock);
+
+    if (port->state != Port::kReceiving)
+      return ERROR_PORT_STATE_UNEXPECTED;
+
+    if (port->peer_closed)
+      return ERROR_PORT_PEER_CLOSED;
+
+    int rv = WillSendMessage_Locked(LockedPort(port), port_ref.name(), m.get());
+    if (rv != OK)
+      return rv;
+
+    // Beyond this point there's no sense in returning anything but OK. Even if
+    // message forwarding or acceptance fails, there's nothing the embedder can
+    // do to recover. Assume that failure beyond this point must be treated as a
+    // transport failure.
+
+    peer_node_name = port->peer_node_name;
+  }
+
+  if (peer_node_name != name_) {
+    delegate_->ForwardMessage(peer_node_name, std::move(m));
+    return OK;
+  }
+
+  int rv = AcceptMessage(std::move(m));
+  if (rv != OK) {
+    // See comment above for why we don't return an error in this case.
+    DVLOG(2) << "AcceptMessage failed: " << rv;
+  }
+
+  return OK;
+}
+
+int Node::MergePorts_Locked(const PortRef& port0_ref,
+                            const PortRef& port1_ref) {
+  Port* port0 = port0_ref.port();
+  Port* port1 = port1_ref.port();
+
+  ports_lock_.AssertAcquired();
+  port0->lock.AssertAcquired();
+  port1->lock.AssertAcquired();
+
+  CHECK(port0->state == Port::kReceiving);
+  CHECK(port1->state == Port::kReceiving);
+
+  // Ports cannot be merged with their own receiving peer!
+  if (port0->peer_node_name == name_ &&
+      port0->peer_port_name == port1_ref.name())
+    return ERROR_PORT_STATE_UNEXPECTED;
+
+  if (port1->peer_node_name == name_ &&
+      port1->peer_port_name == port0_ref.name())
+    return ERROR_PORT_STATE_UNEXPECTED;
+
+  // Only merge if both ports have never sent a message.
+  if (port0->next_sequence_num_to_send == kInitialSequenceNum &&
+      port1->next_sequence_num_to_send == kInitialSequenceNum) {
+    // Swap the ports' peer information and switch them both into buffering
+    // (eventually proxying) mode.
+
+    std::swap(port0->peer_node_name, port1->peer_node_name);
+    std::swap(port0->peer_port_name, port1->peer_port_name);
+
+    port0->state = Port::kBuffering;
+    if (port0->peer_closed)
+      port0->remove_proxy_on_last_message = true;
+
+    port1->state = Port::kBuffering;
+    if (port1->peer_closed)
+      port1->remove_proxy_on_last_message = true;
+
+    int rv1 = BeginProxying_Locked(LockedPort(port0), port0_ref.name());
+    int rv2 = BeginProxying_Locked(LockedPort(port1), port1_ref.name());
+
+    if (rv1 == OK && rv2 == OK) {
+      // If either merged port had a closed peer, its new peer needs to be
+      // informed of this.
+      if (port1->peer_closed) {
+        ObserveClosureEventData data;
+        data.last_sequence_num = port0->last_sequence_num_to_receive;
+        delegate_->ForwardMessage(
+            port0->peer_node_name,
+            NewInternalMessage(port0->peer_port_name,
+                               EventType::kObserveClosure, data));
+      }
+
+      if (port0->peer_closed) {
+        ObserveClosureEventData data;
+        data.last_sequence_num = port1->last_sequence_num_to_receive;
+        delegate_->ForwardMessage(
+            port1->peer_node_name,
+            NewInternalMessage(port1->peer_port_name,
+                               EventType::kObserveClosure, data));
+      }
+
+      return OK;
+    }
+
+    // If either proxy failed to initialize (e.g. had undeliverable messages
+    // or ended up in a bad state somehow), we keep the system in a consistent
+    // state by undoing the peer swap.
+    std::swap(port0->peer_node_name, port1->peer_node_name);
+    std::swap(port0->peer_port_name, port1->peer_port_name);
+    port0->remove_proxy_on_last_message = false;
+    port1->remove_proxy_on_last_message = false;
+    port0->state = Port::kReceiving;
+    port1->state = Port::kReceiving;
+  }
+
+  return ERROR_PORT_STATE_UNEXPECTED;
+}
+
+void Node::WillSendPort(const LockedPort& port,
+                        const NodeName& to_node_name,
+                        PortName* port_name,
+                        PortDescriptor* port_descriptor) {
+  port->lock.AssertAcquired();
+
+  PortName local_port_name = *port_name;
+
+  PortName new_port_name;
+  delegate_->GenerateRandomPortName(&new_port_name);
+
+  // Make sure we don't send messages to the new peer until after we know it
+  // exists. In the meantime, just buffer messages locally.
+  DCHECK(port->state == Port::kReceiving);
+  port->state = Port::kBuffering;
+
+  // If we already know our peer is closed, we already know this proxy can
+  // be removed once it receives and forwards its last expected message.
+  if (port->peer_closed)
+    port->remove_proxy_on_last_message = true;
+
+  *port_name = new_port_name;
+
+  port_descriptor->peer_node_name = port->peer_node_name;
+  port_descriptor->peer_port_name = port->peer_port_name;
+  port_descriptor->referring_node_name = name_;
+  port_descriptor->referring_port_name = local_port_name;
+  port_descriptor->next_sequence_num_to_send = port->next_sequence_num_to_send;
+  port_descriptor->next_sequence_num_to_receive =
+      port->message_queue.next_sequence_num();
+  port_descriptor->last_sequence_num_to_receive =
+      port->last_sequence_num_to_receive;
+  port_descriptor->peer_closed = port->peer_closed;
+  memset(port_descriptor->padding, 0, sizeof(port_descriptor->padding));
+
+  // Configure the local port to point to the new port.
+  port->peer_node_name = to_node_name;
+  port->peer_port_name = new_port_name;
+}
+
+int Node::AcceptPort(const PortName& port_name,
+                     const PortDescriptor& port_descriptor) {
+  scoped_refptr<Port> port = make_scoped_refptr(
+      new Port(port_descriptor.next_sequence_num_to_send,
+               port_descriptor.next_sequence_num_to_receive));
+  port->state = Port::kReceiving;
+  port->peer_node_name = port_descriptor.peer_node_name;
+  port->peer_port_name = port_descriptor.peer_port_name;
+  port->last_sequence_num_to_receive =
+      port_descriptor.last_sequence_num_to_receive;
+  port->peer_closed = port_descriptor.peer_closed;
+
+  DVLOG(2) << "Accepting port " << port_name << " [peer_closed="
+           << port->peer_closed << "; last_sequence_num_to_receive="
+           << port->last_sequence_num_to_receive << "]";
+
+  // A newly accepted port is not signalable until the message referencing the
+  // new port finds its way to the consumer (see GetMessageIf).
+  port->message_queue.set_signalable(false);
+
+  int rv = AddPortWithName(port_name, port);
+  if (rv != OK)
+    return rv;
+
+  // Allow referring port to forward messages.
+  delegate_->ForwardMessage(
+      port_descriptor.referring_node_name,
+      NewInternalMessage(port_descriptor.referring_port_name,
+                         EventType::kPortAccepted));
+  return OK;
+}
+
+int Node::WillSendMessage_Locked(const LockedPort& port,
+                                 const PortName& port_name,
+                                 Message* message) {
+  ports_lock_.AssertAcquired();
+  port->lock.AssertAcquired();
+
+  DCHECK(message);
+
+  // Messages may already have a sequence number if they're being forwarded
+  // by a proxy. Otherwise, use the next outgoing sequence number.
+  uint64_t* sequence_num =
+      &GetMutableEventData<UserEventData>(message)->sequence_num;
+  if (*sequence_num == 0)
+    *sequence_num = port->next_sequence_num_to_send++;
+
+#if DCHECK_IS_ON()
+  std::ostringstream ports_buf;
+  for (size_t i = 0; i < message->num_ports(); ++i) {
+    if (i > 0)
+      ports_buf << ",";
+    ports_buf << message->ports()[i];
+  }
+#endif
+
+  if (message->num_ports() > 0) {
+    // Note: Another thread could be trying to send the same ports, so we need
+    // to ensure that they are ours to send before we mutate their state.
+
+    std::vector<scoped_refptr<Port>> ports;
+    ports.resize(message->num_ports());
+
+    {
+      for (size_t i = 0; i < message->num_ports(); ++i) {
+        ports[i] = GetPort_Locked(message->ports()[i]);
+        DCHECK(ports[i]);
+
+        ports[i]->lock.Acquire();
+        int error = OK;
+        if (ports[i]->state != Port::kReceiving)
+          error = ERROR_PORT_STATE_UNEXPECTED;
+        else if (message->ports()[i] == port->peer_port_name)
+          error = ERROR_PORT_CANNOT_SEND_PEER;
+
+        if (error != OK) {
+          // Oops, we cannot send this port.
+          for (size_t j = 0; j <= i; ++j)
+            ports[i]->lock.Release();
+          // Backpedal on the sequence number.
+          port->next_sequence_num_to_send--;
+          return error;
+        }
+      }
+    }
+
+    PortDescriptor* port_descriptors =
+        GetMutablePortDescriptors(GetMutableEventData<UserEventData>(message));
+
+    for (size_t i = 0; i < message->num_ports(); ++i) {
+      WillSendPort(LockedPort(ports[i].get()),
+                   port->peer_node_name,
+                   message->mutable_ports() + i,
+                   port_descriptors + i);
+    }
+
+    for (size_t i = 0; i < message->num_ports(); ++i)
+      ports[i]->lock.Release();
+  }
+
+#if DCHECK_IS_ON()
+  DVLOG(2) << "Sending message "
+           << GetEventData<UserEventData>(*message)->sequence_num
+           << " [ports=" << ports_buf.str() << "]"
+           << " from " << port_name << "@" << name_
+           << " to " << port->peer_port_name << "@" << port->peer_node_name;
+#endif
+
+  GetMutableEventHeader(message)->port_name = port->peer_port_name;
+  return OK;
+}
+
+int Node::BeginProxying_Locked(const LockedPort& port,
+                               const PortName& port_name) {
+  ports_lock_.AssertAcquired();
+  port->lock.AssertAcquired();
+
+  if (port->state != Port::kBuffering)
+    return OOPS(ERROR_PORT_STATE_UNEXPECTED);
+
+  port->state = Port::kProxying;
+
+  int rv = ForwardMessages_Locked(LockedPort(port), port_name);
+  if (rv != OK)
+    return rv;
+
+  // We may have observed closure while buffering. In that case, we can advance
+  // to removing the proxy without sending out an ObserveProxy message. We
+  // already know the last expected message, etc.
+
+  if (port->remove_proxy_on_last_message) {
+    MaybeRemoveProxy_Locked(LockedPort(port), port_name);
+
+    // Make sure we propagate closure to our current peer.
+    ObserveClosureEventData data;
+    data.last_sequence_num = port->last_sequence_num_to_receive;
+    delegate_->ForwardMessage(
+        port->peer_node_name,
+        NewInternalMessage(port->peer_port_name,
+                           EventType::kObserveClosure, data));
+  } else {
+    InitiateProxyRemoval(LockedPort(port), port_name);
+  }
+
+  return OK;
+}
+
+int Node::BeginProxying(PortRef port_ref) {
+  Port* port = port_ref.port();
+  {
+    base::AutoLock ports_lock(ports_lock_);
+    base::AutoLock lock(port->lock);
+
+    if (port->state != Port::kBuffering)
+      return OOPS(ERROR_PORT_STATE_UNEXPECTED);
+
+    port->state = Port::kProxying;
+
+    int rv = ForwardMessages_Locked(LockedPort(port), port_ref.name());
+    if (rv != OK)
+      return rv;
+  }
+
+  bool should_remove;
+  NodeName peer_node_name;
+  ScopedMessage closure_message;
+  {
+    base::AutoLock lock(port->lock);
+    if (port->state != Port::kProxying)
+      return OOPS(ERROR_PORT_STATE_UNEXPECTED);
+
+    should_remove = port->remove_proxy_on_last_message;
+    if (should_remove) {
+      // Make sure we propagate closure to our current peer.
+      ObserveClosureEventData data;
+      data.last_sequence_num = port->last_sequence_num_to_receive;
+      peer_node_name = port->peer_node_name;
+      closure_message = NewInternalMessage(port->peer_port_name,
+                                           EventType::kObserveClosure, data);
+    } else {
+      InitiateProxyRemoval(LockedPort(port), port_ref.name());
+    }
+  }
+
+  if (should_remove) {
+    TryRemoveProxy(port_ref);
+    delegate_->ForwardMessage(peer_node_name, std::move(closure_message));
+  }
+
+  return OK;
+}
+
+int Node::ForwardMessages_Locked(const LockedPort& port,
+                                 const PortName &port_name) {
+  ports_lock_.AssertAcquired();
+  port->lock.AssertAcquired();
+
+  for (;;) {
+    ScopedMessage message;
+    port->message_queue.GetNextMessageIf(nullptr, &message);
+    if (!message)
+      break;
+
+    int rv = WillSendMessage_Locked(LockedPort(port), port_name, message.get());
+    if (rv != OK)
+      return rv;
+
+    delegate_->ForwardMessage(port->peer_node_name, std::move(message));
+  }
+  return OK;
+}
+
+void Node::InitiateProxyRemoval(const LockedPort& port,
+                                const PortName& port_name) {
+  port->lock.AssertAcquired();
+
+  // To remove this node, we start by notifying the connected graph that we are
+  // a proxy. This allows whatever port is referencing this node to skip it.
+  // Eventually, this node will receive ObserveProxyAck (or ObserveClosure if
+  // the peer was closed in the meantime).
+
+  ObserveProxyEventData data;
+  data.proxy_node_name = name_;
+  data.proxy_port_name = port_name;
+  data.proxy_to_node_name = port->peer_node_name;
+  data.proxy_to_port_name = port->peer_port_name;
+
+  delegate_->ForwardMessage(
+      port->peer_node_name,
+      NewInternalMessage(port->peer_port_name, EventType::kObserveProxy, data));
+}
+
+void Node::MaybeRemoveProxy_Locked(const LockedPort& port,
+                                   const PortName& port_name) {
+  // |ports_lock_| must be held so we can potentilaly ErasePort_Locked().
+  ports_lock_.AssertAcquired();
+  port->lock.AssertAcquired();
+
+  DCHECK(port->state == Port::kProxying);
+
+  // Make sure we have seen ObserveProxyAck before removing the port.
+  if (!port->remove_proxy_on_last_message)
+    return;
+
+  if (!CanAcceptMoreMessages(port.get())) {
+    // This proxy port is done. We can now remove it!
+    ErasePort_Locked(port_name);
+
+    if (port->send_on_proxy_removal) {
+      NodeName to_node = port->send_on_proxy_removal->first;
+      ScopedMessage& message = port->send_on_proxy_removal->second;
+
+      delegate_->ForwardMessage(to_node, std::move(message));
+      port->send_on_proxy_removal.reset();
+    }
+  } else {
+    DVLOG(2) << "Cannot remove port " << port_name << "@" << name_
+             << " now; waiting for more messages";
+  }
+}
+
+void Node::TryRemoveProxy(PortRef port_ref) {
+  Port* port = port_ref.port();
+  bool should_erase = false;
+  ScopedMessage msg;
+  NodeName to_node;
+  {
+    base::AutoLock lock(port->lock);
+
+    // Port already removed. Nothing to do.
+    if (port->state == Port::kClosed)
+      return;
+
+    DCHECK(port->state == Port::kProxying);
+
+    // Make sure we have seen ObserveProxyAck before removing the port.
+    if (!port->remove_proxy_on_last_message)
+      return;
+
+    if (!CanAcceptMoreMessages(port)) {
+      // This proxy port is done. We can now remove it!
+      should_erase = true;
+
+      if (port->send_on_proxy_removal) {
+        to_node = port->send_on_proxy_removal->first;
+        msg = std::move(port->send_on_proxy_removal->second);
+        port->send_on_proxy_removal.reset();
+      }
+    } else {
+      DVLOG(2) << "Cannot remove port " << port_ref.name() << "@" << name_
+               << " now; waiting for more messages";
+    }
+  }
+
+  if (should_erase)
+    ErasePort(port_ref.name());
+
+  if (msg)
+    delegate_->ForwardMessage(to_node, std::move(msg));
+}
+
+void Node::DestroyAllPortsWithPeer(const NodeName& node_name,
+                                   const PortName& port_name) {
+  // Wipes out all ports whose peer node matches |node_name| and whose peer port
+  // matches |port_name|. If |port_name| is |kInvalidPortName|, only the peer
+  // node is matched.
+
+  std::vector<PortRef> ports_to_notify;
+  std::vector<PortName> dead_proxies_to_broadcast;
+  std::deque<PortName> referenced_port_names;
+
+  {
+    base::AutoLock ports_lock(ports_lock_);
+
+    for (auto iter = ports_.begin(); iter != ports_.end(); ++iter) {
+      Port* port = iter->second.get();
+      {
+        base::AutoLock port_lock(port->lock);
+
+        if (port->peer_node_name == node_name &&
+              (port_name == kInvalidPortName ||
+                    port->peer_port_name == port_name)) {
+          if (!port->peer_closed) {
+            // Treat this as immediate peer closure. It's an exceptional
+            // condition akin to a broken pipe, so we don't care about losing
+            // messages.
+
+            port->peer_closed = true;
+            port->last_sequence_num_to_receive =
+                port->message_queue.next_sequence_num() - 1;
+
+            if (port->state == Port::kReceiving)
+              ports_to_notify.push_back(PortRef(iter->first, port));
+          }
+
+          // We don't expect to forward any further messages, and we don't
+          // expect to receive a Port{Accepted,Rejected} event. Because we're
+          // a proxy with no active peer, we cannot use the normal proxy removal
+          // procedure of forward-propagating an ObserveProxy. Instead we
+          // broadcast our own death so it can be back-propagated. This is
+          // inefficient but rare.
+          if (port->state != Port::kReceiving) {
+            dead_proxies_to_broadcast.push_back(iter->first);
+            iter->second->message_queue.GetReferencedPorts(
+                &referenced_port_names);
+          }
+        }
+      }
+    }
+
+    for (const auto& proxy_name : dead_proxies_to_broadcast) {
+      ports_.erase(proxy_name);
+      DVLOG(2) << "Forcibly deleted port " << proxy_name << "@" << name_;
+    }
+  }
+
+  // Wake up any receiving ports who have just observed simulated peer closure.
+  for (const auto& port : ports_to_notify)
+    delegate_->PortStatusChanged(port);
+
+  for (const auto& proxy_name : dead_proxies_to_broadcast) {
+    // Broadcast an event signifying that this proxy is no longer functioning.
+    ObserveProxyEventData event;
+    event.proxy_node_name = name_;
+    event.proxy_port_name = proxy_name;
+    event.proxy_to_node_name = kInvalidNodeName;
+    event.proxy_to_port_name = kInvalidPortName;
+    delegate_->BroadcastMessage(NewInternalMessage(
+        kInvalidPortName, EventType::kObserveProxy, event));
+
+    // Also process death locally since the port that points this closed one
+    // could be on the current node.
+    // Note: Although this is recursive, only a single port is involved which
+    // limits the expected branching to 1.
+    DestroyAllPortsWithPeer(name_, proxy_name);
+  }
+
+  // Close any ports referenced by the closed proxies.
+  for (const auto& name : referenced_port_names) {
+    PortRef ref;
+    if (GetPort(name, &ref) == OK)
+      ClosePort(ref);
+  }
+}
+
+ScopedMessage Node::NewInternalMessage_Helper(const PortName& port_name,
+                                              const EventType& type,
+                                              const void* data,
+                                              size_t num_data_bytes) {
+  ScopedMessage message;
+  delegate_->AllocMessage(sizeof(EventHeader) + num_data_bytes, &message);
+
+  EventHeader* header = GetMutableEventHeader(message.get());
+  header->port_name = port_name;
+  header->type = type;
+  header->padding = 0;
+
+  if (num_data_bytes)
+    memcpy(header + 1, data, num_data_bytes);
+
+  return message;
+}
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/node.h b/mojo/edk/system/ports/node.h
new file mode 100644
index 0000000..3aeadca
--- /dev/null
+++ b/mojo/edk/system/ports/node.h
@@ -0,0 +1,222 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_NODE_H_
+#define MOJO_EDK_SYSTEM_PORTS_NODE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <queue>
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/ports/event.h"
+#include "mojo/edk/system/ports/message.h"
+#include "mojo/edk/system/ports/name.h"
+#include "mojo/edk/system/ports/port.h"
+#include "mojo/edk/system/ports/port_ref.h"
+#include "mojo/edk/system/ports/user_data.h"
+
+#undef SendMessage  // Gah, windows
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+enum : int {
+  OK = 0,
+  ERROR_PORT_UNKNOWN = -10,
+  ERROR_PORT_EXISTS = -11,
+  ERROR_PORT_STATE_UNEXPECTED = -12,
+  ERROR_PORT_CANNOT_SEND_SELF = -13,
+  ERROR_PORT_PEER_CLOSED = -14,
+  ERROR_PORT_CANNOT_SEND_PEER = -15,
+  ERROR_NOT_IMPLEMENTED = -100,
+};
+
+struct PortStatus {
+  bool has_messages;
+  bool receiving_messages;
+  bool peer_closed;
+};
+
+class NodeDelegate;
+
+class Node {
+ public:
+  // Does not take ownership of the delegate.
+  Node(const NodeName& name, NodeDelegate* delegate);
+  ~Node();
+
+  // Returns true iff there are no open ports referring to another node or ports
+  // in the process of being transferred from this node to another. If this
+  // returns false, then to ensure clean shutdown, it is necessary to keep the
+  // node alive and continue routing messages to it via AcceptMessage. This
+  // method may be called again after AcceptMessage to check if the Node is now
+  // ready to be destroyed.
+  //
+  // If |allow_local_ports| is |true|, this will only return |false| when there
+  // are transient ports referring to other nodes.
+  bool CanShutdownCleanly(bool allow_local_ports);
+
+  // Lookup the named port.
+  int GetPort(const PortName& port_name, PortRef* port_ref);
+
+  // Creates a port on this node. Before the port can be used, it must be
+  // initialized using InitializePort. This method is useful for bootstrapping
+  // a connection between two nodes. Generally, ports are created using
+  // CreatePortPair instead.
+  int CreateUninitializedPort(PortRef* port_ref);
+
+  // Initializes a newly created port.
+  int InitializePort(const PortRef& port_ref,
+                     const NodeName& peer_node_name,
+                     const PortName& peer_port_name);
+
+  // Generates a new connected pair of ports bound to this node. These ports
+  // are initialized and ready to go.
+  int CreatePortPair(PortRef* port0_ref, PortRef* port1_ref);
+
+  // User data associated with the port.
+  int SetUserData(const PortRef& port_ref,
+                  const scoped_refptr<UserData>& user_data);
+  int GetUserData(const PortRef& port_ref,
+                  scoped_refptr<UserData>* user_data);
+
+  // Prevents further messages from being sent from this port or delivered to
+  // this port. The port is removed, and the port's peer is notified of the
+  // closure after it has consumed all pending messages.
+  int ClosePort(const PortRef& port_ref);
+
+  // Returns the current status of the port.
+  int GetStatus(const PortRef& port_ref, PortStatus* port_status);
+
+  // Returns the next available message on the specified port or returns a null
+  // message if there are none available. Returns ERROR_PORT_PEER_CLOSED to
+  // indicate that this port's peer has closed. In such cases GetMessage may
+  // be called until it yields a null message, indicating that no more messages
+  // may be read from the port.
+  int GetMessage(const PortRef& port_ref, ScopedMessage* message);
+
+  // Like GetMessage, but the caller may optionally supply a selector function
+  // that decides whether or not to return the message. If |selector| is a
+  // nullptr, then GetMessageIf acts just like GetMessage. The |selector| may
+  // not call any Node methods.
+  int GetMessageIf(const PortRef& port_ref,
+                   std::function<bool(const Message&)> selector,
+                   ScopedMessage* message);
+
+  // Sends a message from the specified port to its peer. Note that the message
+  // notification may arrive synchronously (via PortStatusChanged() on the
+  // delegate) if the peer is local to this Node.
+  int SendMessage(const PortRef& port_ref, ScopedMessage message);
+
+  // Corresponding to NodeDelegate::ForwardMessage.
+  int AcceptMessage(ScopedMessage message);
+
+  // Called to merge two ports with each other. If you have two independent
+  // port pairs A <=> B and C <=> D, the net result of merging B and C is a
+  // single connected port pair A <=> D.
+  //
+  // Note that the behavior of this operation is undefined if either port to be
+  // merged (B or C above) has ever been read from or written to directly, and
+  // this must ONLY be called on one side of the merge, though it doesn't matter
+  // which side.
+  //
+  // It is safe for the non-merged peers (A and D above) to be transferred,
+  // closed, and/or written to before, during, or after the merge.
+  int MergePorts(const PortRef& port_ref,
+                 const NodeName& destination_node_name,
+                 const PortName& destination_port_name);
+
+  // Like above but merges two ports local to this node. Because both ports are
+  // local this can also verify that neither port has been written to before the
+  // merge. If this fails for any reason, both ports are closed. Otherwise OK
+  // is returned and the ports' receiving peers are connected to each other.
+  int MergeLocalPorts(const PortRef& port0_ref, const PortRef& port1_ref);
+
+  // Called to inform this node that communication with another node is lost
+  // indefinitely. This triggers cleanup of ports bound to this node.
+  int LostConnectionToNode(const NodeName& node_name);
+
+ private:
+  class LockedPort;
+
+  // Note: Functions that end with _Locked require |ports_lock_| to be held
+  // before calling.
+  int OnUserMessage(ScopedMessage message);
+  int OnPortAccepted(const PortName& port_name);
+  int OnObserveProxy(const PortName& port_name,
+                     const ObserveProxyEventData& event);
+  int OnObserveProxyAck(const PortName& port_name, uint64_t last_sequence_num);
+  int OnObserveClosure(const PortName& port_name, uint64_t last_sequence_num);
+  int OnMergePort(const PortName& port_name, const MergePortEventData& event);
+
+  int AddPortWithName(const PortName& port_name,
+                      const scoped_refptr<Port>& port);
+  void ErasePort(const PortName& port_name);
+  void ErasePort_Locked(const PortName& port_name);
+  scoped_refptr<Port> GetPort(const PortName& port_name);
+  scoped_refptr<Port> GetPort_Locked(const PortName& port_name);
+
+  int SendMessageInternal(const PortRef& port_ref, ScopedMessage* message);
+  int MergePorts_Locked(const PortRef& port0_ref, const PortRef& port1_ref);
+  void WillSendPort(const LockedPort& port,
+                    const NodeName& to_node_name,
+                    PortName* port_name,
+                    PortDescriptor* port_descriptor);
+  int AcceptPort(const PortName& port_name,
+                 const PortDescriptor& port_descriptor);
+
+  int WillSendMessage_Locked(const LockedPort& port,
+                             const PortName& port_name,
+                             Message* message);
+  int BeginProxying_Locked(const LockedPort& port, const PortName& port_name);
+  int BeginProxying(PortRef port_ref);
+  int ForwardMessages_Locked(const LockedPort& port, const PortName& port_name);
+  void InitiateProxyRemoval(const LockedPort& port, const PortName& port_name);
+  void MaybeRemoveProxy_Locked(const LockedPort& port,
+                               const PortName& port_name);
+  void TryRemoveProxy(PortRef port_ref);
+  void DestroyAllPortsWithPeer(const NodeName& node_name,
+                               const PortName& port_name);
+
+  ScopedMessage NewInternalMessage_Helper(const PortName& port_name,
+                                          const EventType& type,
+                                          const void* data,
+                                          size_t num_data_bytes);
+
+  ScopedMessage NewInternalMessage(const PortName& port_name,
+                                   const EventType& type) {
+    return NewInternalMessage_Helper(port_name, type, nullptr, 0);
+  }
+
+  template <typename EventData>
+  ScopedMessage NewInternalMessage(const PortName& port_name,
+                                   const EventType& type,
+                                   const EventData& data) {
+    return NewInternalMessage_Helper(port_name, type, &data, sizeof(data));
+  }
+
+  const NodeName name_;
+  NodeDelegate* const delegate_;
+
+  // Guards |ports_| as well as any operation which needs to hold multiple port
+  // locks simultaneously. Usage of this is subtle: it must NEVER be acquired
+  // after a Port lock is acquired, and it must ALWAYS be acquired before
+  // calling WillSendMessage_Locked or ForwardMessages_Locked.
+  base::Lock ports_lock_;
+  std::unordered_map<PortName, scoped_refptr<Port>> ports_;
+
+  DISALLOW_COPY_AND_ASSIGN(Node);
+};
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_NODE_H_
diff --git a/mojo/edk/system/ports/node_delegate.h b/mojo/edk/system/ports/node_delegate.h
new file mode 100644
index 0000000..8547302
--- /dev/null
+++ b/mojo/edk/system/ports/node_delegate.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_NODE_DELEGATE_H_
+#define MOJO_EDK_SYSTEM_PORTS_NODE_DELEGATE_H_
+
+#include <stddef.h>
+
+#include "mojo/edk/system/ports/message.h"
+#include "mojo/edk/system/ports/name.h"
+#include "mojo/edk/system/ports/port_ref.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+class NodeDelegate {
+ public:
+  virtual ~NodeDelegate() {}
+
+  // Port names should be difficult to guess.
+  virtual void GenerateRandomPortName(PortName* port_name) = 0;
+
+  // Allocate a message, including a header that can be used by the Node
+  // implementation. |num_header_bytes| will be aligned. The newly allocated
+  // memory need not be zero-filled.
+  virtual void AllocMessage(size_t num_header_bytes,
+                            ScopedMessage* message) = 0;
+
+  // Forward a message asynchronously to the specified node. This method MUST
+  // NOT synchronously call any methods on Node.
+  virtual void ForwardMessage(const NodeName& node, ScopedMessage message) = 0;
+
+  // Broadcast a message to all nodes.
+  virtual void BroadcastMessage(ScopedMessage message) = 0;
+
+  // Indicates that the port's status has changed recently. Use Node::GetStatus
+  // to query the latest status of the port. Note, this event could be spurious
+  // if another thread is simultaneously modifying the status of the port.
+  virtual void PortStatusChanged(const PortRef& port_ref) = 0;
+};
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_NODE_DELEGATE_H_
diff --git a/mojo/edk/system/ports/port.cc b/mojo/edk/system/ports/port.cc
new file mode 100644
index 0000000..e4403ae
--- /dev/null
+++ b/mojo/edk/system/ports/port.cc
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All 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/ports/port.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+Port::Port(uint64_t next_sequence_num_to_send,
+           uint64_t next_sequence_num_to_receive)
+    : state(kUninitialized),
+      next_sequence_num_to_send(next_sequence_num_to_send),
+      last_sequence_num_to_receive(0),
+      message_queue(next_sequence_num_to_receive),
+      remove_proxy_on_last_message(false),
+      peer_closed(false) {}
+
+Port::~Port() {}
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/port.h b/mojo/edk/system/ports/port.h
new file mode 100644
index 0000000..ea53d43
--- /dev/null
+++ b/mojo/edk/system/ports/port.h
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_PORT_H_
+#define MOJO_EDK_SYSTEM_PORTS_PORT_H_
+
+#include <memory>
+#include <queue>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/ports/message_queue.h"
+#include "mojo/edk/system/ports/user_data.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+class Port : public base::RefCountedThreadSafe<Port> {
+ public:
+  enum State {
+    kUninitialized,
+    kReceiving,
+    kBuffering,
+    kProxying,
+    kClosed
+  };
+
+  base::Lock lock;
+  State state;
+  NodeName peer_node_name;
+  PortName peer_port_name;
+  uint64_t next_sequence_num_to_send;
+  uint64_t last_sequence_num_to_receive;
+  MessageQueue message_queue;
+  std::unique_ptr<std::pair<NodeName, ScopedMessage>> send_on_proxy_removal;
+  scoped_refptr<UserData> user_data;
+  bool remove_proxy_on_last_message;
+  bool peer_closed;
+
+  Port(uint64_t next_sequence_num_to_send,
+       uint64_t next_sequence_num_to_receive);
+
+ private:
+  friend class base::RefCountedThreadSafe<Port>;
+
+  ~Port();
+
+  DISALLOW_COPY_AND_ASSIGN(Port);
+};
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_PORT_H_
diff --git a/mojo/edk/system/ports/port_ref.cc b/mojo/edk/system/ports/port_ref.cc
new file mode 100644
index 0000000..bd59629
--- /dev/null
+++ b/mojo/edk/system/ports/port_ref.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All 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/ports/port_ref.h"
+
+#include "mojo/edk/system/ports/port.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+PortRef::~PortRef() {
+}
+
+PortRef::PortRef() {
+}
+
+PortRef::PortRef(const PortName& name, const scoped_refptr<Port>& port)
+    : name_(name), port_(port) {
+}
+
+PortRef::PortRef(const PortRef& other)
+    : name_(other.name_), port_(other.port_) {
+}
+
+PortRef& PortRef::operator=(const PortRef& other) {
+  if (&other != this) {
+    name_ = other.name_;
+    port_ = other.port_;
+  }
+  return *this;
+}
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/port_ref.h b/mojo/edk/system/ports/port_ref.h
new file mode 100644
index 0000000..9af4a8d
--- /dev/null
+++ b/mojo/edk/system/ports/port_ref.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_PORT_REF_H_
+#define MOJO_EDK_SYSTEM_PORTS_PORT_REF_H_
+
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/ports/name.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+class Port;
+class Node;
+
+class PortRef {
+ public:
+  ~PortRef();
+  PortRef();
+  PortRef(const PortName& name, const scoped_refptr<Port>& port);
+
+  PortRef(const PortRef& other);
+  PortRef& operator=(const PortRef& other);
+
+  const PortName& name() const { return name_; }
+
+ private:
+  friend class Node;
+  Port* port() const { return port_.get(); }
+
+  PortName name_;
+  scoped_refptr<Port> port_;
+};
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_PORT_REF_H_
diff --git a/mojo/edk/system/ports/ports_unittest.cc b/mojo/edk/system/ports/ports_unittest.cc
new file mode 100644
index 0000000..200e72b
--- /dev/null
+++ b/mojo/edk/system/ports/ports_unittest.cc
@@ -0,0 +1,1519 @@
+// Copyright 2016 The Chromium Authors. 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 <stdlib.h>
+#include <string.h>
+
+#include <map>
+#include <queue>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "mojo/edk/system/ports/event.h"
+#include "mojo/edk/system/ports/node.h"
+#include "mojo/edk/system/ports/node_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+namespace test {
+
+namespace {
+
+void LogMessage(const Message* message) {
+  std::stringstream ports;
+  for (size_t i = 0; i < message->num_ports(); ++i) {
+    if (i > 0)
+      ports << ",";
+    ports << message->ports()[i];
+  }
+  DVLOG(1) << "message: \""
+           << static_cast<const char*>(message->payload_bytes())
+           << "\" ports=[" << ports.str() << "]";
+}
+
+void ClosePortsInMessage(Node* node, Message* message) {
+  for (size_t i = 0; i < message->num_ports(); ++i) {
+    PortRef port;
+    ASSERT_EQ(OK, node->GetPort(message->ports()[i], &port));
+    EXPECT_EQ(OK, node->ClosePort(port));
+  }
+}
+
+class TestMessage : public Message {
+ public:
+  static ScopedMessage NewUserMessage(size_t num_payload_bytes,
+                                      size_t num_ports) {
+    return ScopedMessage(new TestMessage(num_payload_bytes, num_ports));
+  }
+
+  TestMessage(size_t num_payload_bytes, size_t num_ports)
+      : Message(num_payload_bytes, num_ports) {
+    start_ = new char[num_header_bytes_ + num_ports_bytes_ + num_payload_bytes];
+    InitializeUserMessageHeader(start_);
+  }
+
+  TestMessage(size_t num_header_bytes,
+              size_t num_payload_bytes,
+              size_t num_ports_bytes)
+      : Message(num_header_bytes,
+                num_payload_bytes,
+                num_ports_bytes) {
+    start_ = new char[num_header_bytes + num_payload_bytes + num_ports_bytes];
+  }
+
+  ~TestMessage() override {
+    delete[] start_;
+  }
+};
+
+struct Task {
+  Task(NodeName node_name, ScopedMessage message)
+      : node_name(node_name),
+        message(std::move(message)),
+        priority(base::RandUint64()) {
+  }
+
+  NodeName node_name;
+  ScopedMessage message;
+  uint64_t priority;
+};
+
+struct TaskComparator {
+  bool operator()(const Task* a, const Task* b) {
+    return a->priority < b->priority;
+  }
+};
+
+const size_t kMaxNodes = 3;
+
+std::priority_queue<Task*, std::vector<Task*>, TaskComparator> task_queue;
+Node* node_map[kMaxNodes];
+
+Node* GetNode(const NodeName& name) {
+  return node_map[name.v1];
+}
+
+void SetNode(const NodeName& name, Node* node) {
+  node_map[name.v1] = node;
+}
+
+void PumpTasks() {
+  while (!task_queue.empty()) {
+    Task* task = task_queue.top();
+    task_queue.pop();
+
+    Node* node = GetNode(task->node_name);
+    if (node)
+      node->AcceptMessage(std::move(task->message));
+
+    delete task;
+  }
+}
+
+void PumpUntilTask(EventType type) {
+  while (!task_queue.empty()) {
+    Task* task = task_queue.top();
+
+    const EventHeader* header = GetEventHeader(*task->message);
+    if (header->type == type)
+      return;
+
+    task_queue.pop();
+
+    Node* node = GetNode(task->node_name);
+    if (node)
+      node->AcceptMessage(std::move(task->message));
+
+    delete task;
+  }
+}
+
+void DiscardPendingTasks() {
+  while (!task_queue.empty()) {
+    Task* task = task_queue.top();
+    task_queue.pop();
+    delete task;
+  }
+}
+
+int SendStringMessage(Node* node, const PortRef& port, const std::string& s) {
+  size_t size = s.size() + 1;
+  ScopedMessage message = TestMessage::NewUserMessage(size, 0);
+  memcpy(message->mutable_payload_bytes(), s.data(), size);
+  return node->SendMessage(port, std::move(message));
+}
+
+int SendStringMessageWithPort(Node* node,
+                              const PortRef& port,
+                              const std::string& s,
+                              const PortName& sent_port_name) {
+  size_t size = s.size() + 1;
+  ScopedMessage message = TestMessage::NewUserMessage(size, 1);
+  memcpy(message->mutable_payload_bytes(), s.data(), size);
+  message->mutable_ports()[0] = sent_port_name;
+  return node->SendMessage(port, std::move(message));
+}
+
+int SendStringMessageWithPort(Node* node,
+                              const PortRef& port,
+                              const std::string& s,
+                              const PortRef& sent_port) {
+  return SendStringMessageWithPort(node, port, s, sent_port.name());
+}
+
+const char* ToString(const ScopedMessage& message) {
+  return static_cast<const char*>(message->payload_bytes());
+}
+
+class TestNodeDelegate : public NodeDelegate {
+ public:
+  explicit TestNodeDelegate(const NodeName& node_name)
+      : node_name_(node_name),
+        drop_messages_(false),
+        read_messages_(true),
+        save_messages_(false) {
+  }
+
+  void set_drop_messages(bool value) { drop_messages_ = value; }
+  void set_read_messages(bool value) { read_messages_ = value; }
+  void set_save_messages(bool value) { save_messages_ = value; }
+
+  bool GetSavedMessage(ScopedMessage* message) {
+    if (saved_messages_.empty()) {
+      message->reset();
+      return false;
+    }
+    *message = std::move(saved_messages_.front());
+    saved_messages_.pop();
+    return true;
+  }
+
+  void GenerateRandomPortName(PortName* port_name) override {
+    static uint64_t next_port_name = 1;
+    port_name->v1 = next_port_name++;
+    port_name->v2 = 0;
+  }
+
+  void AllocMessage(size_t num_header_bytes, ScopedMessage* message) override {
+    message->reset(new TestMessage(num_header_bytes, 0, 0));
+  }
+
+  void ForwardMessage(const NodeName& node_name,
+                      ScopedMessage message) override {
+    if (drop_messages_) {
+      DVLOG(1) << "Dropping ForwardMessage from node "
+               << node_name_ << " to " << node_name;
+      ClosePortsInMessage(GetNode(node_name), message.get());
+      return;
+    }
+    DVLOG(1) << "ForwardMessage from node "
+             << node_name_ << " to " << node_name;
+    task_queue.push(new Task(node_name, std::move(message)));
+  }
+
+  void BroadcastMessage(ScopedMessage message) override {
+    for (size_t i = 0; i < kMaxNodes; ++i) {
+      Node* node = node_map[i];
+      // Broadcast doesn't deliver to the local node.
+      if (node && node != GetNode(node_name_)) {
+        // NOTE: We only need to support broadcast of events, which have no
+        // payload or ports bytes.
+        ScopedMessage new_message(
+            new TestMessage(message->num_header_bytes(), 0, 0));
+        memcpy(new_message->mutable_header_bytes(), message->header_bytes(),
+               message->num_header_bytes());
+        node->AcceptMessage(std::move(new_message));
+      }
+    }
+  }
+
+  void PortStatusChanged(const PortRef& port) override {
+    DVLOG(1) << "PortStatusChanged for " << port.name() << "@" << node_name_;
+    if (!read_messages_)
+      return;
+    Node* node = GetNode(node_name_);
+    for (;;) {
+      ScopedMessage message;
+      int rv = node->GetMessage(port, &message);
+      EXPECT_TRUE(rv == OK || rv == ERROR_PORT_PEER_CLOSED);
+      if (rv == ERROR_PORT_PEER_CLOSED || !message)
+        break;
+      if (save_messages_) {
+        SaveMessage(std::move(message));
+      } else {
+        LogMessage(message.get());
+        for (size_t i = 0; i < message->num_ports(); ++i) {
+          std::stringstream buf;
+          buf << "got port: " << message->ports()[i];
+
+          PortRef received_port;
+          node->GetPort(message->ports()[i], &received_port);
+
+          SendStringMessage(node, received_port, buf.str());
+
+          // Avoid leaking these ports.
+          node->ClosePort(received_port);
+        }
+      }
+    }
+  }
+
+ private:
+  void SaveMessage(ScopedMessage message) {
+    saved_messages_.emplace(std::move(message));
+  }
+
+  std::queue<ScopedMessage> saved_messages_;
+  NodeName node_name_;
+  bool drop_messages_;
+  bool read_messages_;
+  bool save_messages_;
+};
+
+class PortsTest : public testing::Test {
+ public:
+  void SetUp() override {
+    DiscardPendingTasks();
+    SetNode(NodeName(0, 1), nullptr);
+    SetNode(NodeName(1, 1), nullptr);
+    SetNode(NodeName(2, 1), nullptr);
+  }
+};
+
+}  // namespace
+
+TEST_F(PortsTest, Basic1) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  SetNode(node1_name, &node1);
+
+  // Setup pipe between node0 and node1.
+  PortRef x0, x1;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+  EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+  EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+
+  // Transfer a port from node0 to node1.
+  PortRef a0, a1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", a1));
+
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, Basic2) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  SetNode(node1_name, &node1);
+
+  // Setup pipe between node0 and node1.
+  PortRef x0, x1;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+  EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+  EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+
+  PortRef b0, b1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&b0, &b1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", b1));
+  EXPECT_EQ(OK, SendStringMessage(&node0, b0, "hello again"));
+
+  // This may cause a SendMessage(b1) failure.
+  EXPECT_EQ(OK, node0.ClosePort(b0));
+
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, Basic3) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  SetNode(node1_name, &node1);
+
+  // Setup pipe between node0 and node1.
+  PortRef x0, x1;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+  EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+  EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+
+  // Transfer a port from node0 to node1.
+  PortRef a0, a1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", a1));
+  EXPECT_EQ(OK, SendStringMessage(&node0, a0, "hello again"));
+
+  // Transfer a0 as well.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "foo", a0));
+
+  PortRef b0, b1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&b0, &b1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "bar", b1));
+  EXPECT_EQ(OK, SendStringMessage(&node0, b0, "baz"));
+
+  // This may cause a SendMessage(b1) failure.
+  EXPECT_EQ(OK, node0.ClosePort(b0));
+
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, LostConnectionToNode1) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  SetNode(node1_name, &node1);
+
+  // Setup pipe between node0 and node1.
+  PortRef x0, x1;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+  EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+  EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+
+  // Transfer port to node1 and simulate a lost connection to node1. Dropping
+  // events from node1 is how we simulate the lost connection.
+
+  node1_delegate.set_drop_messages(true);
+
+  PortRef a0, a1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "foo", a1));
+
+  PumpTasks();
+
+  EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
+
+  PumpTasks();
+
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, LostConnectionToNode2) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  // Setup pipe between node0 and node1.
+  PortRef x0, x1;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+  EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+  EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+
+  node1_delegate.set_read_messages(false);
+
+  PortRef a0, a1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "take a1", a1));
+
+  PumpTasks();
+
+  node1_delegate.set_drop_messages(true);
+
+  EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
+
+  PumpTasks();
+
+  ScopedMessage message;
+  EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.GetMessage(a0, &message));
+  EXPECT_FALSE(message);
+
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+
+  EXPECT_EQ(OK, node1.GetMessage(x1, &message));
+  EXPECT_TRUE(message);
+  ClosePortsInMessage(&node1, message.get());
+
+  EXPECT_EQ(OK, node1.ClosePort(x1));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, LostConnectionToNodeWithSecondaryProxy) {
+  // Tests that a proxy gets cleaned up when its indirect peer lives on a lost
+  // node.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  NodeName node2_name(2, 1);
+  TestNodeDelegate node2_delegate(node2_name);
+  Node node2(node2_name, &node2_delegate);
+  node_map[2] = &node2;
+
+  node1_delegate.set_save_messages(true);
+
+  // Create A-B spanning nodes 0 and 1 and C-D spanning 1 and 2.
+  PortRef A, B, C, D;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&A));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&B));
+  EXPECT_EQ(OK, node0.InitializePort(A, node1_name, B.name()));
+  EXPECT_EQ(OK, node1.InitializePort(B, node0_name, A.name()));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&C));
+  EXPECT_EQ(OK, node2.CreateUninitializedPort(&D));
+  EXPECT_EQ(OK, node1.InitializePort(C, node2_name, D.name()));
+  EXPECT_EQ(OK, node2.InitializePort(D, node1_name, C.name()));
+
+  // Create E-F and send F over A to node 1.
+  PortRef E, F;
+  EXPECT_EQ(OK, node0.CreatePortPair(&E, &F));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, ".", F));
+
+  PumpTasks();
+
+  ScopedMessage message;
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+
+  EXPECT_EQ(OK, node1.GetPort(message->ports()[0], &F));
+
+  // Send F over C to node 2 and then simulate node 2 loss from node 1. Node 1
+  // will trivially become aware of the loss, and this test verifies that the
+  // port A on node 0 will eventually also become aware of it.
+
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node1, C, ".", F));
+
+  node_map[2] = nullptr;
+  EXPECT_EQ(OK, node1.LostConnectionToNode(node2_name));
+
+  PumpTasks();
+
+  // Port F should be gone.
+  EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.GetPort(F.name(), &F));
+
+  // Port E should have detected peer closure despite the fact that there is
+  // no longer a continuous route from F to E over which the event could travel.
+  PortStatus status;
+  EXPECT_EQ(OK, node0.GetStatus(E, &status));
+  EXPECT_TRUE(status.peer_closed);
+
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(B));
+  EXPECT_EQ(OK, node1.ClosePort(C));
+  EXPECT_EQ(OK, node0.ClosePort(E));
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, LostConnectionToNodeWithLocalProxy) {
+  // Tests that a proxy gets cleaned up when its direct peer lives on a lost
+  // node and it's predecessor lives on the same node.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  node1_delegate.set_save_messages(true);
+
+  // Create A-B spanning nodes 0 and 1.
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&A));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&B));
+  EXPECT_EQ(OK, node0.InitializePort(A, node1_name, B.name()));
+  EXPECT_EQ(OK, node1.InitializePort(B, node0_name, A.name()));
+
+  // Create C-D and send D over A to node 1.
+  PortRef C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, ".", D));
+
+  // Pump tasks until the start of port collapse for port D, which should become
+  // a proxy.
+  PumpUntilTask(EventType::kObserveProxy);
+
+  ScopedMessage message;
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+
+  PortRef E;
+  EXPECT_EQ(OK, node1.GetPort(message->ports()[0], &E));
+
+  EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
+  PumpTasks();
+
+  // Port C should have detected peer closure.
+  PortStatus status;
+  EXPECT_EQ(OK, node0.GetStatus(C, &status));
+  EXPECT_TRUE(status.peer_closed);
+
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(B));
+  EXPECT_EQ(OK, node0.ClosePort(C));
+  EXPECT_EQ(OK, node1.ClosePort(E));
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, GetMessage1) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  PortRef a0, a1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+
+  ScopedMessage message;
+  EXPECT_EQ(OK, node0.GetMessage(a0, &message));
+  EXPECT_FALSE(message);
+
+  EXPECT_EQ(OK, node0.ClosePort(a1));
+
+  EXPECT_EQ(OK, node0.GetMessage(a0, &message));
+  EXPECT_FALSE(message);
+
+  PumpTasks();
+
+  EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.GetMessage(a0, &message));
+  EXPECT_FALSE(message);
+
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, GetMessage2) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_read_messages(false);
+
+  PortRef a0, a1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+
+  EXPECT_EQ(OK, SendStringMessage(&node0, a1, "1"));
+
+  ScopedMessage message;
+  EXPECT_EQ(OK, node0.GetMessage(a0, &message));
+
+  ASSERT_TRUE(message);
+  EXPECT_EQ(0, strcmp("1", ToString(message)));
+
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(a1));
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, GetMessage3) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_read_messages(false);
+
+  PortRef a0, a1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+
+  const char* kStrings[] = {
+    "1",
+    "2",
+    "3"
+  };
+
+  for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i)
+    EXPECT_EQ(OK, SendStringMessage(&node0, a1, kStrings[i]));
+
+  ScopedMessage message;
+  for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i) {
+    EXPECT_EQ(OK, node0.GetMessage(a0, &message));
+    ASSERT_TRUE(message);
+    EXPECT_EQ(0, strcmp(kStrings[i], ToString(message)));
+    DVLOG(1) << "got " << kStrings[i];
+  }
+
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(a1));
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, Delegation1) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  node0_delegate.set_save_messages(true);
+  node1_delegate.set_save_messages(true);
+
+  // Setup pipe between node0 and node1.
+  PortRef x0, x1;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+  EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+  EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+  EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+
+  // In this test, we send a message to a port that has been moved.
+
+  PortRef a0, a1;
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "a1", a1));
+
+  PumpTasks();
+
+  ScopedMessage message;
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+
+  ASSERT_EQ(1u, message->num_ports());
+
+  // This is "a1" from the point of view of node1.
+  PortName a2_name = message->ports()[0];
+
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node1, x1, "a2", a2_name));
+
+  PumpTasks();
+
+  EXPECT_EQ(OK, SendStringMessage(&node0, a0, "hello"));
+
+  PumpTasks();
+
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+
+  ASSERT_EQ(1u, message->num_ports());
+
+  // This is "a2" from the point of view of node1.
+  PortName a3_name = message->ports()[0];
+
+  PortRef a3;
+  EXPECT_EQ(OK, node0.GetPort(a3_name, &a3));
+
+  EXPECT_EQ(0, strcmp("a2", ToString(message)));
+
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+
+  EXPECT_EQ(0u, message->num_ports());
+  EXPECT_EQ(0, strcmp("hello", ToString(message)));
+
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(a3));
+
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, Delegation2) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  node0_delegate.set_save_messages(true);
+  node1_delegate.set_save_messages(true);
+
+  for (int i = 0; i < 10; ++i) {
+    // Setup pipe a<->b between node0 and node1.
+    PortRef A, B;
+    EXPECT_EQ(OK, node0.CreateUninitializedPort(&A));
+    EXPECT_EQ(OK, node1.CreateUninitializedPort(&B));
+    EXPECT_EQ(OK, node0.InitializePort(A, node1_name, B.name()));
+    EXPECT_EQ(OK, node1.InitializePort(B, node0_name, A.name()));
+
+    PortRef C, D;
+    EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
+
+    PortRef E, F;
+    EXPECT_EQ(OK, node0.CreatePortPair(&E, &F));
+
+    // Pass D over A to B.
+    EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "1", D));
+
+    // Pass F over C to D.
+    EXPECT_EQ(OK, SendStringMessageWithPort(&node0, C, "1", F));
+
+    // This message should find its way to node1.
+    EXPECT_EQ(OK, SendStringMessage(&node0, E, "hello"));
+
+    PumpTasks();
+
+    EXPECT_EQ(OK, node0.ClosePort(C));
+    EXPECT_EQ(OK, node0.ClosePort(E));
+
+    EXPECT_EQ(OK, node0.ClosePort(A));
+    EXPECT_EQ(OK, node1.ClosePort(B));
+
+    for (;;) {
+      ScopedMessage message;
+      if (node1_delegate.GetSavedMessage(&message)) {
+        ClosePortsInMessage(&node1, message.get());
+        if (strcmp("hello", ToString(message)) == 0)
+          break;
+      } else {
+        ASSERT_TRUE(false);  // "hello" message not delivered!
+        break;
+      }
+    }
+
+    PumpTasks();  // Because ClosePort may have generated tasks.
+  }
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, SendUninitialized) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  PortRef x0;
+  EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+  EXPECT_EQ(ERROR_PORT_STATE_UNEXPECTED,
+            SendStringMessage(&node0, x0, "oops"));
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, SendFailure) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_save_messages(true);
+
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+
+  // Try to send A over itself.
+
+  EXPECT_EQ(ERROR_PORT_CANNOT_SEND_SELF,
+            SendStringMessageWithPort(&node0, A, "oops", A));
+
+  // Try to send B over A.
+
+  EXPECT_EQ(ERROR_PORT_CANNOT_SEND_PEER,
+            SendStringMessageWithPort(&node0, A, "nope", B));
+
+  // B should be closed immediately.
+  EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.GetPort(B.name(), &B));
+
+  PumpTasks();
+
+  // There should have been no messages accepted.
+  ScopedMessage message;
+  EXPECT_FALSE(node0_delegate.GetSavedMessage(&message));
+
+  EXPECT_EQ(OK, node0.ClosePort(A));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, DontLeakUnreceivedPorts) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_read_messages(false);
+
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+
+  PortRef C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
+
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "foo", D));
+
+  PumpTasks();
+
+  EXPECT_EQ(OK, node0.ClosePort(C));
+
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node0.ClosePort(B));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, AllowShutdownWithLocalPortsOpen) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_save_messages(true);
+
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+
+  PortRef C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
+
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "foo", D));
+
+  ScopedMessage message;
+  EXPECT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+
+  PortRef E;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(true));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(true));
+  EXPECT_FALSE(node0.CanShutdownCleanly(false));
+
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node0.ClosePort(B));
+  EXPECT_EQ(OK, node0.ClosePort(C));
+  EXPECT_EQ(OK, node0.ClosePort(E));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, ProxyCollapse1) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_save_messages(true);
+
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+
+  PortRef X, Y;
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
+
+  ScopedMessage message;
+
+  // Send B and receive it as C.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef C;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
+
+  // Send C and receive it as D.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", C));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef D;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D));
+
+  // Send D and receive it as E.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", D));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef E;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
+
+  EXPECT_EQ(OK, node0.ClosePort(X));
+  EXPECT_EQ(OK, node0.ClosePort(Y));
+
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node0.ClosePort(E));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, ProxyCollapse2) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_save_messages(true);
+
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+
+  PortRef X, Y;
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
+
+  ScopedMessage message;
+
+  // Send B and receive it as C.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef C;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
+
+  // Send A and receive it as D.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", A));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef D;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D));
+
+  // At this point we have a scenario with:
+  //
+  // D -> [B] -> C -> [A]
+  //
+  // Ensure that the proxies can collapse.
+
+  EXPECT_EQ(OK, node0.ClosePort(X));
+  EXPECT_EQ(OK, node0.ClosePort(Y));
+
+  EXPECT_EQ(OK, node0.ClosePort(C));
+  EXPECT_EQ(OK, node0.ClosePort(D));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, SendWithClosedPeer) {
+  // This tests that if a port is sent when its peer is already known to be
+  // closed, the newly created port will be aware of that peer closure, and the
+  // proxy will eventually collapse.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_read_messages(false);
+
+  // Send a message from A to B, then close A.
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey"));
+  EXPECT_EQ(OK, node0.ClosePort(A));
+
+  PumpTasks();
+
+  // Now send B over X-Y as new port C.
+  PortRef X, Y;
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
+
+  node0_delegate.set_read_messages(true);
+  node0_delegate.set_save_messages(true);
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
+
+  EXPECT_EQ(OK, node0.ClosePort(X));
+  EXPECT_EQ(OK, node0.ClosePort(Y));
+
+  ScopedMessage message;
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+
+  PortRef C;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
+
+  PumpTasks();
+
+  // C should receive the message originally sent to B, and it should also be
+  // aware of A's closure.
+
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
+
+  PortStatus status;
+  EXPECT_EQ(OK, node0.GetStatus(C, &status));
+  EXPECT_FALSE(status.receiving_messages);
+  EXPECT_FALSE(status.has_messages);
+  EXPECT_TRUE(status.peer_closed);
+
+  node0.ClosePort(C);
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, SendWithClosedPeerSent) {
+  // This tests that if a port is closed while some number of proxies are still
+  // routing messages (directly or indirectly) to it, that the peer port is
+  // eventually notified of the closure, and the dead-end proxies will
+  // eventually be removed.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  node0_delegate.set_save_messages(true);
+
+  PortRef X, Y;
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
+
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+
+  ScopedMessage message;
+
+  // Send A as new port C.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", A));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef C;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
+
+  // Send C as new port D.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", C));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef D;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D));
+
+  node0_delegate.set_read_messages(false);
+
+  // Send a message to B through D, then close D.
+  EXPECT_EQ(OK, SendStringMessage(&node0, D, "hey"));
+  EXPECT_EQ(OK, node0.ClosePort(D));
+
+  PumpTasks();
+
+  // Now send B as new port E.
+
+  node0_delegate.set_read_messages(true);
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
+
+  EXPECT_EQ(OK, node0.ClosePort(X));
+  EXPECT_EQ(OK, node0.ClosePort(Y));
+
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+
+  PortRef E;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
+
+  PumpTasks();
+
+  // E should receive the message originally sent to B, and it should also be
+  // aware of D's closure.
+
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
+
+  PortStatus status;
+  EXPECT_EQ(OK, node0.GetStatus(E, &status));
+  EXPECT_FALSE(status.receiving_messages);
+  EXPECT_FALSE(status.has_messages);
+  EXPECT_TRUE(status.peer_closed);
+
+  node0.ClosePort(E);
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, MergePorts) {
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  // Setup two independent port pairs, A-B on node0 and C-D on node1.
+  PortRef A, B, C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+  node0_delegate.set_read_messages(false);
+  node1_delegate.set_save_messages(true);
+
+  // Write a message on A.
+  EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey"));
+
+  PumpTasks();
+
+  // Initiate a merge between B and C.
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
+
+  PumpTasks();
+
+  // Expect only two receiving ports to be left after pumping tasks.
+  EXPECT_TRUE(node0.CanShutdownCleanly(true));
+  EXPECT_TRUE(node1.CanShutdownCleanly(true));
+
+  // Expect D to have received the message sent on A.
+  ScopedMessage message;
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
+
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(D));
+
+  // No more ports should be open.
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, MergePortWithClosedPeer1) {
+  // This tests that the right thing happens when initiating a merge on a port
+  // whose peer has already been closed.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  // Setup two independent port pairs, A-B on node0 and C-D on node1.
+  PortRef A, B, C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+  node0_delegate.set_read_messages(false);
+  node1_delegate.set_save_messages(true);
+
+  // Write a message on A.
+  EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey"));
+
+  PumpTasks();
+
+  // Close A.
+  EXPECT_EQ(OK, node0.ClosePort(A));
+
+  // Initiate a merge between B and C.
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
+
+  PumpTasks();
+
+  // Expect only one receiving port to be left after pumping tasks.
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(true));
+
+  // Expect D to have received the message sent on A.
+  ScopedMessage message;
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
+
+  EXPECT_EQ(OK, node1.ClosePort(D));
+
+  // No more ports should be open.
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, MergePortWithClosedPeer2) {
+  // This tests that the right thing happens when merging into a port whose peer
+  // has already been closed.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  // Setup two independent port pairs, A-B on node0 and C-D on node1.
+  PortRef A, B, C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+  node0_delegate.set_save_messages(true);
+  node1_delegate.set_read_messages(false);
+
+  // Write a message on D.
+  EXPECT_EQ(OK, SendStringMessage(&node0, D, "hey"));
+
+  PumpTasks();
+
+  // Close D.
+  EXPECT_EQ(OK, node1.ClosePort(D));
+
+  // Initiate a merge between B and C.
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
+
+  PumpTasks();
+
+  // Expect only one receiving port to be left after pumping tasks.
+  EXPECT_TRUE(node0.CanShutdownCleanly(true));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+
+  // Expect A to have received the message sent on D.
+  ScopedMessage message;
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
+
+  EXPECT_EQ(OK, node0.ClosePort(A));
+
+  // No more ports should be open.
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, MergePortsWithClosedPeers) {
+  // This tests that no residual ports are left behind if two ports are merged
+  // when both of their peers have been closed.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  // Setup two independent port pairs, A-B on node0 and C-D on node1.
+  PortRef A, B, C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+  node0_delegate.set_save_messages(true);
+  node1_delegate.set_read_messages(false);
+
+  // Close A and D.
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(D));
+
+  PumpTasks();
+
+  // Initiate a merge between B and C.
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
+
+  PumpTasks();
+
+  // Expect everything to have gone away.
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, MergePortsWithMovedPeers) {
+  // This tests that no ports can be merged successfully even if their peers
+  // are moved around.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  node0_delegate.set_save_messages(true);
+  node1_delegate.set_read_messages(false);
+
+  // Setup two independent port pairs, A-B on node0 and C-D on node1.
+  PortRef A, B, C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+  // Set up another pair X-Y for moving ports on node0.
+  PortRef X, Y;
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
+
+  ScopedMessage message;
+
+  // Move A to new port E.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", A));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef E;
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
+
+  EXPECT_EQ(OK, node0.ClosePort(X));
+  EXPECT_EQ(OK, node0.ClosePort(Y));
+
+  node0_delegate.set_read_messages(false);
+
+  // Write messages on E and D.
+  EXPECT_EQ(OK, SendStringMessage(&node0, E, "hey"));
+  EXPECT_EQ(OK, SendStringMessage(&node1, D, "hi"));
+
+  // Initiate a merge between B and C.
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
+
+  node0_delegate.set_read_messages(true);
+  node1_delegate.set_read_messages(true);
+  node1_delegate.set_save_messages(true);
+
+  PumpTasks();
+
+  // Expect to receive D's message on E and E's message on D.
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hi", ToString(message)));
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
+
+  // Close E and D.
+  EXPECT_EQ(OK, node0.ClosePort(E));
+  EXPECT_EQ(OK, node1.ClosePort(D));
+
+  PumpTasks();
+
+  // Expect everything to have gone away.
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+TEST_F(PortsTest, MergePortsFailsGracefully) {
+  // This tests that the system remains in a well-defined state if something
+  // goes wrong during port merge.
+
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
+
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
+
+  // Setup two independent port pairs, A-B on node0 and C-D on node1.
+  PortRef A, B, C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+  PumpTasks();
+
+  // Initiate a merge between B and C.
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
+
+  // Move C to a new port E. This is dumb and nobody should do it, but it's
+  // possible. MergePorts will fail as a result because C won't be in a
+  // receiving state when the event arrives at node1, so B should be closed.
+  ScopedMessage message;
+  PortRef X, Y;
+  EXPECT_EQ(OK, node1.CreatePortPair(&X, &Y));
+  node1_delegate.set_save_messages(true);
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node1, X, "foo", C));
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+  ASSERT_EQ(1u, message->num_ports());
+  PortRef E;
+  ASSERT_EQ(OK, node1.GetPort(message->ports()[0], &E));
+  EXPECT_EQ(OK, node1.ClosePort(X));
+  EXPECT_EQ(OK, node1.ClosePort(Y));
+
+  // C goes away as a result of normal proxy removal.
+  PumpTasks();
+
+  EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.GetPort(C.name(), &C));
+
+  // B should have been closed cleanly.
+  EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.GetPort(B.name(), &B));
+
+  // Close A, D, and E.
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(D));
+  EXPECT_EQ(OK, node1.ClosePort(E));
+
+  PumpTasks();
+
+  // Expect everything to have gone away.
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
+}
+
+}  // namespace test
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports/user_data.h b/mojo/edk/system/ports/user_data.h
new file mode 100644
index 0000000..73e7d17
--- /dev/null
+++ b/mojo/edk/system/ports/user_data.h
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_USER_DATA_H_
+#define MOJO_EDK_SYSTEM_PORTS_USER_DATA_H_
+
+#include "base/memory/ref_counted.h"
+
+namespace mojo {
+namespace edk {
+namespace ports {
+
+class UserData : public base::RefCountedThreadSafe<UserData> {
+ protected:
+  friend class base::RefCountedThreadSafe<UserData>;
+
+  virtual ~UserData() {}
+};
+
+}  // namespace ports
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_USER_DATA_H_
diff --git a/mojo/edk/system/ports_message.cc b/mojo/edk/system/ports_message.cc
new file mode 100644
index 0000000..5f3e8c0
--- /dev/null
+++ b/mojo/edk/system/ports_message.cc
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All 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/ports_message.h"
+
+#include "base/memory/ptr_util.h"
+#include "mojo/edk/system/node_channel.h"
+
+namespace mojo {
+namespace edk {
+
+// static
+std::unique_ptr<PortsMessage> PortsMessage::NewUserMessage(
+    size_t num_payload_bytes,
+    size_t num_ports,
+    size_t num_handles) {
+  return base::WrapUnique(
+      new PortsMessage(num_payload_bytes, num_ports, num_handles));
+}
+
+PortsMessage::~PortsMessage() {}
+
+PortsMessage::PortsMessage(size_t num_payload_bytes,
+                           size_t num_ports,
+                           size_t num_handles)
+    : ports::Message(num_payload_bytes, num_ports) {
+  size_t size = num_header_bytes_ + num_ports_bytes_ + num_payload_bytes;
+  void* ptr;
+  channel_message_ = NodeChannel::CreatePortsMessage(size, &ptr, num_handles);
+  InitializeUserMessageHeader(ptr);
+}
+
+PortsMessage::PortsMessage(size_t num_header_bytes,
+                           size_t num_payload_bytes,
+                           size_t num_ports_bytes,
+                           Channel::MessagePtr channel_message)
+    : ports::Message(num_header_bytes,
+                     num_payload_bytes,
+                     num_ports_bytes) {
+  if (channel_message) {
+    channel_message_ = std::move(channel_message);
+    void* data;
+    size_t num_data_bytes;
+    NodeChannel::GetPortsMessageData(channel_message_.get(), &data,
+                                     &num_data_bytes);
+    start_ = static_cast<char*>(data);
+  } else {
+    // TODO: Clean this up. In practice this branch of the constructor should
+    // only be reached from Node-internal calls to AllocMessage, which never
+    // carry ports or non-header bytes.
+    CHECK_EQ(num_payload_bytes, 0u);
+    CHECK_EQ(num_ports_bytes, 0u);
+    void* ptr;
+    channel_message_ =
+        NodeChannel::CreatePortsMessage(num_header_bytes, &ptr, 0);
+    start_ = static_cast<char*>(ptr);
+  }
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/ports_message.h b/mojo/edk/system/ports_message.h
new file mode 100644
index 0000000..542b981
--- /dev/null
+++ b/mojo/edk/system/ports_message.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All 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_PORTS_MESSAGE_H__
+#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_H__
+
+#include <memory>
+#include <utility>
+
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/ports/message.h"
+#include "mojo/edk/system/ports/name.h"
+
+namespace mojo {
+namespace edk {
+
+class NodeController;
+
+class PortsMessage : public ports::Message {
+ public:
+  static std::unique_ptr<PortsMessage> NewUserMessage(size_t num_payload_bytes,
+                                                      size_t num_ports,
+                                                      size_t num_handles);
+
+  ~PortsMessage() override;
+
+  size_t num_handles() const { return channel_message_->num_handles(); }
+  bool has_handles() const { return channel_message_->has_handles(); }
+
+  void SetHandles(ScopedPlatformHandleVectorPtr handles) {
+    channel_message_->SetHandles(std::move(handles));
+  }
+
+  ScopedPlatformHandleVectorPtr TakeHandles() {
+    return channel_message_->TakeHandles();
+  }
+
+  Channel::MessagePtr TakeChannelMessage() {
+    return std::move(channel_message_);
+  }
+
+  void set_source_node(const ports::NodeName& name) { source_node_ = name; }
+  const ports::NodeName& source_node() const { return source_node_; }
+
+ private:
+  friend class NodeController;
+
+  // Construct a new user PortsMessage backed by a new Channel::Message.
+  PortsMessage(size_t num_payload_bytes, size_t num_ports, size_t num_handles);
+
+  // Construct a new PortsMessage backed by a Channel::Message. If
+  // |channel_message| is null, a new one is allocated internally.
+  PortsMessage(size_t num_header_bytes,
+               size_t num_payload_bytes,
+               size_t num_ports_bytes,
+               Channel::MessagePtr channel_message);
+
+  Channel::MessagePtr channel_message_;
+
+  // The node name from which this message was received, if known.
+  ports::NodeName source_node_ = ports::kInvalidNodeName;
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_PORTS_MESSAGE_H__
diff --git a/mojo/edk/system/remote_message_pipe_bootstrap.cc b/mojo/edk/system/remote_message_pipe_bootstrap.cc
new file mode 100644
index 0000000..d376cca
--- /dev/null
+++ b/mojo/edk/system/remote_message_pipe_bootstrap.cc
@@ -0,0 +1,149 @@
+// Copyright 2016 The Chromium Authors. All 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/remote_message_pipe_bootstrap.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/system/node_controller.h"
+#include "mojo/edk/system/ports/name.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+struct BootstrapData {
+  // The node name of the sender.
+  ports::NodeName node_name;
+
+  // The port name of the sender's local bootstrap port.
+  ports::PortName port_name;
+};
+
+}  // namespace
+
+// static
+void RemoteMessagePipeBootstrap::Create(
+    NodeController* node_controller,
+    ScopedPlatformHandle platform_handle,
+    const ports::PortRef& port) {
+  CHECK(node_controller);
+  CHECK(node_controller->io_task_runner());
+  if (node_controller->io_task_runner()->RunsTasksOnCurrentThread()) {
+    // Owns itself.
+    new RemoteMessagePipeBootstrap(
+        node_controller, std::move(platform_handle), port);
+  } else {
+    node_controller->io_task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&RemoteMessagePipeBootstrap::Create,
+                   base::Unretained(node_controller),
+                   base::Passed(&platform_handle), port));
+  }
+}
+
+RemoteMessagePipeBootstrap::~RemoteMessagePipeBootstrap() {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+  base::MessageLoop::current()->RemoveDestructionObserver(this);
+  if (channel_)
+    channel_->ShutDown();
+}
+
+RemoteMessagePipeBootstrap::RemoteMessagePipeBootstrap(
+    NodeController* node_controller,
+    ScopedPlatformHandle platform_handle,
+    const ports::PortRef& port)
+    : node_controller_(node_controller),
+      local_port_(port),
+      io_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      channel_(Channel::Create(this, std::move(platform_handle),
+                               io_task_runner_)) {
+  base::MessageLoop::current()->AddDestructionObserver(this);
+  channel_->Start();
+
+  Channel::MessagePtr message(new Channel::Message(sizeof(BootstrapData), 0));
+  BootstrapData* data = static_cast<BootstrapData*>(message->mutable_payload());
+  data->node_name = node_controller_->name();
+  data->port_name = local_port_.name();
+  channel_->Write(std::move(message));
+}
+
+void RemoteMessagePipeBootstrap::ShutDown() {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+
+  if (shutting_down_)
+    return;
+
+  shutting_down_ = true;
+
+  // Shut down asynchronously so ShutDown() can be called from within
+  // OnChannelMessage and OnChannelError.
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&RemoteMessagePipeBootstrap::ShutDownNow,
+                 base::Unretained(this)));
+}
+
+void RemoteMessagePipeBootstrap::WillDestroyCurrentMessageLoop() {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+  ShutDownNow();
+}
+
+void RemoteMessagePipeBootstrap::OnChannelMessage(
+    const void* payload,
+    size_t payload_size,
+    ScopedPlatformHandleVectorPtr handles) {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(!handles || !handles->size());
+
+  const BootstrapData* data = static_cast<const BootstrapData*>(payload);
+  if (peer_info_received_) {
+    // This should only be a confirmation from the other end, which tells us
+    // it's now safe to shut down.
+    if (payload_size != 0)
+      DLOG(ERROR) << "Unexpected message received in message pipe bootstrap.";
+    ShutDown();
+    return;
+  }
+
+  if (payload_size != sizeof(BootstrapData)) {
+    DLOG(ERROR) << "Invalid bootstrap payload.";
+    ShutDown();
+    return;
+  }
+
+  peer_info_received_ = true;
+
+  // We need to choose one side to initiate the port merge. It doesn't matter
+  // who does it as long as they don't both try. Simple solution: pick the one
+  // with the "smaller" port name.
+  if (local_port_.name() < data->port_name) {
+    node_controller_->node()->MergePorts(local_port_, data->node_name,
+                                         data->port_name);
+  }
+
+  // Send another ping to the other end to trigger shutdown. This may race with
+  // the other end sending its own ping, but it doesn't matter. Whoever wins
+  // will cause the other end to tear down, and the ensuing channel error will
+  // in turn clean up the remaining end.
+
+  Channel::MessagePtr message(new Channel::Message(0, 0));
+  channel_->Write(std::move(message));
+}
+
+void RemoteMessagePipeBootstrap::OnChannelError() {
+  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+  ShutDown();
+}
+
+void RemoteMessagePipeBootstrap::ShutDownNow() {
+  delete this;
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/remote_message_pipe_bootstrap.h b/mojo/edk/system/remote_message_pipe_bootstrap.h
new file mode 100644
index 0000000..2a24680
--- /dev/null
+++ b/mojo/edk/system/remote_message_pipe_bootstrap.h
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All 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_REMOTE_MESSAGE_PIPE_BOOTSTRAP_H_
+#define MOJO_EDK_SYSTEM_REMOTE_MESSAGE_PIPE_BOOTSTRAP_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/ports/name.h"
+#include "mojo/edk/system/ports/port_ref.h"
+
+namespace mojo {
+namespace edk {
+
+class NodeController;
+
+// This is used by Core to negotiate a new cross-process message pipe given
+// an arbitrary platform channel. Both ends of the channel must be passed to
+// RemoteMessagePipeBootstrap::Create() in their respective processes.
+//
+// The bootstrapping procedure the same on either end:
+//
+//   1. Select a local port P to be merged with a remote port.
+//   2. Write the local node name and P's name to the bootstrap pipe.
+//   3. When a message is read from the pipe:
+//      - If it's the first message read, extract the remote node+port name and
+//        and send an empty ACK message on the pipe.
+//      - If it's the second message read, close the channel, and delete |this|.
+//   4. When an error occus on the pipe, delete |this|.
+//
+// Excluding irrecoverable error conditions such as either process dying,
+// armageddon, etc., this ensures neither end closes the channel until both ends
+// are aware of each other's port-to-merge.
+//
+// At step 3, one side of the channel is chosen to issue a message at the Ports
+// layer which eventually merges the two ports.
+class RemoteMessagePipeBootstrap
+    : public Channel::Delegate,
+      public base::MessageLoop::DestructionObserver {
+ public:
+  ~RemoteMessagePipeBootstrap() override;
+
+  // |port| must be a reference to an uninitialized local port.
+  static void Create(NodeController* node_controller,
+                     ScopedPlatformHandle platform_handle,
+                     const ports::PortRef& port);
+
+ protected:
+  explicit RemoteMessagePipeBootstrap(NodeController* node_controller,
+                                      ScopedPlatformHandle platform_handle,
+                                      const ports::PortRef& port);
+
+  void ShutDown();
+
+  bool shutting_down_ = false;
+  NodeController* node_controller_;
+  const ports::PortRef local_port_;
+
+  scoped_refptr<base::TaskRunner> io_task_runner_;
+  scoped_refptr<Channel> channel_;
+
+  bool peer_info_received_ = false;
+
+ private:
+  // base::MessageLoop::DestructionObserver:
+  void WillDestroyCurrentMessageLoop() override;
+
+  // Channel::Delegate:
+  void OnChannelMessage(const void* payload,
+                        size_t payload_size,
+                        ScopedPlatformHandleVectorPtr handles) override;
+  void OnChannelError() override;
+
+  void ShutDownNow();
+
+  DISALLOW_COPY_AND_ASSIGN(RemoteMessagePipeBootstrap);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_REMOTE_MESSAGE_PIPE_BOOTSTRAP_H_
diff --git a/mojo/edk/system/request_context.cc b/mojo/edk/system/request_context.cc
new file mode 100644
index 0000000..276e39f
--- /dev/null
+++ b/mojo/edk/system/request_context.cc
@@ -0,0 +1,105 @@
+// Copyright 2016 The Chromium Authors. All 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/request_context.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+base::LazyInstance<base::ThreadLocalPointer<RequestContext>>::Leaky
+    g_current_context = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+RequestContext::RequestContext() : RequestContext(Source::LOCAL_API_CALL) {}
+
+RequestContext::RequestContext(Source source)
+    : source_(source), tls_context_(g_current_context.Pointer()) {
+  // We allow nested RequestContexts to exist as long as they aren't actually
+  // used for anything.
+  if (!tls_context_->Get())
+    tls_context_->Set(this);
+}
+
+RequestContext::~RequestContext() {
+  if (IsCurrent()) {
+    // NOTE: Callbacks invoked by this destructor are allowed to initiate new
+    // EDK requests on this thread, so we need to reset the thread-local context
+    // pointer before calling them. We persist the original notification source
+    // since we're starting over at the bottom of the stack.
+    tls_context_->Set(nullptr);
+
+    MojoWatchNotificationFlags flags = MOJO_WATCH_NOTIFICATION_FLAG_NONE;
+    if (source_ == Source::SYSTEM)
+      flags |= MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM;
+
+    // We run all cancellation finalizers first. This is necessary because it's
+    // possible that one of the cancelled watchers has other pending finalizers
+    // attached to this RequestContext.
+    //
+    // From the application's perspective the watch has already been cancelled,
+    // so we have to honor our contract which guarantees no more notifications.
+    for (const scoped_refptr<Watcher>& watcher :
+            watch_cancel_finalizers_.container())
+      watcher->Cancel();
+
+    for (const WatchNotifyFinalizer& watch :
+        watch_notify_finalizers_.container()) {
+      // Establish a new request context for the extent of each callback to
+      // ensure that they don't themselves invoke callbacks while holding a
+      // watcher lock.
+      RequestContext request_context(source_);
+      watch.watcher->MaybeInvokeCallback(watch.result, watch.state, flags);
+    }
+  } else {
+    // It should be impossible for nested contexts to have finalizers.
+    DCHECK(watch_notify_finalizers_.container().empty());
+    DCHECK(watch_cancel_finalizers_.container().empty());
+  }
+}
+
+// static
+RequestContext* RequestContext::current() {
+  DCHECK(g_current_context.Pointer()->Get());
+  return g_current_context.Pointer()->Get();
+}
+
+void RequestContext::AddWatchNotifyFinalizer(
+    scoped_refptr<Watcher> watcher,
+    MojoResult result,
+    const HandleSignalsState& state) {
+  DCHECK(IsCurrent());
+  watch_notify_finalizers_->push_back(
+      WatchNotifyFinalizer(watcher, result, state));
+}
+
+void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher) {
+  DCHECK(IsCurrent());
+  watch_cancel_finalizers_->push_back(watcher);
+}
+
+bool RequestContext::IsCurrent() const {
+  return tls_context_->Get() == this;
+}
+
+RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
+    scoped_refptr<Watcher> watcher,
+    MojoResult result,
+    const HandleSignalsState& state)
+    : watcher(watcher), result(result), state(state) {
+}
+
+RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
+    const WatchNotifyFinalizer& other) = default;
+
+RequestContext::WatchNotifyFinalizer::~WatchNotifyFinalizer() {}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/request_context.h b/mojo/edk/system/request_context.h
new file mode 100644
index 0000000..7aa0e69
--- /dev/null
+++ b/mojo/edk/system/request_context.h
@@ -0,0 +1,106 @@
+// Copyright 2016 The Chromium Authors. All 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_REQUEST_CONTEXT_H_
+#define MOJO_EDK_SYSTEM_REQUEST_CONTEXT_H_
+
+#include "base/containers/stack_container.h"
+#include "base/macros.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/edk/system/watcher.h"
+
+namespace base {
+template<typename T> class ThreadLocalPointer;
+}
+
+namespace mojo {
+namespace edk {
+
+// A RequestContext is a thread-local object which exists for the duration of
+// a single system API call. It is constructed immediately upon EDK entry and
+// destructed immediately before returning to the caller, after any internal
+// locks have been released.
+//
+// NOTE: It is legal to construct a RequestContext while another one already
+// exists on the current thread, but it is not safe to use the nested context
+// for any reason. Therefore it is important to always use
+// |RequestContext::current()| rather than referring to any local instance
+// directly.
+class MOJO_SYSTEM_IMPL_EXPORT RequestContext {
+ public:
+  // Identifies the source of the current stack frame's RequestContext.
+  enum class Source {
+    LOCAL_API_CALL,
+    SYSTEM,
+  };
+
+  // Constructs a RequestContext with a LOCAL_API_CALL Source.
+  RequestContext();
+
+  explicit RequestContext(Source source);
+  ~RequestContext();
+
+  // Returns the current thread-local RequestContext.
+  static RequestContext* current();
+
+  Source source() const { return source_; }
+
+  // Adds a finalizer to this RequestContext corresponding to a watch callback
+  // which should be triggered in response to some handle state change. If
+  // the Watcher hasn't been cancelled by the time this RequestContext is
+  // destroyed, its WatchCallback will be invoked with |result| and |state|
+  // arguments.
+  void AddWatchNotifyFinalizer(scoped_refptr<Watcher> watcher,
+                               MojoResult result,
+                               const HandleSignalsState& state);
+
+  // Adds a finalizer to this RequestContext which cancels a watch.
+  void AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher);
+
+ private:
+  // Is this request context the current one?
+  bool IsCurrent() const;
+
+  struct WatchNotifyFinalizer {
+    WatchNotifyFinalizer(scoped_refptr<Watcher> watcher,
+                         MojoResult result,
+                         const HandleSignalsState& state);
+    WatchNotifyFinalizer(const WatchNotifyFinalizer& other);
+    ~WatchNotifyFinalizer();
+
+    scoped_refptr<Watcher> watcher;
+    MojoResult result;
+    HandleSignalsState state;
+  };
+
+  // Chosen by fair dice roll.
+  //
+  // TODO: We should measure the distribution of # of finalizers typical to
+  // any RequestContext and adjust this number accordingly. It's probably
+  // almost always 1, but 4 seems like a harmless upper bound for now.
+  static const size_t kStaticWatchFinalizersCapacity = 4;
+
+  using WatchNotifyFinalizerList =
+      base::StackVector<WatchNotifyFinalizer, kStaticWatchFinalizersCapacity>;
+  using WatchCancelFinalizerList =
+      base::StackVector<scoped_refptr<Watcher>, kStaticWatchFinalizersCapacity>;
+
+  const Source source_;
+
+  WatchNotifyFinalizerList watch_notify_finalizers_;
+  WatchCancelFinalizerList watch_cancel_finalizers_;
+
+  // Pointer to the TLS context. Although this can easily be accessed via the
+  // global LazyInstance, accessing a LazyInstance has a large cost relative to
+  // the rest of this class and its usages.
+  base::ThreadLocalPointer<RequestContext>* tls_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestContext);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_REQUEST_CONTEXT_H_
diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc
new file mode 100644
index 0000000..df39105
--- /dev/null
+++ b/mojo/edk/system/shared_buffer_dispatcher.cc
@@ -0,0 +1,339 @@
+// Copyright 2014 The Chromium Authors. All 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 <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/system/configuration.h"
+#include "mojo/edk/system/node_controller.h"
+#include "mojo/edk/system/options_validation.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+#pragma pack(push, 1)
+
+struct SerializedState {
+  uint64_t num_bytes;
+  uint32_t flags;
+  uint32_t padding;
+};
+
+const uint32_t kSerializedStateFlagsReadOnly = 1 << 0;
+
+#pragma pack(pop)
+
+static_assert(sizeof(SerializedState) % 8 == 0,
+              "Invalid SerializedState size.");
+
+}  // namespace
+
+// static
+const MojoCreateSharedBufferOptions
+    SharedBufferDispatcher::kDefaultCreateOptions = {
+        static_cast<uint32_t>(sizeof(MojoCreateSharedBufferOptions)),
+        MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE};
+
+// static
+MojoResult SharedBufferDispatcher::ValidateCreateOptions(
+    const MojoCreateSharedBufferOptions* in_options,
+    MojoCreateSharedBufferOptions* out_options) {
+  const MojoCreateSharedBufferOptionsFlags kKnownFlags =
+      MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
+
+  *out_options = kDefaultCreateOptions;
+  if (!in_options)
+    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(
+    const MojoCreateSharedBufferOptions& /*validated_options*/,
+    NodeController* node_controller,
+    uint64_t num_bytes,
+    scoped_refptr<SharedBufferDispatcher>* result) {
+  if (!num_bytes)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (num_bytes > GetConfiguration().max_shared_memory_num_bytes)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  scoped_refptr<PlatformSharedBuffer> shared_buffer;
+  if (node_controller) {
+    shared_buffer =
+        node_controller->CreateSharedBuffer(static_cast<size_t>(num_bytes));
+  } else {
+    shared_buffer =
+        PlatformSharedBuffer::Create(static_cast<size_t>(num_bytes));
+  }
+  if (!shared_buffer)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  *result = CreateInternal(std::move(shared_buffer));
+  return MOJO_RESULT_OK;
+}
+
+// static
+MojoResult SharedBufferDispatcher::CreateFromPlatformSharedBuffer(
+    const scoped_refptr<PlatformSharedBuffer>& shared_buffer,
+    scoped_refptr<SharedBufferDispatcher>* result) {
+  if (!shared_buffer)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  *result = CreateInternal(shared_buffer);
+  return MOJO_RESULT_OK;
+}
+
+// static
+scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize(
+    const void* bytes,
+    size_t num_bytes,
+    const ports::PortName* ports,
+    size_t num_ports,
+    PlatformHandle* platform_handles,
+    size_t num_platform_handles) {
+  if (num_bytes != sizeof(SerializedState)) {
+    LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)";
+    return nullptr;
+  }
+
+  const SerializedState* serialization =
+      static_cast<const SerializedState*>(bytes);
+  if (!serialization->num_bytes) {
+    LOG(ERROR)
+        << "Invalid serialized shared buffer dispatcher (invalid num_bytes)";
+    return nullptr;
+  }
+
+  if (!platform_handles || num_platform_handles != 1 || num_ports) {
+    LOG(ERROR)
+        << "Invalid serialized shared buffer dispatcher (missing handles)";
+    return nullptr;
+  }
+
+  // Starts off invalid, which is what we want.
+  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);
+
+  // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be
+  // closed even if creation fails.
+  bool read_only = (serialization->flags & kSerializedStateFlagsReadOnly);
+  scoped_refptr<PlatformSharedBuffer> shared_buffer(
+      PlatformSharedBuffer::CreateFromPlatformHandle(
+          static_cast<size_t>(serialization->num_bytes), read_only,
+          ScopedPlatformHandle(platform_handle)));
+  if (!shared_buffer) {
+    LOG(ERROR)
+        << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)";
+    return nullptr;
+  }
+
+  return CreateInternal(std::move(shared_buffer));
+}
+
+scoped_refptr<PlatformSharedBuffer>
+SharedBufferDispatcher::PassPlatformSharedBuffer() {
+  base::AutoLock lock(lock_);
+  if (!shared_buffer_ || in_transit_)
+    return nullptr;
+
+  scoped_refptr<PlatformSharedBuffer> retval = shared_buffer_;
+  shared_buffer_ = nullptr;
+  return retval;
+}
+
+Dispatcher::Type SharedBufferDispatcher::GetType() const {
+  return Type::SHARED_BUFFER;
+}
+
+MojoResult SharedBufferDispatcher::Close() {
+  base::AutoLock lock(lock_);
+  if (in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  shared_buffer_ = nullptr;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult SharedBufferDispatcher::DuplicateBufferHandle(
+    const MojoDuplicateBufferHandleOptions* options,
+    scoped_refptr<Dispatcher>* new_dispatcher) {
+  MojoDuplicateBufferHandleOptions validated_options;
+  MojoResult result = ValidateDuplicateOptions(options, &validated_options);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  // Note: Since this is "duplicate", we keep our ref to |shared_buffer_|.
+  base::AutoLock lock(lock_);
+  if (in_transit_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  if ((validated_options.flags &
+       MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY) &&
+      (!shared_buffer_->IsReadOnly())) {
+    // If a read-only duplicate is requested and |shared_buffer_| is not
+    // read-only, make a read-only duplicate of |shared_buffer_|.
+    scoped_refptr<PlatformSharedBuffer> read_only_buffer =
+        shared_buffer_->CreateReadOnlyDuplicate();
+    if (!read_only_buffer)
+      return MOJO_RESULT_FAILED_PRECONDITION;
+    DCHECK(read_only_buffer->IsReadOnly());
+    *new_dispatcher = CreateInternal(std::move(read_only_buffer));
+    return MOJO_RESULT_OK;
+  }
+
+  *new_dispatcher = CreateInternal(shared_buffer_);
+  return MOJO_RESULT_OK;
+}
+
+MojoResult SharedBufferDispatcher::MapBuffer(
+    uint64_t offset,
+    uint64_t num_bytes,
+    MojoMapBufferFlags flags,
+    std::unique_ptr<PlatformSharedBufferMapping>* mapping) {
+  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;
+
+  base::AutoLock lock(lock_);
+  DCHECK(shared_buffer_);
+  if (in_transit_ ||
+      !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) {
+    LOG(ERROR) << "Unable to map: read_only" << shared_buffer_->IsReadOnly();
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+void SharedBufferDispatcher::StartSerialize(uint32_t* num_bytes,
+                                            uint32_t* num_ports,
+                                            uint32_t* num_platform_handles) {
+  *num_bytes = sizeof(SerializedState);
+  *num_ports = 0;
+  *num_platform_handles = 1;
+}
+
+bool SharedBufferDispatcher::EndSerialize(void* destination,
+                                          ports::PortName* ports,
+                                          PlatformHandle* handles) {
+  SerializedState* serialization =
+      static_cast<SerializedState*>(destination);
+  base::AutoLock lock(lock_);
+  serialization->num_bytes =
+        static_cast<uint64_t>(shared_buffer_->GetNumBytes());
+  serialization->flags =
+      (shared_buffer_->IsReadOnly() ? kSerializedStateFlagsReadOnly : 0);
+  serialization->padding = 0;
+
+  handle_for_transit_ = shared_buffer_->DuplicatePlatformHandle();
+  if (!handle_for_transit_.is_valid()) {
+    shared_buffer_ = nullptr;
+    return false;
+  }
+  handles[0] = handle_for_transit_.get();
+  return true;
+}
+
+bool SharedBufferDispatcher::BeginTransit() {
+  base::AutoLock lock(lock_);
+  if (in_transit_)
+    return false;
+  in_transit_ = static_cast<bool>(shared_buffer_);
+  return in_transit_;
+}
+
+void SharedBufferDispatcher::CompleteTransitAndClose() {
+  base::AutoLock lock(lock_);
+  in_transit_ = false;
+  shared_buffer_ = nullptr;
+  ignore_result(handle_for_transit_.release());
+}
+
+void SharedBufferDispatcher::CancelTransit() {
+  base::AutoLock lock(lock_);
+  in_transit_ = false;
+  handle_for_transit_.reset();
+}
+
+SharedBufferDispatcher::SharedBufferDispatcher(
+    scoped_refptr<PlatformSharedBuffer> shared_buffer)
+    : shared_buffer_(shared_buffer) {
+  DCHECK(shared_buffer_);
+}
+
+SharedBufferDispatcher::~SharedBufferDispatcher() {
+  DCHECK(!shared_buffer_ && !in_transit_);
+}
+
+// static
+MojoResult SharedBufferDispatcher::ValidateDuplicateOptions(
+    const MojoDuplicateBufferHandleOptions* in_options,
+    MojoDuplicateBufferHandleOptions* out_options) {
+  const MojoDuplicateBufferHandleOptionsFlags kKnownFlags =
+      MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY;
+  static const MojoDuplicateBufferHandleOptions kDefaultOptions = {
+      static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)),
+      MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE};
+
+  *out_options = kDefaultOptions;
+  if (!in_options)
+    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;
+}
+
+}  // namespace edk
+}  // 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..1648dd2
--- /dev/null
+++ b/mojo/edk/system/shared_buffer_dispatcher.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_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+
+namespace edk {
+class NodeController;
+class PlatformSupport;
+
+class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher final : public Dispatcher {
+ 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(
+      const MojoCreateSharedBufferOptions* in_options,
+      MojoCreateSharedBufferOptions* out_options);
+
+  // Static factory method: |validated_options| must be validated (obviously).
+  // On failure, |*result| will be left as-is.
+  // TODO(vtl): This should probably be made to return a scoped_refptr and have
+  // a MojoResult out parameter instead.
+  static MojoResult Create(
+      const MojoCreateSharedBufferOptions& validated_options,
+      NodeController* node_controller,
+      uint64_t num_bytes,
+      scoped_refptr<SharedBufferDispatcher>* result);
+
+  // Create a |SharedBufferDispatcher| from |shared_buffer|.
+  static MojoResult CreateFromPlatformSharedBuffer(
+      const scoped_refptr<PlatformSharedBuffer>& shared_buffer,
+      scoped_refptr<SharedBufferDispatcher>* result);
+
+  // The "opposite" of SerializeAndClose(). Called by Dispatcher::Deserialize().
+  static scoped_refptr<SharedBufferDispatcher> Deserialize(
+      const void* bytes,
+      size_t num_bytes,
+      const ports::PortName* ports,
+      size_t num_ports,
+      PlatformHandle* platform_handles,
+      size_t num_platform_handles);
+
+  // Passes the underlying platform shared buffer. This dispatcher must be
+  // closed after calling this function.
+  scoped_refptr<PlatformSharedBuffer> PassPlatformSharedBuffer();
+
+  // Dispatcher:
+  Type GetType() const override;
+  MojoResult Close() override;
+  MojoResult DuplicateBufferHandle(
+      const MojoDuplicateBufferHandleOptions* options,
+      scoped_refptr<Dispatcher>* new_dispatcher) override;
+  MojoResult MapBuffer(
+      uint64_t offset,
+      uint64_t num_bytes,
+      MojoMapBufferFlags flags,
+      std::unique_ptr<PlatformSharedBufferMapping>* mapping) override;
+  void StartSerialize(uint32_t* num_bytes,
+                      uint32_t* num_ports,
+                      uint32_t* num_platform_handles) override;
+  bool EndSerialize(void* destination,
+                    ports::PortName* ports,
+                    PlatformHandle* handles) override;
+  bool BeginTransit() override;
+  void CompleteTransitAndClose() override;
+  void CancelTransit() override;
+
+ private:
+  static scoped_refptr<SharedBufferDispatcher> CreateInternal(
+      scoped_refptr<PlatformSharedBuffer> shared_buffer) {
+    return make_scoped_refptr(
+        new SharedBufferDispatcher(std::move(shared_buffer)));
+  }
+
+  explicit SharedBufferDispatcher(
+      scoped_refptr<PlatformSharedBuffer> shared_buffer);
+  ~SharedBufferDispatcher() override;
+
+  // 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(
+      const MojoDuplicateBufferHandleOptions* in_options,
+      MojoDuplicateBufferHandleOptions* out_options);
+
+  // Guards access to |shared_buffer_|.
+  base::Lock lock_;
+
+  bool in_transit_ = false;
+
+  // We keep a copy of the buffer's platform handle during transit so we can
+  // close it if something goes wrong.
+  ScopedPlatformHandle handle_for_transit_;
+
+  scoped_refptr<PlatformSharedBuffer> shared_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcher);
+};
+
+}  // namespace edk
+}  // 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..c95bdc3
--- /dev/null
+++ b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
@@ -0,0 +1,312 @@
+// Copyright 2014 The Chromium Authors. All 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 <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+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(
+                &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() {}
+  ~SharedBufferDispatcherTest() override {}
+
+ private:
+  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(
+                                  nullptr, &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(
+                    &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(
+                  &options, &unused));
+  }
+
+  // Unknown |flags|.
+  {
+    MojoCreateSharedBufferOptions options = {
+        kSizeOfCreateOptions,  // |struct_size|.
+        ~0u                    // |flags|.
+    };
+    MojoCreateSharedBufferOptions unused;
+    EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+              SharedBufferDispatcher::ValidateCreateOptions(
+                  &options, &unused));
+  }
+}
+
+TEST_F(SharedBufferDispatcherTest, CreateAndMapBuffer) {
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create(
+                                SharedBufferDispatcher::kDefaultCreateOptions,
+                                nullptr, 100, &dispatcher));
+  ASSERT_TRUE(dispatcher);
+  EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, dispatcher->GetType());
+
+  // Make a couple of mappings.
+  std::unique_ptr<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';
+
+  std::unique_ptr<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, CreateAndMapBufferFromPlatformBuffer) {
+  scoped_refptr<PlatformSharedBuffer> platform_shared_buffer =
+      PlatformSharedBuffer::Create(100);
+  ASSERT_TRUE(platform_shared_buffer);
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            SharedBufferDispatcher::CreateFromPlatformSharedBuffer(
+                platform_shared_buffer, &dispatcher));
+  ASSERT_TRUE(dispatcher);
+  EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, dispatcher->GetType());
+
+  // Make a couple of mappings.
+  std::unique_ptr<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';
+
+  std::unique_ptr<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(
+                                SharedBufferDispatcher::kDefaultCreateOptions,
+                                nullptr, 100, &dispatcher1));
+
+  // Map and write something.
+  std::unique_ptr<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(
+                                nullptr, &dispatcher2));
+  ASSERT_TRUE(dispatcher2);
+  EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, 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(
+                                SharedBufferDispatcher::kDefaultCreateOptions,
+                                nullptr, 100, &dispatcher1));
+
+  MojoDuplicateBufferHandleOptions options[] = {
+      {sizeof(MojoDuplicateBufferHandleOptions),
+       MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE},
+      {sizeof(MojoDuplicateBufferHandleOptions),
+       MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY},
+      {sizeof(MojoDuplicateBufferHandleOptionsFlags), ~0u}};
+  for (size_t i = 0; i < arraysize(options); i++) {
+    scoped_refptr<Dispatcher> dispatcher2;
+    EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->DuplicateBufferHandle(
+                                  &options[i], &dispatcher2));
+    ASSERT_TRUE(dispatcher2);
+    EXPECT_EQ(Dispatcher::Type::SHARED_BUFFER, dispatcher2->GetType());
+    {
+      std::unique_ptr<PlatformSharedBufferMapping> mapping;
+      EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->MapBuffer(0, 100, 0, &mapping));
+    }
+    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(
+                                SharedBufferDispatcher::kDefaultCreateOptions,
+                                nullptr, 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(&options, &dispatcher2));
+    EXPECT_FALSE(dispatcher2);
+  }
+
+  // Unknown |flags|.
+  {
+    MojoDuplicateBufferHandleOptions options = {
+        sizeof(MojoDuplicateBufferHandleOptions), ~0u};
+    scoped_refptr<Dispatcher> dispatcher2;
+    EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+              dispatcher1->DuplicateBufferHandle(&options, &dispatcher2));
+    EXPECT_FALSE(dispatcher2);
+  }
+
+  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(
+                SharedBufferDispatcher::kDefaultCreateOptions, nullptr,
+                std::numeric_limits<uint64_t>::max(), &dispatcher));
+  EXPECT_FALSE(dispatcher);
+
+  // Zero size.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            SharedBufferDispatcher::Create(
+                SharedBufferDispatcher::kDefaultCreateOptions, nullptr, 0,
+                &dispatcher));
+  EXPECT_FALSE(dispatcher);
+}
+
+TEST_F(SharedBufferDispatcherTest, MapBufferInvalidArguments) {
+  scoped_refptr<SharedBufferDispatcher> dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create(
+                                SharedBufferDispatcher::kDefaultCreateOptions,
+                                nullptr, 100, &dispatcher));
+
+  std::unique_ptr<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 edk
+}  // namespace mojo
diff --git a/mojo/edk/system/shared_buffer_unittest.cc b/mojo/edk/system/shared_buffer_unittest.cc
new file mode 100644
index 0000000..3a72872
--- /dev/null
+++ b/mojo/edk/system/shared_buffer_unittest.cc
@@ -0,0 +1,318 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/shared_memory.h"
+#include "base/strings/string_piece.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/public/c/system/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+using SharedBufferTest = test::MojoTestBase;
+
+TEST_F(SharedBufferTest, CreateSharedBuffer) {
+  const std::string message = "hello";
+  MojoHandle h = CreateBuffer(message.size());
+  WriteToBuffer(h, 0, message);
+  ExpectBufferContents(h, 0, message);
+}
+
+TEST_F(SharedBufferTest, DuplicateSharedBuffer) {
+  const std::string message = "hello";
+  MojoHandle h = CreateBuffer(message.size());
+  WriteToBuffer(h, 0, message);
+
+  MojoHandle dupe = DuplicateBuffer(h, false);
+  ExpectBufferContents(dupe, 0, message);
+}
+
+TEST_F(SharedBufferTest, PassSharedBufferLocal) {
+  const std::string message = "hello";
+  MojoHandle h = CreateBuffer(message.size());
+  WriteToBuffer(h, 0, message);
+
+  MojoHandle dupe = DuplicateBuffer(h, false);
+  MojoHandle p0, p1;
+  CreateMessagePipe(&p0, &p1);
+
+  WriteMessageWithHandles(p0, "...", &dupe, 1);
+  EXPECT_EQ("...", ReadMessageWithHandles(p1, &dupe, 1));
+
+  ExpectBufferContents(dupe, 0, message);
+}
+
+#if !defined(OS_IOS)
+
+// Reads a single message with a shared buffer handle, maps the buffer, copies
+// the message contents into it, then exits.
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CopyToBufferClient, SharedBufferTest, h) {
+  MojoHandle b;
+  std::string message = ReadMessageWithHandles(h, &b, 1);
+  WriteToBuffer(b, 0, message);
+
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+TEST_F(SharedBufferTest, PassSharedBufferCrossProcess) {
+  const std::string message = "hello";
+  MojoHandle b = CreateBuffer(message.size());
+
+  RUN_CHILD_ON_PIPE(CopyToBufferClient, h)
+    MojoHandle dupe = DuplicateBuffer(b, false);
+    WriteMessageWithHandles(h, message, &dupe, 1);
+    WriteMessage(h, "quit");
+  END_CHILD()
+
+  ExpectBufferContents(b, 0, message);
+}
+
+// Creates a new buffer, maps it, writes a message contents to it, unmaps it,
+// and finally passes it back to the parent.
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateBufferClient, SharedBufferTest, h) {
+  std::string message = ReadMessage(h);
+  MojoHandle b = CreateBuffer(message.size());
+  WriteToBuffer(b, 0, message);
+  WriteMessageWithHandles(h, "have a buffer", &b, 1);
+
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+TEST_F(SharedBufferTest, PassSharedBufferFromChild) {
+  const std::string message = "hello";
+  MojoHandle b;
+  RUN_CHILD_ON_PIPE(CreateBufferClient, h)
+    WriteMessage(h, message);
+    ReadMessageWithHandles(h, &b, 1);
+    WriteMessage(h, "quit");
+  END_CHILD()
+
+  ExpectBufferContents(b, 0, message);
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateAndPassBuffer, SharedBufferTest, h) {
+  // Receive a pipe handle over the primordial pipe. This will be connected to
+  // another child process.
+  MojoHandle other_child;
+  std::string message = ReadMessageWithHandles(h, &other_child, 1);
+
+  // Create a new shared buffer.
+  MojoHandle b = CreateBuffer(message.size());
+
+  // Send a copy of the buffer to the parent and the other child.
+  MojoHandle dupe = DuplicateBuffer(b, false);
+  WriteMessageWithHandles(h, "", &b, 1);
+  WriteMessageWithHandles(other_child, "", &dupe, 1);
+
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceiveAndEditBuffer, SharedBufferTest, h) {
+  // Receive a pipe handle over the primordial pipe. This will be connected to
+  // another child process (running CreateAndPassBuffer).
+  MojoHandle other_child;
+  std::string message = ReadMessageWithHandles(h, &other_child, 1);
+
+  // Receive a shared buffer from the other child.
+  MojoHandle b;
+  ReadMessageWithHandles(other_child, &b, 1);
+
+  // Write the message from the parent into the buffer and exit.
+  WriteToBuffer(b, 0, message);
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
+  EXPECT_EQ("quit", ReadMessage(h));
+}
+
+TEST_F(SharedBufferTest, PassSharedBufferFromChildToChild) {
+  const std::string message = "hello";
+  MojoHandle p0, p1;
+  CreateMessagePipe(&p0, &p1);
+
+  MojoHandle b;
+  RUN_CHILD_ON_PIPE(CreateAndPassBuffer, h0)
+    RUN_CHILD_ON_PIPE(ReceiveAndEditBuffer, h1)
+      // Send one end of the pipe to each child. The first child will create
+      // and pass a buffer to the second child and back to us. The second child
+      // will write our message into the buffer.
+      WriteMessageWithHandles(h0, message, &p0, 1);
+      WriteMessageWithHandles(h1, message, &p1, 1);
+
+      // Receive the buffer back from the first child.
+      ReadMessageWithHandles(h0, &b, 1);
+
+      WriteMessage(h1, "quit");
+    END_CHILD()
+    WriteMessage(h0, "quit");
+  END_CHILD()
+
+  // The second child should have written this message.
+  ExpectBufferContents(b, 0, message);
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateAndPassBufferParent, SharedBufferTest,
+                                  parent) {
+  RUN_CHILD_ON_PIPE(CreateAndPassBuffer, child)
+    // Read a pipe from the parent and forward it to our child.
+    MojoHandle pipe;
+    std::string message = ReadMessageWithHandles(parent, &pipe, 1);
+
+    WriteMessageWithHandles(child, message, &pipe, 1);
+
+    // Read a buffer handle from the child and pass it back to the parent.
+    MojoHandle buffer;
+    EXPECT_EQ("", ReadMessageWithHandles(child, &buffer, 1));
+    WriteMessageWithHandles(parent, "", &buffer, 1);
+
+    EXPECT_EQ("quit", ReadMessage(parent));
+    WriteMessage(child, "quit");
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceiveAndEditBufferParent, SharedBufferTest,
+                                  parent) {
+  RUN_CHILD_ON_PIPE(ReceiveAndEditBuffer, child)
+    // Read a pipe from the parent and forward it to our child.
+    MojoHandle pipe;
+    std::string message = ReadMessageWithHandles(parent, &pipe, 1);
+    WriteMessageWithHandles(child, message, &pipe, 1);
+
+    EXPECT_EQ("quit", ReadMessage(parent));
+    WriteMessage(child, "quit");
+  END_CHILD()
+}
+
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
+// Android multi-process tests are not executing the new process. This is flaky.
+// Passing shared memory handles between cousins is not currently supported on
+// OSX.
+#define MAYBE_PassHandleBetweenCousins DISABLED_PassHandleBetweenCousins
+#else
+#define MAYBE_PassHandleBetweenCousins PassHandleBetweenCousins
+#endif
+TEST_F(SharedBufferTest, MAYBE_PassHandleBetweenCousins) {
+  const std::string message = "hello";
+  MojoHandle p0, p1;
+  CreateMessagePipe(&p0, &p1);
+
+  // Spawn two children who will each spawn their own child. Make sure the
+  // grandchildren (cousins to each other) can pass platform handles.
+  MojoHandle b;
+  RUN_CHILD_ON_PIPE(CreateAndPassBufferParent, child1)
+    RUN_CHILD_ON_PIPE(ReceiveAndEditBufferParent, child2)
+      MojoHandle pipe[2];
+      CreateMessagePipe(&pipe[0], &pipe[1]);
+
+      WriteMessageWithHandles(child1, message, &pipe[0], 1);
+      WriteMessageWithHandles(child2, message, &pipe[1], 1);
+
+      // Receive the buffer back from the first child.
+      ReadMessageWithHandles(child1, &b, 1);
+
+      WriteMessage(child2, "quit");
+    END_CHILD()
+    WriteMessage(child1, "quit");
+  END_CHILD()
+
+  // The second grandchild should have written this message.
+  ExpectBufferContents(b, 0, message);
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadAndMapWriteSharedBuffer,
+                                  SharedBufferTest, h) {
+  // Receive the shared buffer.
+  MojoHandle b;
+  EXPECT_EQ("hello", ReadMessageWithHandles(h, &b, 1));
+
+  // Read from the bufer.
+  ExpectBufferContents(b, 0, "hello");
+
+  // Extract the shared memory handle and try to map it writable.
+  base::SharedMemoryHandle shm_handle;
+  bool read_only = false;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            PassSharedMemoryHandle(b, &shm_handle, nullptr, &read_only));
+  base::SharedMemory shared_memory(shm_handle, false);
+  EXPECT_TRUE(read_only);
+  EXPECT_FALSE(shared_memory.Map(1234));
+
+  EXPECT_EQ("quit", ReadMessage(h));
+  WriteMessage(h, "ok");
+}
+
+#if defined(OS_ANDROID)
+// Android multi-process tests are not executing the new process. This is flaky.
+#define MAYBE_CreateAndPassReadOnlyBuffer DISABLED_CreateAndPassReadOnlyBuffer
+#else
+#define MAYBE_CreateAndPassReadOnlyBuffer CreateAndPassReadOnlyBuffer
+#endif
+TEST_F(SharedBufferTest, MAYBE_CreateAndPassReadOnlyBuffer) {
+  RUN_CHILD_ON_PIPE(ReadAndMapWriteSharedBuffer, h)
+    // Create a new shared buffer.
+    MojoHandle b = CreateBuffer(1234);
+    WriteToBuffer(b, 0, "hello");
+
+    // Send a read-only copy of the buffer to the child.
+    MojoHandle dupe = DuplicateBuffer(b, true /* read_only */);
+    WriteMessageWithHandles(h, "hello", &dupe, 1);
+
+    WriteMessage(h, "quit");
+    EXPECT_EQ("ok", ReadMessage(h));
+  END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateAndPassReadOnlyBuffer,
+                                  SharedBufferTest, h) {
+  // Create a new shared buffer.
+  MojoHandle b = CreateBuffer(1234);
+  WriteToBuffer(b, 0, "hello");
+
+  // Send a read-only copy of the buffer to the parent.
+  MojoHandle dupe = DuplicateBuffer(b, true /* read_only */);
+  WriteMessageWithHandles(h, "", &dupe, 1);
+
+  EXPECT_EQ("quit", ReadMessage(h));
+  WriteMessage(h, "ok");
+}
+
+#if defined(OS_ANDROID)
+// Android multi-process tests are not executing the new process. This is flaky.
+#define MAYBE_CreateAndPassFromChildReadOnlyBuffer \
+    DISABLED_CreateAndPassFromChildReadOnlyBuffer
+#else
+#define MAYBE_CreateAndPassFromChildReadOnlyBuffer \
+    CreateAndPassFromChildReadOnlyBuffer
+#endif
+TEST_F(SharedBufferTest, MAYBE_CreateAndPassFromChildReadOnlyBuffer) {
+  RUN_CHILD_ON_PIPE(CreateAndPassReadOnlyBuffer, h)
+    MojoHandle b;
+    EXPECT_EQ("", ReadMessageWithHandles(h, &b, 1));
+    ExpectBufferContents(b, 0, "hello");
+
+    // Extract the shared memory handle and try to map it writable.
+    base::SharedMemoryHandle shm_handle;
+    bool read_only = false;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              PassSharedMemoryHandle(b, &shm_handle, nullptr, &read_only));
+    base::SharedMemory shared_memory(shm_handle, false);
+    EXPECT_TRUE(read_only);
+    EXPECT_FALSE(shared_memory.Map(1234));
+
+    WriteMessage(h, "quit");
+    EXPECT_EQ("ok", ReadMessage(h));
+  END_CHILD()
+}
+
+#endif  // !defined(OS_IOS)
+
+}  // namespace
+}  // namespace edk
+}  // 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..4a39cf7
--- /dev/null
+++ b/mojo/edk/system/test_utils.cc
@@ -0,0 +1,76 @@
+// 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 <stdint.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"  // For |Sleep()|.
+#include "build/build_config.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds) {
+  return static_cast<MojoDeadline>(milliseconds) * 1000;
+}
+
+MojoDeadline EpsilonDeadline() {
+// 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) || defined(OS_ANDROID)
+  return (TinyDeadline() * 3) / 10;
+#else
+  return (TinyDeadline() * 2) / 10;
+#endif
+}
+
+MojoDeadline TinyDeadline() {
+  return static_cast<MojoDeadline>(
+      TestTimeouts::tiny_timeout().InMicroseconds());
+}
+
+MojoDeadline ActionDeadline() {
+  return static_cast<MojoDeadline>(
+      TestTimeouts::action_timeout().InMicroseconds());
+}
+
+void Sleep(MojoDeadline deadline) {
+  CHECK_LE(deadline,
+           static_cast<MojoDeadline>(std::numeric_limits<int64_t>::max()));
+  base::PlatformThread::Sleep(
+      base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline)));
+}
+
+Stopwatch::Stopwatch() {
+}
+
+Stopwatch::~Stopwatch() {
+}
+
+void Stopwatch::Start() {
+  start_time_ = base::TimeTicks::Now();
+}
+
+MojoDeadline Stopwatch::Elapsed() {
+  int64_t result = (base::TimeTicks::Now() - start_time_).InMicroseconds();
+  // |DCHECK_GE|, not |CHECK_GE|, since this may be performance-important.
+  DCHECK_GE(result, 0);
+  return static_cast<MojoDeadline>(result);
+}
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/test_utils.h b/mojo/edk/system/test_utils.h
new file mode 100644
index 0000000..1c90dc1
--- /dev/null
+++ b/mojo/edk/system/test_utils.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_TEST_UTILS_H_
+#define MOJO_EDK_SYSTEM_TEST_UTILS_H_
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "mojo/public/c/system/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+MojoDeadline DeadlineFromMilliseconds(unsigned milliseconds);
+
+// A timeout smaller than |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|.
+// 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.
+MojoDeadline EpsilonDeadline();
+
+// |TestTimeouts::tiny_timeout()|, as a |MojoDeadline|. (Expect this to be on
+// the order of 100 ms.)
+MojoDeadline TinyDeadline();
+
+// |TestTimeouts::action_timeout()|, as a |MojoDeadline|. (Expect this to be on
+// the order of 10 s.)
+MojoDeadline ActionDeadline();
+
+// Sleeps for at least the specified duration.
+void Sleep(MojoDeadline deadline);
+
+// Stopwatch -------------------------------------------------------------------
+
+// A simple "stopwatch" for measuring time elapsed from a given starting point.
+class Stopwatch {
+ public:
+  Stopwatch();
+  ~Stopwatch();
+
+  void Start();
+  // Returns the amount of time elapsed since the last call to |Start()| (in
+  // microseconds).
+  MojoDeadline Elapsed();
+
+ private:
+  base::TimeTicks start_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(Stopwatch);
+};
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_TEST_UTILS_H_
diff --git a/mojo/edk/system/wait_set_dispatcher.cc b/mojo/edk/system/wait_set_dispatcher.cc
new file mode 100644
index 0000000..edca415
--- /dev/null
+++ b/mojo/edk/system/wait_set_dispatcher.cc
@@ -0,0 +1,312 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/wait_set_dispatcher.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "mojo/edk/system/awakable.h"
+
+namespace mojo {
+namespace edk {
+
+class WaitSetDispatcher::Waiter final : public Awakable {
+ public:
+  explicit Waiter(WaitSetDispatcher* dispatcher) : dispatcher_(dispatcher) {}
+  ~Waiter() {}
+
+  // |Awakable| implementation.
+  bool Awake(MojoResult result, uintptr_t context) override {
+    // Note: This is called with various Mojo locks held.
+    dispatcher_->WakeDispatcher(result, context);
+    // Removes |this| from the dispatcher's list of waiters.
+    return false;
+  }
+
+ private:
+  WaitSetDispatcher* const dispatcher_;
+};
+
+WaitSetDispatcher::WaitState::WaitState() {}
+
+WaitSetDispatcher::WaitState::WaitState(const WaitState& other) = default;
+
+WaitSetDispatcher::WaitState::~WaitState() {}
+
+WaitSetDispatcher::WaitSetDispatcher()
+    : waiter_(new WaitSetDispatcher::Waiter(this)) {}
+
+Dispatcher::Type WaitSetDispatcher::GetType() const {
+  return Type::WAIT_SET;
+}
+
+MojoResult WaitSetDispatcher::Close() {
+  base::AutoLock lock(lock_);
+
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  is_closed_ = true;
+
+  {
+    base::AutoLock locker(awakable_lock_);
+    awakable_list_.CancelAll();
+  }
+
+  for (const auto& entry : waiting_dispatchers_)
+    entry.second.dispatcher->RemoveAwakable(waiter_.get(), nullptr);
+  waiting_dispatchers_.clear();
+
+  base::AutoLock locker(awoken_lock_);
+  awoken_queue_.clear();
+  processed_dispatchers_.clear();
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult WaitSetDispatcher::AddWaitingDispatcher(
+    const scoped_refptr<Dispatcher>& dispatcher,
+    MojoHandleSignals signals,
+    uintptr_t context) {
+  if (dispatcher == this)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  base::AutoLock lock(lock_);
+
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  uintptr_t dispatcher_handle = reinterpret_cast<uintptr_t>(dispatcher.get());
+  auto it = waiting_dispatchers_.find(dispatcher_handle);
+  if (it != waiting_dispatchers_.end()) {
+    return MOJO_RESULT_ALREADY_EXISTS;
+  }
+
+  const MojoResult result = dispatcher->AddAwakable(waiter_.get(), signals,
+                                                    dispatcher_handle, nullptr);
+  if (result == MOJO_RESULT_INVALID_ARGUMENT) {
+    // Dispatcher is closed.
+    return result;
+  } else if (result != MOJO_RESULT_OK) {
+    WakeDispatcher(result, dispatcher_handle);
+  }
+
+  WaitState state;
+  state.dispatcher = dispatcher;
+  state.context = context;
+  state.signals = signals;
+  bool inserted = waiting_dispatchers_.insert(
+      std::make_pair(dispatcher_handle, state)).second;
+  DCHECK(inserted);
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult WaitSetDispatcher::RemoveWaitingDispatcher(
+    const scoped_refptr<Dispatcher>& dispatcher) {
+  uintptr_t dispatcher_handle = reinterpret_cast<uintptr_t>(dispatcher.get());
+
+  base::AutoLock lock(lock_);
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  auto it = waiting_dispatchers_.find(dispatcher_handle);
+  if (it == waiting_dispatchers_.end())
+    return MOJO_RESULT_NOT_FOUND;
+
+  dispatcher->RemoveAwakable(waiter_.get(), nullptr);
+  // At this point, it should not be possible for |waiter_| to be woken with
+  // |dispatcher|.
+  waiting_dispatchers_.erase(it);
+
+  base::AutoLock locker(awoken_lock_);
+  int num_erased = 0;
+  for (auto it = awoken_queue_.begin(); it != awoken_queue_.end();) {
+    if (it->first == dispatcher_handle) {
+      it = awoken_queue_.erase(it);
+      num_erased++;
+    } else {
+      ++it;
+    }
+  }
+  // The dispatcher should only exist in the queue once.
+  DCHECK_LE(num_erased, 1);
+  processed_dispatchers_.erase(
+      std::remove(processed_dispatchers_.begin(), processed_dispatchers_.end(),
+                  dispatcher_handle),
+      processed_dispatchers_.end());
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult WaitSetDispatcher::GetReadyDispatchers(
+    uint32_t* count,
+    DispatcherVector* dispatchers,
+    MojoResult* results,
+    uintptr_t* contexts) {
+  base::AutoLock lock(lock_);
+
+  if (is_closed_)
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  dispatchers->clear();
+
+  // Re-queue any already retrieved dispatchers. These should be the dispatchers
+  // that were returned on the last call to this function. This loop is
+  // necessary to preserve the logically level-triggering behaviour of waiting
+  // in Mojo. In particular, if no action is taken on a signal, that signal
+  // continues to be satisfied, and therefore a |MojoWait()| on that
+  // handle/signal continues to return immediately.
+  std::deque<uintptr_t> pending;
+  {
+    base::AutoLock locker(awoken_lock_);
+    pending.swap(processed_dispatchers_);
+  }
+  for (uintptr_t d : pending) {
+    auto it = waiting_dispatchers_.find(d);
+    // Anything in |processed_dispatchers_| should also be in
+    // |waiting_dispatchers_| since dispatchers are removed from both in
+    // |RemoveWaitingDispatcherImplNoLock()|.
+    DCHECK(it != waiting_dispatchers_.end());
+
+    // |awoken_mutex_| cannot be held here because
+    // |Dispatcher::AddAwakable()| acquires the Dispatcher's mutex. This
+    // mutex is held while running |WakeDispatcher()| below, which needs to
+    // acquire |awoken_mutex_|. Holding |awoken_mutex_| here would result in
+    // a deadlock.
+    const MojoResult result = it->second.dispatcher->AddAwakable(
+        waiter_.get(), it->second.signals, d, nullptr);
+
+    if (result == MOJO_RESULT_INVALID_ARGUMENT) {
+      // Dispatcher is closed. Implicitly remove it from the wait set since
+      // it may be impossible to remove using |MojoRemoveHandle()|.
+      waiting_dispatchers_.erase(it);
+    } else if (result != MOJO_RESULT_OK) {
+      WakeDispatcher(result, d);
+    }
+  }
+
+  const uint32_t max_woken = *count;
+  uint32_t num_woken = 0;
+
+  base::AutoLock locker(awoken_lock_);
+  while (!awoken_queue_.empty() && num_woken < max_woken) {
+    uintptr_t d = awoken_queue_.front().first;
+    MojoResult result = awoken_queue_.front().second;
+    awoken_queue_.pop_front();
+
+    auto it = waiting_dispatchers_.find(d);
+    DCHECK(it != waiting_dispatchers_.end());
+
+    results[num_woken] = result;
+    dispatchers->push_back(it->second.dispatcher);
+    if (contexts)
+      contexts[num_woken] = it->second.context;
+
+    if (result != MOJO_RESULT_CANCELLED) {
+      processed_dispatchers_.push_back(d);
+    } else {
+      // |MOJO_RESULT_CANCELLED| indicates that the dispatcher was closed.
+      // Return it, but also implcitly remove it from the wait set.
+      waiting_dispatchers_.erase(it);
+    }
+
+    num_woken++;
+  }
+
+  *count = num_woken;
+  if (!num_woken)
+    return MOJO_RESULT_SHOULD_WAIT;
+
+  return MOJO_RESULT_OK;
+}
+
+HandleSignalsState WaitSetDispatcher::GetHandleSignalsState() const {
+  base::AutoLock lock(lock_);
+  return GetHandleSignalsStateNoLock();
+}
+
+HandleSignalsState WaitSetDispatcher::GetHandleSignalsStateNoLock() const {
+  lock_.AssertAcquired();
+  if (is_closed_)
+    return HandleSignalsState();
+
+  HandleSignalsState rv;
+  rv.satisfiable_signals = MOJO_HANDLE_SIGNAL_READABLE;
+  base::AutoLock locker(awoken_lock_);
+  if (!awoken_queue_.empty() || !processed_dispatchers_.empty())
+    rv.satisfied_signals = MOJO_HANDLE_SIGNAL_READABLE;
+  return rv;
+}
+
+MojoResult WaitSetDispatcher::AddAwakable(Awakable* awakable,
+                                          MojoHandleSignals signals,
+                                          uintptr_t context,
+                                          HandleSignalsState* signals_state) {
+  base::AutoLock lock(lock_);
+  // |awakable_lock_| is acquired here instead of immediately before adding to
+  // |awakable_list_| because we need to check the signals state and add to
+  // |awakable_list_| as an atomic operation. If the pair isn't atomic, it is
+  // possible for the signals state to change after it is checked, but before
+  // the awakable is added. In that case, the added awakable won't be signalled.
+  base::AutoLock awakable_locker(awakable_lock_);
+  HandleSignalsState state(GetHandleSignalsStateNoLock());
+  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;
+  }
+
+  awakable_list_.Add(awakable, signals, context);
+  return MOJO_RESULT_OK;
+}
+
+void WaitSetDispatcher::RemoveAwakable(Awakable* awakable,
+                                       HandleSignalsState* signals_state) {
+  {
+    base::AutoLock locker(awakable_lock_);
+    awakable_list_.Remove(awakable);
+  }
+  if (signals_state)
+    *signals_state = GetHandleSignalsState();
+}
+
+bool WaitSetDispatcher::BeginTransit() {
+  // You can't transfer wait sets!
+  return false;
+}
+
+WaitSetDispatcher::~WaitSetDispatcher() {
+  DCHECK(waiting_dispatchers_.empty());
+  DCHECK(awoken_queue_.empty());
+  DCHECK(processed_dispatchers_.empty());
+}
+
+void WaitSetDispatcher::WakeDispatcher(MojoResult result, uintptr_t context) {
+  {
+    base::AutoLock locker(awoken_lock_);
+
+    if (result == MOJO_RESULT_ALREADY_EXISTS)
+      result = MOJO_RESULT_OK;
+
+    awoken_queue_.push_back(std::make_pair(context, result));
+  }
+
+  base::AutoLock locker(awakable_lock_);
+  HandleSignalsState signals_state;
+  signals_state.satisfiable_signals = MOJO_HANDLE_SIGNAL_READABLE;
+  signals_state.satisfied_signals = MOJO_HANDLE_SIGNAL_READABLE;
+  awakable_list_.AwakeForStateChange(signals_state);
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/wait_set_dispatcher.h b/mojo/edk/system/wait_set_dispatcher.h
new file mode 100644
index 0000000..619a1be
--- /dev/null
+++ b/mojo/edk/system/wait_set_dispatcher.h
@@ -0,0 +1,103 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_SYSTEM_WAIT_SET_DISPATCHER_H_
+#define MOJO_EDK_SYSTEM_WAIT_SET_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <deque>
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/awakable_list.h"
+#include "mojo/edk/system/dispatcher.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+class MOJO_SYSTEM_IMPL_EXPORT WaitSetDispatcher : public Dispatcher {
+ public:
+  WaitSetDispatcher();
+
+  // Dispatcher:
+  Type GetType() const override;
+  MojoResult Close() override;
+  MojoResult AddWaitingDispatcher(const scoped_refptr<Dispatcher>& dispatcher,
+                                  MojoHandleSignals signals,
+                                  uintptr_t context) override;
+  MojoResult RemoveWaitingDispatcher(
+      const scoped_refptr<Dispatcher>& dispatcher) override;
+  MojoResult GetReadyDispatchers(uint32_t* count,
+                                 DispatcherVector* dispatchers,
+                                 MojoResult* results,
+                                 uintptr_t* contexts) override;
+  HandleSignalsState GetHandleSignalsState() const override;
+  MojoResult AddAwakable(Awakable* awakable,
+                         MojoHandleSignals signals,
+                         uintptr_t context,
+                         HandleSignalsState* signals_state) override;
+  void RemoveAwakable(Awakable* awakable,
+                      HandleSignalsState* signals_state) override;
+  bool BeginTransit() override;
+
+ private:
+  // Internal implementation of Awakable.
+  class Waiter;
+
+  struct WaitState {
+    WaitState();
+    WaitState(const WaitState& other);
+    ~WaitState();
+
+    scoped_refptr<Dispatcher> dispatcher;
+    MojoHandleSignals signals;
+    uintptr_t context;
+  };
+
+  ~WaitSetDispatcher() override;
+
+  HandleSignalsState GetHandleSignalsStateNoLock() const;
+
+  // Signal that the dispatcher indexed by |context| has been woken up with
+  // |result| and is now ready.
+  void WakeDispatcher(MojoResult result, uintptr_t context);
+
+  // Guards |is_closed_|, |waiting_dispatchers_|, and |waiter_|.
+  //
+  // TODO: Consider removing this.
+  mutable base::Lock lock_;
+  bool is_closed_ = false;
+
+  // Map of dispatchers being waited on. Key is a Dispatcher* casted to a
+  // uintptr_t, and should be treated as an opaque value and not casted back.
+  std::unordered_map<uintptr_t, WaitState> waiting_dispatchers_;
+
+  // Separate lock that can be locked without locking |lock_|.
+  mutable base::Lock awoken_lock_;
+  // List of dispatchers that have been woken up. Any dispatcher in this queue
+  // will NOT currently be waited on.
+  std::deque<std::pair<uintptr_t, MojoResult>> awoken_queue_;
+  // List of dispatchers that have been woken up and retrieved.
+  std::deque<uintptr_t> processed_dispatchers_;
+
+  // Separate lock that can be locked without locking |lock_|.
+  base::Lock awakable_lock_;
+  // List of dispatchers being waited on.
+  AwakableList awakable_list_;
+
+  // Waiter used to wait on dispatchers.
+  std::unique_ptr<Waiter> waiter_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaitSetDispatcher);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WAIT_SET_DISPATCHER_H_
diff --git a/mojo/edk/system/wait_set_dispatcher_unittest.cc b/mojo/edk/system/wait_set_dispatcher_unittest.cc
new file mode 100644
index 0000000..42ac865
--- /dev/null
+++ b/mojo/edk/system/wait_set_dispatcher_unittest.cc
@@ -0,0 +1,493 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/system/wait_set_dispatcher.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/system/core.h"
+#include "mojo/edk/system/message_for_transit.h"
+#include "mojo/edk/system/message_pipe_dispatcher.h"
+#include "mojo/edk/system/request_context.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+class WaitSetDispatcherTest : public ::testing::Test {
+ public:
+  WaitSetDispatcherTest() {}
+  ~WaitSetDispatcherTest() override {}
+
+  void SetUp() override {
+    CreateMessagePipe(&dispatcher0_, &dispatcher1_);
+  }
+
+  void TearDown() override {
+    for (auto& d : dispatchers_to_close_)
+      d->Close();
+  }
+
+  MojoResult GetOneReadyDispatcher(
+      const scoped_refptr<WaitSetDispatcher>& wait_set,
+      scoped_refptr<Dispatcher>* ready_dispatcher,
+      uintptr_t* context) {
+    uint32_t count = 1;
+    MojoResult dispatcher_result = MOJO_RESULT_UNKNOWN;
+    DispatcherVector dispatchers;
+    MojoResult result = wait_set->GetReadyDispatchers(
+        &count, &dispatchers, &dispatcher_result, context);
+    if (result == MOJO_RESULT_OK) {
+      CHECK_EQ(1u, dispatchers.size());
+      *ready_dispatcher = dispatchers[0];
+      return dispatcher_result;
+    }
+    return result;
+  }
+
+  void CreateMessagePipe(scoped_refptr<MessagePipeDispatcher>* d0,
+                         scoped_refptr<MessagePipeDispatcher>* d1) {
+    MojoHandle h0, h1;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &h0, &h1));
+
+    Core* core = mojo::edk::internal::g_core;
+    *d0 = scoped_refptr<MessagePipeDispatcher>(
+        static_cast<MessagePipeDispatcher*>(core->GetDispatcher(h0).get()));
+    *d1 = scoped_refptr<MessagePipeDispatcher>(
+        static_cast<MessagePipeDispatcher*>(core->GetDispatcher(h1).get()));
+    pipe_id_generator_++;
+
+    dispatchers_to_close_.push_back(*d0);
+    dispatchers_to_close_.push_back(*d1);
+  }
+
+  void CloseOnShutdown(const scoped_refptr<Dispatcher>& dispatcher) {
+    dispatchers_to_close_.push_back(dispatcher);
+  }
+
+  void WriteMessage(MessagePipeDispatcher* dispatcher,
+                    const void* bytes,
+                    size_t num_bytes) {
+    Core* core = mojo::edk::internal::g_core;
+    MojoMessageHandle msg;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              core->AllocMessage(static_cast<uint32_t>(num_bytes), nullptr, 0,
+                                 MOJO_ALLOC_MESSAGE_FLAG_NONE, &msg));
+    void* buffer;
+    ASSERT_EQ(MOJO_RESULT_OK, core->GetMessageBuffer(msg, &buffer));
+    memcpy(buffer, bytes, num_bytes);
+
+    std::unique_ptr<MessageForTransit> message(
+        reinterpret_cast<MessageForTransit*>(msg));
+    ASSERT_EQ(MOJO_RESULT_OK,
+              dispatcher->WriteMessage(std::move(message),
+                                       MOJO_WRITE_MESSAGE_FLAG_NONE));
+  }
+
+  void ReadMessage(MessagePipeDispatcher* dispatcher,
+                   void* bytes,
+                   uint32_t* num_bytes) {
+    std::unique_ptr<MessageForTransit> message;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              dispatcher->ReadMessage(&message, num_bytes, nullptr, 0,
+                                      MOJO_READ_MESSAGE_FLAG_NONE, false));
+    memcpy(bytes, message->bytes(), *num_bytes);
+  }
+
+ protected:
+  scoped_refptr<MessagePipeDispatcher> dispatcher0_;
+  scoped_refptr<MessagePipeDispatcher> dispatcher1_;
+
+ private:
+  // We keep an active RequestContext for the duration of each test. It's unused
+  // since these tests don't rely on the MojoWatch API.
+  const RequestContext request_context_;
+
+  static uint64_t pipe_id_generator_;
+  DispatcherVector dispatchers_to_close_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaitSetDispatcherTest);
+};
+
+// static
+uint64_t WaitSetDispatcherTest::pipe_id_generator_ = 1;
+
+TEST_F(WaitSetDispatcherTest, Basic) {
+  scoped_refptr<WaitSetDispatcher> wait_set = new WaitSetDispatcher();
+  CloseOnShutdown(wait_set);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(dispatcher0_,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 1));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(dispatcher1_,
+                                           MOJO_HANDLE_SIGNAL_WRITABLE, 2));
+
+  Waiter w;
+  uintptr_t context = 0;
+  w.Init();
+  HandleSignalsState hss;
+  // |dispatcher1_| should already be writable.
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  scoped_refptr<Dispatcher> woken_dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, &context));
+  EXPECT_EQ(dispatcher1_, woken_dispatcher);
+  EXPECT_EQ(2u, context);
+  // If a ready dispatcher isn't removed, it will continue to be returned.
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  woken_dispatcher = nullptr;
+  context = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, &context));
+  EXPECT_EQ(dispatcher1_, woken_dispatcher);
+  EXPECT_EQ(2u, context);
+  ASSERT_EQ(MOJO_RESULT_OK, wait_set->RemoveWaitingDispatcher(dispatcher1_));
+
+  // No ready dispatcher.
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  EXPECT_FALSE(hss.satisfies(MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, nullptr));
+
+  // Write to |dispatcher1_|, which should make |dispatcher0_| readable.
+  char buffer[] = "abcd";
+  w.Init();
+  WriteMessage(dispatcher1_.get(), buffer, sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, nullptr));
+  woken_dispatcher = nullptr;
+  context = 0;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, &context));
+  EXPECT_EQ(dispatcher0_, woken_dispatcher);
+  EXPECT_EQ(1u, context);
+
+  // Again, if a ready dispatcher isn't removed, it will continue to be
+  // returned.
+  woken_dispatcher = nullptr;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, nullptr));
+  EXPECT_EQ(dispatcher0_, woken_dispatcher);
+
+  wait_set->RemoveAwakable(&w, nullptr);
+}
+
+TEST_F(WaitSetDispatcherTest, HandleWithoutRemoving) {
+  scoped_refptr<WaitSetDispatcher> wait_set = new WaitSetDispatcher();
+  CloseOnShutdown(wait_set);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(dispatcher0_,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 1));
+
+  Waiter w;
+  uintptr_t context = 0;
+  w.Init();
+  HandleSignalsState hss;
+  // No ready dispatcher.
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  EXPECT_FALSE(hss.satisfies(MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
+  scoped_refptr<Dispatcher> woken_dispatcher;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, nullptr));
+
+  // The tested behaviour below should be repeatable.
+  for (size_t i = 0; i < 3; i++) {
+    // Write to |dispatcher1_|, which should make |dispatcher0_| readable.
+    char buffer[] = "abcd";
+    w.Init();
+    WriteMessage(dispatcher1_.get(), buffer, sizeof(buffer));
+    EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, nullptr));
+    woken_dispatcher = nullptr;
+    context = 0;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              GetOneReadyDispatcher(wait_set, &woken_dispatcher, &context));
+    EXPECT_EQ(dispatcher0_, woken_dispatcher);
+    EXPECT_EQ(1u, context);
+
+    // Read from |dispatcher0_| which should change it's state to non-readable.
+    char read_buffer[sizeof(buffer) + 5];
+    uint32_t num_bytes = sizeof(read_buffer);
+    ReadMessage(dispatcher0_.get(), read_buffer, &num_bytes);
+    EXPECT_EQ(sizeof(buffer), num_bytes);
+
+    // No dispatchers are ready.
+    w.Init();
+    woken_dispatcher = nullptr;
+    context = 0;
+    EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+              GetOneReadyDispatcher(wait_set, &woken_dispatcher, &context));
+    EXPECT_FALSE(woken_dispatcher);
+    EXPECT_EQ(0u, context);
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
+  }
+
+  wait_set->RemoveAwakable(&w, nullptr);
+}
+
+TEST_F(WaitSetDispatcherTest, MultipleReady) {
+  scoped_refptr<WaitSetDispatcher> wait_set = new WaitSetDispatcher();
+  CloseOnShutdown(wait_set);
+
+  scoped_refptr<MessagePipeDispatcher> mp1_dispatcher0;
+  scoped_refptr<MessagePipeDispatcher> mp1_dispatcher1;
+  CreateMessagePipe(&mp1_dispatcher0, &mp1_dispatcher1);
+
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(dispatcher0_,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 0));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(dispatcher1_,
+                                           MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(mp1_dispatcher0,
+                                           MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(mp1_dispatcher1,
+                                           MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+  Waiter w;
+  w.Init();
+  HandleSignalsState hss;
+  // The three writable dispatchers should be ready.
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  scoped_refptr<Dispatcher> woken_dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, nullptr));
+  // Don't know which dispatcher was returned, just that it was one of the
+  // writable ones.
+  EXPECT_TRUE(woken_dispatcher == dispatcher1_ ||
+              woken_dispatcher == mp1_dispatcher0 ||
+              woken_dispatcher == mp1_dispatcher1);
+
+  DispatcherVector dispatchers_vector;
+  uint32_t count = 4;
+  MojoResult results[4];
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->GetReadyDispatchers(&count,
+                                          &dispatchers_vector,
+                                          results,
+                                          nullptr));
+  EXPECT_EQ(3u, count);
+  std::sort(dispatchers_vector.begin(), dispatchers_vector.end());
+  DispatcherVector expected_dispatchers;
+  expected_dispatchers.push_back(dispatcher1_);
+  expected_dispatchers.push_back(mp1_dispatcher0);
+  expected_dispatchers.push_back(mp1_dispatcher1);
+  std::sort(expected_dispatchers.begin(), expected_dispatchers.end());
+  EXPECT_EQ(expected_dispatchers, dispatchers_vector);
+
+  // If a ready dispatcher isn't removed, it will continue to be returned.
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+  count = 4;
+  dispatchers_vector.clear();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->GetReadyDispatchers(&count,
+                                          &dispatchers_vector,
+                                          results,
+                                          nullptr));
+  EXPECT_EQ(3u, count);
+  std::sort(dispatchers_vector.begin(), dispatchers_vector.end());
+  EXPECT_EQ(expected_dispatchers, dispatchers_vector);
+
+  // Remove one. It shouldn't be returned any longer.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->RemoveWaitingDispatcher(expected_dispatchers.back()));
+  expected_dispatchers.pop_back();
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+  count = 4;
+  dispatchers_vector.clear();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->GetReadyDispatchers(&count,
+                                          &dispatchers_vector,
+                                          results,
+                                          nullptr));
+  EXPECT_EQ(2u, count);
+  std::sort(dispatchers_vector.begin(), dispatchers_vector.end());
+  EXPECT_EQ(expected_dispatchers, dispatchers_vector);
+
+  // Write to |dispatcher1_|, which should make |dispatcher0_| readable.
+  char buffer[] = "abcd";
+  w.Init();
+  WriteMessage(dispatcher1_.get(), buffer, sizeof(buffer));
+  {
+    Waiter mp_w;
+    mp_w.Init();
+    // Wait for |dispatcher0_| to be readable.
+    if (dispatcher0_->AddAwakable(&mp_w, MOJO_HANDLE_SIGNAL_READABLE, 0,
+                                  nullptr) == MOJO_RESULT_OK) {
+      EXPECT_EQ(MOJO_RESULT_OK, mp_w.Wait(MOJO_DEADLINE_INDEFINITE, 0));
+      dispatcher0_->RemoveAwakable(&mp_w, nullptr);
+    }
+  }
+  expected_dispatchers.push_back(dispatcher0_);
+  std::sort(expected_dispatchers.begin(), expected_dispatchers.end());
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+  count = 4;
+  dispatchers_vector.clear();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->GetReadyDispatchers(&count,
+                                          &dispatchers_vector,
+                                          results,
+                                          nullptr));
+  EXPECT_EQ(3u, count);
+  std::sort(dispatchers_vector.begin(), dispatchers_vector.end());
+  EXPECT_EQ(expected_dispatchers, dispatchers_vector);
+}
+
+TEST_F(WaitSetDispatcherTest, InvalidParams) {
+  scoped_refptr<WaitSetDispatcher> wait_set = new WaitSetDispatcher();
+
+  // Can't add a wait set to itself.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            wait_set->AddWaitingDispatcher(wait_set,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+  // Can't add twice.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(dispatcher0_,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 0));
+  EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+            wait_set->AddWaitingDispatcher(dispatcher0_,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+  // Remove a dispatcher that wasn't added.
+  EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
+            wait_set->RemoveWaitingDispatcher(dispatcher1_));
+
+  // Add to a closed wait set.
+  wait_set->Close();
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            wait_set->AddWaitingDispatcher(dispatcher0_,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 0));
+}
+
+TEST_F(WaitSetDispatcherTest, NotSatisfiable) {
+  scoped_refptr<WaitSetDispatcher> wait_set = new WaitSetDispatcher();
+  CloseOnShutdown(wait_set);
+
+  // Wait sets can only satisfy MOJO_HANDLE_SIGNAL_READABLE.
+  Waiter w;
+  w.Init();
+  HandleSignalsState hss;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+  hss = HandleSignalsState();
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 0, &hss));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+}
+
+TEST_F(WaitSetDispatcherTest, ClosedDispatchers) {
+  scoped_refptr<WaitSetDispatcher> wait_set = new WaitSetDispatcher();
+  CloseOnShutdown(wait_set);
+
+  Waiter w;
+  w.Init();
+  HandleSignalsState hss;
+  // A dispatcher that was added and then closed will be cancelled.
+  ASSERT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(dispatcher0_,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 0));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+  dispatcher0_->Close();
+  EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, nullptr));
+  EXPECT_TRUE(
+      wait_set->GetHandleSignalsState().satisfies(MOJO_HANDLE_SIGNAL_READABLE));
+  scoped_refptr<Dispatcher> woken_dispatcher;
+  EXPECT_EQ(MOJO_RESULT_CANCELLED,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, nullptr));
+  EXPECT_EQ(dispatcher0_, woken_dispatcher);
+
+  // Dispatcher will be implicitly removed because it may be impossible to
+  // remove explicitly.
+  woken_dispatcher = nullptr;
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, nullptr));
+  EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
+            wait_set->RemoveWaitingDispatcher(dispatcher0_));
+
+  // A dispatcher that's not satisfiable should give an error.
+  w.Init();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(dispatcher1_,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 0));
+  EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, nullptr));
+  EXPECT_TRUE(
+      wait_set->GetHandleSignalsState().satisfies(MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, nullptr));
+  EXPECT_EQ(dispatcher1_, woken_dispatcher);
+
+  wait_set->RemoveAwakable(&w, nullptr);
+}
+
+TEST_F(WaitSetDispatcherTest, NestedSets) {
+  scoped_refptr<WaitSetDispatcher> wait_set = new WaitSetDispatcher();
+  CloseOnShutdown(wait_set);
+  scoped_refptr<WaitSetDispatcher> nested_wait_set = new WaitSetDispatcher();
+  CloseOnShutdown(nested_wait_set);
+
+  Waiter w;
+  w.Init();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->AddWaitingDispatcher(nested_wait_set,
+                                           MOJO_HANDLE_SIGNAL_READABLE, 0));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            wait_set->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr));
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
+
+  // Writable signal is immediately satisfied by the message pipe.
+  w.Init();
+  EXPECT_EQ(MOJO_RESULT_OK,
+            nested_wait_set->AddWaitingDispatcher(
+                dispatcher0_, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+  EXPECT_EQ(MOJO_RESULT_OK, w.Wait(0, nullptr));
+  scoped_refptr<Dispatcher> woken_dispatcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            GetOneReadyDispatcher(wait_set, &woken_dispatcher, nullptr));
+  EXPECT_EQ(nested_wait_set, woken_dispatcher);
+
+  wait_set->RemoveAwakable(&w, nullptr);
+}
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/waiter.cc b/mojo/edk/system/waiter.cc
new file mode 100644
index 0000000..d98f3c6
--- /dev/null
+++ b/mojo/edk/system/waiter.cc
@@ -0,0 +1,102 @@
+// 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 <stdint.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace mojo {
+namespace edk {
+
+Waiter::Waiter()
+    : cv_(&lock_),
+#if DCHECK_IS_ON()
+      initialized_(false),
+#endif
+      awoken_(false),
+      awake_result_(MOJO_RESULT_INTERNAL),
+      awake_context_(static_cast<uint32_t>(-1)) {
+}
+
+Waiter::~Waiter() {
+}
+
+void Waiter::Init() {
+#if DCHECK_IS_ON()
+  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, uintptr_t* context) {
+  base::AutoLock locker(lock_);
+
+#if DCHECK_IS_ON()
+  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_;
+}
+
+bool Waiter::Awake(MojoResult result, uintptr_t context) {
+  base::AutoLock locker(lock_);
+
+  if (awoken_)
+    return true;
+
+  awoken_ = true;
+  awake_result_ = result;
+  awake_context_ = context;
+  cv_.Signal();
+  // |cv_.Wait()|/|cv_.TimedWait()| will return after |lock_| is released.
+  return true;
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/waiter.h b/mojo/edk/system/waiter.h
new file mode 100644
index 0000000..897ecbe
--- /dev/null
+++ b/mojo/edk/system/waiter.h
@@ -0,0 +1,80 @@
+// 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/logging.h"
+#include "base/macros.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/awakable.h"
+#include "mojo/edk/system/system_impl_export.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+// 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 final : public Awakable {
+ 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::AddAwakable()|, 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::AddAwakable()|) 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, uintptr_t* context);
+
+  // Wake the waiter up with the given result and context (or no-op if it's been
+  // woken up already).
+  bool Awake(MojoResult result, uintptr_t context) override;
+
+ private:
+  base::Lock lock_;             // Protects the following members.
+  base::ConditionVariable cv_;  // Associated to |lock_|.
+#if DCHECK_IS_ON()
+  bool initialized_;
+#endif
+  bool awoken_;
+  MojoResult awake_result_;
+  uintptr_t awake_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(Waiter);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WAITER_H_
diff --git a/mojo/edk/system/waiter_test_utils.cc b/mojo/edk/system/waiter_test_utils.cc
new file mode 100644
index 0000000..c7681e9
--- /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 edk {
+namespace test {
+
+SimpleWaiterThread::SimpleWaiterThread(MojoResult* result, uintptr_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,
+                           uintptr_t context,
+                           bool* did_wait_out,
+                           MojoResult* result_out,
+                           uintptr_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_->AddAwakable(&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_->RemoveAwakable(&waiter_, signals_state_out_);
+}
+
+}  // namespace test
+}  // namespace edk
+}  // 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..eda1396
--- /dev/null
+++ b/mojo/edk/system/waiter_test_utils.h
@@ -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.
+
+#ifndef MOJO_EDK_SYSTEM_WAITER_TEST_UTILS_H_
+#define MOJO_EDK_SYSTEM_WAITER_TEST_UTILS_H_
+
+#include <stdint.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 edk {
+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;
+//    {
+//      AwakableList awakable_list;
+//      test::SimpleWaiterThread thread(&result);
+//      awakable_list.Add(thread.waiter(), ...);
+//      thread.Start();
+//      ... some stuff to wake the waiter ...
+//      awakable_list.Remove(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()|, |AddAwakable()|, and |RemoveAwakable()| 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 |AwakableList| 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, uintptr_t* context);
+  ~SimpleWaiterThread() override;  // Joins the thread.
+
+  Waiter* waiter() { return &waiter_; }
+
+ private:
+  void Run() override;
+
+  MojoResult* const result_;
+  uintptr_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,
+               uintptr_t context,
+               bool* did_wait_out,
+               MojoResult* result_out,
+               uintptr_t* context_out,
+               HandleSignalsState* signals_state_out);
+  ~WaiterThread() override;
+
+ private:
+  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_;
+  uintptr_t* const context_out_;
+  HandleSignalsState* const signals_state_out_;
+
+  Waiter waiter_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaiterThread);
+};
+
+}  // namespace test
+}  // namespace edk
+}  // 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..aa928ff
--- /dev/null
+++ b/mojo/edk/system/waiter_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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonDeadline()| 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/simple_thread.h"
+#include "mojo/edk/system/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+const unsigned kPollTimeMs = 10;
+
+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<uintptr_t>(-1)) {
+    waiter_.Init();
+  }
+
+  ~WaitingThread() override { Join(); }
+
+  void WaitUntilDone(MojoResult* result,
+                     uintptr_t* context,
+                     MojoDeadline* elapsed) {
+    for (;;) {
+      {
+        base::AutoLock locker(lock_);
+        if (done_) {
+          *result = result_;
+          *context = context_;
+          *elapsed = elapsed_;
+          break;
+        }
+      }
+
+      test::Sleep(test::DeadlineFromMilliseconds(kPollTimeMs));
+    }
+  }
+
+  Waiter* waiter() { return &waiter_; }
+
+ private:
+  void Run() override {
+    test::Stopwatch stopwatch;
+    MojoResult result;
+    uintptr_t context = static_cast<uintptr_t>(-1);
+    MojoDeadline 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_;
+  uintptr_t context_;
+  MojoDeadline elapsed_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaitingThread);
+};
+
+TEST(WaiterTest, Basic) {
+  MojoResult result;
+  uintptr_t context;
+  MojoDeadline elapsed;
+
+  // Finite deadline.
+
+  // Awake immediately after thread start.
+  {
+    WaitingThread thread(10 * test::EpsilonDeadline());
+    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::EpsilonDeadline());
+  }
+
+  // Awake before after thread start.
+  {
+    WaitingThread thread(10 * test::EpsilonDeadline());
+    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::EpsilonDeadline());
+  }
+
+  // Awake some time after thread start.
+  {
+    WaitingThread thread(10 * test::EpsilonDeadline());
+    thread.Start();
+    test::Sleep(2 * test::EpsilonDeadline());
+    thread.waiter()->Awake(1, 3);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(1u, result);
+    EXPECT_EQ(3u, context);
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  }
+
+  // Awake some longer time after thread start.
+  {
+    WaitingThread thread(10 * test::EpsilonDeadline());
+    thread.Start();
+    test::Sleep(5 * test::EpsilonDeadline());
+    thread.waiter()->Awake(2, 4);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(2u, result);
+    EXPECT_EQ(4u, context);
+    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonDeadline());
+    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonDeadline());
+  }
+
+  // Don't awake -- time out (on another thread).
+  {
+    WaitingThread thread(2 * test::EpsilonDeadline());
+    thread.Start();
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
+    EXPECT_EQ(static_cast<uintptr_t>(-1), context);
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  }
+
+  // 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::EpsilonDeadline());
+  }
+
+  // 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::EpsilonDeadline());
+  }
+
+  // Awake some time after thread start.
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.Start();
+    test::Sleep(2 * test::EpsilonDeadline());
+    thread.waiter()->Awake(1, 7);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(1u, result);
+    EXPECT_EQ(7u, context);
+    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  }
+
+  // Awake some longer time after thread start.
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.Start();
+    test::Sleep(5 * test::EpsilonDeadline());
+    thread.waiter()->Awake(2, 8);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(2u, result);
+    EXPECT_EQ(8u, context);
+    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonDeadline());
+    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonDeadline());
+  }
+}
+
+TEST(WaiterTest, TimeOut) {
+  test::Stopwatch stopwatch;
+  MojoDeadline elapsed;
+
+  Waiter waiter;
+  uintptr_t context = 123;
+
+  waiter.Init();
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, &context));
+  elapsed = stopwatch.Elapsed();
+  EXPECT_LT(elapsed, test::EpsilonDeadline());
+  EXPECT_EQ(123u, context);
+
+  waiter.Init();
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            waiter.Wait(2 * test::EpsilonDeadline(), &context));
+  elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+  EXPECT_EQ(123u, context);
+
+  waiter.Init();
+  stopwatch.Start();
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            waiter.Wait(5 * test::EpsilonDeadline(), &context));
+  elapsed = stopwatch.Elapsed();
+  EXPECT_GT(elapsed, (5 - 1) * test::EpsilonDeadline());
+  EXPECT_LT(elapsed, (5 + 1) * test::EpsilonDeadline());
+  EXPECT_EQ(123u, context);
+}
+
+// The first |Awake()| should always win.
+TEST(WaiterTest, MultipleAwakes) {
+  MojoResult result;
+  uintptr_t context;
+  MojoDeadline 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::EpsilonDeadline());
+  }
+
+  {
+    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(1u, result);
+    EXPECT_EQ(3u, context);
+    EXPECT_LT(elapsed, test::EpsilonDeadline());
+  }
+
+  {
+    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+    thread.Start();
+    thread.waiter()->Awake(10, 5);
+    test::Sleep(2 * test::EpsilonDeadline());
+    thread.waiter()->Awake(20, 6);
+    thread.WaitUntilDone(&result, &context, &elapsed);
+    EXPECT_EQ(10u, result);
+    EXPECT_EQ(5u, context);
+    EXPECT_LT(elapsed, test::EpsilonDeadline());
+  }
+
+  {
+    WaitingThread thread(10 * test::EpsilonDeadline());
+    thread.Start();
+    test::Sleep(1 * test::EpsilonDeadline());
+    thread.waiter()->Awake(MOJO_RESULT_FAILED_PRECONDITION, 7);
+    test::Sleep(2 * test::EpsilonDeadline());
+    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::EpsilonDeadline());
+    EXPECT_LT(elapsed, (1 + 1) * test::EpsilonDeadline());
+  }
+}
+
+}  // namespace
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/watcher.cc b/mojo/edk/system/watcher.cc
new file mode 100644
index 0000000..25c2276
--- /dev/null
+++ b/mojo/edk/system/watcher.cc
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All 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/watcher.h"
+
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/request_context.h"
+
+namespace mojo {
+namespace edk {
+
+Watcher::Watcher(MojoHandleSignals signals, const WatchCallback& callback)
+    : signals_(signals), callback_(callback) {
+}
+
+void Watcher::MaybeInvokeCallback(MojoResult result,
+                                  const HandleSignalsState& state,
+                                  MojoWatchNotificationFlags flags) {
+  base::AutoLock lock(lock_);
+  if (is_cancelled_)
+    return;
+
+  callback_.Run(result, state, flags);
+}
+
+void Watcher::NotifyForStateChange(const HandleSignalsState& signals_state) {
+  RequestContext* request_context = RequestContext::current();
+  if (signals_state.satisfies(signals_)) {
+    request_context->AddWatchNotifyFinalizer(
+        make_scoped_refptr(this), MOJO_RESULT_OK, signals_state);
+  } else if (!signals_state.can_satisfy(signals_)) {
+    request_context->AddWatchNotifyFinalizer(
+        make_scoped_refptr(this), MOJO_RESULT_FAILED_PRECONDITION,
+        signals_state);
+  }
+}
+
+void Watcher::NotifyClosed() {
+  static const HandleSignalsState closed_state = {0, 0};
+  RequestContext::current()->AddWatchNotifyFinalizer(
+      make_scoped_refptr(this), MOJO_RESULT_CANCELLED, closed_state);
+}
+
+void Watcher::Cancel() {
+  base::AutoLock lock(lock_);
+  is_cancelled_ = true;
+}
+
+Watcher::~Watcher() {}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/watcher.h b/mojo/edk/system/watcher.h
new file mode 100644
index 0000000..b6dc2e4
--- /dev/null
+++ b/mojo/edk/system/watcher.h
@@ -0,0 +1,88 @@
+// Copyright 2016 The Chromium Authors. All 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_WATCHER_H_
+#define MOJO_EDK_SYSTEM_WATCHER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+struct HandleSignalsState;
+
+// This object corresponds to a watch added by a single call to |MojoWatch()|.
+//
+// An event may occur at any time which should trigger a Watcher to run its
+// callback, but the callback needs to be deferred until all EDK locks are
+// released. At the same time, a watch may be cancelled at any time by
+// |MojoCancelWatch()| and it is not OK for the callback to be invoked after
+// that happens.
+//
+// Therefore a Watcher needs to have some associated thread-safe state to track
+// its cancellation, which is why it's ref-counted.
+class Watcher : public base::RefCountedThreadSafe<Watcher> {
+ public:
+  using WatchCallback = base::Callback<void(MojoResult,
+                                            const HandleSignalsState&,
+                                            MojoWatchNotificationFlags)>;
+
+  // Constructs a new Watcher which watches for |signals| to be satisfied on a
+  // handle and which invokes |callback| either when one such signal is
+  // satisfied, or all such signals become unsatisfiable.
+  Watcher(MojoHandleSignals signals, const WatchCallback& callback);
+
+  // Runs the Watcher's callback with the given arguments if it hasn't been
+  // cancelled yet.
+  void MaybeInvokeCallback(MojoResult result,
+                           const HandleSignalsState& state,
+                           MojoWatchNotificationFlags flags);
+
+  // Notifies the Watcher of a state change. This may result in the Watcher
+  // adding a finalizer to the current RequestContext to invoke its callback,
+  // cancellation notwithstanding.
+  void NotifyForStateChange(const HandleSignalsState& signals_state);
+
+  // Notifies the Watcher of handle closure. This always results in the Watcher
+  // adding a finalizer to the current RequestContext to invoke its callback,
+  // cancellation notwithstanding.
+  void NotifyClosed();
+
+  // Explicitly cancels the watch, guaranteeing that its callback will never be
+  // be invoked again.
+  void Cancel();
+
+ private:
+  friend class base::RefCountedThreadSafe<Watcher>;
+
+  ~Watcher();
+
+  // The set of signals which are watched by this Watcher.
+  const MojoHandleSignals signals_;
+
+  // The callback to invoke with a result and signal state any time signals in
+  // |signals_| are satisfied or become permanently unsatisfiable.
+  const WatchCallback callback_;
+
+  // Guards |is_cancelled_|.
+  base::Lock lock_;
+
+  // Indicates whether the watch has been cancelled. A |Watcher| may exist for a
+  // brief period of time after being cancelled if it's been attached as a
+  // RequestContext finalizer. In such cases the callback must not be invoked,
+  // hence this flag.
+  bool is_cancelled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(Watcher);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WATCHER_H_
diff --git a/mojo/edk/system/watcher_set.cc b/mojo/edk/system/watcher_set.cc
new file mode 100644
index 0000000..878f29a
--- /dev/null
+++ b/mojo/edk/system/watcher_set.cc
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All 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/watcher_set.h"
+
+#include "mojo/edk/system/request_context.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+WatcherSet::WatcherSet() {}
+
+WatcherSet::~WatcherSet() {}
+
+void WatcherSet::NotifyForStateChange(const HandleSignalsState& state) {
+  for (const auto& entry : watchers_)
+    entry.second->NotifyForStateChange(state);
+}
+
+void WatcherSet::NotifyClosed() {
+  for (const auto& entry : watchers_)
+    entry.second->NotifyClosed();
+}
+
+MojoResult WatcherSet::Add(MojoHandleSignals signals,
+                           const Watcher::WatchCallback& callback,
+                           uintptr_t context,
+                           const HandleSignalsState& current_state) {
+  auto it = watchers_.find(context);
+  if (it != watchers_.end())
+    return MOJO_RESULT_ALREADY_EXISTS;
+
+  if (!current_state.can_satisfy(signals))
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  scoped_refptr<Watcher> watcher(new Watcher(signals, callback));
+  watchers_.insert(std::make_pair(context, watcher));
+
+  watcher->NotifyForStateChange(current_state);
+
+  return MOJO_RESULT_OK;
+}
+
+MojoResult WatcherSet::Remove(uintptr_t context) {
+  auto it = watchers_.find(context);
+  if (it == watchers_.end())
+    return MOJO_RESULT_INVALID_ARGUMENT;
+
+  RequestContext::current()->AddWatchCancelFinalizer(it->second);
+  watchers_.erase(it);
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/system/watcher_set.h b/mojo/edk/system/watcher_set.h
new file mode 100644
index 0000000..8ae54a1
--- /dev/null
+++ b/mojo/edk/system/watcher_set.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All 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_WATCHER_SET_H_
+#define MOJO_EDK_SYSTEM_WATCHER_SET_H_
+
+#include <unordered_map>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/edk/system/watcher.h"
+#include "mojo/public/c/system/types.h"
+
+namespace mojo {
+namespace edk {
+
+// A WatcherSet maintains a set of Watchers attached to a single handle and
+// keyed on an arbitrary user context.
+class WatcherSet {
+ public:
+  WatcherSet();
+  ~WatcherSet();
+
+  // Notifies all Watchers of a state change.
+  void NotifyForStateChange(const HandleSignalsState& state);
+
+  // Notifies all Watchers that their watched handle has been closed.
+  void NotifyClosed();
+
+  // Adds a new watcher to watch for signals in |signals| to be satisfied or
+  // unsatisfiable. |current_state| is the current signals state of the
+  // handle being watched.
+  MojoResult Add(MojoHandleSignals signals,
+                 const Watcher::WatchCallback& callback,
+                 uintptr_t context,
+                 const HandleSignalsState& current_state);
+
+  // Removes a watcher from the set.
+  MojoResult Remove(uintptr_t context);
+
+ private:
+  // A map of watchers keyed on context value.
+  std::unordered_map<uintptr_t, scoped_refptr<Watcher>> watchers_;
+
+  DISALLOW_COPY_AND_ASSIGN(WatcherSet);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_WATCHER_SET_H_
diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn
new file mode 100644
index 0000000..ebacc64
--- /dev/null
+++ b/mojo/edk/test/BUILD.gn
@@ -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.
+
+import("//testing/test.gni")
+
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "mojo_test_base.cc",
+    "mojo_test_base.h",
+    "scoped_ipc_support.cc",
+    "scoped_ipc_support.h",
+    "test_utils.h",
+    "test_utils_posix.cc",
+    "test_utils_win.cc",
+  ]
+
+  if (!is_ios) {
+    sources += [
+      "multiprocess_test_helper.cc",
+      "multiprocess_test_helper.h",
+    ]
+  }
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//mojo/public/cpp/system",
+    "//testing/gtest",
+  ]
+}
+
+source_set("run_all_unittests") {
+  testonly = true
+  sources = [
+    "run_all_unittests.cc",
+  ]
+
+  deps = [
+    ":test_support",
+    ":test_support_impl",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//mojo/public/c/test_support",
+    "//testing/gtest",
+  ]
+
+  if (is_linux && !is_component_build) {
+    public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
+  }
+}
+
+source_set("run_all_perftests") {
+  testonly = true
+  deps = [
+    ":test_support_impl",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/c/test_support",
+  ]
+
+  sources = [
+    "run_all_perftests.cc",
+  ]
+
+  if (is_linux && !is_component_build) {
+    public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
+  }
+}
+
+static_library("test_support_impl") {
+  testonly = true
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/public/c/test_support",
+    "//mojo/public/cpp/system",
+  ]
+
+  sources = [
+    "test_support_impl.cc",
+    "test_support_impl.h",
+  ]
+}
+
+# Public SDK test targets follow. These targets are not defined within the
+# public SDK itself as running the unittests requires the EDK.
+# TODO(vtl): These don't really belong here. (They should be converted to
+# apptests, but even apart from that these targets belong somewhere else.)
+
+group("public_tests") {
+  testonly = true
+  deps = [
+    ":mojo_public_bindings_unittests",
+    ":mojo_public_system_perftests",
+    ":mojo_public_system_unittests",
+  ]
+}
+
+test("mojo_public_bindings_perftests") {
+  deps = [
+    ":run_all_perftests",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/cpp/bindings/tests:perftests",
+  ]
+}
+
+test("mojo_public_bindings_unittests") {
+  deps = [
+    ":run_all_unittests",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/cpp/bindings/tests",
+  ]
+}
+
+test("mojo_public_system_perftests") {
+  deps = [
+    ":run_all_perftests",
+    "//mojo/public/c/system/tests:perftests",
+  ]
+}
+
+test("mojo_public_system_unittests") {
+  deps = [
+    ":run_all_unittests",
+    "//mojo/public/cpp/system/tests",
+  ]
+}
diff --git a/mojo/edk/test/mojo_test_base.cc b/mojo/edk/test/mojo_test_base.cc
new file mode 100644
index 0000000..5d77a11
--- /dev/null
+++ b/mojo/edk/test/mojo_test_base.cc
@@ -0,0 +1,311 @@
+// Copyright 2016 The Chromium Authors. All 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/mojo_test_base.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/system/handle_signals_state.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mach_port_broker.h"
+#endif
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+namespace {
+base::MachPortBroker* g_mach_broker = nullptr;
+}
+#endif
+
+MojoTestBase::MojoTestBase() {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  if (!g_mach_broker) {
+    g_mach_broker = new base::MachPortBroker("mojo_test");
+    CHECK(g_mach_broker->Init());
+    SetMachPortProvider(g_mach_broker);
+  }
+#endif
+}
+
+MojoTestBase::~MojoTestBase() {}
+
+MojoTestBase::ClientController& MojoTestBase::StartClient(
+    const std::string& client_name) {
+  clients_.push_back(base::MakeUnique<ClientController>(
+      client_name, this, process_error_callback_));
+  return *clients_.back();
+}
+
+MojoTestBase::ClientController::ClientController(
+    const std::string& client_name,
+    MojoTestBase* test,
+    const ProcessErrorCallback& process_error_callback) {
+#if !defined(OS_IOS)
+#if defined(OS_MACOSX)
+  // This lock needs to be held while launching the child because the Mach port
+  // broker only allows task ports to be received from known child processes.
+  // However, it can only know the child process's pid after the child has
+  // launched. To prevent a race where the child process sends its task port
+  // before the pid has been registered, the lock needs to be held over both
+  // launch and child pid registration.
+  base::AutoLock lock(g_mach_broker->GetLock());
+#endif
+  helper_.set_process_error_callback(process_error_callback);
+  pipe_ = helper_.StartChild(client_name);
+#if defined(OS_MACOSX)
+  g_mach_broker->AddPlaceholderForPid(helper_.test_child().Handle());
+#endif
+#endif
+}
+
+MojoTestBase::ClientController::~ClientController() {
+  CHECK(was_shutdown_)
+      << "Test clients should be waited on explicitly with WaitForShutdown().";
+}
+
+int MojoTestBase::ClientController::WaitForShutdown() {
+  was_shutdown_ = true;
+#if !defined(OS_IOS)
+  int retval = helper_.WaitForChildShutdown();
+#if defined(OS_MACOSX)
+  base::AutoLock lock(g_mach_broker->GetLock());
+  g_mach_broker->InvalidatePid(helper_.test_child().Handle());
+#endif
+  return retval;
+#else
+  NOTREACHED();
+  return 1;
+#endif
+}
+
+// static
+void MojoTestBase::CloseHandle(MojoHandle h) {
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h));
+}
+
+// static
+void MojoTestBase::CreateMessagePipe(MojoHandle *p0, MojoHandle* p1) {
+  MojoCreateMessagePipe(nullptr, p0, p1);
+  CHECK_NE(*p0, MOJO_HANDLE_INVALID);
+  CHECK_NE(*p1, MOJO_HANDLE_INVALID);
+}
+
+// static
+void MojoTestBase::WriteMessageWithHandles(MojoHandle mp,
+                                           const std::string& message,
+                                           const MojoHandle *handles,
+                                           uint32_t num_handles) {
+  CHECK_EQ(MojoWriteMessage(mp, message.data(),
+                            static_cast<uint32_t>(message.size()),
+                            handles, num_handles, MOJO_WRITE_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+}
+
+// static
+void MojoTestBase::WriteMessage(MojoHandle mp, const std::string& message) {
+  WriteMessageWithHandles(mp, message, nullptr, 0);
+}
+
+// static
+std::string MojoTestBase::ReadMessageWithHandles(
+    MojoHandle mp,
+    MojoHandle* handles,
+    uint32_t expected_num_handles) {
+  CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
+                    nullptr),
+           MOJO_RESULT_OK);
+
+  uint32_t message_size = 0;
+  uint32_t num_handles = 0;
+  CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles,
+                           MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_RESOURCE_EXHAUSTED);
+  CHECK_EQ(expected_num_handles, num_handles);
+
+  std::string message(message_size, 'x');
+  CHECK_EQ(MojoReadMessage(mp, &message[0], &message_size, handles,
+                           &num_handles, MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  CHECK_EQ(message_size, message.size());
+  CHECK_EQ(num_handles, expected_num_handles);
+
+  return message;
+}
+
+// static
+std::string MojoTestBase::ReadMessageWithOptionalHandle(MojoHandle mp,
+                                                        MojoHandle* handle) {
+  CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
+                    nullptr),
+           MOJO_RESULT_OK);
+
+  uint32_t message_size = 0;
+  uint32_t num_handles = 0;
+  CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles,
+                           MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_RESOURCE_EXHAUSTED);
+  CHECK(num_handles == 0 || num_handles == 1);
+
+  CHECK(handle);
+
+  std::string message(message_size, 'x');
+  CHECK_EQ(MojoReadMessage(mp, &message[0], &message_size, handle,
+                           &num_handles, MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  CHECK_EQ(message_size, message.size());
+  CHECK(num_handles == 0 || num_handles == 1);
+
+  if (num_handles)
+    CHECK_NE(*handle, MOJO_HANDLE_INVALID);
+  else
+    *handle = MOJO_HANDLE_INVALID;
+
+  return message;
+}
+
+// static
+std::string MojoTestBase::ReadMessage(MojoHandle mp) {
+  return ReadMessageWithHandles(mp, nullptr, 0);
+}
+
+// static
+void MojoTestBase::ReadMessage(MojoHandle mp,
+                               char* data,
+                               size_t num_bytes) {
+  CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
+                    nullptr),
+           MOJO_RESULT_OK);
+
+  uint32_t message_size = 0;
+  uint32_t num_handles = 0;
+  CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles,
+                           MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_RESOURCE_EXHAUSTED);
+  CHECK_EQ(num_handles, 0u);
+  CHECK_EQ(message_size, num_bytes);
+
+  CHECK_EQ(MojoReadMessage(mp, data, &message_size, nullptr, &num_handles,
+                           MOJO_READ_MESSAGE_FLAG_NONE),
+           MOJO_RESULT_OK);
+  CHECK_EQ(num_handles, 0u);
+  CHECK_EQ(message_size, num_bytes);
+}
+
+// static
+void MojoTestBase::VerifyTransmission(MojoHandle source,
+                                      MojoHandle dest,
+                                      const std::string& message) {
+  WriteMessage(source, message);
+
+  // We don't use EXPECT_EQ; failures on really long messages make life hard.
+  EXPECT_TRUE(message == ReadMessage(dest));
+}
+
+// static
+void MojoTestBase::VerifyEcho(MojoHandle mp,
+                              const std::string& message) {
+  VerifyTransmission(mp, mp, message);
+}
+
+// static
+MojoHandle MojoTestBase::CreateBuffer(uint64_t size) {
+  MojoHandle h;
+  EXPECT_EQ(MojoCreateSharedBuffer(nullptr, size, &h), MOJO_RESULT_OK);
+  return h;
+}
+
+// static
+MojoHandle MojoTestBase::DuplicateBuffer(MojoHandle h, bool read_only) {
+  MojoHandle new_handle;
+  MojoDuplicateBufferHandleOptions options = {
+    sizeof(MojoDuplicateBufferHandleOptions),
+    MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE
+  };
+  if (read_only)
+    options.flags |= MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoDuplicateBufferHandle(h, &options, &new_handle));
+  return new_handle;
+}
+
+// static
+void MojoTestBase::WriteToBuffer(MojoHandle h,
+                                 size_t offset,
+                                 const base::StringPiece& s) {
+  char* data;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoMapBuffer(h, offset, s.size(), reinterpret_cast<void**>(&data),
+                          MOJO_MAP_BUFFER_FLAG_NONE));
+  memcpy(data, s.data(), s.size());
+  EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(static_cast<void*>(data)));
+}
+
+// static
+void MojoTestBase::ExpectBufferContents(MojoHandle h,
+                                        size_t offset,
+                                        const base::StringPiece& s) {
+  char* data;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoMapBuffer(h, offset, s.size(), reinterpret_cast<void**>(&data),
+                          MOJO_MAP_BUFFER_FLAG_NONE));
+  EXPECT_EQ(s, base::StringPiece(data, s.size()));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(static_cast<void*>(data)));
+}
+
+// static
+void MojoTestBase::CreateDataPipe(MojoHandle *p0,
+                                  MojoHandle* p1,
+                                  size_t capacity) {
+  MojoCreateDataPipeOptions options;
+  options.struct_size = static_cast<uint32_t>(sizeof(options));
+  options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+  options.element_num_bytes = 1;
+  options.capacity_num_bytes = static_cast<uint32_t>(capacity);
+
+  MojoCreateDataPipe(&options, p0, p1);
+  CHECK_NE(*p0, MOJO_HANDLE_INVALID);
+  CHECK_NE(*p1, MOJO_HANDLE_INVALID);
+}
+
+// static
+void MojoTestBase::WriteData(MojoHandle producer, const std::string& data) {
+  CHECK_EQ(MojoWait(producer, MOJO_HANDLE_SIGNAL_WRITABLE,
+                    MOJO_DEADLINE_INDEFINITE, nullptr),
+           MOJO_RESULT_OK);
+  uint32_t num_bytes = static_cast<uint32_t>(data.size());
+  CHECK_EQ(MojoWriteData(producer, data.data(), &num_bytes,
+                         MOJO_WRITE_DATA_FLAG_ALL_OR_NONE),
+           MOJO_RESULT_OK);
+  CHECK_EQ(num_bytes, static_cast<uint32_t>(data.size()));
+}
+
+// static
+std::string MojoTestBase::ReadData(MojoHandle consumer, size_t size) {
+  CHECK_EQ(MojoWait(consumer, MOJO_HANDLE_SIGNAL_READABLE,
+                    MOJO_DEADLINE_INDEFINITE, nullptr),
+           MOJO_RESULT_OK);
+  std::vector<char> buffer(size);
+  uint32_t num_bytes = static_cast<uint32_t>(size);
+  CHECK_EQ(MojoReadData(consumer, buffer.data(), &num_bytes,
+                         MOJO_WRITE_DATA_FLAG_ALL_OR_NONE),
+           MOJO_RESULT_OK);
+  CHECK_EQ(num_bytes, static_cast<uint32_t>(size));
+
+  return std::string(buffer.data(), buffer.size());
+}
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/test/mojo_test_base.h b/mojo/edk/test/mojo_test_base.h
new file mode 100644
index 0000000..4f55145
--- /dev/null
+++ b/mojo/edk/test/mojo_test_base.h
@@ -0,0 +1,235 @@
+// Copyright 2016 The Chromium Authors. All 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_MOJO_TEST_BASE_H_
+#define MOJO_EDK_TEST_MOJO_TEST_BASE_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+class MojoTestBase : public testing::Test {
+ public:
+  MojoTestBase();
+  ~MojoTestBase() override;
+
+ protected:
+  using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>;
+
+  class ClientController {
+   public:
+    ClientController(const std::string& client_name,
+                     MojoTestBase* test,
+                     const ProcessErrorCallback& process_error_callback_);
+    ~ClientController();
+
+    MojoHandle pipe() const { return pipe_.get().value(); }
+
+    int WaitForShutdown();
+
+   private:
+    friend class MojoTestBase;
+
+#if !defined(OS_IOS)
+    MultiprocessTestHelper helper_;
+#endif
+    ScopedMessagePipeHandle pipe_;
+    bool was_shutdown_ = false;
+
+    DISALLOW_COPY_AND_ASSIGN(ClientController);
+  };
+
+  // Set the callback to handle bad messages received from test client
+  // processes. This can be set to a different callback before starting each
+  // client.
+  void set_process_error_callback(const ProcessErrorCallback& callback) {
+    process_error_callback_ = callback;
+  }
+
+  ClientController& StartClient(const std::string& client_name);
+
+  template <typename HandlerFunc>
+  void StartClientWithHandler(const std::string& client_name,
+                              HandlerFunc handler) {
+    int expected_exit_code = 0;
+    ClientController& c = StartClient(client_name);
+    handler(c.pipe(), &expected_exit_code);
+    EXPECT_EQ(expected_exit_code, c.WaitForShutdown());
+  }
+
+  // Closes a handle and expects success.
+  static void CloseHandle(MojoHandle h);
+
+  ////// Message pipe test utilities ///////
+
+  // Creates a new pipe, returning endpoint handles in |p0| and |p1|.
+  static void CreateMessagePipe(MojoHandle* p0, MojoHandle* p1);
+
+  // Writes a string to the pipe, transferring handles in the process.
+  static void WriteMessageWithHandles(MojoHandle mp,
+                                     const std::string& message,
+                                     const MojoHandle* handles,
+                                     uint32_t num_handles);
+
+  // Writes a string to the pipe with no handles.
+  static void WriteMessage(MojoHandle mp, const std::string& message);
+
+  // Reads a string from the pipe, expecting to read an exact number of handles
+  // in the process. Returns the read string.
+  static std::string ReadMessageWithHandles(MojoHandle mp,
+                                           MojoHandle* handles,
+                                           uint32_t expected_num_handles);
+
+  // Reads a string from the pipe, expecting either zero or one handles.
+  // If no handle is read, |handle| will be reset.
+  static std::string ReadMessageWithOptionalHandle(MojoHandle mp,
+                                                  MojoHandle* handle);
+
+  // Reads a string from the pipe, expecting to read no handles.
+  // Returns the string.
+  static std::string ReadMessage(MojoHandle mp);
+
+  // Reads a string from the pipe, expecting to read no handles and exactly
+  // |num_bytes| bytes, which are read into |data|.
+  static void ReadMessage(MojoHandle mp, char* data, size_t num_bytes);
+
+  // Writes |message| to |in| and expects to read it back from |out|.
+  static void VerifyTransmission(MojoHandle in,
+                                 MojoHandle out,
+                                 const std::string& message);
+
+  // Writes |message| to |mp| and expects to read it back from the same handle.
+  static void VerifyEcho(MojoHandle mp, const std::string& message);
+
+  //////// Shared buffer test utilities /////////
+
+  // Creates a new shared buffer.
+  static MojoHandle CreateBuffer(uint64_t size);
+
+  // Duplicates a shared buffer to a new handle.
+  static MojoHandle DuplicateBuffer(MojoHandle h, bool read_only);
+
+  // Maps a buffer, writes some data into it, and unmaps it.
+  static void WriteToBuffer(MojoHandle h,
+                            size_t offset,
+                            const base::StringPiece& s);
+
+  // Maps a buffer, tests the value of some of its contents, and unmaps it.
+  static void ExpectBufferContents(MojoHandle h,
+                                   size_t offset,
+                                   const base::StringPiece& s);
+
+  //////// Data pipe test utilities /////////
+
+  // Creates a new data pipe.
+  static void CreateDataPipe(MojoHandle* producer,
+                             MojoHandle* consumer,
+                             size_t capacity);
+
+  // Writes data to a data pipe.
+  static void WriteData(MojoHandle producer, const std::string& data);
+
+  // Reads data from a data pipe.
+  static std::string ReadData(MojoHandle consumer, size_t size);
+
+ private:
+  friend class ClientController;
+
+  std::vector<std::unique_ptr<ClientController>> clients_;
+
+  ProcessErrorCallback process_error_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoTestBase);
+};
+
+// Launches a new child process running the test client |client_name| connected
+// to a new message pipe bound to |pipe_name|. |pipe_name| is automatically
+// closed on test teardown.
+#define RUN_CHILD_ON_PIPE(client_name, pipe_name)                   \
+    StartClientWithHandler(                                         \
+        #client_name,                                               \
+        [&](MojoHandle pipe_name, int *expected_exit_code) { {
+
+// Waits for the client to terminate and expects a return code of zero.
+#define END_CHILD()               \
+        }                         \
+        *expected_exit_code = 0;  \
+    });
+
+// Wait for the client to terminate with a specific return code.
+#define END_CHILD_AND_EXPECT_EXIT_CODE(code) \
+        }                                    \
+        *expected_exit_code = code;          \
+    });
+
+// Use this to declare the child process's "main()" function for tests using
+// MojoTestBase and MultiprocessTestHelper. It returns an |int|, which will
+// will be the process's exit code (but see the comment about
+// WaitForChildShutdown()).
+//
+// The function is defined as a subclass of |test_base| to facilitate shared
+// code between test clients and to allow clients to spawn children themselves.
+//
+// |pipe_name| will be bound to the MojoHandle of a message pipe connected
+// to the parent process (see RUN_CHILD_ON_PIPE above.) This pipe handle is
+// automatically closed on test client teardown.
+#if !defined(OS_IOS)
+#define DEFINE_TEST_CLIENT_WITH_PIPE(client_name, test_base, pipe_name)     \
+  class client_name##_MainFixture : public test_base {                      \
+    void TestBody() override {}                                             \
+   public:                                                                  \
+    int Main(MojoHandle);                                                   \
+  };                                                                        \
+  MULTIPROCESS_TEST_MAIN_WITH_SETUP(                                        \
+      client_name##TestChildMain,                                           \
+      ::mojo::edk::test::MultiprocessTestHelper::ChildSetup) {              \
+    client_name##_MainFixture test;                                         \
+    return ::mojo::edk::test::MultiprocessTestHelper::RunClientMain(        \
+        base::Bind(&client_name##_MainFixture::Main,                        \
+                   base::Unretained(&test)));                               \
+  }                                                                         \
+  int client_name##_MainFixture::Main(MojoHandle pipe_name)
+
+// This is a version of DEFINE_TEST_CLIENT_WITH_PIPE which can be used with
+// gtest ASSERT/EXPECT macros.
+#define DEFINE_TEST_CLIENT_TEST_WITH_PIPE(client_name, test_base, pipe_name) \
+  class client_name##_MainFixture : public test_base {                       \
+    void TestBody() override {}                                              \
+   public:                                                                   \
+    void Main(MojoHandle);                                                   \
+  };                                                                         \
+  MULTIPROCESS_TEST_MAIN_WITH_SETUP(                                         \
+      client_name##TestChildMain,                                            \
+      ::mojo::edk::test::MultiprocessTestHelper::ChildSetup) {               \
+    client_name##_MainFixture test;                                          \
+    return ::mojo::edk::test::MultiprocessTestHelper::RunClientTestMain(     \
+        base::Bind(&client_name##_MainFixture::Main,                         \
+                   base::Unretained(&test)));                                \
+  }                                                                          \
+  void client_name##_MainFixture::Main(MojoHandle pipe_name)
+#else  // !defined(OS_IOS)
+#define DEFINE_TEST_CLIENT_WITH_PIPE(client_name, test_base, pipe_name)
+#define DEFINE_TEST_CLIENT_TEST_WITH_PIPE(client_name, test_base, pipe_name)
+#endif  // !defined(OS_IOS)
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_TEST_MOJO_TEST_BASE_H_
diff --git a/mojo/edk/test/multiprocess_test_helper.cc b/mojo/edk/test/multiprocess_test_helper.cc
new file mode 100644
index 0000000..82a82c8
--- /dev/null
+++ b/mojo/edk/test/multiprocess_test_helper.cc
@@ -0,0 +1,194 @@
+// 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 <functional>
+#include <set>
+#include <utility>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/process/kill.h"
+#include "base/process/process_handle.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mach_port_broker.h"
+#endif
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+namespace {
+
+const char kMojoPrimordialPipeToken[] = "mojo-primordial-pipe-token";
+
+int RunClientFunction(std::function<int(MojoHandle)> handler) {
+  CHECK(!MultiprocessTestHelper::primordial_pipe_token.empty());
+  ScopedMessagePipeHandle pipe = CreateChildMessagePipe(
+      MultiprocessTestHelper::primordial_pipe_token);
+  return handler(pipe.get().value());
+}
+
+}  // namespace
+
+MultiprocessTestHelper::MultiprocessTestHelper() {}
+
+MultiprocessTestHelper::~MultiprocessTestHelper() {
+  CHECK(!test_child_.IsValid());
+}
+
+ScopedMessagePipeHandle MultiprocessTestHelper::StartChild(
+    const std::string& test_child_name) {
+  return StartChildWithExtraSwitch(
+      test_child_name, std::string(), std::string());
+}
+
+ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch(
+    const std::string& test_child_name,
+    const std::string& switch_string,
+    const std::string& switch_value) {
+  CHECK(!test_child_name.empty());
+  CHECK(!test_child_.IsValid());
+
+  std::string test_child_main = test_child_name + "TestChildMain";
+
+  // Manually construct the new child's commandline to avoid copying unwanted
+  // values.
+  base::CommandLine command_line(
+      base::GetMultiProcessTestChildBaseCommandLine().GetProgram());
+
+  std::set<std::string> uninherited_args;
+  uninherited_args.insert("mojo-platform-channel-handle");
+  uninherited_args.insert(switches::kTestChildProcess);
+
+  // Copy commandline switches from the parent process, except for the
+  // multiprocess client name and mojo message pipe handle; this allows test
+  // clients to spawn other test clients.
+  for (const auto& entry :
+          base::CommandLine::ForCurrentProcess()->GetSwitches()) {
+    if (uninherited_args.find(entry.first) == uninherited_args.end())
+      command_line.AppendSwitchNative(entry.first, entry.second);
+  }
+
+  PlatformChannelPair channel;
+  HandlePassingInformation handle_passing_info;
+  channel.PrepareToPassClientHandleToChildProcess(&command_line,
+                                                  &handle_passing_info);
+
+  std::string pipe_token = mojo::edk::GenerateRandomToken();
+  command_line.AppendSwitchASCII(kMojoPrimordialPipeToken, pipe_token);
+
+  if (!switch_string.empty()) {
+    CHECK(!command_line.HasSwitch(switch_string));
+    if (!switch_value.empty())
+      command_line.AppendSwitchASCII(switch_string, switch_value);
+    else
+      command_line.AppendSwitch(switch_string);
+  }
+
+  base::LaunchOptions options;
+#if defined(OS_POSIX)
+  options.fds_to_remap = &handle_passing_info;
+#elif defined(OS_WIN)
+  options.start_hidden = true;
+  if (base::win::GetVersion() >= base::win::VERSION_VISTA)
+    options.handles_to_inherit = &handle_passing_info;
+  else
+    options.inherit_handles = true;
+#else
+#error "Not supported yet."
+#endif
+
+  std::string child_token = mojo::edk::GenerateRandomToken();
+  ScopedMessagePipeHandle pipe = CreateParentMessagePipe(pipe_token,
+                                                         child_token);
+
+  test_child_ =
+      base::SpawnMultiProcessTestChild(test_child_main, command_line, options);
+  channel.ChildProcessLaunched();
+
+  ChildProcessLaunched(test_child_.Handle(), channel.PassServerHandle(),
+                       child_token, process_error_callback_);
+  CHECK(test_child_.IsValid());
+
+  return pipe;
+}
+
+int MultiprocessTestHelper::WaitForChildShutdown() {
+  CHECK(test_child_.IsValid());
+
+  int rv = -1;
+#if defined(OS_ANDROID)
+  // On Android, we need to use a special function to wait for the child.
+  CHECK(AndroidWaitForChildExitWithTimeout(
+      test_child_, TestTimeouts::action_timeout(), &rv));
+#else
+  CHECK(
+      test_child_.WaitForExitWithTimeout(TestTimeouts::action_timeout(), &rv));
+#endif
+  test_child_.Close();
+  return rv;
+}
+
+bool MultiprocessTestHelper::WaitForChildTestShutdown() {
+  return WaitForChildShutdown() == 0;
+}
+
+// static
+void MultiprocessTestHelper::ChildSetup() {
+  CHECK(base::CommandLine::InitializedForCurrentProcess());
+
+  primordial_pipe_token = base::CommandLine::ForCurrentProcess()
+      ->GetSwitchValueASCII(kMojoPrimordialPipeToken);
+  CHECK(!primordial_pipe_token.empty());
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  CHECK(base::MachPortBroker::ChildSendTaskPortToParent("mojo_test"));
+#endif
+
+  SetParentPipeHandle(
+      PlatformChannelPair::PassClientHandleFromParentProcess(
+          *base::CommandLine::ForCurrentProcess()));
+}
+
+// static
+int MultiprocessTestHelper::RunClientMain(
+    const base::Callback<int(MojoHandle)>& main) {
+  return RunClientFunction([main](MojoHandle handle){
+    return main.Run(handle);
+  });
+}
+
+// static
+int MultiprocessTestHelper::RunClientTestMain(
+    const base::Callback<void(MojoHandle)>& main) {
+  return RunClientFunction([main](MojoHandle handle) {
+    main.Run(handle);
+    return (::testing::Test::HasFatalFailure() ||
+            ::testing::Test::HasNonfatalFailure()) ? 1 : 0;
+  });
+}
+
+// static
+std::string MultiprocessTestHelper::primordial_pipe_token;
+
+}  // namespace test
+}  // namespace edk
+}  // 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..203eb11
--- /dev/null
+++ b/mojo/edk/test/multiprocess_test_helper.h
@@ -0,0 +1,87 @@
+// 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/callback.h"
+#include "base/macros.h"
+#include "base/process/process.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace mojo {
+
+namespace edk {
+class PlatformChannelPair;
+
+namespace test {
+
+class MultiprocessTestHelper {
+ public:
+  using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>;
+
+  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).
+  ScopedMessagePipeHandle StartChild(const std::string& test_child_name);
+
+  // Like |StartChild()|, but appends an extra switch (with ASCII value) to the
+  // command line. (The switch must not already be present in the default
+  // command line.)
+  ScopedMessagePipeHandle StartChildWithExtraSwitch(
+      const std::string& test_child_name,
+      const std::string& switch_string,
+      const std::string& switch_value);
+
+  void set_process_error_callback(const ProcessErrorCallback& callback) {
+    process_error_callback_ = callback;
+  }
+
+  // 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());|.
+  bool WaitForChildTestShutdown();
+
+  const base::Process& test_child() const { return test_child_; }
+
+  // Used by macros in mojo/edk/test/mojo_test_base.h to support multiprocess
+  // test client initialization.
+  static void ChildSetup();
+  static int RunClientMain(const base::Callback<int(MojoHandle)>& main);
+  static int RunClientTestMain(const base::Callback<void(MojoHandle)>& main);
+
+  // For use (and only valid) in the child process:
+  static std::string primordial_pipe_token;
+
+ private:
+  // Valid after |StartChild()| and before |WaitForChildShutdown()|.
+  base::Process test_child_;
+
+  ProcessErrorCallback process_error_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiprocessTestHelper);
+};
+
+}  // namespace test
+}  // namespace edk
+}  // 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..f7e9e83
--- /dev/null
+++ b/mojo/edk/test/multiprocess_test_helper_unittest.cc
@@ -0,0 +1,165 @@
+// 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 <stddef.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/test_utils.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 edk {
+namespace test {
+namespace {
+
+bool IsNonBlocking(const 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.handle, F_GETFL) & O_NONBLOCK;
+#endif
+}
+
+bool WriteByte(const PlatformHandle& handle, char c) {
+  size_t bytes_written = 0;
+  BlockingWrite(handle, &c, 1, &bytes_written);
+  return bytes_written == 1;
+}
+
+bool ReadByte(const PlatformHandle& handle, char* c) {
+  size_t bytes_read = 0;
+  BlockingRead(handle, c, 1, &bytes_read);
+  return bytes_read == 1;
+}
+
+using MultiprocessTestHelperTest = testing::Test;
+
+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.
+  ScopedPlatformHandle handle = std::move(helper.server_platform_handle);
+
+  // 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.
+  ScopedPlatformHandle handle =
+      std::move(MultiprocessTestHelper::client_platform_handle);
+
+  // 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 edk
+}  // namespace mojo
diff --git a/mojo/edk/test/run_all_perftests.cc b/mojo/edk/test/run_all_perftests.cc
new file mode 100644
index 0000000..50c41d7
--- /dev/null
+++ b/mojo/edk/test/run_all_perftests.cc
@@ -0,0 +1,31 @@
+// 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/command_line.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/perf_test_suite.h"
+#include "base/test/test_io_thread.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/test/test_support_impl.h"
+#include "mojo/public/tests/test_support_private.h"
+
+int main(int argc, char** argv) {
+#if defined(OS_ANDROID)
+  base::InitAndroidMultiProcessTestHelper(main);
+#endif
+
+  base::PerfTestSuite test(argc, argv);
+
+  mojo::edk::Init();
+  base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
+  // Leak this because its destructor calls mojo::edk::ShutdownIPCSupport which
+  // really does nothing in the new EDK but does depend on the current message
+  // loop, which is destructed inside base::LaunchUnitTests.
+  new mojo::edk::test::ScopedIPCSupport(test_io_thread.task_runner());
+  mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
+
+  return test.Run();
+}
diff --git a/mojo/edk/test/run_all_unittests.cc b/mojo/edk/test/run_all_unittests.cc
new file mode 100644
index 0000000..05aed91
--- /dev/null
+++ b/mojo/edk/test/run_all_unittests.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 <signal.h>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_io_thread.h"
+#include "base/test/test_suite.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/edk/test/test_support_impl.h"
+#include "mojo/public/tests/test_support_private.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+#if !defined(OS_ANDROID)
+  // 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).
+  // On android, we need to run in the default mode, as the threadsafe mode
+  // relies on execve which is not available.
+  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+#endif
+#if defined(OS_ANDROID)
+  // Enable the alternate test child implementation. This is needed because Mojo
+  // tests need to spawn test children after initialising the Mojo system.
+  base::InitAndroidMultiProcessTestHelper(main);
+
+  // On android, the test framework has a signal handler that will print a
+  // [ CRASH ] line when the application crashes. This breaks death test has the
+  // test runner will consider the death of the child process a test failure.
+  // Removing the signal handler solves this issue.
+  signal(SIGABRT, SIG_DFL);
+#endif
+
+  base::TestSuite test_suite(argc, argv);
+
+  mojo::edk::Init();
+
+  mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
+  base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
+  // Leak this because its destructor calls mojo::edk::ShutdownIPCSupport which
+  // really does nothing in the new EDK but does depend on the current message
+  // loop, which is destructed inside base::LaunchUnitTests.
+  new mojo::edk::test::ScopedIPCSupport(test_io_thread.task_runner());
+
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/mojo/edk/test/scoped_ipc_support.cc b/mojo/edk/test/scoped_ipc_support.cc
new file mode 100644
index 0000000..7dc7c99
--- /dev/null
+++ b/mojo/edk/test/scoped_ipc_support.cc
@@ -0,0 +1,62 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/test/scoped_ipc_support.h"
+
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+namespace {
+base::TaskRunner* g_io_task_runner = nullptr;
+}
+
+base::TaskRunner* GetIoTaskRunner() {
+  return g_io_task_runner;
+}
+
+namespace internal {
+
+ScopedIPCSupportHelper::ScopedIPCSupportHelper() {
+}
+
+ScopedIPCSupportHelper::~ScopedIPCSupportHelper() {
+  ShutdownIPCSupport();
+  run_loop_.Run();
+}
+
+void ScopedIPCSupportHelper::Init(
+    ProcessDelegate* process_delegate,
+    scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+  io_thread_task_runner_ = io_thread_task_runner;
+  InitIPCSupport(process_delegate, io_thread_task_runner_);
+}
+
+void ScopedIPCSupportHelper::OnShutdownCompleteImpl() {
+  run_loop_.Quit();
+}
+
+}  // namespace internal
+
+ScopedIPCSupport::ScopedIPCSupport(
+    scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+  g_io_task_runner = io_thread_task_runner.get();
+  helper_.Init(this, std::move(io_thread_task_runner));
+}
+
+ScopedIPCSupport::~ScopedIPCSupport() {
+}
+
+void ScopedIPCSupport::OnShutdownComplete() {
+  helper_.OnShutdownCompleteImpl();
+}
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/test/scoped_ipc_support.h b/mojo/edk/test/scoped_ipc_support.h
new file mode 100644
index 0000000..04173d3
--- /dev/null
+++ b/mojo/edk/test/scoped_ipc_support.h
@@ -0,0 +1,65 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_
+#define MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/process_delegate.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+base::TaskRunner* GetIoTaskRunner();
+
+namespace internal {
+
+class ScopedIPCSupportHelper {
+ public:
+  ScopedIPCSupportHelper();
+  ~ScopedIPCSupportHelper();
+
+  void Init(ProcessDelegate* process_delegate,
+            scoped_refptr<base::TaskRunner> io_thread_task_runner);
+
+  void OnShutdownCompleteImpl();
+
+ private:
+  scoped_refptr<base::TaskRunner> io_thread_task_runner_;
+
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupportHelper);
+};
+
+}  // namespace internal
+
+// A simple class that calls |InitIPCSupport()| on construction and
+// |ShutdownIPCSupport()| on destruction.
+class ScopedIPCSupport : public ProcessDelegate {
+ public:
+  explicit ScopedIPCSupport(
+      scoped_refptr<base::TaskRunner> io_thread_task_runner);
+  ~ScopedIPCSupport() override;
+
+ private:
+  // |ProcessDelegate| implementation:
+  // Note: Executed on the I/O thread.
+  void OnShutdownComplete() override;
+
+  internal::ScopedIPCSupportHelper helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport);
+};
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_
diff --git a/mojo/edk/test/test_support_impl.cc b/mojo/edk/test/test_support_impl.cc
new file mode 100644
index 0000000..25684e4
--- /dev/null
+++ b/mojo/edk/test/test_support_impl.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/edk/test/test_support_impl.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/perf_log.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+namespace {
+
+base::FilePath ResolveSourceRootRelativePath(const char* relative_path) {
+  base::FilePath path;
+  if (!PathService::Get(base::DIR_SOURCE_ROOT, &path))
+    return base::FilePath();
+
+  for (const base::StringPiece& component : base::SplitStringPiece(
+           relative_path, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+    if (!component.empty())
+      path = path.AppendASCII(component);
+  }
+
+  return path;
+}
+
+}  // namespace
+
+TestSupportImpl::TestSupportImpl() {
+}
+
+TestSupportImpl::~TestSupportImpl() {
+}
+
+void TestSupportImpl::LogPerfResult(const char* test_name,
+                                    const char* sub_test_name,
+                                    double value,
+                                    const char* units) {
+  DCHECK(test_name);
+  if (sub_test_name) {
+    std::string name = base::StringPrintf("%s/%s", test_name, sub_test_name);
+    base::LogPerfResult(name.c_str(), value, units);
+  } else {
+    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 edk
+}  // namespace mojo
diff --git a/mojo/edk/test/test_support_impl.h b/mojo/edk/test/test_support_impl.h
new file mode 100644
index 0000000..ebb5ce6
--- /dev/null
+++ b/mojo/edk/test/test_support_impl.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_EDK_TEST_TEST_SUPPORT_IMPL_H_
+#define MOJO_EDK_TEST_TEST_SUPPORT_IMPL_H_
+
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "mojo/public/tests/test_support_private.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+class TestSupportImpl : public mojo::test::TestSupport {
+ public:
+  TestSupportImpl();
+  ~TestSupportImpl() override;
+
+  void LogPerfResult(const char* test_name,
+                     const char* sub_test_name,
+                     double value,
+                     const char* units) override;
+  FILE* OpenSourceRootRelativeFile(const char* relative_path) override;
+  char** EnumerateSourceRootRelativeDirectory(
+      const char* relative_path) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestSupportImpl);
+};
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_TEST_TEST_SUPPORT_IMPL_H_
diff --git a/mojo/edk/test/test_utils.h b/mojo/edk/test/test_utils.h
new file mode 100644
index 0000000..939171b
--- /dev/null
+++ b/mojo/edk/test/test_utils.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_EDK_TEST_TEST_UTILS_H_
+#define MOJO_EDK_TEST_TEST_UTILS_H_
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include <string>
+
+#include "base/files/scoped_file.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+// On success, |bytes_written| is updated to the number of bytes written;
+// otherwise it is untouched.
+bool BlockingWrite(const 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 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 PlatformHandle& handle,
+                     void* buffer,
+                     size_t buffer_size,
+                     size_t* bytes_read);
+
+// Gets a (scoped) |PlatformHandle| from the given (scoped) |FILE|.
+ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp);
+
+// Gets a (scoped) |FILE| from a (scoped) |PlatformHandle|.
+base::ScopedFILE FILEFromPlatformHandle(ScopedPlatformHandle h,
+                                        const char* mode);
+
+}  // namespace test
+}  // namespace edk
+}  // 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..60d5db5
--- /dev/null
+++ b/mojo/edk/test/test_utils_posix.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/test/test_utils.h"
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+bool BlockingWrite(const PlatformHandle& handle,
+                   const void* buffer,
+                   size_t bytes_to_write,
+                   size_t* bytes_written) {
+  int original_flags = fcntl(handle.handle, F_GETFL);
+  if (original_flags == -1 ||
+      fcntl(handle.handle, F_SETFL, original_flags & (~O_NONBLOCK)) != 0) {
+    return false;
+  }
+
+  ssize_t result = HANDLE_EINTR(write(handle.handle, buffer, bytes_to_write));
+
+  fcntl(handle.handle, F_SETFL, original_flags);
+
+  if (result < 0)
+    return false;
+
+  *bytes_written = result;
+  return true;
+}
+
+bool BlockingRead(const PlatformHandle& handle,
+                  void* buffer,
+                  size_t buffer_size,
+                  size_t* bytes_read) {
+  int original_flags = fcntl(handle.handle, F_GETFL);
+  if (original_flags == -1 ||
+      fcntl(handle.handle, F_SETFL, original_flags & (~O_NONBLOCK)) != 0) {
+    return false;
+  }
+
+  ssize_t result = HANDLE_EINTR(read(handle.handle, buffer, buffer_size));
+
+  fcntl(handle.handle, F_SETFL, original_flags);
+
+  if (result < 0)
+    return false;
+
+  *bytes_read = result;
+  return true;
+}
+
+bool NonBlockingRead(const PlatformHandle& handle,
+                     void* buffer,
+                     size_t buffer_size,
+                     size_t* bytes_read) {
+  ssize_t result = HANDLE_EINTR(read(handle.handle, buffer, buffer_size));
+
+  if (result < 0) {
+    if (errno != EAGAIN && errno != EWOULDBLOCK)
+      return false;
+
+    *bytes_read = 0;
+  } else {
+    *bytes_read = result;
+  }
+
+  return true;
+}
+
+ScopedPlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp) {
+  CHECK(fp);
+  int rv = dup(fileno(fp.get()));
+  PCHECK(rv != -1) << "dup";
+  return ScopedPlatformHandle(PlatformHandle(rv));
+}
+
+base::ScopedFILE FILEFromPlatformHandle(ScopedPlatformHandle h,
+                                        const char* mode) {
+  CHECK(h.is_valid());
+  base::ScopedFILE rv(fdopen(h.release().handle, mode));
+  PCHECK(rv) << "fdopen";
+  return rv;
+}
+
+}  // namespace test
+}  // namespace edk
+}  // 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..17bf5bb
--- /dev/null
+++ b/mojo/edk/test/test_utils_win.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/test/test_utils.h"
+
+#include <windows.h>
+#include <fcntl.h>
+#include <io.h>
+#include <stddef.h>
+#include <string.h>
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+bool BlockingWrite(const 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 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 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;
+}
+
+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 ScopedPlatformHandle(PlatformHandle(rv));
+}
+
+base::ScopedFILE FILEFromPlatformHandle(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;
+}
+
+}  // namespace test
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/gpu/BUILD.gn b/mojo/gpu/BUILD.gn
new file mode 100644
index 0000000..ad37238
--- /dev/null
+++ b/mojo/gpu/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("mojo_gles2_implementation") {
+  sources = [
+    "mojo_gles2_impl_autogen.cc",
+    "mojo_gles2_impl_autogen.h",
+  ]
+
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
+  deps = [
+    "//base",
+    "//gpu/command_buffer/client:gles2_interface",
+    "//mojo/public/c/gles2",
+  ]
+}
diff --git a/mojo/gpu/DEPS b/mojo/gpu/DEPS
new file mode 100644
index 0000000..241389e
--- /dev/null
+++ b/mojo/gpu/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+    "+gpu/command_buffer/client",
+    "+third_party/khronos/GLES2",
+]
diff --git a/mojo/gpu/OWNERS b/mojo/gpu/OWNERS
new file mode 100644
index 0000000..cd07f4d
--- /dev/null
+++ b/mojo/gpu/OWNERS
@@ -0,0 +1,5 @@
+piman@chromium.org
+jbauman@chromium.org
+bajones@chromium.org
+zmo@chromium.org
+vmiura@chromium.org
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.cc b/mojo/gpu/mojo_gles2_impl_autogen.cc
new file mode 100644
index 0000000..93377a5
--- /dev/null
+++ b/mojo/gpu/mojo_gles2_impl_autogen.cc
@@ -0,0 +1,1899 @@
+// Copyright 2014 The Chromium 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!
+
+#include "mojo/gpu/mojo_gles2_impl_autogen.h"
+
+#include "base/logging.h"
+#include "mojo/public/c/gles2/chromium_extension.h"
+#include "mojo/public/c/gles2/gles2.h"
+
+namespace mojo {
+
+void MojoGLES2Impl::ActiveTexture(GLenum texture) {
+  MojoGLES2MakeCurrent(context_);
+  glActiveTexture(texture);
+}
+void MojoGLES2Impl::AttachShader(GLuint program, GLuint shader) {
+  MojoGLES2MakeCurrent(context_);
+  glAttachShader(program, shader);
+}
+void MojoGLES2Impl::BindAttribLocation(GLuint program,
+                                       GLuint index,
+                                       const char* name) {
+  MojoGLES2MakeCurrent(context_);
+  glBindAttribLocation(program, index, name);
+}
+void MojoGLES2Impl::BindBuffer(GLenum target, GLuint buffer) {
+  MojoGLES2MakeCurrent(context_);
+  glBindBuffer(target, buffer);
+}
+void MojoGLES2Impl::BindBufferBase(GLenum target, GLuint index, GLuint buffer) {
+  NOTREACHED() << "Unimplemented BindBufferBase.";
+}
+void MojoGLES2Impl::BindBufferRange(GLenum target,
+                                    GLuint index,
+                                    GLuint buffer,
+                                    GLintptr offset,
+                                    GLsizeiptr size) {
+  NOTREACHED() << "Unimplemented BindBufferRange.";
+}
+void MojoGLES2Impl::BindFramebuffer(GLenum target, GLuint framebuffer) {
+  MojoGLES2MakeCurrent(context_);
+  glBindFramebuffer(target, framebuffer);
+}
+void MojoGLES2Impl::BindRenderbuffer(GLenum target, GLuint renderbuffer) {
+  MojoGLES2MakeCurrent(context_);
+  glBindRenderbuffer(target, renderbuffer);
+}
+void MojoGLES2Impl::BindSampler(GLuint unit, GLuint sampler) {
+  NOTREACHED() << "Unimplemented BindSampler.";
+}
+void MojoGLES2Impl::BindTexture(GLenum target, GLuint texture) {
+  MojoGLES2MakeCurrent(context_);
+  glBindTexture(target, texture);
+}
+void MojoGLES2Impl::BindTransformFeedback(GLenum target,
+                                          GLuint transformfeedback) {
+  NOTREACHED() << "Unimplemented BindTransformFeedback.";
+}
+void MojoGLES2Impl::BlendColor(GLclampf red,
+                               GLclampf green,
+                               GLclampf blue,
+                               GLclampf alpha) {
+  MojoGLES2MakeCurrent(context_);
+  glBlendColor(red, green, blue, alpha);
+}
+void MojoGLES2Impl::BlendEquation(GLenum mode) {
+  MojoGLES2MakeCurrent(context_);
+  glBlendEquation(mode);
+}
+void MojoGLES2Impl::BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {
+  MojoGLES2MakeCurrent(context_);
+  glBlendEquationSeparate(modeRGB, modeAlpha);
+}
+void MojoGLES2Impl::BlendFunc(GLenum sfactor, GLenum dfactor) {
+  MojoGLES2MakeCurrent(context_);
+  glBlendFunc(sfactor, dfactor);
+}
+void MojoGLES2Impl::BlendFuncSeparate(GLenum srcRGB,
+                                      GLenum dstRGB,
+                                      GLenum srcAlpha,
+                                      GLenum dstAlpha) {
+  MojoGLES2MakeCurrent(context_);
+  glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+}
+void MojoGLES2Impl::BufferData(GLenum target,
+                               GLsizeiptr size,
+                               const void* data,
+                               GLenum usage) {
+  MojoGLES2MakeCurrent(context_);
+  glBufferData(target, size, data, usage);
+}
+void MojoGLES2Impl::BufferSubData(GLenum target,
+                                  GLintptr offset,
+                                  GLsizeiptr size,
+                                  const void* data) {
+  MojoGLES2MakeCurrent(context_);
+  glBufferSubData(target, offset, size, data);
+}
+GLenum MojoGLES2Impl::CheckFramebufferStatus(GLenum target) {
+  MojoGLES2MakeCurrent(context_);
+  return glCheckFramebufferStatus(target);
+}
+void MojoGLES2Impl::Clear(GLbitfield mask) {
+  MojoGLES2MakeCurrent(context_);
+  glClear(mask);
+}
+void MojoGLES2Impl::ClearBufferfi(GLenum buffer,
+                                  GLint drawbuffers,
+                                  GLfloat depth,
+                                  GLint stencil) {
+  NOTREACHED() << "Unimplemented ClearBufferfi.";
+}
+void MojoGLES2Impl::ClearBufferfv(GLenum buffer,
+                                  GLint drawbuffers,
+                                  const GLfloat* value) {
+  NOTREACHED() << "Unimplemented ClearBufferfv.";
+}
+void MojoGLES2Impl::ClearBufferiv(GLenum buffer,
+                                  GLint drawbuffers,
+                                  const GLint* value) {
+  NOTREACHED() << "Unimplemented ClearBufferiv.";
+}
+void MojoGLES2Impl::ClearBufferuiv(GLenum buffer,
+                                   GLint drawbuffers,
+                                   const GLuint* value) {
+  NOTREACHED() << "Unimplemented ClearBufferuiv.";
+}
+void MojoGLES2Impl::ClearColor(GLclampf red,
+                               GLclampf green,
+                               GLclampf blue,
+                               GLclampf alpha) {
+  MojoGLES2MakeCurrent(context_);
+  glClearColor(red, green, blue, alpha);
+}
+void MojoGLES2Impl::ClearDepthf(GLclampf depth) {
+  MojoGLES2MakeCurrent(context_);
+  glClearDepthf(depth);
+}
+void MojoGLES2Impl::ClearStencil(GLint s) {
+  MojoGLES2MakeCurrent(context_);
+  glClearStencil(s);
+}
+GLenum MojoGLES2Impl::ClientWaitSync(GLsync sync,
+                                     GLbitfield flags,
+                                     GLuint64 timeout) {
+  NOTREACHED() << "Unimplemented ClientWaitSync.";
+  return 0;
+}
+void MojoGLES2Impl::ColorMask(GLboolean red,
+                              GLboolean green,
+                              GLboolean blue,
+                              GLboolean alpha) {
+  MojoGLES2MakeCurrent(context_);
+  glColorMask(red, green, blue, alpha);
+}
+void MojoGLES2Impl::CompileShader(GLuint shader) {
+  MojoGLES2MakeCurrent(context_);
+  glCompileShader(shader);
+}
+void MojoGLES2Impl::CompressedTexImage2D(GLenum target,
+                                         GLint level,
+                                         GLenum internalformat,
+                                         GLsizei width,
+                                         GLsizei height,
+                                         GLint border,
+                                         GLsizei imageSize,
+                                         const void* data) {
+  MojoGLES2MakeCurrent(context_);
+  glCompressedTexImage2D(target, level, internalformat, width, height, border,
+                         imageSize, data);
+}
+void MojoGLES2Impl::CompressedTexSubImage2D(GLenum target,
+                                            GLint level,
+                                            GLint xoffset,
+                                            GLint yoffset,
+                                            GLsizei width,
+                                            GLsizei height,
+                                            GLenum format,
+                                            GLsizei imageSize,
+                                            const void* data) {
+  MojoGLES2MakeCurrent(context_);
+  glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height,
+                            format, imageSize, data);
+}
+void MojoGLES2Impl::CompressedTexImage3D(GLenum target,
+                                         GLint level,
+                                         GLenum internalformat,
+                                         GLsizei width,
+                                         GLsizei height,
+                                         GLsizei depth,
+                                         GLint border,
+                                         GLsizei imageSize,
+                                         const void* data) {
+  NOTREACHED() << "Unimplemented CompressedTexImage3D.";
+}
+void MojoGLES2Impl::CompressedTexSubImage3D(GLenum target,
+                                            GLint level,
+                                            GLint xoffset,
+                                            GLint yoffset,
+                                            GLint zoffset,
+                                            GLsizei width,
+                                            GLsizei height,
+                                            GLsizei depth,
+                                            GLenum format,
+                                            GLsizei imageSize,
+                                            const void* data) {
+  NOTREACHED() << "Unimplemented CompressedTexSubImage3D.";
+}
+void MojoGLES2Impl::CopyBufferSubData(GLenum readtarget,
+                                      GLenum writetarget,
+                                      GLintptr readoffset,
+                                      GLintptr writeoffset,
+                                      GLsizeiptr size) {
+  NOTREACHED() << "Unimplemented CopyBufferSubData.";
+}
+void MojoGLES2Impl::CopyTexImage2D(GLenum target,
+                                   GLint level,
+                                   GLenum internalformat,
+                                   GLint x,
+                                   GLint y,
+                                   GLsizei width,
+                                   GLsizei height,
+                                   GLint border) {
+  MojoGLES2MakeCurrent(context_);
+  glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+}
+void MojoGLES2Impl::CopyTexSubImage2D(GLenum target,
+                                      GLint level,
+                                      GLint xoffset,
+                                      GLint yoffset,
+                                      GLint x,
+                                      GLint y,
+                                      GLsizei width,
+                                      GLsizei height) {
+  MojoGLES2MakeCurrent(context_);
+  glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+}
+void MojoGLES2Impl::CopyTexSubImage3D(GLenum target,
+                                      GLint level,
+                                      GLint xoffset,
+                                      GLint yoffset,
+                                      GLint zoffset,
+                                      GLint x,
+                                      GLint y,
+                                      GLsizei width,
+                                      GLsizei height) {
+  NOTREACHED() << "Unimplemented CopyTexSubImage3D.";
+}
+GLuint MojoGLES2Impl::CreateProgram() {
+  MojoGLES2MakeCurrent(context_);
+  return glCreateProgram();
+}
+GLuint MojoGLES2Impl::CreateShader(GLenum type) {
+  MojoGLES2MakeCurrent(context_);
+  return glCreateShader(type);
+}
+void MojoGLES2Impl::CullFace(GLenum mode) {
+  MojoGLES2MakeCurrent(context_);
+  glCullFace(mode);
+}
+void MojoGLES2Impl::DeleteBuffers(GLsizei n, const GLuint* buffers) {
+  MojoGLES2MakeCurrent(context_);
+  glDeleteBuffers(n, buffers);
+}
+void MojoGLES2Impl::DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) {
+  MojoGLES2MakeCurrent(context_);
+  glDeleteFramebuffers(n, framebuffers);
+}
+void MojoGLES2Impl::DeleteProgram(GLuint program) {
+  MojoGLES2MakeCurrent(context_);
+  glDeleteProgram(program);
+}
+void MojoGLES2Impl::DeleteRenderbuffers(GLsizei n,
+                                        const GLuint* renderbuffers) {
+  MojoGLES2MakeCurrent(context_);
+  glDeleteRenderbuffers(n, renderbuffers);
+}
+void MojoGLES2Impl::DeleteSamplers(GLsizei n, const GLuint* samplers) {
+  NOTREACHED() << "Unimplemented DeleteSamplers.";
+}
+void MojoGLES2Impl::DeleteSync(GLsync sync) {
+  NOTREACHED() << "Unimplemented DeleteSync.";
+}
+void MojoGLES2Impl::DeleteShader(GLuint shader) {
+  MojoGLES2MakeCurrent(context_);
+  glDeleteShader(shader);
+}
+void MojoGLES2Impl::DeleteTextures(GLsizei n, const GLuint* textures) {
+  MojoGLES2MakeCurrent(context_);
+  glDeleteTextures(n, textures);
+}
+void MojoGLES2Impl::DeleteTransformFeedbacks(GLsizei n, const GLuint* ids) {
+  NOTREACHED() << "Unimplemented DeleteTransformFeedbacks.";
+}
+void MojoGLES2Impl::DepthFunc(GLenum func) {
+  MojoGLES2MakeCurrent(context_);
+  glDepthFunc(func);
+}
+void MojoGLES2Impl::DepthMask(GLboolean flag) {
+  MojoGLES2MakeCurrent(context_);
+  glDepthMask(flag);
+}
+void MojoGLES2Impl::DepthRangef(GLclampf zNear, GLclampf zFar) {
+  MojoGLES2MakeCurrent(context_);
+  glDepthRangef(zNear, zFar);
+}
+void MojoGLES2Impl::DetachShader(GLuint program, GLuint shader) {
+  MojoGLES2MakeCurrent(context_);
+  glDetachShader(program, shader);
+}
+void MojoGLES2Impl::Disable(GLenum cap) {
+  MojoGLES2MakeCurrent(context_);
+  glDisable(cap);
+}
+void MojoGLES2Impl::DisableVertexAttribArray(GLuint index) {
+  MojoGLES2MakeCurrent(context_);
+  glDisableVertexAttribArray(index);
+}
+void MojoGLES2Impl::DrawArrays(GLenum mode, GLint first, GLsizei count) {
+  MojoGLES2MakeCurrent(context_);
+  glDrawArrays(mode, first, count);
+}
+void MojoGLES2Impl::DrawElements(GLenum mode,
+                                 GLsizei count,
+                                 GLenum type,
+                                 const void* indices) {
+  MojoGLES2MakeCurrent(context_);
+  glDrawElements(mode, count, type, indices);
+}
+void MojoGLES2Impl::DrawRangeElements(GLenum mode,
+                                      GLuint start,
+                                      GLuint end,
+                                      GLsizei count,
+                                      GLenum type,
+                                      const void* indices) {
+  NOTREACHED() << "Unimplemented DrawRangeElements.";
+}
+void MojoGLES2Impl::Enable(GLenum cap) {
+  MojoGLES2MakeCurrent(context_);
+  glEnable(cap);
+}
+void MojoGLES2Impl::EnableVertexAttribArray(GLuint index) {
+  MojoGLES2MakeCurrent(context_);
+  glEnableVertexAttribArray(index);
+}
+GLsync MojoGLES2Impl::FenceSync(GLenum condition, GLbitfield flags) {
+  NOTREACHED() << "Unimplemented FenceSync.";
+  return 0;
+}
+void MojoGLES2Impl::Finish() {
+  MojoGLES2MakeCurrent(context_);
+  glFinish();
+}
+void MojoGLES2Impl::Flush() {
+  MojoGLES2MakeCurrent(context_);
+  glFlush();
+}
+void MojoGLES2Impl::FramebufferRenderbuffer(GLenum target,
+                                            GLenum attachment,
+                                            GLenum renderbuffertarget,
+                                            GLuint renderbuffer) {
+  MojoGLES2MakeCurrent(context_);
+  glFramebufferRenderbuffer(target, attachment, renderbuffertarget,
+                            renderbuffer);
+}
+void MojoGLES2Impl::FramebufferTexture2D(GLenum target,
+                                         GLenum attachment,
+                                         GLenum textarget,
+                                         GLuint texture,
+                                         GLint level) {
+  MojoGLES2MakeCurrent(context_);
+  glFramebufferTexture2D(target, attachment, textarget, texture, level);
+}
+void MojoGLES2Impl::FramebufferTextureLayer(GLenum target,
+                                            GLenum attachment,
+                                            GLuint texture,
+                                            GLint level,
+                                            GLint layer) {
+  NOTREACHED() << "Unimplemented FramebufferTextureLayer.";
+}
+void MojoGLES2Impl::FrontFace(GLenum mode) {
+  MojoGLES2MakeCurrent(context_);
+  glFrontFace(mode);
+}
+void MojoGLES2Impl::GenBuffers(GLsizei n, GLuint* buffers) {
+  MojoGLES2MakeCurrent(context_);
+  glGenBuffers(n, buffers);
+}
+void MojoGLES2Impl::GenerateMipmap(GLenum target) {
+  MojoGLES2MakeCurrent(context_);
+  glGenerateMipmap(target);
+}
+void MojoGLES2Impl::GenFramebuffers(GLsizei n, GLuint* framebuffers) {
+  MojoGLES2MakeCurrent(context_);
+  glGenFramebuffers(n, framebuffers);
+}
+void MojoGLES2Impl::GenRenderbuffers(GLsizei n, GLuint* renderbuffers) {
+  MojoGLES2MakeCurrent(context_);
+  glGenRenderbuffers(n, renderbuffers);
+}
+void MojoGLES2Impl::GenSamplers(GLsizei n, GLuint* samplers) {
+  NOTREACHED() << "Unimplemented GenSamplers.";
+}
+void MojoGLES2Impl::GenTextures(GLsizei n, GLuint* textures) {
+  MojoGLES2MakeCurrent(context_);
+  glGenTextures(n, textures);
+}
+void MojoGLES2Impl::GenTransformFeedbacks(GLsizei n, GLuint* ids) {
+  NOTREACHED() << "Unimplemented GenTransformFeedbacks.";
+}
+void MojoGLES2Impl::GetActiveAttrib(GLuint program,
+                                    GLuint index,
+                                    GLsizei bufsize,
+                                    GLsizei* length,
+                                    GLint* size,
+                                    GLenum* type,
+                                    char* name) {
+  MojoGLES2MakeCurrent(context_);
+  glGetActiveAttrib(program, index, bufsize, length, size, type, name);
+}
+void MojoGLES2Impl::GetActiveUniform(GLuint program,
+                                     GLuint index,
+                                     GLsizei bufsize,
+                                     GLsizei* length,
+                                     GLint* size,
+                                     GLenum* type,
+                                     char* name) {
+  MojoGLES2MakeCurrent(context_);
+  glGetActiveUniform(program, index, bufsize, length, size, type, name);
+}
+void MojoGLES2Impl::GetActiveUniformBlockiv(GLuint program,
+                                            GLuint index,
+                                            GLenum pname,
+                                            GLint* params) {
+  NOTREACHED() << "Unimplemented GetActiveUniformBlockiv.";
+}
+void MojoGLES2Impl::GetActiveUniformBlockName(GLuint program,
+                                              GLuint index,
+                                              GLsizei bufsize,
+                                              GLsizei* length,
+                                              char* name) {
+  NOTREACHED() << "Unimplemented GetActiveUniformBlockName.";
+}
+void MojoGLES2Impl::GetActiveUniformsiv(GLuint program,
+                                        GLsizei count,
+                                        const GLuint* indices,
+                                        GLenum pname,
+                                        GLint* params) {
+  NOTREACHED() << "Unimplemented GetActiveUniformsiv.";
+}
+void MojoGLES2Impl::GetAttachedShaders(GLuint program,
+                                       GLsizei maxcount,
+                                       GLsizei* count,
+                                       GLuint* shaders) {
+  MojoGLES2MakeCurrent(context_);
+  glGetAttachedShaders(program, maxcount, count, shaders);
+}
+GLint MojoGLES2Impl::GetAttribLocation(GLuint program, const char* name) {
+  MojoGLES2MakeCurrent(context_);
+  return glGetAttribLocation(program, name);
+}
+void MojoGLES2Impl::GetBooleanv(GLenum pname, GLboolean* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetBooleanv(pname, params);
+}
+void MojoGLES2Impl::GetBufferParameteri64v(GLenum target,
+                                           GLenum pname,
+                                           GLint64* params) {
+  NOTREACHED() << "Unimplemented GetBufferParameteri64v.";
+}
+void MojoGLES2Impl::GetBufferParameteriv(GLenum target,
+                                         GLenum pname,
+                                         GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetBufferParameteriv(target, pname, params);
+}
+GLenum MojoGLES2Impl::GetError() {
+  MojoGLES2MakeCurrent(context_);
+  return glGetError();
+}
+void MojoGLES2Impl::GetFloatv(GLenum pname, GLfloat* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetFloatv(pname, params);
+}
+GLint MojoGLES2Impl::GetFragDataLocation(GLuint program, const char* name) {
+  NOTREACHED() << "Unimplemented GetFragDataLocation.";
+  return 0;
+}
+void MojoGLES2Impl::GetFramebufferAttachmentParameteriv(GLenum target,
+                                                        GLenum attachment,
+                                                        GLenum pname,
+                                                        GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
+}
+void MojoGLES2Impl::GetInteger64v(GLenum pname, GLint64* params) {
+  NOTREACHED() << "Unimplemented GetInteger64v.";
+}
+void MojoGLES2Impl::GetIntegeri_v(GLenum pname, GLuint index, GLint* data) {
+  NOTREACHED() << "Unimplemented GetIntegeri_v.";
+}
+void MojoGLES2Impl::GetInteger64i_v(GLenum pname, GLuint index, GLint64* data) {
+  NOTREACHED() << "Unimplemented GetInteger64i_v.";
+}
+void MojoGLES2Impl::GetIntegerv(GLenum pname, GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetIntegerv(pname, params);
+}
+void MojoGLES2Impl::GetInternalformativ(GLenum target,
+                                        GLenum format,
+                                        GLenum pname,
+                                        GLsizei bufSize,
+                                        GLint* params) {
+  NOTREACHED() << "Unimplemented GetInternalformativ.";
+}
+void MojoGLES2Impl::GetProgramiv(GLuint program, GLenum pname, GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetProgramiv(program, pname, params);
+}
+void MojoGLES2Impl::GetProgramInfoLog(GLuint program,
+                                      GLsizei bufsize,
+                                      GLsizei* length,
+                                      char* infolog) {
+  MojoGLES2MakeCurrent(context_);
+  glGetProgramInfoLog(program, bufsize, length, infolog);
+}
+void MojoGLES2Impl::GetRenderbufferParameteriv(GLenum target,
+                                               GLenum pname,
+                                               GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetRenderbufferParameteriv(target, pname, params);
+}
+void MojoGLES2Impl::GetSamplerParameterfv(GLuint sampler,
+                                          GLenum pname,
+                                          GLfloat* params) {
+  NOTREACHED() << "Unimplemented GetSamplerParameterfv.";
+}
+void MojoGLES2Impl::GetSamplerParameteriv(GLuint sampler,
+                                          GLenum pname,
+                                          GLint* params) {
+  NOTREACHED() << "Unimplemented GetSamplerParameteriv.";
+}
+void MojoGLES2Impl::GetShaderiv(GLuint shader, GLenum pname, GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetShaderiv(shader, pname, params);
+}
+void MojoGLES2Impl::GetShaderInfoLog(GLuint shader,
+                                     GLsizei bufsize,
+                                     GLsizei* length,
+                                     char* infolog) {
+  MojoGLES2MakeCurrent(context_);
+  glGetShaderInfoLog(shader, bufsize, length, infolog);
+}
+void MojoGLES2Impl::GetShaderPrecisionFormat(GLenum shadertype,
+                                             GLenum precisiontype,
+                                             GLint* range,
+                                             GLint* precision) {
+  MojoGLES2MakeCurrent(context_);
+  glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+}
+void MojoGLES2Impl::GetShaderSource(GLuint shader,
+                                    GLsizei bufsize,
+                                    GLsizei* length,
+                                    char* source) {
+  MojoGLES2MakeCurrent(context_);
+  glGetShaderSource(shader, bufsize, length, source);
+}
+const GLubyte* MojoGLES2Impl::GetString(GLenum name) {
+  MojoGLES2MakeCurrent(context_);
+  return glGetString(name);
+}
+const GLubyte* MojoGLES2Impl::GetStringi(GLenum name, GLuint index) {
+  NOTREACHED() << "Unimplemented GetStringi.";
+  return 0;
+}
+void MojoGLES2Impl::GetSynciv(GLsync sync,
+                              GLenum pname,
+                              GLsizei bufsize,
+                              GLsizei* length,
+                              GLint* values) {
+  NOTREACHED() << "Unimplemented GetSynciv.";
+}
+void MojoGLES2Impl::GetTexParameterfv(GLenum target,
+                                      GLenum pname,
+                                      GLfloat* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetTexParameterfv(target, pname, params);
+}
+void MojoGLES2Impl::GetTexParameteriv(GLenum target,
+                                      GLenum pname,
+                                      GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetTexParameteriv(target, pname, params);
+}
+void MojoGLES2Impl::GetTransformFeedbackVarying(GLuint program,
+                                                GLuint index,
+                                                GLsizei bufsize,
+                                                GLsizei* length,
+                                                GLsizei* size,
+                                                GLenum* type,
+                                                char* name) {
+  NOTREACHED() << "Unimplemented GetTransformFeedbackVarying.";
+}
+GLuint MojoGLES2Impl::GetUniformBlockIndex(GLuint program, const char* name) {
+  NOTREACHED() << "Unimplemented GetUniformBlockIndex.";
+  return 0;
+}
+void MojoGLES2Impl::GetUniformfv(GLuint program,
+                                 GLint location,
+                                 GLfloat* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetUniformfv(program, location, params);
+}
+void MojoGLES2Impl::GetUniformiv(GLuint program,
+                                 GLint location,
+                                 GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetUniformiv(program, location, params);
+}
+void MojoGLES2Impl::GetUniformuiv(GLuint program,
+                                  GLint location,
+                                  GLuint* params) {
+  NOTREACHED() << "Unimplemented GetUniformuiv.";
+}
+void MojoGLES2Impl::GetUniformIndices(GLuint program,
+                                      GLsizei count,
+                                      const char* const* names,
+                                      GLuint* indices) {
+  NOTREACHED() << "Unimplemented GetUniformIndices.";
+}
+GLint MojoGLES2Impl::GetUniformLocation(GLuint program, const char* name) {
+  MojoGLES2MakeCurrent(context_);
+  return glGetUniformLocation(program, name);
+}
+void MojoGLES2Impl::GetVertexAttribfv(GLuint index,
+                                      GLenum pname,
+                                      GLfloat* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetVertexAttribfv(index, pname, params);
+}
+void MojoGLES2Impl::GetVertexAttribiv(GLuint index,
+                                      GLenum pname,
+                                      GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetVertexAttribiv(index, pname, params);
+}
+void MojoGLES2Impl::GetVertexAttribIiv(GLuint index,
+                                       GLenum pname,
+                                       GLint* params) {
+  NOTREACHED() << "Unimplemented GetVertexAttribIiv.";
+}
+void MojoGLES2Impl::GetVertexAttribIuiv(GLuint index,
+                                        GLenum pname,
+                                        GLuint* params) {
+  NOTREACHED() << "Unimplemented GetVertexAttribIuiv.";
+}
+void MojoGLES2Impl::GetVertexAttribPointerv(GLuint index,
+                                            GLenum pname,
+                                            void** pointer) {
+  MojoGLES2MakeCurrent(context_);
+  glGetVertexAttribPointerv(index, pname, pointer);
+}
+void MojoGLES2Impl::Hint(GLenum target, GLenum mode) {
+  MojoGLES2MakeCurrent(context_);
+  glHint(target, mode);
+}
+void MojoGLES2Impl::InvalidateFramebuffer(GLenum target,
+                                          GLsizei count,
+                                          const GLenum* attachments) {
+  NOTREACHED() << "Unimplemented InvalidateFramebuffer.";
+}
+void MojoGLES2Impl::InvalidateSubFramebuffer(GLenum target,
+                                             GLsizei count,
+                                             const GLenum* attachments,
+                                             GLint x,
+                                             GLint y,
+                                             GLsizei width,
+                                             GLsizei height) {
+  NOTREACHED() << "Unimplemented InvalidateSubFramebuffer.";
+}
+GLboolean MojoGLES2Impl::IsBuffer(GLuint buffer) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsBuffer(buffer);
+}
+GLboolean MojoGLES2Impl::IsEnabled(GLenum cap) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsEnabled(cap);
+}
+GLboolean MojoGLES2Impl::IsFramebuffer(GLuint framebuffer) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsFramebuffer(framebuffer);
+}
+GLboolean MojoGLES2Impl::IsProgram(GLuint program) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsProgram(program);
+}
+GLboolean MojoGLES2Impl::IsRenderbuffer(GLuint renderbuffer) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsRenderbuffer(renderbuffer);
+}
+GLboolean MojoGLES2Impl::IsSampler(GLuint sampler) {
+  NOTREACHED() << "Unimplemented IsSampler.";
+  return 0;
+}
+GLboolean MojoGLES2Impl::IsShader(GLuint shader) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsShader(shader);
+}
+GLboolean MojoGLES2Impl::IsSync(GLsync sync) {
+  NOTREACHED() << "Unimplemented IsSync.";
+  return 0;
+}
+GLboolean MojoGLES2Impl::IsTexture(GLuint texture) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsTexture(texture);
+}
+GLboolean MojoGLES2Impl::IsTransformFeedback(GLuint transformfeedback) {
+  NOTREACHED() << "Unimplemented IsTransformFeedback.";
+  return 0;
+}
+void MojoGLES2Impl::LineWidth(GLfloat width) {
+  MojoGLES2MakeCurrent(context_);
+  glLineWidth(width);
+}
+void MojoGLES2Impl::LinkProgram(GLuint program) {
+  MojoGLES2MakeCurrent(context_);
+  glLinkProgram(program);
+}
+void MojoGLES2Impl::PauseTransformFeedback() {
+  NOTREACHED() << "Unimplemented PauseTransformFeedback.";
+}
+void MojoGLES2Impl::PixelStorei(GLenum pname, GLint param) {
+  MojoGLES2MakeCurrent(context_);
+  glPixelStorei(pname, param);
+}
+void MojoGLES2Impl::PolygonOffset(GLfloat factor, GLfloat units) {
+  MojoGLES2MakeCurrent(context_);
+  glPolygonOffset(factor, units);
+}
+void MojoGLES2Impl::ReadBuffer(GLenum src) {
+  NOTREACHED() << "Unimplemented ReadBuffer.";
+}
+void MojoGLES2Impl::ReadPixels(GLint x,
+                               GLint y,
+                               GLsizei width,
+                               GLsizei height,
+                               GLenum format,
+                               GLenum type,
+                               void* pixels) {
+  MojoGLES2MakeCurrent(context_);
+  glReadPixels(x, y, width, height, format, type, pixels);
+}
+void MojoGLES2Impl::ReleaseShaderCompiler() {
+  MojoGLES2MakeCurrent(context_);
+  glReleaseShaderCompiler();
+}
+void MojoGLES2Impl::RenderbufferStorage(GLenum target,
+                                        GLenum internalformat,
+                                        GLsizei width,
+                                        GLsizei height) {
+  MojoGLES2MakeCurrent(context_);
+  glRenderbufferStorage(target, internalformat, width, height);
+}
+void MojoGLES2Impl::ResumeTransformFeedback() {
+  NOTREACHED() << "Unimplemented ResumeTransformFeedback.";
+}
+void MojoGLES2Impl::SampleCoverage(GLclampf value, GLboolean invert) {
+  MojoGLES2MakeCurrent(context_);
+  glSampleCoverage(value, invert);
+}
+void MojoGLES2Impl::SamplerParameterf(GLuint sampler,
+                                      GLenum pname,
+                                      GLfloat param) {
+  NOTREACHED() << "Unimplemented SamplerParameterf.";
+}
+void MojoGLES2Impl::SamplerParameterfv(GLuint sampler,
+                                       GLenum pname,
+                                       const GLfloat* params) {
+  NOTREACHED() << "Unimplemented SamplerParameterfv.";
+}
+void MojoGLES2Impl::SamplerParameteri(GLuint sampler,
+                                      GLenum pname,
+                                      GLint param) {
+  NOTREACHED() << "Unimplemented SamplerParameteri.";
+}
+void MojoGLES2Impl::SamplerParameteriv(GLuint sampler,
+                                       GLenum pname,
+                                       const GLint* params) {
+  NOTREACHED() << "Unimplemented SamplerParameteriv.";
+}
+void MojoGLES2Impl::Scissor(GLint x, GLint y, GLsizei width, GLsizei height) {
+  MojoGLES2MakeCurrent(context_);
+  glScissor(x, y, width, height);
+}
+void MojoGLES2Impl::ShaderBinary(GLsizei n,
+                                 const GLuint* shaders,
+                                 GLenum binaryformat,
+                                 const void* binary,
+                                 GLsizei length) {
+  MojoGLES2MakeCurrent(context_);
+  glShaderBinary(n, shaders, binaryformat, binary, length);
+}
+void MojoGLES2Impl::ShaderSource(GLuint shader,
+                                 GLsizei count,
+                                 const GLchar* const* str,
+                                 const GLint* length) {
+  MojoGLES2MakeCurrent(context_);
+  glShaderSource(shader, count, str, length);
+}
+void MojoGLES2Impl::ShallowFinishCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glShallowFinishCHROMIUM();
+}
+void MojoGLES2Impl::ShallowFlushCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glShallowFlushCHROMIUM();
+}
+void MojoGLES2Impl::OrderingBarrierCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glOrderingBarrierCHROMIUM();
+}
+void MojoGLES2Impl::StencilFunc(GLenum func, GLint ref, GLuint mask) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilFunc(func, ref, mask);
+}
+void MojoGLES2Impl::StencilFuncSeparate(GLenum face,
+                                        GLenum func,
+                                        GLint ref,
+                                        GLuint mask) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilFuncSeparate(face, func, ref, mask);
+}
+void MojoGLES2Impl::StencilMask(GLuint mask) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilMask(mask);
+}
+void MojoGLES2Impl::StencilMaskSeparate(GLenum face, GLuint mask) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilMaskSeparate(face, mask);
+}
+void MojoGLES2Impl::StencilOp(GLenum fail, GLenum zfail, GLenum zpass) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilOp(fail, zfail, zpass);
+}
+void MojoGLES2Impl::StencilOpSeparate(GLenum face,
+                                      GLenum fail,
+                                      GLenum zfail,
+                                      GLenum zpass) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilOpSeparate(face, fail, zfail, zpass);
+}
+void MojoGLES2Impl::TexImage2D(GLenum target,
+                               GLint level,
+                               GLint internalformat,
+                               GLsizei width,
+                               GLsizei height,
+                               GLint border,
+                               GLenum format,
+                               GLenum type,
+                               const void* pixels) {
+  MojoGLES2MakeCurrent(context_);
+  glTexImage2D(target, level, internalformat, width, height, border, format,
+               type, pixels);
+}
+void MojoGLES2Impl::TexImage3D(GLenum target,
+                               GLint level,
+                               GLint internalformat,
+                               GLsizei width,
+                               GLsizei height,
+                               GLsizei depth,
+                               GLint border,
+                               GLenum format,
+                               GLenum type,
+                               const void* pixels) {
+  NOTREACHED() << "Unimplemented TexImage3D.";
+}
+void MojoGLES2Impl::TexParameterf(GLenum target, GLenum pname, GLfloat param) {
+  MojoGLES2MakeCurrent(context_);
+  glTexParameterf(target, pname, param);
+}
+void MojoGLES2Impl::TexParameterfv(GLenum target,
+                                   GLenum pname,
+                                   const GLfloat* params) {
+  MojoGLES2MakeCurrent(context_);
+  glTexParameterfv(target, pname, params);
+}
+void MojoGLES2Impl::TexParameteri(GLenum target, GLenum pname, GLint param) {
+  MojoGLES2MakeCurrent(context_);
+  glTexParameteri(target, pname, param);
+}
+void MojoGLES2Impl::TexParameteriv(GLenum target,
+                                   GLenum pname,
+                                   const GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glTexParameteriv(target, pname, params);
+}
+void MojoGLES2Impl::TexStorage3D(GLenum target,
+                                 GLsizei levels,
+                                 GLenum internalFormat,
+                                 GLsizei width,
+                                 GLsizei height,
+                                 GLsizei depth) {
+  NOTREACHED() << "Unimplemented TexStorage3D.";
+}
+void MojoGLES2Impl::TexSubImage2D(GLenum target,
+                                  GLint level,
+                                  GLint xoffset,
+                                  GLint yoffset,
+                                  GLsizei width,
+                                  GLsizei height,
+                                  GLenum format,
+                                  GLenum type,
+                                  const void* pixels) {
+  MojoGLES2MakeCurrent(context_);
+  glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
+                  pixels);
+}
+void MojoGLES2Impl::TexSubImage3D(GLenum target,
+                                  GLint level,
+                                  GLint xoffset,
+                                  GLint yoffset,
+                                  GLint zoffset,
+                                  GLsizei width,
+                                  GLsizei height,
+                                  GLsizei depth,
+                                  GLenum format,
+                                  GLenum type,
+                                  const void* pixels) {
+  NOTREACHED() << "Unimplemented TexSubImage3D.";
+}
+void MojoGLES2Impl::TransformFeedbackVaryings(GLuint program,
+                                              GLsizei count,
+                                              const char* const* varyings,
+                                              GLenum buffermode) {
+  NOTREACHED() << "Unimplemented TransformFeedbackVaryings.";
+}
+void MojoGLES2Impl::Uniform1f(GLint location, GLfloat x) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform1f(location, x);
+}
+void MojoGLES2Impl::Uniform1fv(GLint location,
+                               GLsizei count,
+                               const GLfloat* v) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform1fv(location, count, v);
+}
+void MojoGLES2Impl::Uniform1i(GLint location, GLint x) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform1i(location, x);
+}
+void MojoGLES2Impl::Uniform1iv(GLint location, GLsizei count, const GLint* v) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform1iv(location, count, v);
+}
+void MojoGLES2Impl::Uniform1ui(GLint location, GLuint x) {
+  NOTREACHED() << "Unimplemented Uniform1ui.";
+}
+void MojoGLES2Impl::Uniform1uiv(GLint location,
+                                GLsizei count,
+                                const GLuint* v) {
+  NOTREACHED() << "Unimplemented Uniform1uiv.";
+}
+void MojoGLES2Impl::Uniform2f(GLint location, GLfloat x, GLfloat y) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform2f(location, x, y);
+}
+void MojoGLES2Impl::Uniform2fv(GLint location,
+                               GLsizei count,
+                               const GLfloat* v) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform2fv(location, count, v);
+}
+void MojoGLES2Impl::Uniform2i(GLint location, GLint x, GLint y) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform2i(location, x, y);
+}
+void MojoGLES2Impl::Uniform2iv(GLint location, GLsizei count, const GLint* v) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform2iv(location, count, v);
+}
+void MojoGLES2Impl::Uniform2ui(GLint location, GLuint x, GLuint y) {
+  NOTREACHED() << "Unimplemented Uniform2ui.";
+}
+void MojoGLES2Impl::Uniform2uiv(GLint location,
+                                GLsizei count,
+                                const GLuint* v) {
+  NOTREACHED() << "Unimplemented Uniform2uiv.";
+}
+void MojoGLES2Impl::Uniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform3f(location, x, y, z);
+}
+void MojoGLES2Impl::Uniform3fv(GLint location,
+                               GLsizei count,
+                               const GLfloat* v) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform3fv(location, count, v);
+}
+void MojoGLES2Impl::Uniform3i(GLint location, GLint x, GLint y, GLint z) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform3i(location, x, y, z);
+}
+void MojoGLES2Impl::Uniform3iv(GLint location, GLsizei count, const GLint* v) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform3iv(location, count, v);
+}
+void MojoGLES2Impl::Uniform3ui(GLint location, GLuint x, GLuint y, GLuint z) {
+  NOTREACHED() << "Unimplemented Uniform3ui.";
+}
+void MojoGLES2Impl::Uniform3uiv(GLint location,
+                                GLsizei count,
+                                const GLuint* v) {
+  NOTREACHED() << "Unimplemented Uniform3uiv.";
+}
+void MojoGLES2Impl::Uniform4f(GLint location,
+                              GLfloat x,
+                              GLfloat y,
+                              GLfloat z,
+                              GLfloat w) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform4f(location, x, y, z, w);
+}
+void MojoGLES2Impl::Uniform4fv(GLint location,
+                               GLsizei count,
+                               const GLfloat* v) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform4fv(location, count, v);
+}
+void MojoGLES2Impl::Uniform4i(GLint location,
+                              GLint x,
+                              GLint y,
+                              GLint z,
+                              GLint w) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform4i(location, x, y, z, w);
+}
+void MojoGLES2Impl::Uniform4iv(GLint location, GLsizei count, const GLint* v) {
+  MojoGLES2MakeCurrent(context_);
+  glUniform4iv(location, count, v);
+}
+void MojoGLES2Impl::Uniform4ui(GLint location,
+                               GLuint x,
+                               GLuint y,
+                               GLuint z,
+                               GLuint w) {
+  NOTREACHED() << "Unimplemented Uniform4ui.";
+}
+void MojoGLES2Impl::Uniform4uiv(GLint location,
+                                GLsizei count,
+                                const GLuint* v) {
+  NOTREACHED() << "Unimplemented Uniform4uiv.";
+}
+void MojoGLES2Impl::UniformBlockBinding(GLuint program,
+                                        GLuint index,
+                                        GLuint binding) {
+  NOTREACHED() << "Unimplemented UniformBlockBinding.";
+}
+void MojoGLES2Impl::UniformMatrix2fv(GLint location,
+                                     GLsizei count,
+                                     GLboolean transpose,
+                                     const GLfloat* value) {
+  MojoGLES2MakeCurrent(context_);
+  glUniformMatrix2fv(location, count, transpose, value);
+}
+void MojoGLES2Impl::UniformMatrix2x3fv(GLint location,
+                                       GLsizei count,
+                                       GLboolean transpose,
+                                       const GLfloat* value) {
+  NOTREACHED() << "Unimplemented UniformMatrix2x3fv.";
+}
+void MojoGLES2Impl::UniformMatrix2x4fv(GLint location,
+                                       GLsizei count,
+                                       GLboolean transpose,
+                                       const GLfloat* value) {
+  NOTREACHED() << "Unimplemented UniformMatrix2x4fv.";
+}
+void MojoGLES2Impl::UniformMatrix3fv(GLint location,
+                                     GLsizei count,
+                                     GLboolean transpose,
+                                     const GLfloat* value) {
+  MojoGLES2MakeCurrent(context_);
+  glUniformMatrix3fv(location, count, transpose, value);
+}
+void MojoGLES2Impl::UniformMatrix3x2fv(GLint location,
+                                       GLsizei count,
+                                       GLboolean transpose,
+                                       const GLfloat* value) {
+  NOTREACHED() << "Unimplemented UniformMatrix3x2fv.";
+}
+void MojoGLES2Impl::UniformMatrix3x4fv(GLint location,
+                                       GLsizei count,
+                                       GLboolean transpose,
+                                       const GLfloat* value) {
+  NOTREACHED() << "Unimplemented UniformMatrix3x4fv.";
+}
+void MojoGLES2Impl::UniformMatrix4fv(GLint location,
+                                     GLsizei count,
+                                     GLboolean transpose,
+                                     const GLfloat* value) {
+  MojoGLES2MakeCurrent(context_);
+  glUniformMatrix4fv(location, count, transpose, value);
+}
+void MojoGLES2Impl::UniformMatrix4x2fv(GLint location,
+                                       GLsizei count,
+                                       GLboolean transpose,
+                                       const GLfloat* value) {
+  NOTREACHED() << "Unimplemented UniformMatrix4x2fv.";
+}
+void MojoGLES2Impl::UniformMatrix4x3fv(GLint location,
+                                       GLsizei count,
+                                       GLboolean transpose,
+                                       const GLfloat* value) {
+  NOTREACHED() << "Unimplemented UniformMatrix4x3fv.";
+}
+void MojoGLES2Impl::UseProgram(GLuint program) {
+  MojoGLES2MakeCurrent(context_);
+  glUseProgram(program);
+}
+void MojoGLES2Impl::ValidateProgram(GLuint program) {
+  MojoGLES2MakeCurrent(context_);
+  glValidateProgram(program);
+}
+void MojoGLES2Impl::VertexAttrib1f(GLuint indx, GLfloat x) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttrib1f(indx, x);
+}
+void MojoGLES2Impl::VertexAttrib1fv(GLuint indx, const GLfloat* values) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttrib1fv(indx, values);
+}
+void MojoGLES2Impl::VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttrib2f(indx, x, y);
+}
+void MojoGLES2Impl::VertexAttrib2fv(GLuint indx, const GLfloat* values) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttrib2fv(indx, values);
+}
+void MojoGLES2Impl::VertexAttrib3f(GLuint indx,
+                                   GLfloat x,
+                                   GLfloat y,
+                                   GLfloat z) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttrib3f(indx, x, y, z);
+}
+void MojoGLES2Impl::VertexAttrib3fv(GLuint indx, const GLfloat* values) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttrib3fv(indx, values);
+}
+void MojoGLES2Impl::VertexAttrib4f(GLuint indx,
+                                   GLfloat x,
+                                   GLfloat y,
+                                   GLfloat z,
+                                   GLfloat w) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttrib4f(indx, x, y, z, w);
+}
+void MojoGLES2Impl::VertexAttrib4fv(GLuint indx, const GLfloat* values) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttrib4fv(indx, values);
+}
+void MojoGLES2Impl::VertexAttribI4i(GLuint indx,
+                                    GLint x,
+                                    GLint y,
+                                    GLint z,
+                                    GLint w) {
+  NOTREACHED() << "Unimplemented VertexAttribI4i.";
+}
+void MojoGLES2Impl::VertexAttribI4iv(GLuint indx, const GLint* values) {
+  NOTREACHED() << "Unimplemented VertexAttribI4iv.";
+}
+void MojoGLES2Impl::VertexAttribI4ui(GLuint indx,
+                                     GLuint x,
+                                     GLuint y,
+                                     GLuint z,
+                                     GLuint w) {
+  NOTREACHED() << "Unimplemented VertexAttribI4ui.";
+}
+void MojoGLES2Impl::VertexAttribI4uiv(GLuint indx, const GLuint* values) {
+  NOTREACHED() << "Unimplemented VertexAttribI4uiv.";
+}
+void MojoGLES2Impl::VertexAttribIPointer(GLuint indx,
+                                         GLint size,
+                                         GLenum type,
+                                         GLsizei stride,
+                                         const void* ptr) {
+  NOTREACHED() << "Unimplemented VertexAttribIPointer.";
+}
+void MojoGLES2Impl::VertexAttribPointer(GLuint indx,
+                                        GLint size,
+                                        GLenum type,
+                                        GLboolean normalized,
+                                        GLsizei stride,
+                                        const void* ptr) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttribPointer(indx, size, type, normalized, stride, ptr);
+}
+void MojoGLES2Impl::Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
+  MojoGLES2MakeCurrent(context_);
+  glViewport(x, y, width, height);
+}
+void MojoGLES2Impl::WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  NOTREACHED() << "Unimplemented WaitSync.";
+}
+void MojoGLES2Impl::BlitFramebufferCHROMIUM(GLint srcX0,
+                                            GLint srcY0,
+                                            GLint srcX1,
+                                            GLint srcY1,
+                                            GLint dstX0,
+                                            GLint dstY0,
+                                            GLint dstX1,
+                                            GLint dstY1,
+                                            GLbitfield mask,
+                                            GLenum filter) {
+  MojoGLES2MakeCurrent(context_);
+  glBlitFramebufferCHROMIUM(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
+                            dstY1, mask, filter);
+}
+void MojoGLES2Impl::RenderbufferStorageMultisampleCHROMIUM(
+    GLenum target,
+    GLsizei samples,
+    GLenum internalformat,
+    GLsizei width,
+    GLsizei height) {
+  MojoGLES2MakeCurrent(context_);
+  glRenderbufferStorageMultisampleCHROMIUM(target, samples, internalformat,
+                                           width, height);
+}
+void MojoGLES2Impl::RenderbufferStorageMultisampleEXT(GLenum target,
+                                                      GLsizei samples,
+                                                      GLenum internalformat,
+                                                      GLsizei width,
+                                                      GLsizei height) {
+  MojoGLES2MakeCurrent(context_);
+  glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width,
+                                      height);
+}
+void MojoGLES2Impl::FramebufferTexture2DMultisampleEXT(GLenum target,
+                                                       GLenum attachment,
+                                                       GLenum textarget,
+                                                       GLuint texture,
+                                                       GLint level,
+                                                       GLsizei samples) {
+  MojoGLES2MakeCurrent(context_);
+  glFramebufferTexture2DMultisampleEXT(target, attachment, textarget, texture,
+                                       level, samples);
+}
+void MojoGLES2Impl::TexStorage2DEXT(GLenum target,
+                                    GLsizei levels,
+                                    GLenum internalFormat,
+                                    GLsizei width,
+                                    GLsizei height) {
+  MojoGLES2MakeCurrent(context_);
+  glTexStorage2DEXT(target, levels, internalFormat, width, height);
+}
+void MojoGLES2Impl::GenQueriesEXT(GLsizei n, GLuint* queries) {
+  MojoGLES2MakeCurrent(context_);
+  glGenQueriesEXT(n, queries);
+}
+void MojoGLES2Impl::DeleteQueriesEXT(GLsizei n, const GLuint* queries) {
+  MojoGLES2MakeCurrent(context_);
+  glDeleteQueriesEXT(n, queries);
+}
+void MojoGLES2Impl::QueryCounterEXT(GLuint id, GLenum target) {
+  MojoGLES2MakeCurrent(context_);
+  glQueryCounterEXT(id, target);
+}
+GLboolean MojoGLES2Impl::IsQueryEXT(GLuint id) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsQueryEXT(id);
+}
+void MojoGLES2Impl::BeginQueryEXT(GLenum target, GLuint id) {
+  MojoGLES2MakeCurrent(context_);
+  glBeginQueryEXT(target, id);
+}
+void MojoGLES2Impl::BeginTransformFeedback(GLenum primitivemode) {
+  NOTREACHED() << "Unimplemented BeginTransformFeedback.";
+}
+void MojoGLES2Impl::EndQueryEXT(GLenum target) {
+  MojoGLES2MakeCurrent(context_);
+  glEndQueryEXT(target);
+}
+void MojoGLES2Impl::EndTransformFeedback() {
+  NOTREACHED() << "Unimplemented EndTransformFeedback.";
+}
+void MojoGLES2Impl::GetQueryivEXT(GLenum target, GLenum pname, GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetQueryivEXT(target, pname, params);
+}
+void MojoGLES2Impl::GetQueryObjectivEXT(GLuint id,
+                                        GLenum pname,
+                                        GLint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetQueryObjectivEXT(id, pname, params);
+}
+void MojoGLES2Impl::GetQueryObjectuivEXT(GLuint id,
+                                         GLenum pname,
+                                         GLuint* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetQueryObjectuivEXT(id, pname, params);
+}
+void MojoGLES2Impl::GetQueryObjecti64vEXT(GLuint id,
+                                          GLenum pname,
+                                          GLint64* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetQueryObjecti64vEXT(id, pname, params);
+}
+void MojoGLES2Impl::GetQueryObjectui64vEXT(GLuint id,
+                                           GLenum pname,
+                                           GLuint64* params) {
+  MojoGLES2MakeCurrent(context_);
+  glGetQueryObjectui64vEXT(id, pname, params);
+}
+void MojoGLES2Impl::SetDisjointValueSyncCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glSetDisjointValueSyncCHROMIUM();
+}
+void MojoGLES2Impl::InsertEventMarkerEXT(GLsizei length, const GLchar* marker) {
+  MojoGLES2MakeCurrent(context_);
+  glInsertEventMarkerEXT(length, marker);
+}
+void MojoGLES2Impl::PushGroupMarkerEXT(GLsizei length, const GLchar* marker) {
+  MojoGLES2MakeCurrent(context_);
+  glPushGroupMarkerEXT(length, marker);
+}
+void MojoGLES2Impl::PopGroupMarkerEXT() {
+  MojoGLES2MakeCurrent(context_);
+  glPopGroupMarkerEXT();
+}
+void MojoGLES2Impl::GenVertexArraysOES(GLsizei n, GLuint* arrays) {
+  MojoGLES2MakeCurrent(context_);
+  glGenVertexArraysOES(n, arrays);
+}
+void MojoGLES2Impl::DeleteVertexArraysOES(GLsizei n, const GLuint* arrays) {
+  MojoGLES2MakeCurrent(context_);
+  glDeleteVertexArraysOES(n, arrays);
+}
+GLboolean MojoGLES2Impl::IsVertexArrayOES(GLuint array) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsVertexArrayOES(array);
+}
+void MojoGLES2Impl::BindVertexArrayOES(GLuint array) {
+  MojoGLES2MakeCurrent(context_);
+  glBindVertexArrayOES(array);
+}
+void MojoGLES2Impl::SwapBuffers() {
+  MojoGLES2MakeCurrent(context_);
+  glSwapBuffers();
+}
+GLuint MojoGLES2Impl::GetMaxValueInBufferCHROMIUM(GLuint buffer_id,
+                                                  GLsizei count,
+                                                  GLenum type,
+                                                  GLuint offset) {
+  MojoGLES2MakeCurrent(context_);
+  return glGetMaxValueInBufferCHROMIUM(buffer_id, count, type, offset);
+}
+GLboolean MojoGLES2Impl::EnableFeatureCHROMIUM(const char* feature) {
+  MojoGLES2MakeCurrent(context_);
+  return glEnableFeatureCHROMIUM(feature);
+}
+void* MojoGLES2Impl::MapBufferCHROMIUM(GLuint target, GLenum access) {
+  MojoGLES2MakeCurrent(context_);
+  return glMapBufferCHROMIUM(target, access);
+}
+GLboolean MojoGLES2Impl::UnmapBufferCHROMIUM(GLuint target) {
+  MojoGLES2MakeCurrent(context_);
+  return glUnmapBufferCHROMIUM(target);
+}
+void* MojoGLES2Impl::MapBufferSubDataCHROMIUM(GLuint target,
+                                              GLintptr offset,
+                                              GLsizeiptr size,
+                                              GLenum access) {
+  MojoGLES2MakeCurrent(context_);
+  return glMapBufferSubDataCHROMIUM(target, offset, size, access);
+}
+void MojoGLES2Impl::UnmapBufferSubDataCHROMIUM(const void* mem) {
+  MojoGLES2MakeCurrent(context_);
+  glUnmapBufferSubDataCHROMIUM(mem);
+}
+void* MojoGLES2Impl::MapBufferRange(GLenum target,
+                                    GLintptr offset,
+                                    GLsizeiptr size,
+                                    GLbitfield access) {
+  NOTREACHED() << "Unimplemented MapBufferRange.";
+  return 0;
+}
+GLboolean MojoGLES2Impl::UnmapBuffer(GLenum target) {
+  NOTREACHED() << "Unimplemented UnmapBuffer.";
+  return 0;
+}
+void* MojoGLES2Impl::MapTexSubImage2DCHROMIUM(GLenum target,
+                                              GLint level,
+                                              GLint xoffset,
+                                              GLint yoffset,
+                                              GLsizei width,
+                                              GLsizei height,
+                                              GLenum format,
+                                              GLenum type,
+                                              GLenum access) {
+  MojoGLES2MakeCurrent(context_);
+  return glMapTexSubImage2DCHROMIUM(target, level, xoffset, yoffset, width,
+                                    height, format, type, access);
+}
+void MojoGLES2Impl::UnmapTexSubImage2DCHROMIUM(const void* mem) {
+  MojoGLES2MakeCurrent(context_);
+  glUnmapTexSubImage2DCHROMIUM(mem);
+}
+void MojoGLES2Impl::ResizeCHROMIUM(GLuint width,
+                                   GLuint height,
+                                   GLfloat scale_factor,
+                                   GLboolean alpha) {
+  MojoGLES2MakeCurrent(context_);
+  glResizeCHROMIUM(width, height, scale_factor, alpha);
+}
+const GLchar* MojoGLES2Impl::GetRequestableExtensionsCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  return glGetRequestableExtensionsCHROMIUM();
+}
+void MojoGLES2Impl::RequestExtensionCHROMIUM(const char* extension) {
+  MojoGLES2MakeCurrent(context_);
+  glRequestExtensionCHROMIUM(extension);
+}
+void MojoGLES2Impl::GetProgramInfoCHROMIUM(GLuint program,
+                                           GLsizei bufsize,
+                                           GLsizei* size,
+                                           void* info) {
+  MojoGLES2MakeCurrent(context_);
+  glGetProgramInfoCHROMIUM(program, bufsize, size, info);
+}
+void MojoGLES2Impl::GetUniformBlocksCHROMIUM(GLuint program,
+                                             GLsizei bufsize,
+                                             GLsizei* size,
+                                             void* info) {
+  NOTREACHED() << "Unimplemented GetUniformBlocksCHROMIUM.";
+}
+void MojoGLES2Impl::GetTransformFeedbackVaryingsCHROMIUM(GLuint program,
+                                                         GLsizei bufsize,
+                                                         GLsizei* size,
+                                                         void* info) {
+  NOTREACHED() << "Unimplemented GetTransformFeedbackVaryingsCHROMIUM.";
+}
+void MojoGLES2Impl::GetUniformsES3CHROMIUM(GLuint program,
+                                           GLsizei bufsize,
+                                           GLsizei* size,
+                                           void* info) {
+  NOTREACHED() << "Unimplemented GetUniformsES3CHROMIUM.";
+}
+GLuint MojoGLES2Impl::CreateImageCHROMIUM(ClientBuffer buffer,
+                                          GLsizei width,
+                                          GLsizei height,
+                                          GLenum internalformat) {
+  MojoGLES2MakeCurrent(context_);
+  return glCreateImageCHROMIUM(buffer, width, height, internalformat);
+}
+void MojoGLES2Impl::DestroyImageCHROMIUM(GLuint image_id) {
+  MojoGLES2MakeCurrent(context_);
+  glDestroyImageCHROMIUM(image_id);
+}
+GLuint MojoGLES2Impl::CreateGpuMemoryBufferImageCHROMIUM(GLsizei width,
+                                                         GLsizei height,
+                                                         GLenum internalformat,
+                                                         GLenum usage) {
+  MojoGLES2MakeCurrent(context_);
+  return glCreateGpuMemoryBufferImageCHROMIUM(width, height, internalformat,
+                                              usage);
+}
+void MojoGLES2Impl::GetTranslatedShaderSourceANGLE(GLuint shader,
+                                                   GLsizei bufsize,
+                                                   GLsizei* length,
+                                                   char* source) {
+  MojoGLES2MakeCurrent(context_);
+  glGetTranslatedShaderSourceANGLE(shader, bufsize, length, source);
+}
+void MojoGLES2Impl::PostSubBufferCHROMIUM(GLint x,
+                                          GLint y,
+                                          GLint width,
+                                          GLint height) {
+  MojoGLES2MakeCurrent(context_);
+  glPostSubBufferCHROMIUM(x, y, width, height);
+}
+void MojoGLES2Impl::CopyTextureCHROMIUM(GLenum source_id,
+                                        GLenum dest_id,
+                                        GLint internalformat,
+                                        GLenum dest_type,
+                                        GLboolean unpack_flip_y,
+                                        GLboolean unpack_premultiply_alpha,
+                                        GLboolean unpack_unmultiply_alpha) {
+  MojoGLES2MakeCurrent(context_);
+  glCopyTextureCHROMIUM(source_id, dest_id, internalformat, dest_type,
+                        unpack_flip_y, unpack_premultiply_alpha,
+                        unpack_unmultiply_alpha);
+}
+void MojoGLES2Impl::CopySubTextureCHROMIUM(GLenum source_id,
+                                           GLenum dest_id,
+                                           GLint xoffset,
+                                           GLint yoffset,
+                                           GLint x,
+                                           GLint y,
+                                           GLsizei width,
+                                           GLsizei height,
+                                           GLboolean unpack_flip_y,
+                                           GLboolean unpack_premultiply_alpha,
+                                           GLboolean unpack_unmultiply_alpha) {
+  MojoGLES2MakeCurrent(context_);
+  glCopySubTextureCHROMIUM(source_id, dest_id, xoffset, yoffset, x, y, width,
+                           height, unpack_flip_y, unpack_premultiply_alpha,
+                           unpack_unmultiply_alpha);
+}
+void MojoGLES2Impl::CompressedCopyTextureCHROMIUM(GLenum source_id,
+                                                  GLenum dest_id) {
+  MojoGLES2MakeCurrent(context_);
+  glCompressedCopyTextureCHROMIUM(source_id, dest_id);
+}
+void MojoGLES2Impl::DrawArraysInstancedANGLE(GLenum mode,
+                                             GLint first,
+                                             GLsizei count,
+                                             GLsizei primcount) {
+  MojoGLES2MakeCurrent(context_);
+  glDrawArraysInstancedANGLE(mode, first, count, primcount);
+}
+void MojoGLES2Impl::DrawElementsInstancedANGLE(GLenum mode,
+                                               GLsizei count,
+                                               GLenum type,
+                                               const void* indices,
+                                               GLsizei primcount) {
+  MojoGLES2MakeCurrent(context_);
+  glDrawElementsInstancedANGLE(mode, count, type, indices, primcount);
+}
+void MojoGLES2Impl::VertexAttribDivisorANGLE(GLuint index, GLuint divisor) {
+  MojoGLES2MakeCurrent(context_);
+  glVertexAttribDivisorANGLE(index, divisor);
+}
+void MojoGLES2Impl::GenMailboxCHROMIUM(GLbyte* mailbox) {
+  MojoGLES2MakeCurrent(context_);
+  glGenMailboxCHROMIUM(mailbox);
+}
+void MojoGLES2Impl::ProduceTextureCHROMIUM(GLenum target,
+                                           const GLbyte* mailbox) {
+  MojoGLES2MakeCurrent(context_);
+  glProduceTextureCHROMIUM(target, mailbox);
+}
+void MojoGLES2Impl::ProduceTextureDirectCHROMIUM(GLuint texture,
+                                                 GLenum target,
+                                                 const GLbyte* mailbox) {
+  MojoGLES2MakeCurrent(context_);
+  glProduceTextureDirectCHROMIUM(texture, target, mailbox);
+}
+void MojoGLES2Impl::ConsumeTextureCHROMIUM(GLenum target,
+                                           const GLbyte* mailbox) {
+  MojoGLES2MakeCurrent(context_);
+  glConsumeTextureCHROMIUM(target, mailbox);
+}
+GLuint MojoGLES2Impl::CreateAndConsumeTextureCHROMIUM(GLenum target,
+                                                      const GLbyte* mailbox) {
+  MojoGLES2MakeCurrent(context_);
+  return glCreateAndConsumeTextureCHROMIUM(target, mailbox);
+}
+void MojoGLES2Impl::BindUniformLocationCHROMIUM(GLuint program,
+                                                GLint location,
+                                                const char* name) {
+  MojoGLES2MakeCurrent(context_);
+  glBindUniformLocationCHROMIUM(program, location, name);
+}
+void MojoGLES2Impl::BindTexImage2DCHROMIUM(GLenum target, GLint imageId) {
+  MojoGLES2MakeCurrent(context_);
+  glBindTexImage2DCHROMIUM(target, imageId);
+}
+void MojoGLES2Impl::ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) {
+  MojoGLES2MakeCurrent(context_);
+  glReleaseTexImage2DCHROMIUM(target, imageId);
+}
+void MojoGLES2Impl::TraceBeginCHROMIUM(const char* category_name,
+                                       const char* trace_name) {
+  MojoGLES2MakeCurrent(context_);
+  glTraceBeginCHROMIUM(category_name, trace_name);
+}
+void MojoGLES2Impl::TraceEndCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glTraceEndCHROMIUM();
+}
+void MojoGLES2Impl::DiscardFramebufferEXT(GLenum target,
+                                          GLsizei count,
+                                          const GLenum* attachments) {
+  MojoGLES2MakeCurrent(context_);
+  glDiscardFramebufferEXT(target, count, attachments);
+}
+void MojoGLES2Impl::LoseContextCHROMIUM(GLenum current, GLenum other) {
+  MojoGLES2MakeCurrent(context_);
+  glLoseContextCHROMIUM(current, other);
+}
+GLuint64 MojoGLES2Impl::InsertFenceSyncCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  return glInsertFenceSyncCHROMIUM();
+}
+void MojoGLES2Impl::GenSyncTokenCHROMIUM(GLuint64 fence_sync,
+                                         GLbyte* sync_token) {
+  MojoGLES2MakeCurrent(context_);
+  glGenSyncTokenCHROMIUM(fence_sync, sync_token);
+}
+void MojoGLES2Impl::GenUnverifiedSyncTokenCHROMIUM(GLuint64 fence_sync,
+                                                   GLbyte* sync_token) {
+  MojoGLES2MakeCurrent(context_);
+  glGenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token);
+}
+void MojoGLES2Impl::VerifySyncTokensCHROMIUM(GLbyte** sync_tokens,
+                                             GLsizei count) {
+  MojoGLES2MakeCurrent(context_);
+  glVerifySyncTokensCHROMIUM(sync_tokens, count);
+}
+void MojoGLES2Impl::WaitSyncTokenCHROMIUM(const GLbyte* sync_token) {
+  MojoGLES2MakeCurrent(context_);
+  glWaitSyncTokenCHROMIUM(sync_token);
+}
+void MojoGLES2Impl::DrawBuffersEXT(GLsizei count, const GLenum* bufs) {
+  MojoGLES2MakeCurrent(context_);
+  glDrawBuffersEXT(count, bufs);
+}
+void MojoGLES2Impl::DiscardBackbufferCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glDiscardBackbufferCHROMIUM();
+}
+void MojoGLES2Impl::ScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
+                                                 GLenum plane_transform,
+                                                 GLuint overlay_texture_id,
+                                                 GLint bounds_x,
+                                                 GLint bounds_y,
+                                                 GLint bounds_width,
+                                                 GLint bounds_height,
+                                                 GLfloat uv_x,
+                                                 GLfloat uv_y,
+                                                 GLfloat uv_width,
+                                                 GLfloat uv_height) {
+  MojoGLES2MakeCurrent(context_);
+  glScheduleOverlayPlaneCHROMIUM(
+      plane_z_order, plane_transform, overlay_texture_id, bounds_x, bounds_y,
+      bounds_width, bounds_height, uv_x, uv_y, uv_width, uv_height);
+}
+void MojoGLES2Impl::ScheduleCALayerCHROMIUM(GLuint contents_texture_id,
+                                            const GLfloat* contents_rect,
+                                            GLfloat opacity,
+                                            GLuint background_color,
+                                            GLuint edge_aa_mask,
+                                            const GLfloat* bounds_rect,
+                                            GLboolean is_clipped,
+                                            const GLfloat* clip_rect,
+                                            GLint sorting_context_id,
+                                            const GLfloat* transform,
+                                            GLuint filter) {
+  MojoGLES2MakeCurrent(context_);
+  glScheduleCALayerCHROMIUM(contents_texture_id, contents_rect, opacity,
+                            background_color, edge_aa_mask, bounds_rect,
+                            is_clipped, clip_rect, sorting_context_id,
+                            transform, filter);
+}
+void MojoGLES2Impl::CommitOverlayPlanesCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glCommitOverlayPlanesCHROMIUM();
+}
+void MojoGLES2Impl::SwapInterval(GLint interval) {
+  MojoGLES2MakeCurrent(context_);
+  glSwapInterval(interval);
+}
+void MojoGLES2Impl::FlushDriverCachesCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glFlushDriverCachesCHROMIUM();
+}
+GLuint MojoGLES2Impl::GetLastFlushIdCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  return glGetLastFlushIdCHROMIUM();
+}
+void MojoGLES2Impl::MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat* m) {
+  MojoGLES2MakeCurrent(context_);
+  glMatrixLoadfCHROMIUM(matrixMode, m);
+}
+void MojoGLES2Impl::MatrixLoadIdentityCHROMIUM(GLenum matrixMode) {
+  MojoGLES2MakeCurrent(context_);
+  glMatrixLoadIdentityCHROMIUM(matrixMode);
+}
+GLuint MojoGLES2Impl::GenPathsCHROMIUM(GLsizei range) {
+  MojoGLES2MakeCurrent(context_);
+  return glGenPathsCHROMIUM(range);
+}
+void MojoGLES2Impl::DeletePathsCHROMIUM(GLuint path, GLsizei range) {
+  MojoGLES2MakeCurrent(context_);
+  glDeletePathsCHROMIUM(path, range);
+}
+GLboolean MojoGLES2Impl::IsPathCHROMIUM(GLuint path) {
+  MojoGLES2MakeCurrent(context_);
+  return glIsPathCHROMIUM(path);
+}
+void MojoGLES2Impl::PathCommandsCHROMIUM(GLuint path,
+                                         GLsizei numCommands,
+                                         const GLubyte* commands,
+                                         GLsizei numCoords,
+                                         GLenum coordType,
+                                         const GLvoid* coords) {
+  MojoGLES2MakeCurrent(context_);
+  glPathCommandsCHROMIUM(path, numCommands, commands, numCoords, coordType,
+                         coords);
+}
+void MojoGLES2Impl::PathParameterfCHROMIUM(GLuint path,
+                                           GLenum pname,
+                                           GLfloat value) {
+  MojoGLES2MakeCurrent(context_);
+  glPathParameterfCHROMIUM(path, pname, value);
+}
+void MojoGLES2Impl::PathParameteriCHROMIUM(GLuint path,
+                                           GLenum pname,
+                                           GLint value) {
+  MojoGLES2MakeCurrent(context_);
+  glPathParameteriCHROMIUM(path, pname, value);
+}
+void MojoGLES2Impl::PathStencilFuncCHROMIUM(GLenum func,
+                                            GLint ref,
+                                            GLuint mask) {
+  MojoGLES2MakeCurrent(context_);
+  glPathStencilFuncCHROMIUM(func, ref, mask);
+}
+void MojoGLES2Impl::StencilFillPathCHROMIUM(GLuint path,
+                                            GLenum fillMode,
+                                            GLuint mask) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilFillPathCHROMIUM(path, fillMode, mask);
+}
+void MojoGLES2Impl::StencilStrokePathCHROMIUM(GLuint path,
+                                              GLint reference,
+                                              GLuint mask) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilStrokePathCHROMIUM(path, reference, mask);
+}
+void MojoGLES2Impl::CoverFillPathCHROMIUM(GLuint path, GLenum coverMode) {
+  MojoGLES2MakeCurrent(context_);
+  glCoverFillPathCHROMIUM(path, coverMode);
+}
+void MojoGLES2Impl::CoverStrokePathCHROMIUM(GLuint path, GLenum coverMode) {
+  MojoGLES2MakeCurrent(context_);
+  glCoverStrokePathCHROMIUM(path, coverMode);
+}
+void MojoGLES2Impl::StencilThenCoverFillPathCHROMIUM(GLuint path,
+                                                     GLenum fillMode,
+                                                     GLuint mask,
+                                                     GLenum coverMode) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilThenCoverFillPathCHROMIUM(path, fillMode, mask, coverMode);
+}
+void MojoGLES2Impl::StencilThenCoverStrokePathCHROMIUM(GLuint path,
+                                                       GLint reference,
+                                                       GLuint mask,
+                                                       GLenum coverMode) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilThenCoverStrokePathCHROMIUM(path, reference, mask, coverMode);
+}
+void MojoGLES2Impl::StencilFillPathInstancedCHROMIUM(
+    GLsizei numPaths,
+    GLenum pathNameType,
+    const GLvoid* paths,
+    GLuint pathBase,
+    GLenum fillMode,
+    GLuint mask,
+    GLenum transformType,
+    const GLfloat* transformValues) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilFillPathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
+                                     fillMode, mask, transformType,
+                                     transformValues);
+}
+void MojoGLES2Impl::StencilStrokePathInstancedCHROMIUM(
+    GLsizei numPaths,
+    GLenum pathNameType,
+    const GLvoid* paths,
+    GLuint pathBase,
+    GLint reference,
+    GLuint mask,
+    GLenum transformType,
+    const GLfloat* transformValues) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilStrokePathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
+                                       reference, mask, transformType,
+                                       transformValues);
+}
+void MojoGLES2Impl::CoverFillPathInstancedCHROMIUM(
+    GLsizei numPaths,
+    GLenum pathNameType,
+    const GLvoid* paths,
+    GLuint pathBase,
+    GLenum coverMode,
+    GLenum transformType,
+    const GLfloat* transformValues) {
+  MojoGLES2MakeCurrent(context_);
+  glCoverFillPathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
+                                   coverMode, transformType, transformValues);
+}
+void MojoGLES2Impl::CoverStrokePathInstancedCHROMIUM(
+    GLsizei numPaths,
+    GLenum pathNameType,
+    const GLvoid* paths,
+    GLuint pathBase,
+    GLenum coverMode,
+    GLenum transformType,
+    const GLfloat* transformValues) {
+  MojoGLES2MakeCurrent(context_);
+  glCoverStrokePathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
+                                     coverMode, transformType, transformValues);
+}
+void MojoGLES2Impl::StencilThenCoverFillPathInstancedCHROMIUM(
+    GLsizei numPaths,
+    GLenum pathNameType,
+    const GLvoid* paths,
+    GLuint pathBase,
+    GLenum fillMode,
+    GLuint mask,
+    GLenum coverMode,
+    GLenum transformType,
+    const GLfloat* transformValues) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilThenCoverFillPathInstancedCHROMIUM(
+      numPaths, pathNameType, paths, pathBase, fillMode, mask, coverMode,
+      transformType, transformValues);
+}
+void MojoGLES2Impl::StencilThenCoverStrokePathInstancedCHROMIUM(
+    GLsizei numPaths,
+    GLenum pathNameType,
+    const GLvoid* paths,
+    GLuint pathBase,
+    GLint reference,
+    GLuint mask,
+    GLenum coverMode,
+    GLenum transformType,
+    const GLfloat* transformValues) {
+  MojoGLES2MakeCurrent(context_);
+  glStencilThenCoverStrokePathInstancedCHROMIUM(
+      numPaths, pathNameType, paths, pathBase, reference, mask, coverMode,
+      transformType, transformValues);
+}
+void MojoGLES2Impl::BindFragmentInputLocationCHROMIUM(GLuint program,
+                                                      GLint location,
+                                                      const char* name) {
+  MojoGLES2MakeCurrent(context_);
+  glBindFragmentInputLocationCHROMIUM(program, location, name);
+}
+void MojoGLES2Impl::ProgramPathFragmentInputGenCHROMIUM(GLuint program,
+                                                        GLint location,
+                                                        GLenum genMode,
+                                                        GLint components,
+                                                        const GLfloat* coeffs) {
+  MojoGLES2MakeCurrent(context_);
+  glProgramPathFragmentInputGenCHROMIUM(program, location, genMode, components,
+                                        coeffs);
+}
+void MojoGLES2Impl::CoverageModulationCHROMIUM(GLenum components) {
+  MojoGLES2MakeCurrent(context_);
+  glCoverageModulationCHROMIUM(components);
+}
+GLenum MojoGLES2Impl::GetGraphicsResetStatusKHR() {
+  MojoGLES2MakeCurrent(context_);
+  return glGetGraphicsResetStatusKHR();
+}
+void MojoGLES2Impl::BlendBarrierKHR() {
+  MojoGLES2MakeCurrent(context_);
+  glBlendBarrierKHR();
+}
+void MojoGLES2Impl::ApplyScreenSpaceAntialiasingCHROMIUM() {
+  MojoGLES2MakeCurrent(context_);
+  glApplyScreenSpaceAntialiasingCHROMIUM();
+}
+void MojoGLES2Impl::BindFragDataLocationIndexedEXT(GLuint program,
+                                                   GLuint colorNumber,
+                                                   GLuint index,
+                                                   const char* name) {
+  MojoGLES2MakeCurrent(context_);
+  glBindFragDataLocationIndexedEXT(program, colorNumber, index, name);
+}
+void MojoGLES2Impl::BindFragDataLocationEXT(GLuint program,
+                                            GLuint colorNumber,
+                                            const char* name) {
+  MojoGLES2MakeCurrent(context_);
+  glBindFragDataLocationEXT(program, colorNumber, name);
+}
+GLint MojoGLES2Impl::GetFragDataIndexEXT(GLuint program, const char* name) {
+  MojoGLES2MakeCurrent(context_);
+  return glGetFragDataIndexEXT(program, name);
+}
+void MojoGLES2Impl::UniformMatrix4fvStreamTextureMatrixCHROMIUM(
+    GLint location,
+    GLboolean transpose,
+    const GLfloat* default_value) {
+  MojoGLES2MakeCurrent(context_);
+  glUniformMatrix4fvStreamTextureMatrixCHROMIUM(location, transpose,
+                                                default_value);
+}
+
+}  // namespace mojo
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.h b/mojo/gpu/mojo_gles2_impl_autogen.h
new file mode 100644
index 0000000..fd0e5bd
--- /dev/null
+++ b/mojo/gpu/mojo_gles2_impl_autogen.h
@@ -0,0 +1,890 @@
+// Copyright 2014 The Chromium 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!
+
+// This file is included by gles2_interface.h to declare the
+// GL api functions.
+#ifndef MOJO_GPU_MOJO_GLES2_IMPL_AUTOGEN_H_
+#define MOJO_GPU_MOJO_GLES2_IMPL_AUTOGEN_H_
+
+#include <memory>
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "mojo/public/c/gles2/gles2.h"
+
+namespace mojo {
+
+class MojoGLES2Impl : public gpu::gles2::GLES2Interface {
+ public:
+  explicit MojoGLES2Impl(MojoGLES2Context context) { context_ = context; }
+  ~MojoGLES2Impl() override {}
+  void ActiveTexture(GLenum texture) override;
+  void AttachShader(GLuint program, GLuint shader) override;
+  void BindAttribLocation(GLuint program,
+                          GLuint index,
+                          const char* name) override;
+  void BindBuffer(GLenum target, GLuint buffer) override;
+  void BindBufferBase(GLenum target, GLuint index, GLuint buffer) override;
+  void BindBufferRange(GLenum target,
+                       GLuint index,
+                       GLuint buffer,
+                       GLintptr offset,
+                       GLsizeiptr size) override;
+  void BindFramebuffer(GLenum target, GLuint framebuffer) override;
+  void BindRenderbuffer(GLenum target, GLuint renderbuffer) override;
+  void BindSampler(GLuint unit, GLuint sampler) override;
+  void BindTexture(GLenum target, GLuint texture) override;
+  void BindTransformFeedback(GLenum target, GLuint transformfeedback) override;
+  void BlendColor(GLclampf red,
+                  GLclampf green,
+                  GLclampf blue,
+                  GLclampf alpha) override;
+  void BlendEquation(GLenum mode) override;
+  void BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) override;
+  void BlendFunc(GLenum sfactor, GLenum dfactor) override;
+  void BlendFuncSeparate(GLenum srcRGB,
+                         GLenum dstRGB,
+                         GLenum srcAlpha,
+                         GLenum dstAlpha) override;
+  void BufferData(GLenum target,
+                  GLsizeiptr size,
+                  const void* data,
+                  GLenum usage) override;
+  void BufferSubData(GLenum target,
+                     GLintptr offset,
+                     GLsizeiptr size,
+                     const void* data) override;
+  GLenum CheckFramebufferStatus(GLenum target) override;
+  void Clear(GLbitfield mask) override;
+  void ClearBufferfi(GLenum buffer,
+                     GLint drawbuffers,
+                     GLfloat depth,
+                     GLint stencil) override;
+  void ClearBufferfv(GLenum buffer,
+                     GLint drawbuffers,
+                     const GLfloat* value) override;
+  void ClearBufferiv(GLenum buffer,
+                     GLint drawbuffers,
+                     const GLint* value) override;
+  void ClearBufferuiv(GLenum buffer,
+                      GLint drawbuffers,
+                      const GLuint* value) override;
+  void ClearColor(GLclampf red,
+                  GLclampf green,
+                  GLclampf blue,
+                  GLclampf alpha) override;
+  void ClearDepthf(GLclampf depth) override;
+  void ClearStencil(GLint s) override;
+  GLenum ClientWaitSync(GLsync sync,
+                        GLbitfield flags,
+                        GLuint64 timeout) override;
+  void ColorMask(GLboolean red,
+                 GLboolean green,
+                 GLboolean blue,
+                 GLboolean alpha) override;
+  void CompileShader(GLuint shader) override;
+  void CompressedTexImage2D(GLenum target,
+                            GLint level,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height,
+                            GLint border,
+                            GLsizei imageSize,
+                            const void* data) override;
+  void CompressedTexSubImage2D(GLenum target,
+                               GLint level,
+                               GLint xoffset,
+                               GLint yoffset,
+                               GLsizei width,
+                               GLsizei height,
+                               GLenum format,
+                               GLsizei imageSize,
+                               const void* data) override;
+  void CompressedTexImage3D(GLenum target,
+                            GLint level,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height,
+                            GLsizei depth,
+                            GLint border,
+                            GLsizei imageSize,
+                            const void* data) override;
+  void CompressedTexSubImage3D(GLenum target,
+                               GLint level,
+                               GLint xoffset,
+                               GLint yoffset,
+                               GLint zoffset,
+                               GLsizei width,
+                               GLsizei height,
+                               GLsizei depth,
+                               GLenum format,
+                               GLsizei imageSize,
+                               const void* data) override;
+  void CopyBufferSubData(GLenum readtarget,
+                         GLenum writetarget,
+                         GLintptr readoffset,
+                         GLintptr writeoffset,
+                         GLsizeiptr size) override;
+  void CopyTexImage2D(GLenum target,
+                      GLint level,
+                      GLenum internalformat,
+                      GLint x,
+                      GLint y,
+                      GLsizei width,
+                      GLsizei height,
+                      GLint border) override;
+  void CopyTexSubImage2D(GLenum target,
+                         GLint level,
+                         GLint xoffset,
+                         GLint yoffset,
+                         GLint x,
+                         GLint y,
+                         GLsizei width,
+                         GLsizei height) override;
+  void CopyTexSubImage3D(GLenum target,
+                         GLint level,
+                         GLint xoffset,
+                         GLint yoffset,
+                         GLint zoffset,
+                         GLint x,
+                         GLint y,
+                         GLsizei width,
+                         GLsizei height) override;
+  GLuint CreateProgram() override;
+  GLuint CreateShader(GLenum type) override;
+  void CullFace(GLenum mode) override;
+  void DeleteBuffers(GLsizei n, const GLuint* buffers) override;
+  void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) override;
+  void DeleteProgram(GLuint program) override;
+  void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) override;
+  void DeleteSamplers(GLsizei n, const GLuint* samplers) override;
+  void DeleteSync(GLsync sync) override;
+  void DeleteShader(GLuint shader) override;
+  void DeleteTextures(GLsizei n, const GLuint* textures) override;
+  void DeleteTransformFeedbacks(GLsizei n, const GLuint* ids) override;
+  void DepthFunc(GLenum func) override;
+  void DepthMask(GLboolean flag) override;
+  void DepthRangef(GLclampf zNear, GLclampf zFar) override;
+  void DetachShader(GLuint program, GLuint shader) override;
+  void Disable(GLenum cap) override;
+  void DisableVertexAttribArray(GLuint index) override;
+  void DrawArrays(GLenum mode, GLint first, GLsizei count) override;
+  void DrawElements(GLenum mode,
+                    GLsizei count,
+                    GLenum type,
+                    const void* indices) override;
+  void DrawRangeElements(GLenum mode,
+                         GLuint start,
+                         GLuint end,
+                         GLsizei count,
+                         GLenum type,
+                         const void* indices) override;
+  void Enable(GLenum cap) override;
+  void EnableVertexAttribArray(GLuint index) override;
+  GLsync FenceSync(GLenum condition, GLbitfield flags) override;
+  void Finish() override;
+  void Flush() override;
+  void FramebufferRenderbuffer(GLenum target,
+                               GLenum attachment,
+                               GLenum renderbuffertarget,
+                               GLuint renderbuffer) override;
+  void FramebufferTexture2D(GLenum target,
+                            GLenum attachment,
+                            GLenum textarget,
+                            GLuint texture,
+                            GLint level) override;
+  void FramebufferTextureLayer(GLenum target,
+                               GLenum attachment,
+                               GLuint texture,
+                               GLint level,
+                               GLint layer) override;
+  void FrontFace(GLenum mode) override;
+  void GenBuffers(GLsizei n, GLuint* buffers) override;
+  void GenerateMipmap(GLenum target) override;
+  void GenFramebuffers(GLsizei n, GLuint* framebuffers) override;
+  void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) override;
+  void GenSamplers(GLsizei n, GLuint* samplers) override;
+  void GenTextures(GLsizei n, GLuint* textures) override;
+  void GenTransformFeedbacks(GLsizei n, GLuint* ids) override;
+  void GetActiveAttrib(GLuint program,
+                       GLuint index,
+                       GLsizei bufsize,
+                       GLsizei* length,
+                       GLint* size,
+                       GLenum* type,
+                       char* name) override;
+  void GetActiveUniform(GLuint program,
+                        GLuint index,
+                        GLsizei bufsize,
+                        GLsizei* length,
+                        GLint* size,
+                        GLenum* type,
+                        char* name) override;
+  void GetActiveUniformBlockiv(GLuint program,
+                               GLuint index,
+                               GLenum pname,
+                               GLint* params) override;
+  void GetActiveUniformBlockName(GLuint program,
+                                 GLuint index,
+                                 GLsizei bufsize,
+                                 GLsizei* length,
+                                 char* name) override;
+  void GetActiveUniformsiv(GLuint program,
+                           GLsizei count,
+                           const GLuint* indices,
+                           GLenum pname,
+                           GLint* params) override;
+  void GetAttachedShaders(GLuint program,
+                          GLsizei maxcount,
+                          GLsizei* count,
+                          GLuint* shaders) override;
+  GLint GetAttribLocation(GLuint program, const char* name) override;
+  void GetBooleanv(GLenum pname, GLboolean* params) override;
+  void GetBufferParameteri64v(GLenum target,
+                              GLenum pname,
+                              GLint64* params) override;
+  void GetBufferParameteriv(GLenum target,
+                            GLenum pname,
+                            GLint* params) override;
+  GLenum GetError() override;
+  void GetFloatv(GLenum pname, GLfloat* params) override;
+  GLint GetFragDataLocation(GLuint program, const char* name) override;
+  void GetFramebufferAttachmentParameteriv(GLenum target,
+                                           GLenum attachment,
+                                           GLenum pname,
+                                           GLint* params) override;
+  void GetInteger64v(GLenum pname, GLint64* params) override;
+  void GetIntegeri_v(GLenum pname, GLuint index, GLint* data) override;
+  void GetInteger64i_v(GLenum pname, GLuint index, GLint64* data) override;
+  void GetIntegerv(GLenum pname, GLint* params) override;
+  void GetInternalformativ(GLenum target,
+                           GLenum format,
+                           GLenum pname,
+                           GLsizei bufSize,
+                           GLint* params) override;
+  void GetProgramiv(GLuint program, GLenum pname, GLint* params) override;
+  void GetProgramInfoLog(GLuint program,
+                         GLsizei bufsize,
+                         GLsizei* length,
+                         char* infolog) override;
+  void GetRenderbufferParameteriv(GLenum target,
+                                  GLenum pname,
+                                  GLint* params) override;
+  void GetSamplerParameterfv(GLuint sampler,
+                             GLenum pname,
+                             GLfloat* params) override;
+  void GetSamplerParameteriv(GLuint sampler,
+                             GLenum pname,
+                             GLint* params) override;
+  void GetShaderiv(GLuint shader, GLenum pname, GLint* params) override;
+  void GetShaderInfoLog(GLuint shader,
+                        GLsizei bufsize,
+                        GLsizei* length,
+                        char* infolog) override;
+  void GetShaderPrecisionFormat(GLenum shadertype,
+                                GLenum precisiontype,
+                                GLint* range,
+                                GLint* precision) override;
+  void GetShaderSource(GLuint shader,
+                       GLsizei bufsize,
+                       GLsizei* length,
+                       char* source) override;
+  const GLubyte* GetString(GLenum name) override;
+  const GLubyte* GetStringi(GLenum name, GLuint index) override;
+  void GetSynciv(GLsync sync,
+                 GLenum pname,
+                 GLsizei bufsize,
+                 GLsizei* length,
+                 GLint* values) override;
+  void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) override;
+  void GetTexParameteriv(GLenum target, GLenum pname, GLint* params) override;
+  void GetTransformFeedbackVarying(GLuint program,
+                                   GLuint index,
+                                   GLsizei bufsize,
+                                   GLsizei* length,
+                                   GLsizei* size,
+                                   GLenum* type,
+                                   char* name) override;
+  GLuint GetUniformBlockIndex(GLuint program, const char* name) override;
+  void GetUniformfv(GLuint program, GLint location, GLfloat* params) override;
+  void GetUniformiv(GLuint program, GLint location, GLint* params) override;
+  void GetUniformuiv(GLuint program, GLint location, GLuint* params) override;
+  void GetUniformIndices(GLuint program,
+                         GLsizei count,
+                         const char* const* names,
+                         GLuint* indices) override;
+  GLint GetUniformLocation(GLuint program, const char* name) override;
+  void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) override;
+  void GetVertexAttribiv(GLuint index, GLenum pname, GLint* params) override;
+  void GetVertexAttribIiv(GLuint index, GLenum pname, GLint* params) override;
+  void GetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* params) override;
+  void GetVertexAttribPointerv(GLuint index,
+                               GLenum pname,
+                               void** pointer) override;
+  void Hint(GLenum target, GLenum mode) override;
+  void InvalidateFramebuffer(GLenum target,
+                             GLsizei count,
+                             const GLenum* attachments) override;
+  void InvalidateSubFramebuffer(GLenum target,
+                                GLsizei count,
+                                const GLenum* attachments,
+                                GLint x,
+                                GLint y,
+                                GLsizei width,
+                                GLsizei height) override;
+  GLboolean IsBuffer(GLuint buffer) override;
+  GLboolean IsEnabled(GLenum cap) override;
+  GLboolean IsFramebuffer(GLuint framebuffer) override;
+  GLboolean IsProgram(GLuint program) override;
+  GLboolean IsRenderbuffer(GLuint renderbuffer) override;
+  GLboolean IsSampler(GLuint sampler) override;
+  GLboolean IsShader(GLuint shader) override;
+  GLboolean IsSync(GLsync sync) override;
+  GLboolean IsTexture(GLuint texture) override;
+  GLboolean IsTransformFeedback(GLuint transformfeedback) override;
+  void LineWidth(GLfloat width) override;
+  void LinkProgram(GLuint program) override;
+  void PauseTransformFeedback() override;
+  void PixelStorei(GLenum pname, GLint param) override;
+  void PolygonOffset(GLfloat factor, GLfloat units) override;
+  void ReadBuffer(GLenum src) override;
+  void ReadPixels(GLint x,
+                  GLint y,
+                  GLsizei width,
+                  GLsizei height,
+                  GLenum format,
+                  GLenum type,
+                  void* pixels) override;
+  void ReleaseShaderCompiler() override;
+  void RenderbufferStorage(GLenum target,
+                           GLenum internalformat,
+                           GLsizei width,
+                           GLsizei height) override;
+  void ResumeTransformFeedback() override;
+  void SampleCoverage(GLclampf value, GLboolean invert) override;
+  void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param) override;
+  void SamplerParameterfv(GLuint sampler,
+                          GLenum pname,
+                          const GLfloat* params) override;
+  void SamplerParameteri(GLuint sampler, GLenum pname, GLint param) override;
+  void SamplerParameteriv(GLuint sampler,
+                          GLenum pname,
+                          const GLint* params) override;
+  void Scissor(GLint x, GLint y, GLsizei width, GLsizei height) override;
+  void ShaderBinary(GLsizei n,
+                    const GLuint* shaders,
+                    GLenum binaryformat,
+                    const void* binary,
+                    GLsizei length) override;
+  void ShaderSource(GLuint shader,
+                    GLsizei count,
+                    const GLchar* const* str,
+                    const GLint* length) override;
+  void ShallowFinishCHROMIUM() override;
+  void ShallowFlushCHROMIUM() override;
+  void OrderingBarrierCHROMIUM() override;
+  void StencilFunc(GLenum func, GLint ref, GLuint mask) override;
+  void StencilFuncSeparate(GLenum face,
+                           GLenum func,
+                           GLint ref,
+                           GLuint mask) override;
+  void StencilMask(GLuint mask) override;
+  void StencilMaskSeparate(GLenum face, GLuint mask) override;
+  void StencilOp(GLenum fail, GLenum zfail, GLenum zpass) override;
+  void StencilOpSeparate(GLenum face,
+                         GLenum fail,
+                         GLenum zfail,
+                         GLenum zpass) override;
+  void TexImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  const void* pixels) override;
+  void TexImage3D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  const void* pixels) override;
+  void TexParameterf(GLenum target, GLenum pname, GLfloat param) override;
+  void TexParameterfv(GLenum target,
+                      GLenum pname,
+                      const GLfloat* params) override;
+  void TexParameteri(GLenum target, GLenum pname, GLint param) override;
+  void TexParameteriv(GLenum target,
+                      GLenum pname,
+                      const GLint* params) override;
+  void TexStorage3D(GLenum target,
+                    GLsizei levels,
+                    GLenum internalFormat,
+                    GLsizei width,
+                    GLsizei height,
+                    GLsizei depth) override;
+  void TexSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     const void* pixels) override;
+  void TexSubImage3D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     const void* pixels) override;
+  void TransformFeedbackVaryings(GLuint program,
+                                 GLsizei count,
+                                 const char* const* varyings,
+                                 GLenum buffermode) override;
+  void Uniform1f(GLint location, GLfloat x) override;
+  void Uniform1fv(GLint location, GLsizei count, const GLfloat* v) override;
+  void Uniform1i(GLint location, GLint x) override;
+  void Uniform1iv(GLint location, GLsizei count, const GLint* v) override;
+  void Uniform1ui(GLint location, GLuint x) override;
+  void Uniform1uiv(GLint location, GLsizei count, const GLuint* v) override;
+  void Uniform2f(GLint location, GLfloat x, GLfloat y) override;
+  void Uniform2fv(GLint location, GLsizei count, const GLfloat* v) override;
+  void Uniform2i(GLint location, GLint x, GLint y) override;
+  void Uniform2iv(GLint location, GLsizei count, const GLint* v) override;
+  void Uniform2ui(GLint location, GLuint x, GLuint y) override;
+  void Uniform2uiv(GLint location, GLsizei count, const GLuint* v) override;
+  void Uniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) override;
+  void Uniform3fv(GLint location, GLsizei count, const GLfloat* v) override;
+  void Uniform3i(GLint location, GLint x, GLint y, GLint z) override;
+  void Uniform3iv(GLint location, GLsizei count, const GLint* v) override;
+  void Uniform3ui(GLint location, GLuint x, GLuint y, GLuint z) override;
+  void Uniform3uiv(GLint location, GLsizei count, const GLuint* v) override;
+  void Uniform4f(GLint location,
+                 GLfloat x,
+                 GLfloat y,
+                 GLfloat z,
+                 GLfloat w) override;
+  void Uniform4fv(GLint location, GLsizei count, const GLfloat* v) override;
+  void Uniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) override;
+  void Uniform4iv(GLint location, GLsizei count, const GLint* v) override;
+  void Uniform4ui(GLint location,
+                  GLuint x,
+                  GLuint y,
+                  GLuint z,
+                  GLuint w) override;
+  void Uniform4uiv(GLint location, GLsizei count, const GLuint* v) override;
+  void UniformBlockBinding(GLuint program,
+                           GLuint index,
+                           GLuint binding) override;
+  void UniformMatrix2fv(GLint location,
+                        GLsizei count,
+                        GLboolean transpose,
+                        const GLfloat* value) override;
+  void UniformMatrix2x3fv(GLint location,
+                          GLsizei count,
+                          GLboolean transpose,
+                          const GLfloat* value) override;
+  void UniformMatrix2x4fv(GLint location,
+                          GLsizei count,
+                          GLboolean transpose,
+                          const GLfloat* value) override;
+  void UniformMatrix3fv(GLint location,
+                        GLsizei count,
+                        GLboolean transpose,
+                        const GLfloat* value) override;
+  void UniformMatrix3x2fv(GLint location,
+                          GLsizei count,
+                          GLboolean transpose,
+                          const GLfloat* value) override;
+  void UniformMatrix3x4fv(GLint location,
+                          GLsizei count,
+                          GLboolean transpose,
+                          const GLfloat* value) override;
+  void UniformMatrix4fv(GLint location,
+                        GLsizei count,
+                        GLboolean transpose,
+                        const GLfloat* value) override;
+  void UniformMatrix4x2fv(GLint location,
+                          GLsizei count,
+                          GLboolean transpose,
+                          const GLfloat* value) override;
+  void UniformMatrix4x3fv(GLint location,
+                          GLsizei count,
+                          GLboolean transpose,
+                          const GLfloat* value) override;
+  void UseProgram(GLuint program) override;
+  void ValidateProgram(GLuint program) override;
+  void VertexAttrib1f(GLuint indx, GLfloat x) override;
+  void VertexAttrib1fv(GLuint indx, const GLfloat* values) override;
+  void VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) override;
+  void VertexAttrib2fv(GLuint indx, const GLfloat* values) override;
+  void VertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) override;
+  void VertexAttrib3fv(GLuint indx, const GLfloat* values) override;
+  void VertexAttrib4f(GLuint indx,
+                      GLfloat x,
+                      GLfloat y,
+                      GLfloat z,
+                      GLfloat w) override;
+  void VertexAttrib4fv(GLuint indx, const GLfloat* values) override;
+  void VertexAttribI4i(GLuint indx,
+                       GLint x,
+                       GLint y,
+                       GLint z,
+                       GLint w) override;
+  void VertexAttribI4iv(GLuint indx, const GLint* values) override;
+  void VertexAttribI4ui(GLuint indx,
+                        GLuint x,
+                        GLuint y,
+                        GLuint z,
+                        GLuint w) override;
+  void VertexAttribI4uiv(GLuint indx, const GLuint* values) override;
+  void VertexAttribIPointer(GLuint indx,
+                            GLint size,
+                            GLenum type,
+                            GLsizei stride,
+                            const void* ptr) override;
+  void VertexAttribPointer(GLuint indx,
+                           GLint size,
+                           GLenum type,
+                           GLboolean normalized,
+                           GLsizei stride,
+                           const void* ptr) override;
+  void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) override;
+  void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
+  void BlitFramebufferCHROMIUM(GLint srcX0,
+                               GLint srcY0,
+                               GLint srcX1,
+                               GLint srcY1,
+                               GLint dstX0,
+                               GLint dstY0,
+                               GLint dstX1,
+                               GLint dstY1,
+                               GLbitfield mask,
+                               GLenum filter) override;
+  void RenderbufferStorageMultisampleCHROMIUM(GLenum target,
+                                              GLsizei samples,
+                                              GLenum internalformat,
+                                              GLsizei width,
+                                              GLsizei height) override;
+  void RenderbufferStorageMultisampleEXT(GLenum target,
+                                         GLsizei samples,
+                                         GLenum internalformat,
+                                         GLsizei width,
+                                         GLsizei height) override;
+  void FramebufferTexture2DMultisampleEXT(GLenum target,
+                                          GLenum attachment,
+                                          GLenum textarget,
+                                          GLuint texture,
+                                          GLint level,
+                                          GLsizei samples) override;
+  void TexStorage2DEXT(GLenum target,
+                       GLsizei levels,
+                       GLenum internalFormat,
+                       GLsizei width,
+                       GLsizei height) override;
+  void GenQueriesEXT(GLsizei n, GLuint* queries) override;
+  void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override;
+  void QueryCounterEXT(GLuint id, GLenum target) override;
+  GLboolean IsQueryEXT(GLuint id) override;
+  void BeginQueryEXT(GLenum target, GLuint id) override;
+  void BeginTransformFeedback(GLenum primitivemode) override;
+  void EndQueryEXT(GLenum target) override;
+  void EndTransformFeedback() override;
+  void GetQueryivEXT(GLenum target, GLenum pname, GLint* params) override;
+  void GetQueryObjectivEXT(GLuint id, GLenum pname, GLint* params) override;
+  void GetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint* params) override;
+  void GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64* params) override;
+  void GetQueryObjectui64vEXT(GLuint id,
+                              GLenum pname,
+                              GLuint64* params) override;
+  void SetDisjointValueSyncCHROMIUM() override;
+  void InsertEventMarkerEXT(GLsizei length, const GLchar* marker) override;
+  void PushGroupMarkerEXT(GLsizei length, const GLchar* marker) override;
+  void PopGroupMarkerEXT() override;
+  void GenVertexArraysOES(GLsizei n, GLuint* arrays) override;
+  void DeleteVertexArraysOES(GLsizei n, const GLuint* arrays) override;
+  GLboolean IsVertexArrayOES(GLuint array) override;
+  void BindVertexArrayOES(GLuint array) override;
+  void SwapBuffers() override;
+  GLuint GetMaxValueInBufferCHROMIUM(GLuint buffer_id,
+                                     GLsizei count,
+                                     GLenum type,
+                                     GLuint offset) override;
+  GLboolean EnableFeatureCHROMIUM(const char* feature) override;
+  void* MapBufferCHROMIUM(GLuint target, GLenum access) override;
+  GLboolean UnmapBufferCHROMIUM(GLuint target) override;
+  void* MapBufferSubDataCHROMIUM(GLuint target,
+                                 GLintptr offset,
+                                 GLsizeiptr size,
+                                 GLenum access) override;
+  void UnmapBufferSubDataCHROMIUM(const void* mem) override;
+  void* MapBufferRange(GLenum target,
+                       GLintptr offset,
+                       GLsizeiptr size,
+                       GLbitfield access) override;
+  GLboolean UnmapBuffer(GLenum target) override;
+  void* MapTexSubImage2DCHROMIUM(GLenum target,
+                                 GLint level,
+                                 GLint xoffset,
+                                 GLint yoffset,
+                                 GLsizei width,
+                                 GLsizei height,
+                                 GLenum format,
+                                 GLenum type,
+                                 GLenum access) override;
+  void UnmapTexSubImage2DCHROMIUM(const void* mem) override;
+  void ResizeCHROMIUM(GLuint width,
+                      GLuint height,
+                      GLfloat scale_factor,
+                      GLboolean alpha) override;
+  const GLchar* GetRequestableExtensionsCHROMIUM() override;
+  void RequestExtensionCHROMIUM(const char* extension) override;
+  void GetProgramInfoCHROMIUM(GLuint program,
+                              GLsizei bufsize,
+                              GLsizei* size,
+                              void* info) override;
+  void GetUniformBlocksCHROMIUM(GLuint program,
+                                GLsizei bufsize,
+                                GLsizei* size,
+                                void* info) override;
+  void GetTransformFeedbackVaryingsCHROMIUM(GLuint program,
+                                            GLsizei bufsize,
+                                            GLsizei* size,
+                                            void* info) override;
+  void GetUniformsES3CHROMIUM(GLuint program,
+                              GLsizei bufsize,
+                              GLsizei* size,
+                              void* info) override;
+  GLuint CreateImageCHROMIUM(ClientBuffer buffer,
+                             GLsizei width,
+                             GLsizei height,
+                             GLenum internalformat) override;
+  void DestroyImageCHROMIUM(GLuint image_id) override;
+  GLuint CreateGpuMemoryBufferImageCHROMIUM(GLsizei width,
+                                            GLsizei height,
+                                            GLenum internalformat,
+                                            GLenum usage) override;
+  void GetTranslatedShaderSourceANGLE(GLuint shader,
+                                      GLsizei bufsize,
+                                      GLsizei* length,
+                                      char* source) override;
+  void PostSubBufferCHROMIUM(GLint x,
+                             GLint y,
+                             GLint width,
+                             GLint height) override;
+  void CopyTextureCHROMIUM(GLenum source_id,
+                           GLenum dest_id,
+                           GLint internalformat,
+                           GLenum dest_type,
+                           GLboolean unpack_flip_y,
+                           GLboolean unpack_premultiply_alpha,
+                           GLboolean unpack_unmultiply_alpha) override;
+  void CopySubTextureCHROMIUM(GLenum source_id,
+                              GLenum dest_id,
+                              GLint xoffset,
+                              GLint yoffset,
+                              GLint x,
+                              GLint y,
+                              GLsizei width,
+                              GLsizei height,
+                              GLboolean unpack_flip_y,
+                              GLboolean unpack_premultiply_alpha,
+                              GLboolean unpack_unmultiply_alpha) override;
+  void CompressedCopyTextureCHROMIUM(GLenum source_id, GLenum dest_id) override;
+  void DrawArraysInstancedANGLE(GLenum mode,
+                                GLint first,
+                                GLsizei count,
+                                GLsizei primcount) override;
+  void DrawElementsInstancedANGLE(GLenum mode,
+                                  GLsizei count,
+                                  GLenum type,
+                                  const void* indices,
+                                  GLsizei primcount) override;
+  void VertexAttribDivisorANGLE(GLuint index, GLuint divisor) override;
+  void GenMailboxCHROMIUM(GLbyte* mailbox) override;
+  void ProduceTextureCHROMIUM(GLenum target, const GLbyte* mailbox) override;
+  void ProduceTextureDirectCHROMIUM(GLuint texture,
+                                    GLenum target,
+                                    const GLbyte* mailbox) override;
+  void ConsumeTextureCHROMIUM(GLenum target, const GLbyte* mailbox) override;
+  GLuint CreateAndConsumeTextureCHROMIUM(GLenum target,
+                                         const GLbyte* mailbox) override;
+  void BindUniformLocationCHROMIUM(GLuint program,
+                                   GLint location,
+                                   const char* name) override;
+  void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override;
+  void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override;
+  void TraceBeginCHROMIUM(const char* category_name,
+                          const char* trace_name) override;
+  void TraceEndCHROMIUM() override;
+  void DiscardFramebufferEXT(GLenum target,
+                             GLsizei count,
+                             const GLenum* attachments) override;
+  void LoseContextCHROMIUM(GLenum current, GLenum other) override;
+  GLuint64 InsertFenceSyncCHROMIUM() override;
+  void GenSyncTokenCHROMIUM(GLuint64 fence_sync, GLbyte* sync_token) override;
+  void GenUnverifiedSyncTokenCHROMIUM(GLuint64 fence_sync,
+                                      GLbyte* sync_token) override;
+  void VerifySyncTokensCHROMIUM(GLbyte** sync_tokens, GLsizei count) override;
+  void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override;
+  void DrawBuffersEXT(GLsizei count, const GLenum* bufs) override;
+  void DiscardBackbufferCHROMIUM() override;
+  void ScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
+                                    GLenum plane_transform,
+                                    GLuint overlay_texture_id,
+                                    GLint bounds_x,
+                                    GLint bounds_y,
+                                    GLint bounds_width,
+                                    GLint bounds_height,
+                                    GLfloat uv_x,
+                                    GLfloat uv_y,
+                                    GLfloat uv_width,
+                                    GLfloat uv_height) override;
+  void ScheduleCALayerCHROMIUM(GLuint contents_texture_id,
+                               const GLfloat* contents_rect,
+                               GLfloat opacity,
+                               GLuint background_color,
+                               GLuint edge_aa_mask,
+                               const GLfloat* bounds_rect,
+                               GLboolean is_clipped,
+                               const GLfloat* clip_rect,
+                               GLint sorting_context_id,
+                               const GLfloat* transform,
+                               GLuint filter) override;
+  void CommitOverlayPlanesCHROMIUM() override;
+  void SwapInterval(GLint interval) override;
+  void FlushDriverCachesCHROMIUM() override;
+  GLuint GetLastFlushIdCHROMIUM() override;
+  void MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat* m) override;
+  void MatrixLoadIdentityCHROMIUM(GLenum matrixMode) override;
+  GLuint GenPathsCHROMIUM(GLsizei range) override;
+  void DeletePathsCHROMIUM(GLuint path, GLsizei range) override;
+  GLboolean IsPathCHROMIUM(GLuint path) override;
+  void PathCommandsCHROMIUM(GLuint path,
+                            GLsizei numCommands,
+                            const GLubyte* commands,
+                            GLsizei numCoords,
+                            GLenum coordType,
+                            const GLvoid* coords) override;
+  void PathParameterfCHROMIUM(GLuint path,
+                              GLenum pname,
+                              GLfloat value) override;
+  void PathParameteriCHROMIUM(GLuint path, GLenum pname, GLint value) override;
+  void PathStencilFuncCHROMIUM(GLenum func, GLint ref, GLuint mask) override;
+  void StencilFillPathCHROMIUM(GLuint path,
+                               GLenum fillMode,
+                               GLuint mask) override;
+  void StencilStrokePathCHROMIUM(GLuint path,
+                                 GLint reference,
+                                 GLuint mask) override;
+  void CoverFillPathCHROMIUM(GLuint path, GLenum coverMode) override;
+  void CoverStrokePathCHROMIUM(GLuint path, GLenum coverMode) override;
+  void StencilThenCoverFillPathCHROMIUM(GLuint path,
+                                        GLenum fillMode,
+                                        GLuint mask,
+                                        GLenum coverMode) override;
+  void StencilThenCoverStrokePathCHROMIUM(GLuint path,
+                                          GLint reference,
+                                          GLuint mask,
+                                          GLenum coverMode) override;
+  void StencilFillPathInstancedCHROMIUM(
+      GLsizei numPaths,
+      GLenum pathNameType,
+      const GLvoid* paths,
+      GLuint pathBase,
+      GLenum fillMode,
+      GLuint mask,
+      GLenum transformType,
+      const GLfloat* transformValues) override;
+  void StencilStrokePathInstancedCHROMIUM(
+      GLsizei numPaths,
+      GLenum pathNameType,
+      const GLvoid* paths,
+      GLuint pathBase,
+      GLint reference,
+      GLuint mask,
+      GLenum transformType,
+      const GLfloat* transformValues) override;
+  void CoverFillPathInstancedCHROMIUM(GLsizei numPaths,
+                                      GLenum pathNameType,
+                                      const GLvoid* paths,
+                                      GLuint pathBase,
+                                      GLenum coverMode,
+                                      GLenum transformType,
+                                      const GLfloat* transformValues) override;
+  void CoverStrokePathInstancedCHROMIUM(
+      GLsizei numPaths,
+      GLenum pathNameType,
+      const GLvoid* paths,
+      GLuint pathBase,
+      GLenum coverMode,
+      GLenum transformType,
+      const GLfloat* transformValues) override;
+  void StencilThenCoverFillPathInstancedCHROMIUM(
+      GLsizei numPaths,
+      GLenum pathNameType,
+      const GLvoid* paths,
+      GLuint pathBase,
+      GLenum fillMode,
+      GLuint mask,
+      GLenum coverMode,
+      GLenum transformType,
+      const GLfloat* transformValues) override;
+  void StencilThenCoverStrokePathInstancedCHROMIUM(
+      GLsizei numPaths,
+      GLenum pathNameType,
+      const GLvoid* paths,
+      GLuint pathBase,
+      GLint reference,
+      GLuint mask,
+      GLenum coverMode,
+      GLenum transformType,
+      const GLfloat* transformValues) override;
+  void BindFragmentInputLocationCHROMIUM(GLuint program,
+                                         GLint location,
+                                         const char* name) override;
+  void ProgramPathFragmentInputGenCHROMIUM(GLuint program,
+                                           GLint location,
+                                           GLenum genMode,
+                                           GLint components,
+                                           const GLfloat* coeffs) override;
+  void CoverageModulationCHROMIUM(GLenum components) override;
+  GLenum GetGraphicsResetStatusKHR() override;
+  void BlendBarrierKHR() override;
+  void ApplyScreenSpaceAntialiasingCHROMIUM() override;
+  void BindFragDataLocationIndexedEXT(GLuint program,
+                                      GLuint colorNumber,
+                                      GLuint index,
+                                      const char* name) override;
+  void BindFragDataLocationEXT(GLuint program,
+                               GLuint colorNumber,
+                               const char* name) override;
+  GLint GetFragDataIndexEXT(GLuint program, const char* name) override;
+  void UniformMatrix4fvStreamTextureMatrixCHROMIUM(
+      GLint location,
+      GLboolean transpose,
+      const GLfloat* default_value) override;
+
+ private:
+  MojoGLES2Context context_;
+};
+
+}  // namespace mojo
+#endif  // MOJO_GPU_MOJO_GLES2_IMPL_AUTOGEN_H_
diff --git a/mojo/message_pump/BUILD.gn b/mojo/message_pump/BUILD.gn
new file mode 100644
index 0000000..e1ae4af
--- /dev/null
+++ b/mojo/message_pump/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+component("message_pump") {
+  sources = [
+    "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_MESSAGE_PUMP_IMPLEMENTATION" ]
+
+  public_deps = [
+    "//base",
+    "//mojo/public/cpp/system",
+  ]
+}
diff --git a/mojo/message_pump/handle_watcher.cc b/mojo/message_pump/handle_watcher.cc
new file mode 100644
index 0000000..7f6f561
--- /dev/null
+++ b/mojo/message_pump/handle_watcher.cc
@@ -0,0 +1,480 @@
+// 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/message_pump/handle_watcher.h"
+
+#include <stddef.h>
+#include <stdint.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/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/message_pump/message_pump_mojo_handler.h"
+#include "mojo/message_pump/time_helper.h"
+#include "mojo/public/c/system/message_pipe.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), task_runner(NULL) {}
+
+  WatcherID id;
+  Handle handle;
+  MojoHandleSignals handle_signals;
+  base::TimeTicks deadline;
+  base::Callback<void(MojoResult)> callback;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner;
+};
+
+// 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();
+  ~WatcherBackend() override;
+
+  void StartWatching(const WatchData& data);
+  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:
+  void OnHandleReady(const Handle& handle) override;
+  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))
+    return;
+
+  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.task_runner->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.
+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) {}
+
+    RequestType type;
+    WatchData start_data;
+    WatcherID stop_id;
+  };
+
+  typedef std::vector<RequestData> Requests;
+
+  friend struct base::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 base::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.task_runner = base::ThreadTaskRunnerHandle::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;
+      }
+    }
+  }
+
+  RequestData request_data;
+  request_data.type = REQUEST_STOP;
+  request_data.stop_id = watcher_id;
+  AddRequest(request_data);
+}
+
+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 outlive |thread_|, so it's safe to use Unretained() here.
+  thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&WatcherThreadManager::ProcessRequestsOnBackendThread,
+                 base::Unretained(this)));
+}
+
+void WatcherThreadManager::ProcessRequestsOnBackendThread() {
+  DCHECK(thread_.task_runner()->BelongsToCurrentThread());
+
+  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);
+    }
+  }
+}
+
+WatcherThreadManager::WatcherThreadManager()
+    : thread_(kWatcherThreadName) {
+  base::Thread::Options thread_options;
+  thread_options.message_pump_factory = base::Bind(&MessagePumpMojo::Create);
+  thread_.StartWithOptions(thread_options);
+}
+
+}  // namespace
+
+// 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);
+  }
+
+  ~StateBase() override {
+    base::MessageLoop::current()->RemoveDestructionObserver(this);
+  }
+
+ protected:
+  void NotifyHandleReady(MojoResult result) {
+    got_ready_ = true;
+    NotifyAndDestroy(result);
+  }
+
+  bool got_ready() const { return got_ready_; }
+
+ private:
+  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));
+  }
+
+  ~SameThreadWatchingState() override {
+    if (!got_ready())
+      MessagePumpMojo::current()->RemoveHandler(handle_);
+  }
+
+ private:
+  // MessagePumpMojoHandler overrides:
+  void OnHandleReady(const Handle& handle) override {
+    StopWatchingAndNotifyReady(handle, MOJO_RESULT_OK);
+  }
+
+  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()));
+  }
+
+  ~SecondaryThreadWatchingState() override {
+    // 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);
+
+  // Need to clear the state before creating a new one.
+  state_.reset();
+  if (MessagePumpMojo::IsCurrent()) {
+    state_.reset(new SameThreadWatchingState(
+        this, handle, handle_signals, deadline, callback));
+  } else {
+#if !defined(OFFICIAL_BUILD)
+    // Just for making debugging non-transferable message pipes easier. Since
+    // they can't be sent after they're read/written/listened to,
+    // MessagePipeDispatcher saves the callstack of when it's "bound" to a
+    // pipe id. Triggering a read here, instead of later in the PostTask, means
+    // we have a callstack that is useful to check if the pipe is erronously
+    // attempted to be sent.
+    uint32_t temp = 0;
+    MojoReadMessage(handle.value(), nullptr, &temp, nullptr, nullptr,
+                    MOJO_READ_MESSAGE_FLAG_NONE);
+#endif
+    state_.reset(new SecondaryThreadWatchingState(
+        this, handle, handle_signals, deadline, callback));
+  }
+}
+
+void HandleWatcher::Stop() {
+  state_.reset();
+}
+
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/message_pump/handle_watcher.h b/mojo/message_pump/handle_watcher.h
new file mode 100644
index 0000000..10056b1
--- /dev/null
+++ b/mojo/message_pump/handle_watcher.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_MESSAGE_PUMP_HANDLE_WATCHER_H_
+#define MOJO_MESSAGE_PUMP_HANDLE_WATCHER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "mojo/message_pump/mojo_message_pump_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_MESSAGE_PUMP_EXPORT HandleWatcher {
+ public:
+  HandleWatcher();
+
+  ~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.
+  void Stop();
+
+  bool is_watching() const { return !!state_; }
+
+ private:
+  class StateBase;
+  class SameThreadWatchingState;
+  class SecondaryThreadWatchingState;
+
+  // If non-NULL Start() has been invoked.
+  std::unique_ptr<StateBase> state_;
+
+  DISALLOW_COPY_AND_ASSIGN(HandleWatcher);
+};
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_MESSAGE_PUMP_HANDLE_WATCHER_H_
diff --git a/mojo/message_pump/handle_watcher_perftest.cc b/mojo/message_pump/handle_watcher_perftest.cc
new file mode 100644
index 0000000..96c8495
--- /dev/null
+++ b/mojo/message_pump/handle_watcher_perftest.cc
@@ -0,0 +1,207 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "mojo/message_pump/handle_watcher.h"
+#include "mojo/message_pump/message_pump_mojo.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"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+enum MessageLoopConfig {
+  MESSAGE_LOOP_CONFIG_DEFAULT = 0,
+  MESSAGE_LOOP_CONFIG_MOJO = 1
+};
+
+std::unique_ptr<base::MessageLoop> CreateMessageLoop(MessageLoopConfig config) {
+  std::unique_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;
+}
+
+void OnWatcherSignaled(const base::Closure& callback, MojoResult /* result */) {
+  callback.Run();
+}
+
+class ScopedPerfTimer {
+ public:
+  ScopedPerfTimer(const std::string& test_name,
+                  const std::string& sub_test_name,
+                  uint64_t iterations)
+      : test_name_(test_name),
+        sub_test_name_(sub_test_name),
+        iterations_(iterations),
+        start_time_(base::TimeTicks::Now()) {}
+  ~ScopedPerfTimer() {
+    base::TimeTicks end_time = base::TimeTicks::Now();
+    mojo::test::LogPerfResult(
+        test_name_.c_str(), sub_test_name_.c_str(),
+        iterations_ / (end_time - start_time_).InSecondsF(),
+        "iterations/second");
+  }
+
+ private:
+  const std::string test_name_;
+  const std::string sub_test_name_;
+  const uint64_t iterations_;
+  base::TimeTicks start_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPerfTimer);
+};
+
+class HandleWatcherPerftest : public testing::TestWithParam<MessageLoopConfig> {
+ public:
+  HandleWatcherPerftest() : message_loop_(CreateMessageLoop(GetParam())) {}
+
+ protected:
+  std::string GetMessageLoopName() const {
+    return (GetParam() == MESSAGE_LOOP_CONFIG_DEFAULT) ? "DefaultMessageLoop"
+                                                       : "MojoMessageLoop";
+  }
+
+ private:
+  std::unique_ptr<base::MessageLoop> message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(HandleWatcherPerftest);
+};
+
+INSTANTIATE_TEST_CASE_P(MultipleMessageLoopConfigs,
+                        HandleWatcherPerftest,
+                        testing::Values(MESSAGE_LOOP_CONFIG_DEFAULT,
+                                        MESSAGE_LOOP_CONFIG_MOJO));
+
+void NeverReached(MojoResult result) {
+  FAIL() << "Callback should never be invoked " << result;
+}
+
+TEST_P(HandleWatcherPerftest, StartStop) {
+  const uint64_t kIterations = 100000;
+  MessagePipe pipe;
+  HandleWatcher watcher;
+
+  ScopedPerfTimer timer("StartStop", GetMessageLoopName(), kIterations);
+  for (uint64_t i = 0; i < kIterations; i++) {
+    watcher.Start(pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                  MOJO_DEADLINE_INDEFINITE, base::Bind(&NeverReached));
+    watcher.Stop();
+  }
+}
+
+TEST_P(HandleWatcherPerftest, StartAllThenStop_1000Handles) {
+  const uint64_t kIterations = 100;
+  const uint64_t kHandles = 1000;
+
+  struct TestData {
+    MessagePipe pipe;
+    HandleWatcher watcher;
+  };
+  ScopedVector<TestData> data_vector;
+  // Create separately from the start/stop loops to avoid affecting the
+  // benchmark.
+  for (uint64_t i = 0; i < kHandles; i++) {
+    std::unique_ptr<TestData> test_data(new TestData);
+    ASSERT_TRUE(test_data->pipe.handle0.is_valid());
+    data_vector.push_back(std::move(test_data));
+  }
+
+  ScopedPerfTimer timer("StartAllThenStop_1000Handles", GetMessageLoopName(),
+                        kIterations * kHandles);
+  for (uint64_t iter = 0; iter < kIterations; iter++) {
+    for (uint64_t i = 0; i < kHandles; i++) {
+      TestData* test_data = data_vector[i];
+      test_data->watcher.Start(
+          test_data->pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+          MOJO_DEADLINE_INDEFINITE, base::Bind(&NeverReached));
+    }
+    for (uint64_t i = 0; i < kHandles; i++) {
+      TestData* test_data = data_vector[i];
+      test_data->watcher.Stop();
+    }
+  }
+}
+
+TEST_P(HandleWatcherPerftest, StartAndSignal) {
+  const uint64_t kIterations = 10000;
+  const std::string kMessage = "hello";
+  MessagePipe pipe;
+  HandleWatcher watcher;
+  std::string received_message;
+
+  ScopedPerfTimer timer("StartAndSignal", GetMessageLoopName(), kIterations);
+  for (uint64_t i = 0; i < kIterations; i++) {
+    base::RunLoop run_loop;
+    watcher.Start(pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                  MOJO_DEADLINE_INDEFINITE,
+                  base::Bind(&OnWatcherSignaled, run_loop.QuitClosure()));
+    ASSERT_TRUE(mojo::test::WriteTextMessage(pipe.handle1.get(), kMessage));
+    run_loop.Run();
+    watcher.Stop();
+
+    ASSERT_TRUE(
+        mojo::test::ReadTextMessage(pipe.handle0.get(), &received_message));
+    EXPECT_EQ(kMessage, received_message);
+    received_message.clear();
+  }
+}
+
+TEST_P(HandleWatcherPerftest, StartAndSignal_1000Waiting) {
+  const uint64_t kIterations = 10000;
+  const uint64_t kWaitingHandles = 1000;
+  const std::string kMessage = "hello";
+  MessagePipe pipe;
+  HandleWatcher watcher;
+  std::string received_message;
+
+  struct TestData {
+    MessagePipe pipe;
+    HandleWatcher watcher;
+  };
+  ScopedVector<TestData> data_vector;
+  for (uint64_t i = 0; i < kWaitingHandles; i++) {
+    std::unique_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(std::move(test_data));
+  }
+
+  ScopedPerfTimer timer("StartAndSignal_1000Waiting", GetMessageLoopName(),
+                        kIterations);
+  for (uint64_t i = 0; i < kIterations; i++) {
+    base::RunLoop run_loop;
+    watcher.Start(pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                  MOJO_DEADLINE_INDEFINITE,
+                  base::Bind(&OnWatcherSignaled, run_loop.QuitClosure()));
+    ASSERT_TRUE(mojo::test::WriteTextMessage(pipe.handle1.get(), kMessage));
+    run_loop.Run();
+    watcher.Stop();
+
+    ASSERT_TRUE(
+        mojo::test::ReadTextMessage(pipe.handle0.get(), &received_message));
+    EXPECT_EQ(kMessage, received_message);
+    received_message.clear();
+  }
+}
+
+}  // namespace test
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/message_pump/handle_watcher_unittest.cc b/mojo/message_pump/handle_watcher_unittest.cc
new file mode 100644
index 0000000..fd1f49b
--- /dev/null
+++ b/mojo/message_pump/handle_watcher_unittest.cc
@@ -0,0 +1,493 @@
+// 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/message_pump/handle_watcher.h"
+
+#include <memory>
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/macros.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/message_pump/message_pump_mojo.h"
+#include "mojo/message_pump/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);
+}
+
+std::unique_ptr<base::MessageLoop> CreateMessageLoop(MessageLoopConfig config) {
+  std::unique_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;
+}
+
+// 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()
+      : at_exit_(new base::ShadowingAtExitManager),
+        message_loop_(CreateMessageLoop(GetParam())) {}
+  virtual ~HandleWatcherTest() {
+    // By explicitly destroying |at_exit_| before resetting the tick clock, it
+    // ensures that the handle watcher thread (if there is one) is shut down,
+    // preventing a race with users of the tick clock in MessagePumpMojo.
+    at_exit_.reset();
+    test::SetTickClockForTest(NULL);
+  }
+
+ protected:
+  void TearDownMessageLoop() {
+    message_loop_.reset();
+  }
+
+  // This should be called at the beginning of any test that needs it, so that
+  // it is installed before the handle watcher thread starts.
+  void InstallTickClock() {
+    test::SetTickClockForTest(&tick_clock_);
+  }
+
+  base::SimpleTestTickClock tick_clock_;
+
+ private:
+  std::unique_ptr<base::ShadowingAtExitManager> at_exit_;
+  std::unique_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 Start() invoked a second time on the same handle works.
+TEST_P(HandleWatcherTest, RestartOnSameHandle) {
+  MessagePipe test_pipe;
+  CallbackHelper callback_helper;
+  ASSERT_TRUE(test_pipe.handle0.is_valid());
+
+  HandleWatcher watcher;
+  callback_helper.Start(&watcher, test_pipe.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper.got_callback());
+
+  callback_helper.Start(&watcher, test_pipe.handle0.get());
+  RunUntilIdle();
+  EXPECT_FALSE(callback_helper.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 {
+      std::unique_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) {
+    std::unique_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/message_pump/message_pump_mojo.cc b/mojo/message_pump/message_pump_mojo.cc
new file mode 100644
index 0000000..b907ba3
--- /dev/null
+++ b/mojo/message_pump/message_pump_mojo.cc
@@ -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.
+
+#include "mojo/message_pump/message_pump_mojo.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "base/containers/small_map.h"
+#include "base/debug/alias.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "mojo/message_pump/message_pump_mojo_handler.h"
+#include "mojo/message_pump/time_helper.h"
+#include "mojo/public/c/system/wait_set.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
+
+struct MessagePumpMojo::RunState {
+  RunState() : should_quit(false) {}
+
+  base::TimeTicks delayed_work_time;
+
+  bool should_quit;
+};
+
+MessagePumpMojo::MessagePumpMojo()
+    : run_state_(NULL),
+      next_handler_id_(0),
+      event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+             base::WaitableEvent::InitialState::NOT_SIGNALED) {
+  DCHECK(!current())
+      << "There is already a MessagePumpMojo instance on this thread.";
+  g_tls_current_pump.Pointer()->Set(this);
+
+  MojoResult result = CreateMessagePipe(nullptr, &read_handle_, &write_handle_);
+  CHECK_EQ(result, MOJO_RESULT_OK);
+  CHECK(read_handle_.is_valid());
+  CHECK(write_handle_.is_valid());
+
+  MojoHandle handle;
+  result = MojoCreateWaitSet(&handle);
+  CHECK_EQ(result, MOJO_RESULT_OK);
+  wait_set_handle_.reset(Handle(handle));
+  CHECK(wait_set_handle_.is_valid());
+
+  result =
+      MojoAddHandle(wait_set_handle_.get().value(), read_handle_.get().value(),
+                    MOJO_HANDLE_SIGNAL_READABLE);
+  CHECK_EQ(result, MOJO_RESULT_OK);
+}
+
+MessagePumpMojo::~MessagePumpMojo() {
+  DCHECK_EQ(this, current());
+  g_tls_current_pump.Pointer()->Set(NULL);
+}
+
+// static
+std::unique_ptr<base::MessagePump> MessagePumpMojo::Create() {
+  return std::unique_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;
+  if (!deadline.is_null()) {
+    bool inserted = deadline_handles_.insert(handle).second;
+    DCHECK(inserted);
+  }
+
+  MojoResult result = MojoAddHandle(wait_set_handle_.get().value(),
+                                    handle.value(), wait_signals);
+  // Because stopping a HandleWatcher is now asynchronous, it's possible for the
+  // handle to no longer be open at this point.
+  CHECK(result == MOJO_RESULT_OK || result == MOJO_RESULT_INVALID_ARGUMENT);
+}
+
+void MessagePumpMojo::RemoveHandler(const Handle& handle) {
+  MojoResult result =
+      MojoRemoveHandle(wait_set_handle_.get().value(), handle.value());
+  // At this point, it's possible that the handle has been closed, which would
+  // cause MojoRemoveHandle() to return MOJO_RESULT_INVALID_ARGUMENT. It's also
+  // possible for the handle to have already been removed, so all of the
+  // possible error codes are valid here.
+  CHECK(result == MOJO_RESULT_OK || result == MOJO_RESULT_NOT_FOUND ||
+        result == MOJO_RESULT_INVALID_ARGUMENT);
+
+  handlers_.erase(handle);
+  deadline_handles_.erase(handle);
+}
+
+void MessagePumpMojo::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void MessagePumpMojo::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void MessagePumpMojo::Run(Delegate* delegate) {
+  RunState run_state;
+  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() {
+  SignalControlPipe();
+}
+
+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;
+    if (read_handle_.is_valid()) {
+      more_work_is_plausible = DoInternalWork(*run_state, block);
+    } else {
+      more_work_is_plausible = DoNonMojoWork(*run_state, block);
+    }
+
+    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;
+  }
+}
+
+bool MessagePumpMojo::DoInternalWork(const RunState& run_state, bool block) {
+  bool did_work = block;
+  if (block) {
+    // If the wait isn't blocking (deadline == 0), there's no point in waiting.
+    // Wait sets do not require a wait operation to be performed in order to
+    // retreive any ready handles. Performing a wait with deadline == 0 is
+    // unnecessary work.
+    did_work = WaitForReadyHandles(run_state);
+  }
+
+  did_work |= ProcessReadyHandles();
+  did_work |= RemoveExpiredHandles();
+
+  return did_work;
+}
+
+bool MessagePumpMojo::DoNonMojoWork(const RunState& run_state, bool block) {
+  bool did_work = block;
+  if (block) {
+    const MojoDeadline deadline = GetDeadlineForWait(run_state);
+    // Stolen from base/message_loop/message_pump_default.cc
+    base::ThreadRestrictions::ScopedAllowWait allow_wait;
+    if (deadline == MOJO_DEADLINE_INDEFINITE) {
+      event_.Wait();
+    } else {
+      if (deadline > 0) {
+        event_.TimedWait(base::TimeDelta::FromMicroseconds(deadline));
+      } else {
+        did_work = false;
+      }
+    }
+    // Since event_ is auto-reset, we don't need to do anything special here
+    // other than service each delegate method.
+  }
+
+  did_work |= RemoveExpiredHandles();
+
+  return did_work;
+}
+
+bool MessagePumpMojo::WaitForReadyHandles(const RunState& run_state) const {
+  const MojoDeadline deadline = GetDeadlineForWait(run_state);
+  const MojoResult wait_result = Wait(
+      wait_set_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE, deadline, nullptr);
+  if (wait_result == MOJO_RESULT_OK) {
+    // Handles may be ready. Or not since wake-ups can be spurious in certain
+    // circumstances.
+    return true;
+  } else if (wait_result == MOJO_RESULT_DEADLINE_EXCEEDED) {
+    return false;
+  }
+
+  base::debug::Alias(&wait_result);
+  // Unexpected result is likely fatal, crash so we can determine cause.
+  CHECK(false);
+  return false;
+}
+
+bool MessagePumpMojo::ProcessReadyHandles() {
+  // Maximum number of handles to retrieve and process. Experimentally, the 95th
+  // percentile is 1 handle, and the long-term average is 1.1. However, this has
+  // been seen to reach >10 under heavy load. 8 is a hand-wavy compromise.
+  const uint32_t kMaxServiced = 8;
+  uint32_t num_ready_handles = kMaxServiced;
+  MojoHandle handles[kMaxServiced];
+  MojoResult handle_results[kMaxServiced];
+
+  const MojoResult get_result =
+      MojoGetReadyHandles(wait_set_handle_.get().value(), &num_ready_handles,
+                          handles, handle_results, nullptr);
+  CHECK(get_result == MOJO_RESULT_OK || get_result == MOJO_RESULT_SHOULD_WAIT);
+  if (get_result != MOJO_RESULT_OK)
+    return false;
+
+  DCHECK(num_ready_handles);
+  DCHECK_LE(num_ready_handles, kMaxServiced);
+  // Do this in two steps, because notifying a handler may remove/add other
+  // handles that may have also been woken up.
+  // First, enumerate the IDs of the ready handles. Then, iterate over the
+  // handles and only take action if the ID hasn't changed.
+  // Since the size of this map is bounded by |kMaxServiced|, use a SmallMap to
+  // avoid the per-element allocation.
+  base::SmallMap<std::map<Handle, int>, kMaxServiced> ready_handles;
+  for (uint32_t i = 0; i < num_ready_handles; i++) {
+    const Handle handle = Handle(handles[i]);
+    // Skip the control handle. It's special.
+    if (handle.value() == read_handle_.get().value())
+      continue;
+    DCHECK(handle.is_valid());
+    const auto it = handlers_.find(handle);
+    // Skip handles that have been removed. This is possible because
+    // RemoveHandler() can be called with a handle that has been closed. Because
+    // the handle is closed, the MojoRemoveHandle() call in RemoveHandler()
+    // would have failed, but the handle is still in the wait set. Once the
+    // handle is retrieved using MojoGetReadyHandles(), it is implicitly removed
+    // from the set. The result is either the pending result that existed when
+    // the handle was closed, or |MOJO_RESULT_CANCELLED| to indicate that the
+    // handle was closed.
+    if (it == handlers_.end())
+      continue;
+    ready_handles[handle] = it->second.id;
+  }
+
+  for (uint32_t i = 0; i < num_ready_handles; i++) {
+    const Handle handle = Handle(handles[i]);
+
+    // If the handle has been removed, or it's ID has changed, skip over it.
+    // If the handle's ID has changed, and it still satisfies its signals,
+    // then it'll be caught in the next message pump iteration.
+    const auto it = handlers_.find(handle);
+    if ((handle.value() != read_handle_.get().value()) &&
+        (it == handlers_.end() || it->second.id != ready_handles[handle])) {
+      continue;
+    }
+
+    switch (handle_results[i]) {
+      case MOJO_RESULT_CANCELLED:
+      case MOJO_RESULT_FAILED_PRECONDITION:
+        DVLOG(1) << "Error: " << handle_results[i]
+                 << " handle: " << handle.value();
+        if (handle.value() == read_handle_.get().value()) {
+          // The Mojo EDK is shutting down. We can't just quit the message pump
+          // because that may cause the thread to quit, which causes the
+          // thread's MessageLoop to be destroyed, which races with any use of
+          // |Thread::task_runner()|. So instead, we enter a "dumb" mode which
+          // bypasses Mojo and just acts like a trivial message pump. That way,
+          // we can wait for the usual thread exiting mechanism to happen.
+          // The dumb mode is indicated by releasing the control pipe's read
+          // handle.
+          read_handle_.reset();
+        } else {
+          SignalHandleError(handle, handle_results[i]);
+        }
+        break;
+      case MOJO_RESULT_OK:
+        if (handle.value() == read_handle_.get().value()) {
+          DVLOG(1) << "Signaled control pipe";
+          // Control pipe was written to.
+          ReadMessageRaw(read_handle_.get(), nullptr, nullptr, nullptr, nullptr,
+                         MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+        } else {
+          DVLOG(1) << "Handle ready: " << handle.value();
+          SignalHandleReady(handle);
+        }
+        break;
+      default:
+        base::debug::Alias(&i);
+        base::debug::Alias(&handle_results[i]);
+        // Unexpected result is likely fatal, crash so we can determine cause.
+        CHECK(false);
+    }
+  }
+  return true;
+}
+
+bool MessagePumpMojo::RemoveExpiredHandles() {
+  bool removed = false;
+  // Notify and remove any handlers whose time has expired. First, iterate over
+  // the set of handles that have a deadline, and add the expired handles to a
+  // map of <Handle, id>. Then, iterate over those expired handles and remove
+  // them. The two-step process is because a handler can add/remove new
+  // handlers.
+  std::map<Handle, int> expired_handles;
+  const base::TimeTicks now(internal::NowTicks());
+  for (const Handle handle : deadline_handles_) {
+    const auto it = handlers_.find(handle);
+    // Expect any handle in |deadline_handles_| to also be in |handlers_| since
+    // the two are modified in lock-step.
+    DCHECK(it != handlers_.end());
+    if (!it->second.deadline.is_null() && it->second.deadline < now)
+      expired_handles[handle] = it->second.id;
+  }
+  for (const auto& pair : expired_handles) {
+    auto it = handlers_.find(pair.first);
+    // Don't need to check deadline again since it can't change if id hasn't
+    // changed.
+    if (it != handlers_.end() && it->second.id == pair.second) {
+      SignalHandleError(pair.first, MOJO_RESULT_DEADLINE_EXCEEDED);
+      removed = true;
+    }
+  }
+  return removed;
+}
+
+void MessagePumpMojo::SignalControlPipe() {
+  const MojoResult result =
+      WriteMessageRaw(write_handle_.get(), NULL, 0, NULL, 0,
+                      MOJO_WRITE_MESSAGE_FLAG_NONE);
+  if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+    // Mojo EDK is shutting down.
+    event_.Signal();
+    return;
+  }
+
+  // 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);
+}
+
+MojoDeadline MessagePumpMojo::GetDeadlineForWait(
+    const RunState& run_state) const {
+  const base::TimeTicks now(internal::NowTicks());
+  MojoDeadline deadline = TimeTicksToMojoDeadline(run_state.delayed_work_time,
+                                                  now);
+  for (const Handle handle : deadline_handles_) {
+    auto it = handlers_.find(handle);
+    DCHECK(it != handlers_.end());
+    deadline = std::min(
+        TimeTicksToMojoDeadline(it->second.deadline, now), deadline);
+  }
+  return deadline;
+}
+
+void MessagePumpMojo::SignalHandleReady(Handle handle) {
+  auto it = handlers_.find(handle);
+  DCHECK(it != handlers_.end());
+  MessagePumpMojoHandler* handler = it->second.handler;
+
+  WillSignalHandler();
+  handler->OnHandleReady(handle);
+  DidSignalHandler();
+}
+
+void MessagePumpMojo::SignalHandleError(Handle handle, MojoResult result) {
+  auto it = handlers_.find(handle);
+  DCHECK(it != handlers_.end());
+  MessagePumpMojoHandler* handler = it->second.handler;
+
+  RemoveHandler(handle);
+  WillSignalHandler();
+  handler->OnHandleError(handle, result);
+  DidSignalHandler();
+}
+
+void MessagePumpMojo::WillSignalHandler() {
+  FOR_EACH_OBSERVER(Observer, observers_, WillSignalHandler());
+}
+
+void MessagePumpMojo::DidSignalHandler() {
+  FOR_EACH_OBSERVER(Observer, observers_, DidSignalHandler());
+}
+
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/message_pump/message_pump_mojo.h b/mojo/message_pump/message_pump_mojo.h
new file mode 100644
index 0000000..ef2f55a
--- /dev/null
+++ b/mojo/message_pump/message_pump_mojo.h
@@ -0,0 +1,178 @@
+// 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_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_H_
+#define MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_H_
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <set>
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "base/message_loop/message_pump.h"
+#include "base/observer_list.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+#include "mojo/message_pump/mojo_message_pump_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace common {
+
+class MessagePumpMojoHandler;
+
+// Mojo implementation of MessagePump.
+class MOJO_MESSAGE_PUMP_EXPORT MessagePumpMojo : public base::MessagePump {
+ public:
+  class MOJO_MESSAGE_PUMP_EXPORT Observer {
+   public:
+    Observer() {}
+
+    virtual void WillSignalHandler() = 0;
+    virtual void DidSignalHandler() = 0;
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  MessagePumpMojo();
+  ~MessagePumpMojo() override;
+
+  // Static factory function (for using with |base::Thread::Options|, wrapped
+  // using |base::Bind()|).
+  static std::unique_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);
+
+  void AddObserver(Observer*);
+  void RemoveObserver(Observer*);
+
+  // MessagePump:
+  void Run(Delegate* delegate) override;
+  void Quit() override;
+  void ScheduleWork() override;
+  void ScheduleDelayedWork(const base::TimeTicks& delayed_work_time) override;
+
+ private:
+  struct RunState;
+
+  // 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;
+  };
+
+  struct HandleHasher {
+    size_t operator()(const Handle& handle) const {
+      return std::hash<uint32_t>()(static_cast<uint32_t>(handle.value()));
+    }
+  };
+
+  using HandleToHandler = std::unordered_map<Handle, Handler, HandleHasher>;
+
+  // 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. Returns |true| if a
+  // handle has become ready, |false| otherwise.
+  bool DoInternalWork(const RunState& run_state, bool block);
+
+  bool DoNonMojoWork(const RunState& run_state, bool block);
+
+  // Waits for handles in the wait set to become ready. Returns |true| if ready
+  // handles may be available, or |false| if the wait's deadline was exceeded.
+  // Note, ready handles may be unavailable, even though |true| was returned.
+  bool WaitForReadyHandles(const RunState& run_state) const;
+
+  // Retrieves any 'ready' handles from the wait set, and runs the handler's
+  // OnHandleReady() or OnHandleError() functions as necessary. Returns |true|
+  // if any handles were ready and processed.
+  bool ProcessReadyHandles();
+
+  // Removes any handles that have expired their deadline. Runs the handler's
+  // OnHandleError() function with |MOJO_RESULT_DEADLINE_EXCEEDED| as the
+  // result. Returns |true| if any handles were removed.
+  bool RemoveExpiredHandles();
+
+  void SignalControlPipe();
+
+  // Returns the deadline for the call to MojoWait().
+  MojoDeadline GetDeadlineForWait(const RunState& run_state) const;
+
+  // Run |OnHandleReady()| for the handler registered with |handle|. |handle|
+  // must be registered.
+  void SignalHandleReady(Handle handle);
+
+  // Run |OnHandleError()| for the handler registered with |handle| and the
+  // error code |result|. |handle| must be registered, and will be removed
+  // before calling |OnHandleError()|.
+  void SignalHandleError(Handle handle, MojoResult result);
+
+  void WillSignalHandler();
+  void DidSignalHandler();
+
+  // 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_;
+  // Set of handles that have a deadline set. Avoids iterating over all elements
+  // in |handles_| in the common case (no deadline set).
+  // TODO(amistry): Make this better and avoid special-casing deadlines.
+  std::set<Handle> deadline_handles_;
+
+  // 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_;
+
+  base::ObserverList<Observer> observers_;
+
+  // Mojo handle for the wait set.
+  ScopedHandle wait_set_handle_;
+  // Used to wake up run loop from |SignalControlPipe()|.
+  ScopedMessagePipeHandle read_handle_;
+  ScopedMessagePipeHandle write_handle_;
+
+  // Used to sleep until there is more work to do, when the Mojo EDK is shutting
+  // down.
+  base::WaitableEvent event_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePumpMojo);
+};
+
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_H_
diff --git a/mojo/message_pump/message_pump_mojo_handler.h b/mojo/message_pump/message_pump_mojo_handler.h
new file mode 100644
index 0000000..8beb9ba
--- /dev/null
+++ b/mojo/message_pump/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_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_HANDLER_H_
+#define MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_HANDLER_H_
+
+#include "mojo/message_pump/mojo_message_pump_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. In case of error, the handler will be removed.
+class MOJO_MESSAGE_PUMP_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_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_HANDLER_H_
diff --git a/mojo/message_pump/message_pump_mojo_unittest.cc b/mojo/message_pump/message_pump_mojo_unittest.cc
new file mode 100644
index 0000000..1abb27c
--- /dev/null
+++ b/mojo/message_pump/message_pump_mojo_unittest.cc
@@ -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.
+
+#include "mojo/message_pump/message_pump_mojo.h"
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop_test.h"
+#include "base/run_loop.h"
+#include "mojo/message_pump/message_pump_mojo_handler.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+std::unique_ptr<base::MessagePump> CreateMojoMessagePump() {
+  return std::unique_ptr<base::MessagePump>(new MessagePumpMojo());
+}
+
+RUN_MESSAGE_LOOP_TESTS(Mojo, &CreateMojoMessagePump);
+
+class CountingMojoHandler : public MessagePumpMojoHandler {
+ public:
+  CountingMojoHandler() : success_count_(0), error_count_(0) {}
+
+  void OnHandleReady(const Handle& handle) override {
+    ReadMessageRaw(static_cast<const MessagePipeHandle&>(handle),
+                   NULL,
+                   NULL,
+                   NULL,
+                   NULL,
+                   MOJO_READ_MESSAGE_FLAG_NONE);
+    ++success_count_;
+    if (success_count_ == success_callback_count_ &&
+        !success_callback_.is_null()) {
+      success_callback_.Run();
+      success_callback_.Reset();
+    }
+  }
+
+  void set_success_callback(const base::Closure& callback,
+                            int success_count) {
+    success_callback_ = callback;
+    success_callback_count_ = success_count;
+  }
+
+  void OnHandleError(const Handle& handle, MojoResult result) override {
+    ++error_count_;
+    if (!error_callback_.is_null()) {
+      error_callback_.Run();
+      error_callback_.Reset();
+    }
+  }
+
+  void set_error_callback(const base::Closure& callback) {
+    error_callback_ = callback;
+  }
+
+  int success_count() { return success_count_; }
+  int error_count() { return error_count_; }
+
+ private:
+  int success_count_;
+  int error_count_;
+
+  base::Closure error_callback_;
+  int success_callback_count_;
+
+  base::Closure success_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CountingMojoHandler);
+};
+
+class CountingObserver : public MessagePumpMojo::Observer {
+ public:
+  void WillSignalHandler() override { will_signal_handler_count++; }
+  void DidSignalHandler() override { did_signal_handler_count++; }
+
+  int will_signal_handler_count = 0;
+  int did_signal_handler_count = 0;
+};
+
+TEST(MessagePumpMojo, RunUntilIdle) {
+  base::MessageLoop message_loop(MessagePumpMojo::Create());
+  CountingMojoHandler handler;
+  base::RunLoop run_loop;
+  handler.set_success_callback(run_loop.QuitClosure(), 2);
+  MessagePipe handles;
+  MessagePumpMojo::current()->AddHandler(&handler,
+                                         handles.handle0.get(),
+                                         MOJO_HANDLE_SIGNAL_READABLE,
+                                         base::TimeTicks());
+  WriteMessageRaw(
+      handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+  WriteMessageRaw(
+      handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+  MojoHandleSignalsState hss;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(handles.handle0.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
+                      MOJO_DEADLINE_INDEFINITE, &hss));
+  run_loop.Run();
+  EXPECT_EQ(2, handler.success_count());
+}
+
+TEST(MessagePumpMojo, Observer) {
+  base::MessageLoop message_loop(MessagePumpMojo::Create());
+
+  CountingObserver observer;
+  MessagePumpMojo::current()->AddObserver(&observer);
+
+  CountingMojoHandler handler;
+  base::RunLoop run_loop;
+  handler.set_success_callback(run_loop.QuitClosure(), 1);
+  MessagePipe handles;
+  MessagePumpMojo::current()->AddHandler(&handler,
+                                         handles.handle0.get(),
+                                         MOJO_HANDLE_SIGNAL_READABLE,
+                                         base::TimeTicks());
+  WriteMessageRaw(
+      handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+
+  MojoHandleSignalsState hss;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(handles.handle0.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
+                      MOJO_DEADLINE_INDEFINITE, &hss));
+  run_loop.Run();
+  EXPECT_EQ(1, handler.success_count());
+  EXPECT_EQ(1, observer.will_signal_handler_count);
+  EXPECT_EQ(1, observer.did_signal_handler_count);
+  MessagePumpMojo::current()->RemoveObserver(&observer);
+
+  base::RunLoop run_loop2;
+  handler.set_success_callback(run_loop2.QuitClosure(), 2);
+  WriteMessageRaw(
+      handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK,
+            MojoWait(handles.handle0.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
+                      MOJO_DEADLINE_INDEFINITE, &hss));
+  run_loop2.Run();
+  EXPECT_EQ(2, handler.success_count());
+  EXPECT_EQ(1, observer.will_signal_handler_count);
+  EXPECT_EQ(1, observer.did_signal_handler_count);
+}
+
+TEST(MessagePumpMojo, UnregisterAfterDeadline) {
+  base::MessageLoop message_loop(MessagePumpMojo::Create());
+  CountingMojoHandler handler;
+  base::RunLoop run_loop;
+  handler.set_error_callback(run_loop.QuitClosure());
+  MessagePipe handles;
+  MessagePumpMojo::current()->AddHandler(
+      &handler,
+      handles.handle0.get(),
+      MOJO_HANDLE_SIGNAL_READABLE,
+      base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1));
+  run_loop.Run();
+  EXPECT_EQ(1, handler.error_count());
+}
+
+TEST(MessagePumpMojo, AddClosedHandle) {
+  base::MessageLoop message_loop(MessagePumpMojo::Create());
+  CountingMojoHandler handler;
+  MessagePipe handles;
+  Handle closed_handle = handles.handle0.get();
+  handles.handle0.reset();
+  MessagePumpMojo::current()->AddHandler(
+      &handler, closed_handle, MOJO_HANDLE_SIGNAL_READABLE, base::TimeTicks());
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+  MessagePumpMojo::current()->RemoveHandler(closed_handle);
+  EXPECT_EQ(0, handler.error_count());
+  EXPECT_EQ(0, handler.success_count());
+}
+
+TEST(MessagePumpMojo, CloseAfterAdding) {
+  base::MessageLoop message_loop(MessagePumpMojo::Create());
+  CountingMojoHandler handler;
+  MessagePipe handles;
+  MessagePumpMojo::current()->AddHandler(&handler, handles.handle0.get(),
+                                         MOJO_HANDLE_SIGNAL_READABLE,
+                                         base::TimeTicks());
+  handles.handle0.reset();
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+  EXPECT_EQ(1, handler.error_count());
+  EXPECT_EQ(0, handler.success_count());
+}
+
+}  // namespace test
+}  // namespace common
+}  // namespace mojo
diff --git a/mojo/message_pump/mojo_message_pump_export.h b/mojo/message_pump/mojo_message_pump_export.h
new file mode 100644
index 0000000..f8c1864
--- /dev/null
+++ b/mojo/message_pump/mojo_message_pump_export.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_MESSAGE_PUMP_MOJO_MESSAGE_PUMP_EXPORT_H_
+#define MOJO_MESSAGE_PUMP_MOJO_MESSAGE_PUMP_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_MESSAGE_PUMP_IMPLEMENTATION)
+#define MOJO_MESSAGE_PUMP_EXPORT __declspec(dllexport)
+#else
+#define MOJO_MESSAGE_PUMP_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_MESSAGE_PUMP_IMPLEMENTATION)
+#define MOJO_MESSAGE_PUMP_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_MESSAGE_PUMP_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_MESSAGE_PUMP_EXPORT
+#endif
+
+#endif  // MOJO_MESSAGE_PUMP_MOJO_MESSAGE_PUMP_EXPORT_H_
diff --git a/mojo/message_pump/time_helper.cc b/mojo/message_pump/time_helper.cc
new file mode 100644
index 0000000..ffd667e
--- /dev/null
+++ b/mojo/message_pump/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/message_pump/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/message_pump/time_helper.h b/mojo/message_pump/time_helper.h
new file mode 100644
index 0000000..6079000
--- /dev/null
+++ b/mojo/message_pump/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_MESSAGE_PUMP_TIME_HELPER_H_
+#define MOJO_MESSAGE_PUMP_TIME_HELPER_H_
+
+#include "base/time/time.h"
+#include "mojo/message_pump/mojo_message_pump_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_MESSAGE_PUMP_EXPORT void SetTickClockForTest(base::TickClock* clock);
+
+}  // namespace test
+
+namespace internal {
+
+// Returns now. Used internally; generally not useful.
+MOJO_MESSAGE_PUMP_EXPORT base::TimeTicks NowTicks();
+
+}  // namespace internal
+}  // namespace common
+}  // namespace mojo
+
+#endif  // MOJO_MESSAGE_PUMP_TIME_HELPER_H_
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp
new file mode 100644
index 0000000..1ef0c4d
--- /dev/null
+++ b/mojo/mojo.gyp
@@ -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.
+
+{
+  'targets': [
+    {
+      # GN version: //mojo
+      'target_name': 'mojo',
+      'type': 'none',
+      'dependencies': [
+        'mojo_base.gyp:mojo_base',
+        'mojo_edk_tests.gyp:mojo_edk_tests',
+        'mojo_public.gyp:mojo_public',
+      ],
+    },
+  ]
+}
diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp
new file mode 100644
index 0000000..a067353
--- /dev/null
+++ b/mojo/mojo_base.gyp
@@ -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.
+
+# 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.
+{
+  'variables': {
+    'chromium_code': 1,
+  },
+  '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',
+      ],
+      'conditions': [
+        ['OS == "android"', {
+          'dependencies': [
+            'mojo_public.gyp:mojo_bindings_java',
+            'mojo_public.gyp:mojo_public_java',
+          ],
+        }],
+      ]
+    },
+    {
+      'target_name': 'mojo_none',
+      'type': 'none',
+    },
+    {
+      # GN version: //mojo/common
+      'target_name': 'mojo_common_lib',
+      'type': '<(component)',
+      'defines': [
+        'MOJO_COMMON_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../mojo/mojo_public.gyp:mojo_public_system',
+      ],
+      'sources': [
+        'common/common_type_converters.cc',
+        'common/common_type_converters.h',
+        'common/data_pipe_file_utils.cc',
+        'common/data_pipe_utils.cc',
+        'common/data_pipe_utils.h',
+      ],
+    },
+    {
+      # GN version: //mojo/common:common_custom_types
+      'target_name': 'mojo_common_custom_types_mojom',
+      'type': 'none',
+      'variables': {
+        'mojom_files': [
+          'common/common_custom_types.mojom',
+        ],
+        'mojom_typemaps': [
+          'common/common_custom_types.typemap',
+        ],
+      },
+      'dependencies': [
+        '../ipc/ipc.gyp:ipc',
+      ],
+      'includes': [ 'mojom_bindings_generator_explicit.gypi' ],
+    },
+    {
+      # GN version: //mojo/common:test_common_custom_types
+      'target_name': 'mojo_test_common_custom_types',
+      'type': 'static_library',
+      'variables': {
+        'mojom_typemaps': [
+          'common/common_custom_types.typemap',
+        ],
+      },
+      'sources': [
+        'common/test_common_custom_types.mojom',
+      ],
+      'dependencies': [
+        'mojo_common_custom_types_mojom',
+      ],
+      'includes': [ 'mojom_bindings_generator.gypi' ],
+    },
+    {
+      # GN version: //mojo/common:mojo_common_unittests
+      'target_name': 'mojo_common_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:test_support_base',
+        '../base/base.gyp:base_message_loop_tests',
+        '../testing/gtest.gyp:gtest',
+        '../url/url.gyp:url_lib',
+        'mojo_common_custom_types_mojom',
+        'mojo_common_lib',
+        'mojo_test_common_custom_types',
+        'mojo_edk.gyp:mojo_system_impl',
+        'mojo_edk.gyp:mojo_common_test_support',
+        'mojo_edk.gyp:mojo_run_all_unittests',
+        'mojo_public.gyp:mojo_cpp_bindings',
+        'mojo_public.gyp:mojo_public_test_utils',
+      ],
+      'sources': [
+        'common/common_custom_types_unittest.cc',
+        'common/common_type_converters_unittest.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',
+          ],
+          'variables': {
+            'jni_gen_package': 'mojo',
+          },
+          'includes': [ '../build/jni_generator.gypi' ],
+        },
+        {
+          'target_name': 'libmojo_system_java',
+          'type': 'static_library',
+          'dependencies': [
+            '../base/base.gyp:base',
+            'mojo_common_lib',
+            'mojo_edk.gyp:mojo_system_impl',
+            'mojo_jni_headers',
+            'mojo_public.gyp:mojo_message_pump_lib',
+          ],
+          'sources': [
+            'android/system/core_impl.cc',
+            'android/system/core_impl.h',
+          ],
+        },
+        {
+          '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',
+            'libmojo_system_java',
+            'mojo_public.gyp:mojo_public_java',
+          ],
+          'variables': {
+            'java_in_dir': '<(DEPTH)/mojo/android/system',
+          },
+          'includes': [ '../build/java.gypi' ],
+        },
+      ],
+    }],
+    ['test_isolation_mode != "noop"', {
+      'targets': [
+        {
+          'target_name': 'mojo_common_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_common_unittests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_common_unittests.isolate',
+          ],
+        },
+      ],
+    }],
+  ]
+}
diff --git a/mojo/mojo_common_unittests.isolate b/mojo/mojo_common_unittests.isolate
new file mode 100644
index 0000000..a72311c
--- /dev/null
+++ b/mojo/mojo_common_unittests.isolate
@@ -0,0 +1,23 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'includes': [
+    '../base/base.isolate',
+  ],
+  'conditions': [
+    ['OS=="win" or OS=="mac" or OS=="linux"', {
+      'variables': {
+        'command': [
+          '../testing/test_env.py',
+          '<(PRODUCT_DIR)/mojo_common_unittests<(EXECUTABLE_SUFFIX)',
+          '--brave-new-test-launcher',
+          '--test-launcher-bot-mode',
+        ],
+        'files': [
+          '../testing/test_env.py',
+        ],
+      },
+    }],
+  ],
+}
diff --git a/mojo/mojo_edk.gyp b/mojo/mojo_edk.gyp
new file mode 100644
index 0000000..5541839
--- /dev/null
+++ b/mojo/mojo_edk.gyp
@@ -0,0 +1,233 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'chromium_code': 1,
+  },
+  'includes': [
+    'mojo_edk.gypi',
+  ],
+  'target_defaults' : {
+    'include_dirs': [
+      '..',
+    ],
+    'direct_dependent_settings': {
+      'include_dirs': [
+        '..',
+      ],
+    },
+  },
+  'targets': [
+    {
+      # GN version: //mojo/edk/system/ports
+      'target_name': 'mojo_system_ports',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../crypto/crypto.gyp:crypto',
+      ],
+      'sources': [
+        '<@(mojo_edk_ports_sources)',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/system
+      'target_name': 'mojo_system_impl',
+      'type': '<(component)',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../crypto/crypto.gyp:crypto',
+        'mojo_public.gyp:mojo_public_system',
+        'mojo_system_ports',
+      ],
+      'defines': [
+        'MOJO_SYSTEM_IMPL_IMPLEMENTATION',
+      ],
+      'sources': [
+        '<@(mojo_edk_system_impl_sources)',
+        '<@(mojo_edk_system_impl_non_nacl_sources)',
+      ],
+      'conditions': [
+        ['OS=="android"', {
+          'dependencies': [
+            '../third_party/ashmem/ashmem.gyp:ashmem',
+          ],
+        }],
+        ['OS=="android" or chromeos==1', {
+          'defines': [
+            'MOJO_EDK_LEGACY_PROTOCOL',
+          ],
+        }],
+        ['OS=="win"', {
+           # Structure was padded due to __declspec(align()), which is
+           # uninteresting.
+          'msvs_disabled_warnings': [ 4324 ],
+        }],
+        ['OS=="mac" and OS!="ios"', {
+          'sources': [
+            'edk/system/mach_port_relay.cc',
+            'edk/system/mach_port_relay.h',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/edk/js
+      'target_name': 'mojo_js_lib',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../gin/gin.gyp:gin',
+        '../v8/src/v8.gyp:v8',
+      ],
+      'export_dependent_settings': [
+        '../base/base.gyp:base',
+        '../gin/gin.gyp:gin',
+      ],
+      'sources': [
+        # Sources list duplicated in GN build.
+        'edk/js/core.cc',
+        'edk/js/core.h',
+        'edk/js/drain_data.cc',
+        'edk/js/drain_data.h',
+        'edk/js/handle.cc',
+        'edk/js/handle.h',
+        'edk/js/handle_close_observer.h',
+        'edk/js/mojo_runner_delegate.cc',
+        'edk/js/mojo_runner_delegate.h',
+        'edk/js/support.cc',
+        'edk/js/support.h',
+        'edk/js/threading.cc',
+        'edk/js/threading.h',
+        'edk/js/waiting_callback.cc',
+        'edk/js/waiting_callback.h',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/test:test_support_impl
+      'target_name': 'mojo_test_support_impl',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+      ],
+      'sources': [
+        'edk/test/test_support_impl.cc',
+        'edk/test/test_support_impl.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/mojo_test_base.cc',
+        'edk/test/mojo_test_base.h',
+        'edk/test/multiprocess_test_helper.cc',
+        'edk/test/multiprocess_test_helper.h',
+        'edk/test/scoped_ipc_support.cc',
+        'edk/test/scoped_ipc_support.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/edk/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_common_test_support',
+        'mojo_public.gyp:mojo_public_test_support',
+        'mojo_system_impl',
+        'mojo_test_support_impl',
+      ],
+      'sources': [
+        'edk/test/run_all_unittests.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/test:run_all_perftests
+      'target_name': 'mojo_run_all_perftests',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../base/base.gyp:test_support_base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_common_test_support',
+        'mojo_public.gyp:mojo_public_test_support',
+        'mojo_system_impl',
+        'mojo_test_support_impl',
+      ],
+      'sources': [
+        'edk/test/run_all_perftests.cc',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS == "win" and target_arch=="ia32"', {
+      'targets': [
+        {
+          # GN version: //mojo/edk/system/ports
+          'target_name': 'mojo_system_ports_win64',
+          'type': 'static_library',
+          'dependencies': [
+            '../base/base.gyp:base_win64',
+            '../crypto/crypto.gyp:crypto_nacl_win64',
+          ],
+          'sources': [
+            '<@(mojo_edk_ports_sources)',
+          ],
+          'configurations': {
+            'Common_Base': {
+              'msvs_target_platform': 'x64',
+            },
+          },
+        },
+        {
+          # GN version: //mojo/edk/system
+          'target_name': 'mojo_system_impl_win64',
+          'type': '<(component)',
+          'dependencies': [
+            '../base/base.gyp:base_win64',
+            '../crypto/crypto.gyp:crypto_nacl_win64',
+            'mojo_public.gyp:mojo_public_system_win64',
+            'mojo_system_ports_win64',
+          ],
+          'defines': [
+            'MOJO_SYSTEM_IMPL_IMPLEMENTATION',
+          ],
+          'sources': [
+            '<@(mojo_edk_system_impl_sources)',
+            '<@(mojo_edk_system_impl_non_nacl_sources)',
+          ],
+          # Structure was padded due to __declspec(align()), which is
+          # uninteresting.
+          'msvs_disabled_warnings': [ 4324 ],
+          'configurations': {
+            'Common_Base': {
+              'msvs_target_platform': 'x64',
+            },
+          },
+        },
+      ],
+    }],
+  ]
+}
diff --git a/mojo/mojo_edk_tests.gyp b/mojo/mojo_edk_tests.gyp
new file mode 100644
index 0000000..e192462
--- /dev/null
+++ b/mojo/mojo_edk_tests.gyp
@@ -0,0 +1,397 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'chromium_code': 1,
+  },
+  'targets': [
+    {
+      'target_name': 'mojo_edk_tests',
+      '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_message_pipe_perftests',
+        'mojo_public_bindings_perftests',
+        'mojo_public_bindings_unittests',
+        'mojo_public_system_perftests',
+        'mojo_public_system_unittests',
+        'mojo_system_unittests',
+        'mojo_js_unittests',
+        'mojo_js_integration_tests',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/test:mojo_public_bindings_unittests
+      'target_name': 'mojo_public_bindings_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../testing/gtest.gyp:gtest',
+        'mojo_edk.gyp:mojo_run_all_unittests',
+        'mojo_public.gyp:mojo_cpp_bindings',
+        'mojo_public.gyp:mojo_public_bindings_test_utils',
+        'mojo_public.gyp:mojo_public_test_utils',
+        'mojo_public_tests.gyp:mojo_public_test_associated_interfaces',
+        'mojo_public_tests.gyp:mojo_public_test_interfaces',
+        'mojo_public_tests.gyp:mojo_public_test_interfaces_blink',
+        'mojo_public_tests.gyp:mojo_public_test_interfaces_struct_traits',
+      ],
+      'variables': {
+        'clang_warning_flags_unset': [ '-Wglobal-constructors' ],
+      },
+      'sources': [
+        'public/cpp/bindings/tests/array_common_test.h',
+        'public/cpp/bindings/tests/array_unittest.cc',
+        'public/cpp/bindings/tests/associated_interface_unittest.cc',
+        'public/cpp/bindings/tests/bind_task_runner_unittest.cc',
+        'public/cpp/bindings/tests/binding_callback_unittest.cc',
+        'public/cpp/bindings/tests/binding_unittest.cc',
+        'public/cpp/bindings/tests/buffer_unittest.cc',
+        'public/cpp/bindings/tests/connector_unittest.cc',
+        'public/cpp/bindings/tests/constant_unittest.cc',
+        'public/cpp/bindings/tests/container_test_util.cc',
+        'public/cpp/bindings/tests/container_test_util.h',
+        'public/cpp/bindings/tests/equals_unittest.cc',
+        'public/cpp/bindings/tests/handle_passing_unittest.cc',
+        'public/cpp/bindings/tests/interface_ptr_unittest.cc',
+        'public/cpp/bindings/tests/map_common_test.h',
+        'public/cpp/bindings/tests/map_unittest.cc',
+        'public/cpp/bindings/tests/message_queue.cc',
+        'public/cpp/bindings/tests/message_queue.h',
+        'public/cpp/bindings/tests/multiplex_router_unittest.cc',
+        'public/cpp/bindings/tests/pickle_unittest.cc',
+        'public/cpp/bindings/tests/pickled_types_blink.cc',
+        'public/cpp/bindings/tests/pickled_types_blink.h',
+        'public/cpp/bindings/tests/pickled_types_chromium.cc',
+        'public/cpp/bindings/tests/pickled_types_chromium.h',
+        'public/cpp/bindings/tests/rect_blink.h',
+        'public/cpp/bindings/tests/rect_blink_traits.h',
+        'public/cpp/bindings/tests/rect_chromium.h',
+        'public/cpp/bindings/tests/rect_chromium_traits.h',
+        'public/cpp/bindings/tests/request_response_unittest.cc',
+        'public/cpp/bindings/tests/router_test_util.cc',
+        'public/cpp/bindings/tests/router_test_util.h',
+        '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/stl_converters_unittest.cc',
+        'public/cpp/bindings/tests/string_unittest.cc',
+        'public/cpp/bindings/tests/struct_traits_unittest.cc',
+        'public/cpp/bindings/tests/struct_unittest.cc',
+        'public/cpp/bindings/tests/struct_with_traits_impl.cc',
+        'public/cpp/bindings/tests/struct_with_traits_impl.h',
+        'public/cpp/bindings/tests/struct_with_traits_impl_traits.cc',
+        'public/cpp/bindings/tests/struct_with_traits_impl_traits.h',
+        'public/cpp/bindings/tests/sync_method_unittest.cc',
+        'public/cpp/bindings/tests/type_conversion_unittest.cc',
+        'public/cpp/bindings/tests/union_unittest.cc',
+        'public/cpp/bindings/tests/validation_context_unittest.cc',
+        'public/cpp/bindings/tests/validation_unittest.cc',
+        'public/cpp/bindings/tests/variant_test_util.h',
+      ],
+      'conditions': [
+        # TODO(yzshen): Blink-flavor bindings tests should be moved into
+        # mojo_public_bindings_for_blink_tests (which should eventually be moved
+        # into blink).
+        ['OS=="ios"', {
+          'dependencies!': [
+            'mojo_public.gyp:mojo_public_test_interfaces_blink',
+          ],
+          'sources!': [
+            'public/cpp/bindings/tests/pickle_unittest.cc',
+            'public/cpp/bindings/tests/pickled_types_blink.cc',
+            'public/cpp/bindings/tests/pickled_types_blink.h',
+            'public/cpp/bindings/tests/pickled_types_chromium.cc',
+            'public/cpp/bindings/tests/pickled_types_chromium.h',
+            'public/cpp/bindings/tests/rect_blink.h',
+            'public/cpp/bindings/tests/rect_blink_traits.h',
+            'public/cpp/bindings/tests/struct_traits_unittest.cc',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/bindings/tests:for_blink_tests
+      'target_name': 'mojo_public_bindings_for_blink_tests',
+      'type': 'static_library',
+      'dependencies': [
+        '../testing/gtest.gyp:gtest',
+        'mojo_public.gyp:mojo_cpp_bindings',
+        'mojo_public_tests.gyp:mojo_public_test_interfaces',
+        'mojo_public_tests.gyp:mojo_public_test_wtf_types',
+        'mojo_public_tests.gyp:mojo_public_test_wtf_types_blink',
+      ],
+      'variables': {
+         'clang_warning_flags_unset': [ '-Wglobal-constructors' ],
+      },
+      'sources': [
+        'public/cpp/bindings/tests/array_common_test.h',
+        'public/cpp/bindings/tests/container_test_util.cc',
+        'public/cpp/bindings/tests/container_test_util.h',
+        'public/cpp/bindings/tests/map_common_test.h',
+        'public/cpp/bindings/tests/variant_test_util.h',
+        'public/cpp/bindings/tests/wtf_array_unittest.cc',
+        'public/cpp/bindings/tests/wtf_map_unittest.cc',
+        'public/cpp/bindings/tests/wtf_types_unittest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/test:mojo_public_bindings_perftests
+      'target_name': 'mojo_public_bindings_perftests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:test_support_base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_edk.gyp:mojo_run_all_perftests',
+        'mojo_public.gyp:mojo_cpp_bindings',
+        'mojo_public.gyp:mojo_public_bindings_test_utils',
+        'mojo_public.gyp:mojo_public_test_utils',
+        'mojo_public_tests.gyp:mojo_public_test_interfaces',
+      ],
+      'sources': [
+        'public/cpp/bindings/tests/bindings_perftest.cc',
+        'public/cpp/bindings/tests/e2e_perftest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/system/tests:mojo_public_system_unittests
+      # and         //mojo/public/c/system/tests
+      'target_name': 'mojo_public_system_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../testing/gtest.gyp:gtest',
+        'mojo_edk.gyp:mojo_run_all_unittests',
+        'mojo_public.gyp:mojo_cpp_system',
+        'mojo_public.gyp:mojo_public_test_utils',
+      ],
+      'sources': [
+        '<(DEPTH)/mojo/public/c/system/tests/core_unittest.cc',
+        '<(DEPTH)/mojo/public/c/system/tests/core_unittest_pure_c.c',
+        '<(DEPTH)/mojo/public/c/system/tests/macros_unittest.cc',
+        '<(DEPTH)/mojo/public/cpp/system/tests/core_unittest.cc',
+        '<(DEPTH)/mojo/public/cpp/system/tests/watcher_unittest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/test:mojo_public_system_perftests
+      'target_name': 'mojo_public_system_perftests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_edk.gyp:mojo_run_all_perftests',
+        'mojo_public.gyp:mojo_public_system',
+        'mojo_public.gyp:mojo_public_test_utils',
+      ],
+      'sources': [
+        'public/c/system/tests/core_perftest.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/system:mojo_system_unittests
+      'target_name': 'mojo_system_unittests',
+      'type': '<(gtest_target_type)',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+        'mojo_edk.gyp:mojo_common_test_support',
+        'mojo_edk.gyp:mojo_run_all_unittests',
+        'mojo_edk.gyp:mojo_system_impl',
+        'mojo_edk.gyp:mojo_system_ports',
+        'mojo_public.gyp:mojo_public_system',
+      ],
+      'sources': [
+        'edk/embedder/embedder_unittest.cc',
+        'edk/embedder/platform_channel_pair_posix_unittest.cc',
+        'edk/embedder/platform_shared_buffer_unittest.cc',
+        'edk/system/awakable_list_unittest.cc',
+        'edk/system/core_test_base.cc',
+        'edk/system/core_test_base.h',
+        'edk/system/core_unittest.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/platform_wrapper_unittest.cc',
+        'edk/system/ports/ports_unittest.cc',
+        'edk/system/shared_buffer_dispatcher_unittest.cc',
+        'edk/system/shared_buffer_unittest.cc',
+        'edk/system/test_utils.cc',
+        'edk/system/test_utils.h',
+        'edk/system/wait_set_dispatcher_unittest.cc',
+        'edk/system/waiter_test_utils.cc',
+        'edk/system/waiter_test_utils.h',
+        'edk/system/waiter_unittest.cc',
+        'edk/system/watch_unittest.cc',
+      ],
+      'conditions': [
+        ['OS=="ios"', {
+          'sources!': [
+            'edk/system/multiprocess_message_pipe_unittest.cc',
+          ],
+        }],
+        ['OS == "android"', {
+          'dependencies': [
+            '../testing/android/native_test.gyp:native_test_native_code',
+          ],
+        }],
+      ],
+    },
+    {
+      # 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',
+        '../testing/gtest.gyp:gtest',
+        'mojo_edk.gyp:mojo_common_test_support',
+        'mojo_edk.gyp:mojo_run_all_perftests',
+        'mojo_edk.gyp:mojo_system_impl',
+        'mojo_public.gyp:mojo_public_system',
+      ],
+      'sources': [
+        'edk/system/message_pipe_perftest.cc',
+        'edk/system/test_utils.cc',
+        'edk/system/test_utils.h',
+      ],
+    },
+    # TODO(yzshen): fix the following two targets.
+    {
+      # GN version: //mojo/edk/js/test:js_unittests
+      'target_name': 'mojo_js_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../gin/gin.gyp:gin_test',
+        'mojo_edk.gyp:mojo_common_test_support',
+        'mojo_edk.gyp:mojo_run_all_unittests',
+        'mojo_edk.gyp:mojo_js_lib',
+        'mojo_public_tests.gyp:mojo_public_test_interfaces',
+      ],
+      'sources': [
+        'edk/js/handle_unittest.cc',
+        'edk/js/test/run_js_tests.cc',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/js/test:js_integration_tests
+      'target_name': 'mojo_js_integration_tests',
+      'type': 'executable',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../gin/gin.gyp:gin_test',
+        'mojo_base.gyp:mojo_common_lib',
+        'mojo_edk.gyp:mojo_js_lib',
+        'mojo_edk.gyp:mojo_run_all_unittests',
+        'mojo_js_to_cpp_bindings',
+        'mojo_public_tests.gyp:mojo_public_test_interfaces',
+      ],
+      'sources': [
+        'edk/js/test/run_js_integration_tests.cc',
+        'edk/js/tests/js_to_cpp_tests.cc',
+      ],
+    },
+    {
+      'target_name': 'mojo_js_to_cpp_bindings',
+      'type': 'none',
+      'variables': {
+        'mojom_files': [
+          'edk/js/tests/js_to_cpp.mojom',
+        ],
+      },
+      'includes': [ 'mojom_bindings_generator_explicit.gypi' ],
+    },
+  ],
+  'conditions': [
+    ['test_isolation_mode != "noop"', {
+      'targets': [
+        {
+          'target_name': 'mojo_public_bindings_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_public_bindings_unittests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_public_bindings_unittests.isolate',
+          ],
+        },
+        {
+          'target_name': 'mojo_public_system_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_public_system_unittests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_public_system_unittests.isolate',
+          ],
+        },
+        {
+          'target_name': 'mojo_js_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_js_unittests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_js_unittests.isolate',
+          ],
+        },
+        {
+          'target_name': 'mojo_js_integration_tests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_js_integration_tests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_js_integration_tests.isolate',
+          ],
+        },
+        {
+          'target_name': 'mojo_system_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'mojo_system_unittests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+          ],
+          'sources': [
+            'mojo_system_unittests.isolate',
+          ],
+        },
+      ],
+    }],
+    ['OS == "android"', {
+      'targets': [
+        {
+          'target_name': 'mojo_system_unittests_apk',
+          'type': 'none',
+          'dependencies': [
+            'mojo_system_unittests',
+          ],
+          'variables': {
+            'test_suite_name': 'mojo_system_unittests',
+          },
+          'includes': [ '../build/apk_test.gypi' ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/mojo/mojo_js_unittests.isolate b/mojo/mojo_js_unittests.isolate
new file mode 100644
index 0000000..81bae0b
--- /dev/null
+++ b/mojo/mojo_js_unittests.isolate
@@ -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.
+{
+  'includes': [
+    '../base/base.isolate',
+    '../gin/v8.isolate',
+    '../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',
+          'public/interfaces/bindings/tests/data/validation/',
+          'public/js/',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/math_calculator.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/no_module.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/ping_service.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/rect.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/regression_tests.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_factory.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_import.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_import2.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_service.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/scoping.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_constants.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_native_types.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_structs.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_sync_methods.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_unions.mojom.js',
+          '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.js',
+        ],
+      },
+    }],
+  ],
+}
diff --git a/mojo/mojo_public.gyp b/mojo/mojo_public.gyp
new file mode 100644
index 0000000..005e012
--- /dev/null
+++ b/mojo/mojo_public.gyp
@@ -0,0 +1,310 @@
+# Copyright 2014 The Chroium 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': [
+    'mojo_public.gypi',
+  ],
+  'variables': {
+    'chromium_code': 1,
+  },
+  'target_defaults' : {
+    'include_dirs': [
+      '..',
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'mojo_public',
+      'type': 'none',
+      'dependencies': [
+        'mojo_js_bindings',
+        'mojo_public_system',
+      ],
+    },
+    {
+      # GN version: //mojo/public/c/system
+      'target_name': 'mojo_public_system',
+      'type': '<(component)',
+      'sources': [
+        '<@(mojo_public_system_sources)',
+      ],
+      'defines': [
+        'MOJO_SYSTEM_IMPLEMENTATION',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/system
+      'target_name': 'mojo_cpp_system',
+      'type': 'static_library',
+      'sources': [
+        '<@(mojo_cpp_system_sources)',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        'mojo_public_system',
+      ],
+    },
+    {
+      # GN version: //mojo/public/cpp/bindings
+      'target_name': 'mojo_cpp_bindings',
+      'type': 'static_library',
+      'include_dirs': [
+        '..'
+      ],
+      'sources': [
+        '<@(mojo_cpp_bindings_sources)',
+
+        # This comes from the mojo_interface_bindings_cpp_sources dependency.
+        '>@(mojom_generated_sources)',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        'mojo_cpp_system',
+        'mojo_interface_bindings_cpp_sources',
+      ],
+    },
+    {
+      # GN version: //mojo/message_pump
+      'target_name': 'mojo_message_pump_lib',
+      'type': '<(component)',
+      'defines': [
+        'MOJO_MESSAGE_PUMP_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        'mojo_cpp_system',
+      ],
+      'sources': [
+        'message_pump/handle_watcher.cc',
+        'message_pump/handle_watcher.h',
+        'message_pump/message_pump_mojo.cc',
+        'message_pump/message_pump_mojo.h',
+        'message_pump/message_pump_mojo_handler.h',
+        'message_pump/time_helper.cc',
+        'message_pump/time_helper.h',
+      ],
+    },
+    {
+      # GN version: //mojo/public/js
+      'target_name': 'mojo_js_bindings',
+      'type': 'static_library',
+      'include_dirs': [
+        '..'
+      ],
+      'sources': [
+        'public/js/constants.cc',
+        'public/js/constants.h',
+      ],
+    },
+    {
+      'target_name': 'mojo_interface_bindings_mojom',
+      'type': 'none',
+      'variables': {
+        'require_interface_bindings': 0,
+        'mojom_files': [
+          'public/interfaces/bindings/interface_control_messages.mojom',
+          'public/interfaces/bindings/pipe_control_messages.mojom',
+        ],
+      },
+      'includes': [ 'mojom_bindings_generator_explicit.gypi' ],
+    },
+    {
+      'target_name': 'mojo_interface_bindings_cpp_sources',
+      'type': 'none',
+      'dependencies': [
+        'mojo_interface_bindings_mojom',
+      ],
+    },
+    {
+      # This target can be used to introduce a dependency on interface bindings
+      # generation without introducing any side-effects in the dependent
+      # target's configuration.
+      'target_name': 'mojo_interface_bindings_generation',
+      'type': 'none',
+      'dependencies': [
+        'mojo_interface_bindings_cpp_sources',
+      ],
+    },
+    {
+      # GN version: //mojo/public/c/test_support
+      'target_name': 'mojo_public_test_support',
+      'type': 'static_library',
+      'include_dirs': [
+        '..',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '..',
+        ],
+      },
+      'sources': [
+        'public/c/test_support/test_support.h',
+        # TODO(vtl): Convert this to thunks http://crbug.com/386799
+        'public/tests/test_support_private.cc',
+        'public/tests/test_support_private.h',
+      ],
+    },
+    {
+      # 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_public_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',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS == "android"', {
+      'targets': [
+        {
+          # GN version: //mojo/public/java:system
+          'target_name': 'mojo_public_java',
+          'type': 'none',
+          'variables': {
+            'chromium_code': 0,
+            'java_in_dir': 'public/java/system',
+          },
+          'includes': [ '../build/java.gypi' ],
+        },
+        {
+          'target_name': 'mojo_interface_bindings_java_sources',
+          'type': 'none',
+          'dependencies': [
+            'mojo_interface_bindings_mojom',
+          ],
+        },
+        {
+          # GN version: //mojo/public/java:bindings
+          'target_name': 'mojo_bindings_java',
+          'type': 'none',
+          'variables': {
+            'chromium_code': 0,
+            'java_in_dir': 'public/java/bindings',
+           },
+           'dependencies': [
+             'mojo_interface_bindings_java_sources',
+             'mojo_public_java',
+             '<(DEPTH)/base/base.gyp:base_java',
+           ],
+           'includes': [ '../build/java.gypi' ],
+        },
+      ],
+    }],
+    ['OS != "ios"', {
+      'targets': [
+        {
+          # TODO(yzshen): crbug.com/617718 Consider moving this into blink.
+          # GN version: //mojo/public/cpp/bindings:wtf_support
+          'target_name': 'mojo_cpp_bindings_wtf_support',
+          'type': 'static_library',
+          'include_dirs': [
+            '..'
+          ],
+          'sources': [
+            'public/cpp/bindings/array_traits_wtf.h',
+            'public/cpp/bindings/array_traits_wtf_vector.h',
+            'public/cpp/bindings/lib/string_traits_wtf.cc',
+            'public/cpp/bindings/lib/wtf_clone_equals_util.h',
+            'public/cpp/bindings/lib/wtf_serialization.h',
+            'public/cpp/bindings/map_traits_wtf.h',
+            'public/cpp/bindings/map_traits_wtf_hash_map.h',
+            'public/cpp/bindings/string_traits_wtf.h',
+            'public/cpp/bindings/wtf_array.h',
+            'public/cpp/bindings/wtf_map.h',
+          ],
+          'dependencies': [
+            'mojo_cpp_bindings',
+            '../third_party/WebKit/Source/config.gyp:config',
+            '../third_party/WebKit/Source/wtf/wtf.gyp:wtf',
+          ],
+          'export_dependent_settings': [
+            'mojo_cpp_bindings',
+            '../third_party/WebKit/Source/config.gyp:config',
+          ],
+        },
+      ],
+    }],
+    ['OS == "win" and target_arch=="ia32"', {
+      'targets': [
+        {
+          # GN version: //mojo/public/c/system
+          'target_name': 'mojo_public_system_win64',
+          'type': '<(component)',
+          'sources': [
+            '<@(mojo_public_system_sources)',
+          ],
+          'defines': [
+            'MOJO_SYSTEM_IMPLEMENTATION',
+          ],
+          'configurations': {
+            'Common_Base': {
+              'msvs_target_platform': 'x64',
+            },
+          },
+        },
+        {
+          # GN version: //mojo/public/cpp/system
+          'target_name': 'mojo_cpp_system_win64',
+          'type': 'static_library',
+          'sources': [
+            '<@(mojo_cpp_system_sources)',
+          ],
+          'dependencies': [
+            '../base/base.gyp:base_win64',
+            'mojo_public_system_win64',
+          ],
+          'configurations': {
+            'Common_Base': {
+              'msvs_target_platform': 'x64',
+            },
+          },
+        },
+        {
+          # GN version: //mojo/public/cpp/bindings
+          'target_name': 'mojo_cpp_bindings_win64',
+          'type': 'static_library',
+          'include_dirs': [
+            '..'
+          ],
+          'sources': [
+            '<@(mojo_cpp_bindings_sources)',
+
+            # This comes from the mojo_interface_bindings_cpp_sources dependency.
+            '>@(mojom_generated_sources)',
+          ],
+          'dependencies': [
+            '../base/base.gyp:base_win64',
+            'mojo_cpp_system_win64',
+            'mojo_interface_bindings_cpp_sources',
+          ],
+          'configurations': {
+            'Common_Base': {
+              'msvs_target_platform': 'x64',
+            },
+          },
+        },
+      ],
+    }],
+  ],
+}
diff --git a/mojo/mojo_public_bindings_unittests.isolate b/mojo/mojo_public_bindings_unittests.isolate
new file mode 100644
index 0000000..846bf78
--- /dev/null
+++ b/mojo/mojo_public_bindings_unittests.isolate
@@ -0,0 +1,24 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'includes': [
+    '../base/base.isolate',
+  ],
+  'conditions': [
+    ['OS=="win" or OS=="mac" or OS=="linux"', {
+      'variables': {
+        'command': [
+          '../testing/test_env.py',
+          '<(PRODUCT_DIR)/mojo_public_bindings_unittests<(EXECUTABLE_SUFFIX)',
+          '--brave-new-test-launcher',
+          '--test-launcher-bot-mode',
+        ],
+        'files': [
+          '../testing/test_env.py',
+          'public/interfaces/bindings/tests/data/validation/',
+        ],
+      },
+    }],
+  ],
+}
diff --git a/mojo/mojo_public_system_unittests.isolate b/mojo/mojo_public_system_unittests.isolate
new file mode 100644
index 0000000..f761bf7
--- /dev/null
+++ b/mojo/mojo_public_system_unittests.isolate
@@ -0,0 +1,23 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'includes': [
+    '../base/base.isolate',
+  ],
+  'conditions': [
+    ['OS=="win" or OS=="mac" or OS=="linux"', {
+      'variables': {
+        'command': [
+          '../testing/test_env.py',
+          '<(PRODUCT_DIR)/mojo_public_system_unittests<(EXECUTABLE_SUFFIX)',
+          '--brave-new-test-launcher',
+          '--test-launcher-bot-mode',
+        ],
+        'files': [
+          '../testing/test_env.py',
+        ],
+      },
+    }],
+  ],
+}
diff --git a/mojo/mojom_bindings_generator.gypi b/mojo/mojom_bindings_generator.gypi
new file mode 100644
index 0000000..c8c5f15
--- /dev/null
+++ b/mojo/mojom_bindings_generator.gypi
@@ -0,0 +1,178 @@
+# 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.
+
+{
+  'includes': [
+    'mojom_bindings_generator_variables.gypi',
+  ],
+  'variables': {
+    'variables': {
+      'variables': {
+        'for_blink%': 'false',
+        'use_new_wrapper_types%': 'false',
+      },
+      'for_blink%': '<(for_blink)',
+      'use_new_wrapper_types%': '<(use_new_wrapper_types)',
+      'conditions': [
+        ['for_blink=="true"', {
+          'mojom_output_languages%': 'c++',
+          'mojom_variant%': 'blink',
+          'mojom_generator_wtf_arg%': [
+            '--for_blink',
+          ],
+          'wtf_dependencies%': [
+            '<(DEPTH)/mojo/mojo_public.gyp:mojo_cpp_bindings_wtf_support',
+            '<(DEPTH)/third_party/WebKit/Source/wtf/wtf.gyp:wtf',
+          ],
+        }, {
+          'mojom_output_languages%': 'c++,javascript,java',
+          'mojom_variant%': 'none',
+          'mojom_generator_wtf_arg%': [],
+          'wtf_dependencies%': [],
+        }],
+        ['use_new_wrapper_types=="true"', {
+          'mojom_generator_new_wrappers_arg%': [
+            '--use_new_wrapper_types',
+          ],
+        }, {
+          'mojom_generator_new_wrappers_arg%': [],
+        }],
+      ],
+    },
+    'for_blink%': '<(for_blink)',
+    'use_new_wrapper_types%': '<(use_new_wrapper_types)',
+    'mojom_variant%': '<(mojom_variant)',
+    'mojom_generator_wtf_arg%': '<(mojom_generator_wtf_arg)',
+    'mojom_generator_new_wrappers_arg%': '<(mojom_generator_new_wrappers_arg)',
+    'wtf_dependencies%': '<(wtf_dependencies)',
+    'mojom_output_languages%': '<(mojom_output_languages)',
+    'mojom_typemaps%': [],
+    'mojom_base_output_dir':
+        '<!(python <(DEPTH)/build/inverse_depth.py <(DEPTH))',
+    'mojom_generated_outputs': [
+      '<!@(python <(DEPTH)/mojo/public/tools/bindings/mojom_list_outputs.py --basedir <(mojom_base_output_dir) --variant <(mojom_variant) <@(_sources))',
+    ],
+    'generated_typemap_file': '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(_target_name)_type_mappings',
+  },
+  'actions': [
+    {
+      'variables': {
+        'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+        'stamp_filename': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/<(_target_name).stamp',
+      },
+      'action_name': '<(_target_name)_mojom_bindings_stamp',
+      # The java output directory is deleted to ensure that the java library
+      # doesn't try to compile stale files.
+      'action': [
+        'python', '<(DEPTH)/build/rmdir_and_stamp.py',
+        '<(java_out_dir)',
+        '<(stamp_filename)',
+      ],
+      'inputs': [ '<@(_sources)' ],
+      'outputs': [ '<(stamp_filename)' ],
+    },
+    {
+      'variables': {
+        'output': '<(generated_typemap_file)',
+      },
+      'action_name': '<(_target_name)_type_mappings',
+      'action': [
+        'python', '<(DEPTH)/mojo/public/tools/bindings/generate_type_mappings.py',
+        '--output',
+        '<(output)',
+        '<!@(python <(DEPTH)/mojo/public/tools/bindings/format_typemap_generator_args.py <@(mojom_typemaps))',
+      ],
+      'inputs':[
+        '<(DEPTH)/mojo/public/tools/bindings/generate_type_mappings.py',
+      ],
+      'outputs': [ '<(output)' ],
+    },
+  ],
+  'rules': [
+    {
+      'rule_name': '<(_target_name)_mojom_bindings_generator',
+      'extension': 'mojom',
+      'variables': {
+        'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+        'mojom_import_args%': [
+         '-I<(DEPTH)',
+         '-I<(DEPTH)/mojo/services',
+        ],
+        'stamp_filename': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/<(_target_name).stamp',
+      },
+      'inputs': [
+        '<@(mojom_bindings_generator_sources)',
+        '<(stamp_filename)',
+        '<(generated_typemap_file)',
+        '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/cpp_templates.zip',
+        '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/java_templates.zip',
+        '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/js_templates.zip',
+      ],
+      'conditions': [
+        ['mojom_variant=="none"', {
+          '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-internal.h',
+          ]
+        }, {
+          'outputs': [
+            '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-<(mojom_variant).cc',
+            '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-<(mojom_variant).h',
+            '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-<(mojom_variant)-internal.h',
+          ],
+        }]
+      ],
+      'action': [
+        'python', '<@(mojom_bindings_generator)',
+        '--use_bundled_pylibs', 'generate',
+        './<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+        '-d', '<(DEPTH)',
+        '<@(mojom_import_args)',
+        '-o', '<(SHARED_INTERMEDIATE_DIR)',
+        '--java_output_directory=<(java_out_dir)',
+        '--variant', '<(mojom_variant)',
+        '-g', '<(mojom_output_languages)',
+        '--typemap',
+        '<(generated_typemap_file)',
+        '<@(mojom_generator_wtf_arg)',
+        '<@(mojom_generator_new_wrappers_arg)',
+        '--bytecode_path',
+        '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings',
+      ],
+      'message': 'Generating Mojo bindings from <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+      'process_outputs_as_sources': 1,
+    }
+  ],
+  'dependencies': [
+    '<(DEPTH)/base/base.gyp:base',
+    '<(DEPTH)/mojo/mojo_public.gyp:mojo_interface_bindings_generation',
+    '<(DEPTH)/mojo/public/tools/bindings/bindings.gyp:precompile_mojom_bindings_generator_templates',
+    '<@(wtf_dependencies)',
+  ],
+  'export_dependent_settings': [
+    '<@(wtf_dependencies)',
+  ],
+  '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',
+      ],
+      'additional_input_paths': [
+        '<@(mojom_bindings_generator_sources)',
+        '<@(_sources)',
+      ],
+    },
+  },
+  'hard_dependency': 1,
+}
diff --git a/mojo/mojom_bindings_generator_explicit.gypi b/mojo/mojom_bindings_generator_explicit.gypi
new file mode 100644
index 0000000..51676e9
--- /dev/null
+++ b/mojo/mojom_bindings_generator_explicit.gypi
@@ -0,0 +1,195 @@
+# 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.
+
+{
+  'includes': [
+    'mojom_bindings_generator_variables.gypi',
+  ],
+  'variables': {
+    'variables': {
+      'variables': {
+        'for_blink%': 'false',
+        'use_new_wrapper_types%': 'false',
+      },
+      'for_blink%': 'false',
+      'use_new_wrapper_types%': 'false',
+      'conditions': [
+        ['for_blink=="true"', {
+          'mojom_output_languages%': 'c++',
+          'mojom_variant%': 'blink',
+          'mojom_generator_wtf_arg%': [
+            '--for_blink',
+          ],
+          'wtf_dependencies%': [
+            '<(DEPTH)/mojo/mojo_public.gyp:mojo_cpp_bindings_wtf_support',
+            '<(DEPTH)/third_party/WebKit/Source/wtf/wtf.gyp:wtf',
+          ],
+        }, {
+          'mojom_output_languages%': 'c++,javascript,java',
+          'mojom_variant%': 'none',
+          'mojom_generator_wtf_arg%': [],
+          'wtf_dependencies%': [],
+        }],
+        ['use_new_wrapper_types=="true"', {
+          'mojom_generator_new_wrappers_arg%': [
+            '--use_new_wrapper_types',
+          ],
+        }, {
+          'mojom_generator_new_wrappers_arg%': [],
+        }],
+      ],
+    },
+    'for_blink%': '<(for_blink)',
+    'use_new_wrapper_types%': '<(use_new_wrapper_types)',
+    'mojom_variant%': '<(mojom_variant)',
+    'mojom_generator_wtf_arg%': '<(mojom_generator_wtf_arg)',
+    'mojom_generator_new_wrappers_arg%': '<(mojom_generator_new_wrappers_arg)',
+    'wtf_dependencies%': '<(wtf_dependencies)',
+    'mojom_output_languages%': '<(mojom_output_languages)',
+    'mojom_typemaps%': [],
+    'mojom_base_output_dir':
+        '<!(python <(DEPTH)/build/inverse_depth.py <(DEPTH))',
+    'mojom_generated_outputs': [
+      '<!@(python <(DEPTH)/mojo/public/tools/bindings/mojom_list_outputs.py --basedir <(mojom_base_output_dir) --variant <(mojom_variant) <@(mojom_files))',
+    ],
+    'generated_typemap_file': '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(_target_name)_type_mappings',
+    'mojom_include_path%': '<(DEPTH)',
+    'require_interface_bindings%': 1,
+  },
+  # Given mojom files as inputs, generate sources.  These sources will be
+  # exported to another target (via dependent_settings) to be compiled.  This
+  # keeps code generation separate from compilation, allowing the same sources
+  # to be compiled with multiple toolchains - target, NaCl, etc.
+  'actions': [
+    {
+      'variables': {
+        'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+        'stamp_filename': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/<(_target_name).stamp',
+      },
+      'action_name': '<(_target_name)_mojom_bindings_stamp',
+      # The java output directory is deleted to ensure that the java library
+      # doesn't try to compile stale files.
+      'action': [
+        'python', '<(DEPTH)/build/rmdir_and_stamp.py',
+        '<(java_out_dir)',
+        '<(stamp_filename)',
+      ],
+      'inputs': [
+        '<@(mojom_files)',
+      ],
+      'outputs': [ '<(stamp_filename)' ],
+    },
+    {
+      'variables': {
+        'output': '<(generated_typemap_file)',
+      },
+      'action_name': '<(_target_name)_type_mappings',
+      'action': [
+        'python', '<(DEPTH)/mojo/public/tools/bindings/generate_type_mappings.py',
+        '--output',
+        '<(output)',
+        '<!@(python <(DEPTH)/mojo/public/tools/bindings/format_typemap_generator_args.py <@(mojom_typemaps))',
+      ],
+      'inputs':[
+        '<(DEPTH)/mojo/public/tools/bindings/generate_type_mappings.py',
+      ],
+      'outputs': [ '<(output)' ],
+    },
+    {
+      'action_name': '<(_target_name)_mojom_bindings_generator',
+      'variables': {
+        'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+        'stamp_filename': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/<(_target_name).stamp',
+        'mojom_import_args%': [
+         '-I<(DEPTH)',
+         '-I<(DEPTH)/mojo/services',
+         '-I<(mojom_include_path)',
+        ],
+      },
+      'inputs': [
+        '<@(mojom_bindings_generator_sources)',
+        '<@(mojom_files)',
+        '<(stamp_filename)',
+        '<(generated_typemap_file)',
+        '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/cpp_templates.zip',
+        '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/java_templates.zip',
+        '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/js_templates.zip',
+      ],
+      'outputs': [
+        '<@(mojom_generated_outputs)',
+      ],
+      'action': [
+        'python', '<@(mojom_bindings_generator)',
+        '--use_bundled_pylibs', 'generate',
+        '<@(mojom_files)',
+        '-d', '<(DEPTH)',
+        '<@(mojom_import_args)',
+        '-o', '<(SHARED_INTERMEDIATE_DIR)',
+        '--java_output_directory=<(java_out_dir)',
+        '--variant', '<(mojom_variant)',
+        '-g', '<(mojom_output_languages)',
+        '--typemap',
+        '<(generated_typemap_file)',
+        '<@(mojom_generator_wtf_arg)',
+        '<@(mojom_generator_new_wrappers_arg)',
+        '--bytecode_path',
+        '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings',
+      ],
+      'message': 'Generating Mojo bindings from <@(mojom_files)',
+    }
+  ],
+  'conditions': [
+    ['require_interface_bindings==1', {
+      'dependencies': [
+        '<(DEPTH)/base/base.gyp:base',
+        '<(DEPTH)/mojo/mojo_public.gyp:mojo_interface_bindings_generation',
+      ],
+    }],
+  ],
+  'dependencies': [
+    '<(DEPTH)/mojo/public/tools/bindings/bindings.gyp:precompile_mojom_bindings_generator_templates',
+    '<@(wtf_dependencies)',
+  ],
+  'export_dependent_settings': [
+    '<@(wtf_dependencies)',
+  ],
+  # Prevent the generated sources from being injected into the "all" target by
+  # preventing the code generator from being directly depended on by the "all"
+  # target.
+  'suppress_wildcard': '1',
+  'hard_dependency': '1',
+  'direct_dependent_settings': {
+    # A target directly depending on this action will compile the generated
+    # sources.
+    'sources': [
+      '<@(mojom_generated_outputs)',
+    ],
+    # Include paths needed to compile the generated sources into a library.
+    'include_dirs': [
+      '<(DEPTH)',
+      '<(SHARED_INTERMEDIATE_DIR)',
+    ],
+    # Make sure the generated header files are available for any static library
+    # that depends on a static library that depends on this generator.
+    'hard_dependency': 1,
+    'direct_dependent_settings': {
+      # Include paths needed to find the generated header files and their
+      # transitive dependancies when using the library.
+      'include_dirs': [
+        '<(DEPTH)',
+        '<(SHARED_INTERMEDIATE_DIR)',
+      ],
+      'variables': {
+        'generated_src_dirs': [
+          '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+        ],
+        'additional_input_paths': [
+          '<@(mojom_bindings_generator_sources)',
+          '<@(mojom_files)',
+        ],
+        'mojom_generated_sources': [ '<@(mojom_generated_outputs)' ],
+      },
+    }
+  },
+}
diff --git a/mojo/mojom_bindings_generator_variables.gypi b/mojo/mojom_bindings_generator_variables.gypi
new file mode 100644
index 0000000..9e3c400
--- /dev/null
+++ b/mojo/mojom_bindings_generator_variables.gypi
@@ -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.
+
+{
+  'variables': {
+    'mojom_bindings_generator':
+        '<(DEPTH)/mojo/public/tools/bindings/mojom_bindings_generator.py',
+    'mojom_bindings_generator_sources': [
+      '<(mojom_bindings_generator)',
+      '<(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/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',
+    ]
+  }
+}
diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn
new file mode 100644
index 0000000..9442479
--- /dev/null
+++ b/mojo/public/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.
+
+group("public") {
+  # Meta-target, don't link into production code.
+  testonly = true
+  deps = [
+    ":sdk",
+    "cpp/bindings",
+    "interfaces/bindings/tests:test_interfaces",
+  ]
+
+  if (is_android) {
+    deps += [
+      "java:bindings",
+      "java:system",
+    ]
+  }
+}
+
+group("sdk") {
+  deps = [
+    "c/system",
+    "cpp/bindings",
+    "interfaces/bindings",
+    "js",
+  ]
+}
diff --git a/mojo/public/DEPS b/mojo/public/DEPS
new file mode 100644
index 0000000..2e49995
--- /dev/null
+++ b/mojo/public/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+  # This code is checked into the chromium repo so it's fine to depend on this.
+  "+base",
+  "+build",
+  "+testing",
+
+  "+ipc/ipc_param_traits.h",
+
+  # internal includes.
+  "+mojo",
+]
diff --git a/mojo/public/LICENSE b/mojo/public/LICENSE
new file mode 100644
index 0000000..972bb2e
--- /dev/null
+++ b/mojo/public/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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/README.md b/mojo/public/c/README.md
new file mode 100644
index 0000000..0b022fc
--- /dev/null
+++ b/mojo/public/c/README.md
@@ -0,0 +1,22 @@
+Mojo Public C API
+=================
+
+This directory contains C language bindings for the Mojo Public API.
+
+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/system/BUILD.gn b/mojo/public/c/system/BUILD.gn
new file mode 100644
index 0000000..3ec12f6
--- /dev/null
+++ b/mojo/public/c/system/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+component("system") {
+  output_name = "mojo_public_system"
+
+  sources = [
+    "buffer.h",
+    "core.h",
+    "data_pipe.h",
+    "functions.h",
+    "macros.h",
+    "message_pipe.h",
+    "platform_handle.h",
+    "system_export.h",
+    "thunks.cc",
+    "thunks.h",
+    "types.h",
+    "wait_set.h",
+  ]
+
+  defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
+}
+
+# This should ONLY be depended upon directly by shared_library targets which
+# need to export the MojoSetSystemThunks symbol, like targets generated by the
+# mojo_native_application template in //mojo/public/mojo_application.gni.
+source_set("set_thunks_for_app") {
+  sources = [
+    "set_thunks_for_app.cc",
+  ]
+
+  public_deps = [
+    ":system",
+  ]
+}
diff --git a/mojo/public/c/system/buffer.h b/mojo/public/c/system/buffer.h
new file mode 100644
index 0000000..0f02737
--- /dev/null
+++ b/mojo/public/c/system/buffer.h
@@ -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.
+
+// 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 <stdint.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_STATIC_ASSERT(MOJO_ALIGNOF(int64_t) == 8, "int64_t has weird alignment");
+struct MOJO_ALIGNAS(8) MojoCreateSharedBufferOptions {
+  uint32_t struct_size;
+  MojoCreateSharedBufferOptionsFlags flags;
+};
+MOJO_STATIC_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.
+//       |MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY|: The duplicate
+//       shared buffer can only be mapped read-only. A read-only duplicate can
+//       only be created before the buffer is passed over a message pipe.
+//
+// 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;
+const MojoDuplicateBufferHandleOptionsFlags
+    MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY = 1 << 0;
+#else
+#define MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE \
+  ((MojoDuplicateBufferHandleOptionsFlags)0)
+#define MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY \
+  ((MojoDuplicateBufferHandleOptionsFlags)1 << 0)
+#endif
+
+struct MojoDuplicateBufferHandleOptions {
+  uint32_t struct_size;
+  MojoDuplicateBufferHandleOptionsFlags flags;
+};
+MOJO_STATIC_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 other pointer inside
+// the mapped memory), and the entire mapping will be removed (partial unmapping
+// is not supported). A mapping may only be unmapped 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..3775e90
--- /dev/null
+++ b/mojo/public/c/system/core.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.
+
+// 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/platform_handle.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/c/system/wait_set.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..4a7dcef
--- /dev/null
+++ b/mojo/public/c/system/data_pipe.h
@@ -0,0 +1,368 @@
+// Copyright 2014 The Chromium 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 <stdint.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.
+//   |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;
+#else
+#define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE \
+  ((MojoCreateDataPipeOptionsFlags)0)
+#endif
+
+MOJO_STATIC_ASSERT(MOJO_ALIGNOF(int64_t) == 8, "int64_t has weird alignment");
+struct MOJO_ALIGNAS(8) MojoCreateDataPipeOptions {
+  MOJO_ALIGNAS(4) uint32_t struct_size;
+  MOJO_ALIGNAS(4) MojoCreateDataPipeOptionsFlags flags;
+  MOJO_ALIGNAS(4) uint32_t element_num_bytes;
+  MOJO_ALIGNAS(4) uint32_t capacity_num_bytes;
+};
+MOJO_STATIC_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.
+//   |MOJO_READ_DATA_FLAG_PEEK| - Read elements without removing them. For use
+//       with |MojoReadData()| only. Mutually exclusive with
+//       |MOJO_READ_DATA_FLAG_DISCARD| and |MOJO_READ_DATA_FLAG_QUERY|.
+
+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;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_PEEK = 1 << 3;
+#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)
+#define MOJO_READ_DATA_FLAG_PEEK ((MojoReadDataFlags)1 << 3)
+#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.
+//
+// When |MojoBeginWriteData()| returns MOJO_RESULT_OK, and the caller has
+// finished writing data to |*buffer|, it should call |MojoEndWriteData()| to
+// specify the amount written and to complete the two-phase write.
+// |MojoEndWriteData()| need not be called for other return values.
+//
+// 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. Additionally, if flags has
+// |MOJO_READ_DATA_FLAG_PEEK| set, the data read will remain in the pipe and be
+// available to future reads.
+//
+// If flags has |MOJO_READ_DATA_FLAG_DISCARD| set, it discards up to
+// |*num_bytes| (which again must 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|, |MOJO_READ_DATA_FLAG_QUERY|, or
+// |MOJO_READ_DATA_FLAG_PEEK| 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..f0a23d9
--- /dev/null
+++ b/mojo/public/c/system/functions.h
@@ -0,0 +1,223 @@
+// Copyright 2014 The Chromium 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_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// A callback used to notify watchers registered via |MojoWatch()|. Called when
+// some watched signals are satisfied or become unsatisfiable. See the
+// documentation for |MojoWatch()| for more details.
+typedef void (*MojoWatchCallback)(uintptr_t context,
+                                  MojoResult result,
+                                  struct MojoHandleSignalsState signals_state,
+                                  MojoWatchNotificationFlags flags);
+
+// 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.)
+
+// Returns the time, in microseconds, since some undefined point in the past.
+// The values are only meaningful relative to other values that were obtained
+// from the same device without an intervening system restart. Such values are
+// guaranteed to be monotonically non-decreasing with the passage of real time.
+// Although the units are microseconds, the resolution of the clock may vary and
+// is typically in the range of ~1-15 ms.
+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 one of the following happens:
+//   - 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.)
+//   - 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.
+//
+// |signals_state| (optional): See documentation for |MojoHandleSignalsState|.
+//
+// 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). The |signals_state| value is unchanged.
+//   |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 satisfied, all waiters
+// will be awoken.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoWait(MojoHandle handle,
+         MojoHandleSignals signals,
+         MojoDeadline deadline,
+         struct MojoHandleSignalsState* signals_state);  // Optional out.
+
+// Waits on |handles[0]|, ..., |handles[num_handles-1]| until:
+//   - (At least) one handle 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.
+//   - |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|.
+//
+// |result_index| (optional) is used to return the index of the handle that
+//     caused the call to return. For example, the index |i| (from 0 to
+//     |num_handles-1|) if |handle[i]| satisfies a signal from |signals[i]|. You
+//     must manually initialize this to a suitable sentinel value (e.g. -1)
+//     before you make this call because this value is not updated if there is
+//     no specific handle that causes the function to return. Pass null if you
+//     don't need this value to be returned.
+//
+// |signals_states| (optional) points to an array of size |num_handles| of
+//     MojoHandleSignalsState. See |MojoHandleSignalsState| for more details
+//     about the meaning of each array entry. This array is not an atomic
+//     snapshot. The array will be updated if the function does not return
+//     |MOJO_RESULT_INVALID_ARGUMENT| or |MOJO_RESULT_RESOURCE_EXHAUSTED|.
+//
+// Returns:
+//   |MOJO_RESULT_CANCELLED| if some |handle[i]| was closed (necessarily from
+//       another thread) during the wait.
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if there are too many handles. The
+//       |signals_state| array is unchanged.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some |handle[i]| is not a valid handle
+//       (e.g., if it is zero or if it has already been closed). The
+//       |signals_state| array is unchanged.
+//   |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,
+             uint32_t* result_index,                          // Optional out
+             struct MojoHandleSignalsState* signals_states);  // Optional out
+
+// Watches the given handle for one of the following events to happen:
+//   - 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.)
+//   - The handle is closed.
+//
+// |handle|: The handle to watch. Must be an open message pipe or data pipe
+//     handle.
+// |signals|: The signals to watch for.
+// |callback|: A function to be called any time one of the above events happens.
+//     The function must be safe to call from any thread at any time.
+// |context|: User-provided context passed to |callback| when called. |context|
+//     is used to uniquely identify a registered watch and can be used to cancel
+//     the watch later using |MojoCancelWatch()|.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if the watch has been successfully registered. Note that
+//       if the signals are already satisfied this may synchronously invoke
+//       |callback| before returning.
+//   |MOJO_RESULT_CANCELLED| if the watch was cancelled. In this case it is not
+//       necessary to explicitly call |MojoCancelWatch()|, and in fact it may be
+//       an error to do so as the handle may have been closed.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not an open message pipe
+//       handle.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if it is already known that |signals| can
+//       never be satisfied.
+//   |MOJO_RESULT_ALREADY_EXISTS| if there is already a watch registered for
+//       the same combination of |handle| and |context|.
+//
+// Callback result codes:
+//   The callback may be called at any time on any thread with one of the
+//   following result codes to indicate various events:
+//
+//   |MOJO_RESULT_OK| indicates that some signal in |signals| has been
+//       satisfied.
+//   |MOJO_RESULT_FAILED_PRECONDITION| indicates that no signals in |signals|
+//       can ever be satisfied again.
+//   |MOJO_RESULT_CANCELLED| indicates that the handle has been closed. In this
+//       case the watch is implicitly cancelled and there is no need to call
+//       |MojoCancelWatch()|.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoWatch(MojoHandle handle,
+          MojoHandleSignals signals,
+          MojoWatchCallback callback,
+          uintptr_t context);
+
+// Cancels a handle watch corresponding to some prior call to |MojoWatch()|.
+//
+// NOTE: If the watch callback corresponding to |context| is currently running
+// this will block until the callback completes execution. It is therefore
+// illegal to call |MojoCancelWatch()| on a given |handle| and |context| from
+// within the associated callback itself, as this will always deadlock.
+//
+// After |MojoCancelWatch()| function returns, the watch's associated callback
+// will NEVER be called again by Mojo.
+//
+// |context|: The same user-provided context given to some prior call to
+//     |MojoWatch()|. Only the watch corresponding to this context will be
+//     cancelled.
+//
+// Returns:
+//     |MOJO_RESULT_OK| if the watch corresponding to |context| was cancelled.
+//     |MOJO_RESULT_INVALID_ARGUMENT| if no watch was registered with |context|
+//         for the given |handle|, or if |handle| is invalid.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoCancelWatch(MojoHandle handle, uintptr_t context);
+
+// Retrieves system properties. See the documentation for |MojoPropertyType| for
+// supported property types and their corresponding output value type.
+//
+// Returns:
+//     |MOJO_RESULT_OK| on success.
+//     |MOJO_RESULT_INVALID_ARGUMENT| if |type| is not recognized. In this case,
+//         |value| is untouched.
+MOJO_SYSTEM_EXPORT MojoResult MojoGetProperty(MojoPropertyType type,
+                                              void* value);
+
+#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..917c69c
--- /dev/null
+++ b/mojo/public/c/system/macros.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_C_SYSTEM_MACROS_H_
+#define MOJO_PUBLIC_C_SYSTEM_MACROS_H_
+
+#include <stddef.h>
+
+// 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_STATIC_ASSERT(sizeof(Foo) == 12, "Foo has invalid size");
+#if defined(__cplusplus)
+#define MOJO_STATIC_ASSERT(expr, msg) static_assert(expr, msg)
+#else
+#define MOJO_STATIC_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..65d0837
--- /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 application_request) {
+//   ...
+// }
+//
+// 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 application_request);
+#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..b759bc7
--- /dev/null
+++ b/mojo/public/c/system/message_pipe.h
@@ -0,0 +1,341 @@
+// Copyright 2014 The Chromium 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 <stdint.h>
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoMessageHandle|: Used to refer to message objects created by
+//     |MojoAllocMessage()| and transferred by |MojoWriteMessageNew()| or
+//     |MojoReadMessageNew()|.
+
+typedef uintptr_t MojoMessageHandle;
+
+#ifdef __cplusplus
+const MojoMessageHandle MOJO_MESSAGE_HANDLE_INVALID = 0;
+#else
+#define MOJO_MESSAGE_HANDLE_INVALID ((MojoMessageHandle)0)
+#endif
+
+// |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|: Used to specify different modes
+//       of operation.
+//       |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_STATIC_ASSERT(MOJO_ALIGNOF(int64_t) == 8, "int64_t has weird alignment");
+struct MOJO_ALIGNAS(8) MojoCreateMessagePipeOptions {
+  uint32_t struct_size;
+  MojoCreateMessagePipeOptionsFlags flags;
+};
+MOJO_STATIC_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
+
+// |MojoAllocMessageFlags|: Used to specify different options for
+// |MojoAllocMessage()|.
+//   |MOJO_ALLOC_MESSAGE_FLAG_NONE| - No flags; default mode.
+
+typedef uint32_t MojoAllocMessageFlags;
+
+#ifdef __cplusplus
+const MojoAllocMessageFlags MOJO_ALLOC_MESSAGE_FLAG_NONE = 0;
+#else
+#define MOJO_ALLOC_MESSAGE_FLAG_NONE ((MojoAllocMessageFlags)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.
+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, the handles will no longer be valid (on success 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 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);
+
+// Writes a message to the message pipe endpoint given by |message_pipe_handle|.
+//
+// |message|: A message object allocated by |MojoAllocMessage()|. Ownership of
+//     the message is passed into Mojo.
+//
+// Returns results corresponding to |MojoWriteMessage()| above.
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoWriteMessageNew(MojoHandle message_pipe_handle,
+                        MojoMessageHandle message,
+                        MojoWriteMessageFlags);
+
+// Reads the next message from a message pipe, or indicates the size of the
+// message if it cannot fit in the provided buffers. The message will be read
+// in its entirety or not at all; if it is not, it will remain enqueued unless
+// the |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| flag was passed. At most one
+// message will be consumed from the queue, and the return value will indicate
+// whether a message was successfully read.
+//
+// |num_bytes| and |num_handles| are optional in/out parameters that on input
+// must be set to the sizes of the |bytes| and |handles| arrays, and on output
+// will be set to the actual number of bytes or handles contained in the
+// message (even if the message was not retrieved due to being too large).
+// Either |num_bytes| or |num_handles| may be null if the message is not
+// expected to contain the corresponding type of data, but such a call would
+// fail with |MOJO_RESULT_RESOURCE_EXHAUSTED| if the message in fact did
+// contain that type of data.
+//
+// |bytes| and |handles| will receive the contents of the message, if it is
+// retrieved. Either or both may be null, in which case the corresponding size
+// parameter(s) must also be set to zero or passed as null.
+//
+// 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 the message was too large to fit in the
+//       provided buffer(s). The message will have been left in the queue or
+//       discarded, depending on flags.
+//   |MOJO_RESULT_SHOULD_WAIT| if no message was available to be read.
+//
+// TODO(vtl): Reconsider the |MOJO_RESULT_RESOURCE_EXHAUSTED| error code; should
+// distinguish this from the hitting-system-limits case.
+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);
+
+// Reads the next message from a message pipe and returns a message containing
+// the message bytes. The returned message must eventually be freed using
+// |MojoFreeMessage()|.
+//
+// Message payload can be accessed using |MojoGetMessageBuffer()|.
+//
+//   |message_pipe_handle|, |num_bytes|, |handles|, |num_handles|, and |flags|
+//       correspond to their use in |MojoReadMessage()| above, with the
+//       exception that |num_bytes| is only an output argument.
+//   |message| must be non-null unless |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| is
+//       set in flags.
+//
+// Return values correspond to the return values for |MojoReadMessage()| above.
+// On success (MOJO_RESULT_OK), |*message| will contain a handle to a message
+// object which may be passed to |MojoGetMessageBuffer()|. The caller owns the
+// message object and is responsible for freeing it via |MojoFreeMessage()|.
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoReadMessageNew(MojoHandle message_pipe_handle,
+                       MojoMessageHandle* message,  // Optional out.
+                       uint32_t* num_bytes,         // Optional out.
+                       MojoHandle* handles,         // Optional out.
+                       uint32_t* num_handles,       // Optional in/out.
+                       MojoReadMessageFlags flags);
+
+// Fuses two message pipe endpoints together. Given two pipes:
+//
+//     A <-> B    and    C <-> D
+//
+// Fusing handle B and handle C results in a single pipe:
+//
+//     A <-> D
+//
+// Handles B and C are ALWAYS closed. Any unread messages at C will eventually
+// be delivered to A, and any unread messages at B will eventually be delivered
+// to D.
+//
+// NOTE: A handle may only be fused if it is an open message pipe handle which
+// has not been written to.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if both handles were valid message pipe
+//       handles but could not be merged (e.g. one of them has been written to).
+//   |MOJO_INVALID_ARGUMENT| if either handle is not a fusable message pipe
+//       handle.
+MOJO_SYSTEM_EXPORT MojoResult
+    MojoFuseMessagePipes(MojoHandle handle0, MojoHandle handle1);
+
+// Allocates a new message whose ownership may be passed to
+// |MojoWriteMessageNew()|. Use |MojoGetMessageBuffer()| to retrieve the address
+// of the mutable message payload.
+//
+// |num_bytes|: The size of the message payload in bytes.
+// |handles|: An array of handles to transfer in the message. This takes
+//     ownership of and invalidates all contained handles. Must be null if and
+//     only if |num_handles| is 0.
+// |num_handles|: The number of handles contained in |handles|.
+// |flags|: Must be |MOJO_CREATE_MESSAGE_FLAG_NONE|.
+// |message|: The address of a handle to be filled with the allocated message's
+//     handle. Must be non-null.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if the message was successfully allocated. In this case
+//       |*message| will be populated with a handle to an allocated message
+//       with a buffer large enough to hold |num_bytes| contiguous bytes.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if one or more handles in |handles| was
+//       invalid, or |handles| was null with a non-zero |num_handles|.
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if allocation failed because either
+//       |num_bytes| or |num_handles| exceeds an implementation-defined maximum.
+//   |MOJO_RESULT_BUSY| if one or more handles in |handles| cannot be sent at
+//       the time of this call.
+//
+// Only upon successful message allocation will all handles in |handles| be
+// transferred into the message and invalidated.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoAllocMessage(uint32_t num_bytes,
+                 const MojoHandle* handles,
+                 uint32_t num_handles,
+                 MojoAllocMessageFlags flags,
+                 MojoMessageHandle* message);  // Out
+
+// Frees a message allocated by |MojoAllocMessage()| or |MojoReadMessageNew()|.
+//
+// |message|: The message to free. This must correspond to a message previously
+//     allocated by |MojoAllocMessage()| or |MojoReadMessageNew()|. Note that if
+//     the message has already been passed to |MojoWriteMessageNew()| it should
+//     NOT also be freed with this API.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if |message| was valid and has been freed.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |message| was not a valid message.
+MOJO_SYSTEM_EXPORT MojoResult MojoFreeMessage(MojoMessageHandle message);
+
+// Retrieves the address of mutable message bytes for a message allocated by
+// either |MojoAllocMessage()| or |MojoReadMessageNew()|.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if |message| is a valid message object. |*buffer| will
+//       be updated to point to mutable message bytes.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message object.
+//
+// NOTE: A returned buffer address is always guaranteed to be 8-byte aligned.
+MOJO_SYSTEM_EXPORT MojoResult MojoGetMessageBuffer(MojoMessageHandle message,
+                                                   void** buffer);  // Out
+
+// Notifies the system that a bad message was received on a message pipe,
+// according to whatever criteria the caller chooses. This ultimately tries to
+// notify the embedder about the bad message, and the embedder may enforce some
+// policy for dealing with the source of the message (e.g. close the pipe,
+// terminate, a process, etc.) The embedder may not be notified if the calling
+// process has lost its connection to the source process.
+//
+// |message|: The message to report as bad. This must have come from a call to
+//     |MojoReadMessageNew()|.
+// |error|: An error string which may provide the embedder with context when
+//     notified of this error.
+// |error_num_bytes|: The length of |error| in bytes.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if successful.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoNotifyBadMessage(MojoMessageHandle message,
+                     const char* error,
+                     size_t error_num_bytes);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
diff --git a/mojo/public/c/system/platform_handle.h b/mojo/public/c/system/platform_handle.h
new file mode 100644
index 0000000..0b02357
--- /dev/null
+++ b/mojo/public/c/system/platform_handle.h
@@ -0,0 +1,184 @@
+// Copyright 2016 The Chromium 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/functions and constants for platform handle wrapping
+// and unwrapping APIs.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_PLATFORM_HANDLE_H_
+#define MOJO_PUBLIC_C_SYSTEM_PLATFORM_HANDLE_H_
+
+#include <stdint.h>
+
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// |MojoPlatformHandleType|: A value indicating the specific type of platform
+//     handle encapsulated by a MojoPlatformHandle (see below.) This is stored
+//     in the MojoPlatformHandle's |type| field and determines how the |value|
+//     field is interpreted.
+//
+//   |MOJO_PLATFORM_HANDLE_TYPE_INVALID| - An invalid platform handle.
+//   |MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR| - A file descriptor. Only valid
+//       on POSIX systems.
+//   |MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT| - A Mach port. Only valid on OS X.
+//   |MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE| - A Windows HANDLE value. Only
+//       valid on Windows.
+
+typedef uint32_t MojoPlatformHandleType;
+
+#ifdef __cplusplus
+const MojoPlatformHandleType MOJO_PLATFORM_HANDLE_TYPE_INVALID = 0;
+const MojoPlatformHandleType MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR = 1;
+const MojoPlatformHandleType MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT = 2;
+const MojoPlatformHandleType MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE = 3;
+#else
+#define MOJO_PLATFORM_HANDLE_TYPE_INVALID ((MojoPlatformHandleType)0)
+#define MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR ((MojoPlatformHandleType)1)
+#define MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT ((MojoPlatformHandleType)2)
+#define MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE ((MojoPlatformHandleType)3)
+#endif
+
+// |MojoPlatformHandle|: A handle to an OS object.
+//     |uint32_t struct_size|: The size of this structure. Used for versioning
+//         to allow for future extensions.
+//     |MojoPlatformHandleType type|: The type of handle stored in |value|.
+//     |uint64_t value|: The value of this handle. Ignored if |type| is
+//         MOJO_PLATFORM_HANDLE_TYPE_INVALID.
+//
+
+struct MOJO_ALIGNAS(8) MojoPlatformHandle {
+  uint32_t struct_size;
+  MojoPlatformHandleType type;
+  uint64_t value;
+};
+MOJO_STATIC_ASSERT(sizeof(MojoPlatformHandle) == 16,
+                   "MojoPlatformHandle has wrong size");
+
+// |MojoPlatformSharedBufferHandleFlags|: Flags relevant to wrapped platform
+//     shared buffers.
+//
+//   |MOJO_PLATFORM_SHARED_BUFFER_HANDLE_NONE| - No flags.
+//   |MOJO_PLATFORM_SHARED_BUFFER_HANDLE_READ_ONLY| - Indicates that the wrapped
+//       buffer handle may only be mapped for reading.
+
+typedef uint32_t MojoPlatformSharedBufferHandleFlags;
+
+#ifdef __cplusplus
+const MojoPlatformSharedBufferHandleFlags
+MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE = 0;
+
+const MojoPlatformSharedBufferHandleFlags
+MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY = 1 << 0;
+#else
+#define MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE \
+    ((MojoPlatformSharedBufferHandleFlags)0)
+
+#define MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY \
+    ((MojoPlatformSharedBufferHandleFlags)1 << 0)
+#endif
+
+// Wraps a generic platform handle as a Mojo handle which can be transferred
+// over a message pipe. Takes ownership of the underlying platform object.
+//
+// |platform_handle|: The platform handle to wrap.
+//
+// Returns:
+//     |MOJO_RESULT_OK| if the handle was successfully wrapped. In this case
+//         |*mojo_handle| contains the Mojo handle of the wrapped object.
+//     |MOJO_RESULT_RESOURCE_EXHAUSTED| if the system is out of handles.
+//     |MOJO_RESULT_INVALID_ARGUMENT| if |platform_handle| was not a valid
+//          platform handle.
+//
+// NOTE: It is not always possible to detect if |platform_handle| is valid,
+// particularly when |platform_handle->type| is valid but
+// |platform_handle->value| does not represent a valid platform object.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoWrapPlatformHandle(const struct MojoPlatformHandle* platform_handle,
+                       MojoHandle* mojo_handle);  // Out
+
+// Unwraps a generic platform handle from a Mojo handle. If this call succeeds,
+// ownership of the underlying platform object is bound to the returned platform
+// handle and becomes the caller's responsibility. The Mojo handle is always
+// closed regardless of success or failure.
+//
+// |mojo_handle|: The Mojo handle from which to unwrap the platform handle.
+//
+// Returns:
+//     |MOJO_RESULT_OK| if the handle was successfully unwrapped. In this case
+//         |*platform_handle| contains the unwrapped platform handle.
+//     |MOJO_RESULT_INVALID_ARGUMENT| if |mojo_handle| was not a valid Mojo
+//         handle wrapping a platform handle.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoUnwrapPlatformHandle(MojoHandle mojo_handle,
+                         struct MojoPlatformHandle* platform_handle);  // Out
+
+// Wraps a platform shared buffer handle as a Mojo shared buffer handle which
+// can be transferred over a message pipe. Takes ownership of the platform
+// shared buffer handle.
+//
+// |platform_handle|: The platform handle to wrap. Must be a handle to a
+//     shared buffer object.
+// |num_bytes|: The size of the shared buffer in bytes.
+// |flags|: Flags which influence the treatment of the shared buffer object. See
+//     below.
+//
+// Flags:
+//    |MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE| indicates default behavior.
+//        No flags set.
+//    |MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY| indicates that the
+//        buffer handled to be wrapped may only be mapped as read-only. This
+//        flag does NOT change the access control of the buffer in any way.
+//
+// Returns:
+//     |MOJO_RESULT_OK| if the handle was successfully wrapped. In this case
+//         |*mojo_handle| contains a Mojo shared buffer handle.
+//     |MOJO_RESULT_INVALID_ARGUMENT| if |platform_handle| was not a valid
+//         platform shared buffer handle.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoWrapPlatformSharedBufferHandle(
+    const struct MojoPlatformHandle* platform_handle,
+    size_t num_bytes,
+    MojoPlatformSharedBufferHandleFlags flags,
+    MojoHandle* mojo_handle);  // Out
+
+// Unwraps a platform shared buffer handle from a Mojo shared buffer handle.
+// If this call succeeds, ownership of the underlying shared buffer object is
+// bound to the returned platform handle and becomes the caller's
+// responsibility. The Mojo handle is always closed regardless of success or
+// failure.
+//
+// |mojo_handle|: The Mojo shared buffer handle to unwrap.
+//
+// |platform_handle|, |num_bytes| and |flags| are used to receive output values
+// and MUST always be non-null.
+//
+// Returns:
+//    |MOJO_RESULT_OK| if the handle was successfully unwrapped. In this case
+//        |*platform_handle| contains a platform shared buffer handle,
+//        |*num_bytes| contains the size of the shared buffer object, and
+//        |*flags| indicates flags relevant to the wrapped buffer (see below).
+//    |MOJO_RESULT_INVALID_ARGUMENT| if |mojo_handle| is not a valid Mojo
+//        shared buffer handle.
+//
+// Flags which may be set in |*flags| upon success:
+//    |MOJO_PLATFORM_SHARED_BUFFER_FLAG_READ_ONLY| is set iff the unwrapped
+//        shared buffer handle may only be mapped as read-only.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoUnwrapPlatformSharedBufferHandle(
+    MojoHandle mojo_handle,
+    struct MojoPlatformHandle* platform_handle,
+    size_t* num_bytes,
+    MojoPlatformSharedBufferHandleFlags* flags);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_PLATFORM_HANDLE_H_
diff --git a/mojo/public/c/system/set_thunks_for_app.cc b/mojo/public/c/system/set_thunks_for_app.cc
new file mode 100644
index 0000000..335cc02
--- /dev/null
+++ b/mojo/public/c/system/set_thunks_for_app.cc
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All 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/system/thunks.h"
+
+extern "C" {
+
+#if defined(WIN32)
+#define THUNKS_EXPORT __declspec(dllexport)
+#else
+#define THUNKS_EXPORT __attribute__((visibility("default")))
+#endif
+
+THUNKS_EXPORT size_t MojoSetSystemThunks(
+    const MojoSystemThunks* system_thunks) {
+  return MojoEmbedderSetSystemThunks(system_thunks);
+}
+
+}  // extern "C"
diff --git a/mojo/public/c/system/system_export.h b/mojo/public/c/system/system_export.h
new file mode 100644
index 0000000..775f667
--- /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)
+#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)
+
+#define MOJO_SYSTEM_EXPORT
+
+#endif  // defined(COMPONENT_BUILD)
+
+#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..0dd7052
--- /dev/null
+++ b/mojo/public/c/system/tests/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("tests") {
+  testonly = true
+
+  visibility = [
+    "//mojo/public/cpp/system/tests:mojo_public_system_unittests",
+    "//mojo/public/cpp/system/tests:tests",
+  ]
+
+  sources = [
+    "core_unittest.cc",
+    "core_unittest_pure_c.c",
+    "macros_unittest.cc",
+  ]
+
+  deps = [
+    "//mojo/public/c/system",
+    "//testing/gtest",
+  ]
+}
+
+source_set("perftests") {
+  testonly = true
+
+  sources = [
+    "core_perftest.cc",
+  ]
+
+  deps = [
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//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..5d4e56b
--- /dev/null
+++ b/mojo/public/c/system/tests/core_perftest.cc
@@ -0,0 +1,329 @@
+// 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 <stdint.h>
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "base/threading/simple_thread.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"
+
+#if !defined(WIN32)
+#include <time.h>
+#endif  // !defined(WIN32)
+
+namespace {
+
+#if !defined(WIN32)
+class MessagePipeWriterThread : public base::SimpleThread {
+ public:
+  MessagePipeWriterThread(MojoHandle handle, uint32_t num_bytes)
+      : SimpleThread("MessagePipeWriterThread"),
+        handle_(handle),
+        num_bytes_(num_bytes),
+        num_writes_(0) {}
+  ~MessagePipeWriterThread() override {}
+
+  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_, nullptr,
+                                           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_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePipeWriterThread);
+};
+
+class MessagePipeReaderThread : public base::SimpleThread {
+ public:
+  explicit MessagePipeReaderThread(MojoHandle handle)
+      : SimpleThread("MessagePipeReaderThread"),
+        handle_(handle),
+        num_reads_(0) {}
+  ~MessagePipeReaderThread() override {}
+
+  void Run() override {
+    char buffer[10000];
+
+    for (;;) {
+      uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+      MojoResult result = MojoReadMessage(handle_, buffer, &num_bytes, nullptr,
+                                          nullptr, 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, nullptr);
+        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_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePipeReaderThread);
+};
+#endif  // !defined(WIN32)
+
+class CorePerftest : public testing::Test {
+ public:
+  CorePerftest() : buffer_(nullptr), num_bytes_(0) {}
+  ~CorePerftest() override {}
+
+  static void NoOp(void* /*closure*/) {}
+
+  static void MessagePipe_CreateAndClose(void* closure) {
+    CorePerftest* self = static_cast<CorePerftest*>(closure);
+    MojoResult result = MojoCreateMessagePipe(nullptr, &self->h0_, &self->h1_);
+    ALLOW_UNUSED_LOCAL(result);
+    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 =
+        MojoWriteMessage(self->h0_, self->buffer_, self->num_bytes_, nullptr, 0,
+                         MOJO_WRITE_MESSAGE_FLAG_NONE);
+    ALLOW_UNUSED_LOCAL(result);
+    assert(result == MOJO_RESULT_OK);
+    uint32_t read_bytes = self->num_bytes_;
+    result = MojoReadMessage(self->h1_, self->buffer_, &read_bytes, nullptr,
+                             nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
+    assert(result == MOJO_RESULT_OK);
+  }
+
+  static void MessagePipe_EmptyRead(void* closure) {
+    CorePerftest* self = static_cast<CorePerftest*>(closure);
+    MojoResult result =
+        MojoReadMessage(self->h0_, nullptr, nullptr, nullptr, nullptr,
+                        MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+    ALLOW_UNUSED_LOCAL(result);
+    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 = MojoCreateMessagePipe(nullptr, &h0_, &h1_);
+    ALLOW_UNUSED_LOCAL(result);
+    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 sub_test_name[200];
+    sprintf(sub_test_name, "%uw_%ur_%ubytes", num_writers, num_readers,
+            static_cast<unsigned>(num_bytes));
+    mojo::test::LogPerfResult(
+        "MessagePipe_Threaded_Writes", sub_test_name,
+        1000000.0 * static_cast<double>(num_writes) / (end_time - start_time),
+        "writes/second");
+    mojo::test::LogPerfResult(
+        "MessagePipe_Threaded_Reads", sub_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 = nanosleep(&req, nullptr);
+    ALLOW_UNUSED_LOCAL(rv);
+    assert(rv == 0);
+  }
+#endif  // !defined(WIN32)
+
+  DISALLOW_COPY_AND_ASSIGN(CorePerftest);
+};
+
+// A no-op test so we can compare performance.
+TEST_F(CorePerftest, NoOp) {
+  mojo::test::IterateAndReportPerf("Iterate_NoOp", nullptr, &CorePerftest::NoOp,
+                                   this);
+}
+
+TEST_F(CorePerftest, MessagePipe_CreateAndClose) {
+  mojo::test::IterateAndReportPerf("MessagePipe_CreateAndClose", nullptr,
+                                   &CorePerftest::MessagePipe_CreateAndClose,
+                                   this);
+}
+
+TEST_F(CorePerftest, MessagePipe_WriteAndRead) {
+  MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_);
+  ALLOW_UNUSED_LOCAL(result);
+  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 = MojoCreateMessagePipe(nullptr, &h0_, &h1_);
+  ALLOW_UNUSED_LOCAL(result);
+  assert(result == MOJO_RESULT_OK);
+  mojo::test::IterateAndReportPerf("MessagePipe_EmptyRead", nullptr,
+                                   &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..ef44a87
--- /dev/null
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -0,0 +1,345 @@
+// 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 <stdint.h>
+#include <string.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const MojoHandleSignals kSignalReadadableWritable =
+    MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE;
+
+const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE |
+                                     MOJO_HANDLE_SIGNAL_WRITABLE |
+                                     MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+
+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,
+                     nullptr));
+
+  h0 = MOJO_HANDLE_INVALID;
+  sig = ~MOJO_HANDLE_SIGNAL_NONE;
+  EXPECT_EQ(
+      MOJO_RESULT_INVALID_ARGUMENT,
+      MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE, nullptr, nullptr));
+
+  // Message pipe:
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoWriteMessage(h0, buffer, 3, nullptr, 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, nullptr, nullptr,
+                            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 = nullptr;
+  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 = nullptr;
+  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, nullptr, &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(nullptr, &h0, &h1));
+  EXPECT_NE(h0, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+  // Shouldn't be readable, we haven't written anything.
+  MojoHandleSignalsState state;
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+  // Should be writable.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state));
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+  // Last parameter is optional.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, nullptr));
+
+  // Try to read.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+            MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr,
+                            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, nullptr,
+                                             0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  // |h0| should be readable.
+  uint32_t result_index = 1;
+  MojoHandleSignalsState states[1];
+  sig = MOJO_HANDLE_SIGNAL_READABLE;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE,
+                                         &result_index, states));
+
+  EXPECT_EQ(0u, result_index);
+  EXPECT_EQ(kSignalReadadableWritable, states[0].satisfied_signals);
+  EXPECT_EQ(kSignalAll, states[0].satisfiable_signals);
+
+  // Read from |h0|.
+  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr,
+                            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, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+  // Close |h0|.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWait(h1, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  // |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, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+}
+
+TEST(CoreTest, 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(nullptr, &hp, &hc));
+  EXPECT_NE(hp, MOJO_HANDLE_INVALID);
+  EXPECT_NE(hc, MOJO_HANDLE_INVALID);
+
+  // The consumer |hc| shouldn't be readable.
+  MojoHandleSignalsState state;
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  // The producer |hp| should be writable.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWait(hp, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  // 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 = nullptr;
+  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.
+  uint32_t result_index = 1;
+  MojoHandleSignalsState states[1];
+  sig = MOJO_HANDLE_SIGNAL_READABLE;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWaitMany(&hc, &sig, 1, MOJO_DEADLINE_INDEFINITE,
+                                         &result_index, states));
+
+  EXPECT_EQ(0u, result_index);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, states[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            states[0].satisfiable_signals);
+
+  // 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_PEER_CLOSED,
+                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  // Do a two-phase read from |hc|.
+  read_pointer = nullptr;
+  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, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+
+  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, BasicSharedBuffer) {
+  MojoHandle h0, h1;
+  void* pointer;
+
+  // Create a shared buffer (|h0|).
+  h0 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(nullptr, 100, &h0));
+  EXPECT_NE(h0, MOJO_HANDLE_INVALID);
+
+  // Map everything.
+  pointer = nullptr;
+  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, nullptr, &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 = nullptr;
+  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_FALSE(failure) << 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..a01e14b
--- /dev/null
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -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.
+
+#ifdef __cplusplus
+#error "This file should be compiled as C, not C++."
+#endif
+
+#include <stddef.h>
+#include <stdint.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/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, NULL));
+
+  handle1 = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &handle0, &handle1));
+
+  signals = MOJO_HANDLE_SIGNAL_READABLE;
+  uint32_t result_index = 123;
+  struct MojoHandleSignalsState states[1];
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+            MojoWaitMany(&handle0, &signals, 1, 1, &result_index, states));
+
+  // "Deadline exceeded" doesn't apply to a single handle, so this should leave
+  // |result_index| untouched.
+  EXPECT_EQ(123u, result_index);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[0].satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+                MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            states[0].satisfiable_signals);
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWriteMessage(handle0, kHello, (uint32_t)sizeof(kHello), NULL,
+                             0u, MOJO_WRITE_DATA_FLAG_NONE));
+
+  struct MojoHandleSignalsState state;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(handle1, MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+            state.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+                MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+            state.satisfiable_signals);
+
+  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..fb9ff76
--- /dev/null
+++ b/mojo/public/c/system/tests/macros_unittest.cc
@@ -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.
+
+// 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 {
+
+// First test |MOJO_STATIC_ASSERT()| in a global scope.
+MOJO_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t),
+                   "Bad static_assert() failure in global scope");
+
+TEST(MacrosTest, CompileAssert) {
+  // Then in a local scope.
+  MOJO_STATIC_ASSERT(sizeof(int32_t) == 2 * sizeof(int16_t),
+                     "Bad static_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/thunks.cc b/mojo/public/c/system/thunks.cc
new file mode 100644
index 0000000..d6bfd95
--- /dev/null
+++ b/mojo/public/c/system/thunks.cc
@@ -0,0 +1,296 @@
+// 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/c/system/thunks.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.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,
+                    struct MojoHandleSignalsState* signals_state) {
+  assert(g_thunks.Wait);
+  return g_thunks.Wait(handle, signals, deadline, signals_state);
+}
+
+MojoResult MojoWaitMany(const MojoHandle* handles,
+                        const MojoHandleSignals* signals,
+                        uint32_t num_handles,
+                        MojoDeadline deadline,
+                        uint32_t* result_index,
+                        struct MojoHandleSignalsState* signals_states) {
+  assert(g_thunks.WaitMany);
+  return g_thunks.WaitMany(handles, signals, num_handles, deadline,
+                           result_index, signals_states);
+}
+
+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);
+}
+
+MojoResult MojoCreateWaitSet(MojoHandle* wait_set) {
+  assert(g_thunks.CreateWaitSet);
+  return g_thunks.CreateWaitSet(wait_set);
+}
+
+MojoResult MojoAddHandle(MojoHandle wait_set,
+                         MojoHandle handle,
+                         MojoHandleSignals signals) {
+  assert(g_thunks.AddHandle);
+  return g_thunks.AddHandle(wait_set, handle, signals);
+}
+
+MojoResult MojoRemoveHandle(MojoHandle wait_set, MojoHandle handle) {
+  assert(g_thunks.RemoveHandle);
+  return g_thunks.RemoveHandle(wait_set, handle);
+}
+
+MojoResult MojoGetReadyHandles(MojoHandle wait_set,
+                               uint32_t* count,
+                               MojoHandle* handles,
+                               MojoResult* results,
+                               struct MojoHandleSignalsState* signals_states) {
+  assert(g_thunks.GetReadyHandles);
+  return g_thunks.GetReadyHandles(wait_set, count, handles, results,
+                                  signals_states);
+}
+
+MojoResult MojoWatch(MojoHandle handle,
+                     MojoHandleSignals signals,
+                     MojoWatchCallback callback,
+                     uintptr_t context) {
+  assert(g_thunks.Watch);
+  return g_thunks.Watch(handle, signals, callback, context);
+}
+
+MojoResult MojoCancelWatch(MojoHandle handle, uintptr_t context) {
+  assert(g_thunks.CancelWatch);
+  return g_thunks.CancelWatch(handle, context);
+}
+
+MojoResult MojoFuseMessagePipes(MojoHandle handle0, MojoHandle handle1) {
+  assert(g_thunks.FuseMessagePipes);
+  return g_thunks.FuseMessagePipes(handle0, handle1);
+}
+
+MojoResult MojoWriteMessageNew(MojoHandle message_pipe_handle,
+                               MojoMessageHandle message,
+                               MojoWriteMessageFlags flags) {
+  assert(g_thunks.WriteMessageNew);
+  return g_thunks.WriteMessageNew(message_pipe_handle, message, flags);
+}
+
+MojoResult MojoReadMessageNew(MojoHandle message_pipe_handle,
+                              MojoMessageHandle* message,
+                              uint32_t* num_bytes,
+                              MojoHandle* handles,
+                              uint32_t* num_handles,
+                              MojoReadMessageFlags flags) {
+  assert(g_thunks.ReadMessageNew);
+  return g_thunks.ReadMessageNew(message_pipe_handle, message, num_bytes,
+                                 handles, num_handles, flags);
+}
+
+MojoResult MojoAllocMessage(uint32_t num_bytes,
+                            const MojoHandle* handles,
+                            uint32_t num_handles,
+                            MojoAllocMessageFlags flags,
+                            MojoMessageHandle* message) {
+  assert(g_thunks.AllocMessage);
+  return g_thunks.AllocMessage(
+      num_bytes, handles, num_handles, flags, message);
+}
+
+MojoResult MojoFreeMessage(MojoMessageHandle message) {
+  assert(g_thunks.FreeMessage);
+  return g_thunks.FreeMessage(message);
+}
+
+MojoResult MojoGetMessageBuffer(MojoMessageHandle message, void** buffer) {
+  assert(g_thunks.GetMessageBuffer);
+  return g_thunks.GetMessageBuffer(message, buffer);
+}
+
+MojoResult MojoWrapPlatformHandle(
+    const struct MojoPlatformHandle* platform_handle,
+    MojoHandle* mojo_handle) {
+  assert(g_thunks.WrapPlatformHandle);
+  return g_thunks.WrapPlatformHandle(platform_handle, mojo_handle);
+}
+
+MojoResult MojoUnwrapPlatformHandle(
+    MojoHandle mojo_handle,
+    struct MojoPlatformHandle* platform_handle) {
+  assert(g_thunks.UnwrapPlatformHandle);
+  return g_thunks.UnwrapPlatformHandle(mojo_handle, platform_handle);
+}
+
+MojoResult MojoWrapPlatformSharedBufferHandle(
+    const struct MojoPlatformHandle* platform_handle,
+    size_t num_bytes,
+    MojoPlatformSharedBufferHandleFlags flags,
+    MojoHandle* mojo_handle) {
+  assert(g_thunks.WrapPlatformSharedBufferHandle);
+  return g_thunks.WrapPlatformSharedBufferHandle(platform_handle, num_bytes,
+                                                 flags, mojo_handle);
+}
+
+MojoResult MojoUnwrapPlatformSharedBufferHandle(
+    MojoHandle mojo_handle,
+    struct MojoPlatformHandle* platform_handle,
+    size_t* num_bytes,
+    MojoPlatformSharedBufferHandleFlags* flags) {
+  assert(g_thunks.UnwrapPlatformSharedBufferHandle);
+  return g_thunks.UnwrapPlatformSharedBufferHandle(mojo_handle, platform_handle,
+                                                   num_bytes, flags);
+}
+
+MojoResult MojoNotifyBadMessage(MojoMessageHandle message,
+                                const char* error,
+                                size_t error_num_bytes) {
+  assert(g_thunks.NotifyBadMessage);
+  return g_thunks.NotifyBadMessage(message, error, error_num_bytes);
+}
+
+MojoResult MojoGetProperty(MojoPropertyType type, void* value) {
+  assert(g_thunks.GetProperty);
+  return g_thunks.GetProperty(type, value);
+}
+
+}  // extern "C"
+
+size_t MojoEmbedderSetSystemThunks(const MojoSystemThunks* system_thunks) {
+  if (system_thunks->size >= sizeof(g_thunks))
+    g_thunks = *system_thunks;
+  return sizeof(g_thunks);
+}
diff --git a/mojo/public/c/system/thunks.h b/mojo/public/c/system/thunks.h
new file mode 100644
index 0000000..161faf1
--- /dev/null
+++ b/mojo/public/c/system/thunks.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.
+
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_THUNKS_H_
+#define MOJO_PUBLIC_C_SYSTEM_THUNKS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/c/system/system_export.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,
+                     struct MojoHandleSignalsState* signals_state);
+  MojoResult (*WaitMany)(const MojoHandle* handles,
+                         const MojoHandleSignals* signals,
+                         uint32_t num_handles,
+                         MojoDeadline deadline,
+                         uint32_t* result_index,
+                         struct MojoHandleSignalsState* signals_states);
+  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);
+
+  MojoResult (*CreateWaitSet)(MojoHandle* wait_set);
+  MojoResult (*AddHandle)(MojoHandle wait_set,
+                          MojoHandle handle,
+                          MojoHandleSignals signals);
+  MojoResult (*RemoveHandle)(MojoHandle wait_set,
+                             MojoHandle handle);
+  MojoResult (*GetReadyHandles)(MojoHandle wait_set,
+                                uint32_t* count,
+                                MojoHandle* handles,
+                                MojoResult* results,
+                                struct MojoHandleSignalsState* signals_states);
+  MojoResult (*Watch)(MojoHandle handle,
+                      MojoHandleSignals signals,
+                      MojoWatchCallback callback,
+                      uintptr_t context);
+  MojoResult (*CancelWatch)(MojoHandle handle, uintptr_t context);
+  MojoResult (*FuseMessagePipes)(MojoHandle handle0, MojoHandle handle1);
+  MojoResult (*WriteMessageNew)(MojoHandle message_pipe_handle,
+                                MojoMessageHandle message,
+                                MojoWriteMessageFlags flags);
+  MojoResult (*ReadMessageNew)(MojoHandle message_pipe_handle,
+                               MojoMessageHandle* message,
+                               uint32_t* num_bytes,
+                               MojoHandle* handles,
+                               uint32_t* num_handles,
+                               MojoReadMessageFlags flags);
+  MojoResult (*AllocMessage)(uint32_t num_bytes,
+                             const MojoHandle* handles,
+                             uint32_t num_handles,
+                             MojoAllocMessageFlags flags,
+                             MojoMessageHandle* message);
+  MojoResult (*FreeMessage)(MojoMessageHandle message);
+  MojoResult (*GetMessageBuffer)(MojoMessageHandle message, void** buffer);
+  MojoResult (*WrapPlatformHandle)(
+      const struct MojoPlatformHandle* platform_handle,
+      MojoHandle* mojo_handle);
+  MojoResult (*UnwrapPlatformHandle)(
+      MojoHandle mojo_handle,
+      struct MojoPlatformHandle* platform_handle);
+  MojoResult (*WrapPlatformSharedBufferHandle)(
+      const struct MojoPlatformHandle* platform_handle,
+      size_t num_bytes,
+      MojoPlatformSharedBufferHandleFlags flags,
+      MojoHandle* mojo_handle);
+  MojoResult (*UnwrapPlatformSharedBufferHandle)(
+      MojoHandle mojo_handle,
+      struct MojoPlatformHandle* platform_handle,
+      size_t* num_bytes,
+      MojoPlatformSharedBufferHandleFlags* flags);
+  MojoResult (*NotifyBadMessage)(MojoMessageHandle message,
+                                 const char* error,
+                                 size_t error_num_bytes);
+  MojoResult (*GetProperty)(MojoPropertyType type, void* value);
+};
+#pragma pack(pop)
+
+// 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);
+
+// A function for setting up the embedder's own system thunks. This should only
+// be called by Mojo embedder code.
+MOJO_SYSTEM_EXPORT size_t MojoEmbedderSetSystemThunks(
+    const struct MojoSystemThunks* system_thunks);
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_THUNKS_H_
diff --git a/mojo/public/c/system/types.h b/mojo/public/c/system/types.h
new file mode 100644
index 0000000..3482d4e
--- /dev/null
+++ b/mojo/public/c/system/types.h
@@ -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.
+
+// 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|: A time delta, in microseconds, the meaning of which is
+// source-dependent.
+
+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. The only success code is zero
+// (|MOJO_RESULT_OK|); all non-zero values should be considered as error/failure
+// codes (even if the value is not recognized).
+//   |MOJO_RESULT_OK| - Not an error; returned on 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()|.
+//
+// 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 uint32_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.
+//   |MOJO_HANDLE_SIGNAL_PEER_CLOSED| - The peer handle is closed.
+
+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;
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_PEER_CLOSED = 1 << 2;
+#else
+#define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals)0)
+#define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals)1 << 0)
+#define MOJO_HANDLE_SIGNAL_WRITABLE ((MojoHandleSignals)1 << 1)
+#define MOJO_HANDLE_SIGNAL_PEER_CLOSED ((MojoHandleSignals)1 << 2)
+#endif
+
+// |MojoHandleSignalsState|: Returned by wait functions to indicate the
+// signaling state of handles. Members are as follows:
+//   - |satisfied signals|: Bitmask of signals that were satisfied at some time
+//         before the call returned.
+//   - |satisfiable signals|: These are the signals that are possible to
+//         satisfy. For example, if the return value was
+//         |MOJO_RESULT_FAILED_PRECONDITION|, you can use this field to
+//         determine which, if any, of the signals can still be satisfied.
+// Note: This struct is not extensible (and only has 32-bit quantities), so it's
+// 32-bit-aligned.
+MOJO_STATIC_ASSERT(MOJO_ALIGNOF(int32_t) == 4, "int32_t has weird alignment");
+struct MOJO_ALIGNAS(4) MojoHandleSignalsState {
+  MojoHandleSignals satisfied_signals;
+  MojoHandleSignals satisfiable_signals;
+};
+MOJO_STATIC_ASSERT(sizeof(MojoHandleSignalsState) == 8,
+                   "MojoHandleSignalsState has wrong size");
+
+// |MojoWatchNotificationFlags|: Passed to a callback invoked as a result of
+// signals being raised on a handle watched by |MojoWatch()|. May take the
+// following values:
+//   |MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM| - The callback is being invoked
+//       as a result of a system-level event rather than a direct API call from
+//       user code. This may be used as an indication that user code is safe to
+//       call without fear of reentry.
+
+typedef uint32_t MojoWatchNotificationFlags;
+
+#ifdef __cplusplus
+const MojoWatchNotificationFlags MOJO_WATCH_NOTIFICATION_FLAG_NONE = 0;
+const MojoWatchNotificationFlags MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM =
+    1 << 0;
+#else
+#define MOJO_WATCH_NOTIFICATION_FLAG_NONE ((MojoWatchNotificationFlags)0)
+#define MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM \
+    ((MojoWatchNotificationFlags)1 << 0);
+#endif
+
+// |MojoPropertyType|: Property types that can be passed to |MojoGetProperty()|
+// to retrieve system properties. May take the following values:
+//   |MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED| - Whether making synchronous calls
+//       (i.e., blocking to wait for a response to an outbound message) is
+//       allowed. The property value is of boolean type. If the value is true,
+//       users should refrain from making sync calls.
+typedef uint32_t MojoPropertyType;
+
+#ifdef __cplusplus
+const MojoPropertyType MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED = 0;
+#else
+#define MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED ((MojoPropertyType)0)
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_TYPES_H_
diff --git a/mojo/public/c/system/wait_set.h b/mojo/public/c/system/wait_set.h
new file mode 100644
index 0000000..3a127f5
--- /dev/null
+++ b/mojo/public/c/system/wait_set.h
@@ -0,0 +1,137 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains types/constants and functions specific to wait sets.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_WAIT_SET_H_
+#define MOJO_PUBLIC_C_SYSTEM_WAIT_SET_H_
+
+#include <stdint.h>
+
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a wait set. A wait set is a way to efficiently wait on multiple
+// handles.
+//
+// On success, |*wait_set_handle| will contain a handle to a wait set.
+//
+// Returns:
+//   |MOJO_RESULT_OK| on success.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |wait_set_handle| is null.
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+//       been reached.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateWaitSet(
+    MojoHandle* wait_set_handle);  // Out.
+
+// Adds a wait on |handle| to |wait_set_handle|.
+//
+// A handle can only be added to any given wait set once, but may be added to
+// any number of different wait sets. To modify the signals being waited for,
+// the handle must first be removed, and then added with the new signals.
+//
+// If a handle is closed while still in the wait set, it is implicitly removed
+// from the set after being returned from |MojoGetReadyHandles()| with the
+// result |MOJO_RESULT_CANCELLED|.
+//
+// It is safe to add a handle to a wait set while performing a wait on another
+// thread. If the added handle already has its signals satisfied, the waiting
+// thread will be woken.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if |handle| was successfully added to |wait_set_handle|.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle, or
+//       |wait_set_handle| is not a valid wait set.
+//   |MOJO_RESULT_ALREADY_EXISTS| if |handle| already exists in
+//       |wait_set_handle|.
+MOJO_SYSTEM_EXPORT MojoResult MojoAddHandle(
+    MojoHandle wait_set_handle,
+    MojoHandle handle,
+    MojoHandleSignals signals);
+
+// Removes |handle| from |wait_set_handle|.
+//
+// It is safe to remove a handle from a wait set while performing a wait on
+// another thread. If handle has its signals satisfied while it is being
+// removed, the waiting thread may be woken up, but no handle may be available
+// when |MojoGetReadyHandles()| is called.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if |handle| was successfully removed from
+//       |wait_set_handle|.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle, or
+//       |wait_set_handle| is not a valid wait set.
+//   |MOJO_RESULT_NOT_FOUND| if |handle| does not exist in |wait_set_handle|.
+MOJO_SYSTEM_EXPORT MojoResult MojoRemoveHandle(
+    MojoHandle wait_set_handle,
+    MojoHandle handle);
+
+// Retrieves a set of ready handles from |wait_set_handle|. A handle is ready if
+// at least of of the following is true:
+//   - The handle's signals are satisfied.
+//   - It becomes known that no signal for the handle will ever be satisfied.
+//   - The handle is closed.
+//
+// A wait set may have ready handles when it satisfies the
+// |MOJO_HANDLE_SIGNAL_READABLE| signal. Since handles may be added and removed
+// from a wait set concurrently, it is possible for a wait set to satisfy
+// |MOJO_HANDLE_SIGNAL_READABLE|, but not have any ready handles when
+// |MojoGetReadyHandles()| is called. These spurious wake-ups must be gracefully
+// handled.
+//
+// |*count| on input, must contain the maximum number of ready handles to be
+// returned. On output, it will contain the number of ready handles returned.
+//
+// |handles| must point to an array of size |*count| of |MojoHandle|. It will be
+// populated with handles that are considered ready. The number of handles
+// returned will be in |*count|.
+//
+// |results| must point to an array of size |*count| of |MojoResult|. It will be
+// populated with the wait result of the corresponding handle in |*handles|.
+// Care should be taken that if a handle is closed on another thread, the handle
+// would be invalid, but the result may not be |MOJO_RESULT_CANCELLED|. See
+// documentation for |MojoWait()| for possible results.
+//
+// |signals_state| (optional) if non-null, must point to an array of size
+// |*count| of |MojoHandleSignalsState|. It will be populated with the signals
+// state of the corresponding handle in |*handles|. See documentation for
+// |MojoHandleSignalsState| for more details about the meaning of each array
+// entry. The array will always be updated for every returned handle.
+//
+// Mojo signals and satisfiability are logically 'level-triggered'. Therefore,
+// if a signal continues to be satisfied and is not removed from the wait set,
+// subsequent calls to |MojoGetReadyHandles()| will return the same handle.
+//
+// If multiple handles have their signals satisfied, the order in which handles
+// are returned is undefined. The same handle, if not removed, may be returned
+// in consecutive calls. Callers must not rely on any fairness and handles
+// could be starved if not acted on.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if ready handles are available.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |wait_set_handle| is not a valid wait
+//       set, if |*count| is 0, or if either |count|, |handles|, or |results| is
+//       null.
+//   |MOJO_RESULT_SHOULD_WAIT| if there are no ready handles.
+MOJO_SYSTEM_EXPORT MojoResult MojoGetReadyHandles(
+    MojoHandle wait_set_handle,
+    uint32_t* count,                                 // In/out.
+    MojoHandle* handles,                             // Out.
+    MojoResult* results,                             // Out.
+    struct MojoHandleSignalsState *signals_states);  // Optional out.
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // MOJO_PUBLIC_C_SYSTEM_WAIT_SET_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..75d9c13
--- /dev/null
+++ b/mojo/public/c/test_support/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.
+
+# GYP version: mojo/mojo_public.gyp:mojo_test_support
+static_library("test_support") {
+  output_name = "mojo_public_test_support"
+
+  sources = [
+    "test_support.h",
+
+    # TODO(vtl): Convert this to thunks http://crbug.com/386799
+    "../../tests/test_support_private.cc",
+    "../../tests/test_support_private.h",
+  ]
+}
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..8e50441
--- /dev/null
+++ b/mojo/public/c/test_support/test_support.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_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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// |sub_test_name| is optional. If not null, it usually describes one particular
+// configuration of the test. For example, if |test_name| is "TestPacketRate",
+// |sub_test_name| could be "100BytesPerPacket".
+// When the perf data is visualized by the performance dashboard, data with
+// different |sub_test_name|s (but the same |test_name|) are depicted as
+// different traces on the same chart.
+void MojoTestSupportLogPerfResult(
+    const char* test_name,
+    const char* sub_test_name,
+    double value,
+    const char* units);
+
+// Opens a "/"-delimited file path relative to the source root.
+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;
+//
+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/cpp/README.md b/mojo/public/cpp/README.md
new file mode 100644
index 0000000..d0f1238
--- /dev/null
+++ b/mojo/public/cpp/README.md
@@ -0,0 +1,38 @@
+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
+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).
+
+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.
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
new file mode 100644
index 0000000..3ec9824
--- /dev/null
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -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.
+
+static_library("bindings") {
+  sources = [
+    "array.h",
+    "array_traits.h",
+    "array_traits_carray.h",
+    "array_traits_standard.h",
+    "array_traits_stl.h",
+    "associated_binding.h",
+    "associated_group.h",
+    "associated_group_controller.h",
+    "associated_interface_ptr.h",
+    "associated_interface_ptr_info.h",
+    "associated_interface_request.h",
+    "binding.h",
+    "binding_set.h",
+    "connector.h",
+    "enum_traits.h",
+    "interface_endpoint_client.h",
+    "interface_endpoint_controller.h",
+    "interface_id.h",
+    "interface_ptr.h",
+    "interface_ptr_info.h",
+    "interface_ptr_set.h",
+    "interface_request.h",
+    "lib/array_internal.cc",
+    "lib/array_internal.h",
+    "lib/array_serialization.h",
+    "lib/associated_group.cc",
+    "lib/associated_group_controller.cc",
+    "lib/associated_interface_ptr_state.h",
+    "lib/binding_state.h",
+    "lib/bindings_internal.cc",
+    "lib/bindings_internal.h",
+    "lib/buffer.h",
+    "lib/clone_equals_util.h",
+    "lib/connector.cc",
+    "lib/control_message_handler.cc",
+    "lib/control_message_handler.h",
+    "lib/control_message_proxy.cc",
+    "lib/control_message_proxy.h",
+    "lib/filter_chain.cc",
+    "lib/filter_chain.h",
+    "lib/fixed_buffer.cc",
+    "lib/fixed_buffer.h",
+    "lib/handle_interface_serialization.h",
+    "lib/interface_endpoint_client.cc",
+    "lib/interface_ptr_state.h",
+    "lib/map_data_internal.h",
+    "lib/map_serialization.h",
+    "lib/message.cc",
+    "lib/message_buffer.cc",
+    "lib/message_buffer.h",
+    "lib/message_builder.cc",
+    "lib/message_builder.h",
+    "lib/message_filter.cc",
+    "lib/message_header_validator.cc",
+    "lib/message_internal.h",
+    "lib/multiplex_router.cc",
+    "lib/multiplex_router.h",
+    "lib/native_enum_data.h",
+    "lib/native_enum_serialization.h",
+    "lib/native_struct.cc",
+    "lib/native_struct_data.cc",
+    "lib/native_struct_data.h",
+    "lib/native_struct_serialization.cc",
+    "lib/native_struct_serialization.h",
+    "lib/no_interface.cc",
+    "lib/pipe_control_message_handler.cc",
+    "lib/pipe_control_message_proxy.cc",
+    "lib/router.cc",
+    "lib/router.h",
+    "lib/scoped_interface_endpoint_handle.cc",
+    "lib/serialization.h",
+    "lib/serialization_context.cc",
+    "lib/serialization_context.h",
+    "lib/serialization_forward.h",
+    "lib/serialization_util.h",
+    "lib/string_serialization.h",
+    "lib/string_traits_string16.cc",
+    "lib/sync_call_restrictions.cc",
+    "lib/sync_handle_registry.cc",
+    "lib/sync_handle_watcher.cc",
+    "lib/template_util.h",
+    "lib/union_accessor.h",
+    "lib/validate_params.h",
+    "lib/validation_context.cc",
+    "lib/validation_context.h",
+    "lib/validation_errors.cc",
+    "lib/validation_errors.h",
+    "lib/validation_util.cc",
+    "lib/validation_util.h",
+    "map.h",
+    "map_traits.h",
+    "map_traits_standard.h",
+    "map_traits_stl.h",
+    "message.h",
+    "message_filter.h",
+    "message_header_validator.h",
+    "native_enum.h",
+    "native_struct.h",
+    "no_interface.h",
+    "pipe_control_message_handler.h",
+    "pipe_control_message_handler_delegate.h",
+    "pipe_control_message_proxy.h",
+    "scoped_interface_endpoint_handle.h",
+    "stl_converters.h",
+    "string.h",
+    "string_traits.h",
+    "string_traits_standard.h",
+    "string_traits_stl.h",
+    "string_traits_string16.h",
+    "string_traits_string_piece.h",
+    "strong_binding.h",
+    "struct_ptr.h",
+    "sync_call_restrictions.h",
+    "sync_handle_registry.h",
+    "sync_handle_watcher.h",
+    "type_converter.h",
+  ]
+
+  public_deps = [
+    ":struct_traits",
+    "//base",
+    "//ipc:param_traits",
+    "//mojo/public/cpp/system",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/public/interfaces/bindings:bindings_cpp_sources",
+  ]
+}
+
+source_set("struct_traits") {
+  sources = [
+    "struct_traits.h",
+  ]
+}
+
+if (!is_ios) {
+  # TODO(yzshen): crbug.com/617718 Consider moving this into blink.
+  source_set("wtf_support") {
+    sources = [
+      "array_traits_wtf.h",
+      "array_traits_wtf_vector.h",
+      "lib/string_traits_wtf.cc",
+      "lib/wtf_clone_equals_util.h",
+      "lib/wtf_serialization.h",
+      "map_traits_wtf.h",
+      "map_traits_wtf_hash_map.h",
+      "string_traits_wtf.h",
+      "wtf_array.h",
+      "wtf_map.h",
+    ]
+
+    public_deps = [
+      ":bindings",
+      "//third_party/WebKit/Source/wtf",
+    ]
+
+    public_configs = [ "//third_party/WebKit/Source:config" ]
+  }
+}
diff --git a/mojo/public/cpp/bindings/DEPS b/mojo/public/cpp/bindings/DEPS
new file mode 100644
index 0000000..36eba44
--- /dev/null
+++ b/mojo/public/cpp/bindings/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/WebKit/Source/wtf",
+]
diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h
new file mode 100644
index 0000000..a253da1
--- /dev/null
+++ b/mojo/public/cpp/bindings/array.h
@@ -0,0 +1,279 @@
+// 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 <stddef.h>
+#include <string.h>
+#include <algorithm>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/clone_equals_util.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+// Represents a moveable array with contents of type |T|. The array can be null,
+// meaning that no value has been assigned to it. Null is distinct from empty.
+template <typename T>
+class Array {
+ public:
+  using ConstRefType = typename std::vector<T>::const_reference;
+  using RefType = typename std::vector<T>::reference;
+
+  using Element = T;
+
+  using iterator = typename std::vector<T>::iterator;
+  using const_iterator = typename std::vector<T>::const_iterator;
+
+  // Constructs an empty array.
+  Array() : is_null_(false) {}
+  // Constructs a null array.
+  Array(std::nullptr_t null_pointer) : is_null_(true) {}
+
+  // Constructs a new non-null array of the specified size. The elements will
+  // be value-initialized (meaning that they will be initialized by their
+  // default constructor, if any, or else zero-initialized).
+  explicit Array(size_t size) : vec_(size), is_null_(false) {}
+  ~Array() {}
+
+  // Copies the contents of |other| into this array.
+  Array(const std::vector<T>& other) : vec_(other), is_null_(false) {}
+
+  // Moves the contents of |other| into this array.
+  Array(std::vector<T>&& other) : vec_(std::move(other)), is_null_(false) {}
+  Array(Array&& other) : is_null_(true) { Take(&other); }
+
+  Array& operator=(std::vector<T>&& other) {
+    vec_ = std::move(other);
+    is_null_ = false;
+    return *this;
+  }
+  Array& operator=(Array&& other) {
+    Take(&other);
+    return *this;
+  }
+
+  Array& operator=(std::nullptr_t null_pointer) {
+    is_null_ = true;
+    vec_.clear();
+    return *this;
+  }
+
+  // Creates a non-null array of the specified size. The elements will be
+  // value-initialized (meaning that they will be initialized by their default
+  // constructor, if any, or else zero-initialized).
+  static Array New(size_t size) { return Array(size); }
+
+  // Creates a new array with a copy of the contents of |other|.
+  template <typename U>
+  static Array From(const U& other) {
+    return TypeConverter<Array, U>::Convert(other);
+  }
+
+  // Copies the contents of this array to a new object of type |U|.
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, Array>::Convert(*this);
+  }
+
+  // Indicates whether the array is null (which is distinct from empty).
+  bool is_null() const { return is_null_; }
+
+  // Indicates whether the array is empty (which is distinct from null).
+  bool empty() const { return vec_.empty() && !is_null_; }
+
+  // Returns a reference to the first element of the array. Calling this on a
+  // null or empty array causes undefined behavior.
+  ConstRefType front() const { return vec_.front(); }
+  RefType front() { return vec_.front(); }
+
+  iterator begin() { return vec_.begin(); }
+  const_iterator begin() const { return vec_.begin(); }
+  iterator end() { return vec_.end(); }
+  const_iterator end() const { return vec_.end(); }
+
+  // Returns the size of the array, which will be zero if the array is null.
+  size_t size() const { return vec_.size(); }
+
+  // Returns a reference to the element at zero-based |offset|. Calling this on
+  // an array with size less than |offset|+1 causes undefined behavior.
+  ConstRefType at(size_t offset) const { return vec_.at(offset); }
+  ConstRefType operator[](size_t offset) const { return at(offset); }
+  RefType at(size_t offset) { return vec_.at(offset); }
+  RefType operator[](size_t offset) { return at(offset); }
+
+  // Pushes |value| onto the back of the array. If this array was null, it will
+  // become non-null with a size of 1.
+  void push_back(const T& value) {
+    is_null_ = false;
+    vec_.push_back(value);
+  }
+  void push_back(T&& value) {
+    is_null_ = false;
+    vec_.push_back(std::move(value));
+  }
+
+  // Resizes the array to |size| and makes it non-null. Otherwise, works just
+  // like the resize method of |std::vector|.
+  void resize(size_t size) {
+    is_null_ = false;
+    vec_.resize(size);
+  }
+
+  // Sets the array to empty (even if previously it was null.)
+  void SetToEmpty() { resize(0); }
+
+  // Returns a const reference to the |std::vector| managed by this class. If
+  // the array is null, this will be an empty vector.
+  const std::vector<T>& storage() const { return vec_; }
+
+  // Passes the underlying storage and resets this array to null.
+  std::vector<T> PassStorage() {
+    is_null_ = true;
+    return std::move(vec_);
+  }
+
+  operator const std::vector<T>&() const { return vec_; }
+
+  void Swap(Array* other) {
+    std::swap(is_null_, other->is_null_);
+    vec_.swap(other->vec_);
+  }
+
+  // Swaps the contents of this array with the specified vector, making this
+  // array non-null. Since the vector cannot represent null, it will just be
+  // made empty if this array is null.
+  void Swap(std::vector<T>* other) {
+    is_null_ = false;
+    vec_.swap(*other);
+  }
+
+  // Returns a copy of the array where each value of the new array has been
+  // "cloned" from the corresponding value of this array. If the element type
+  // defines a Clone() method, it will be used; otherwise copy
+  // constructor/assignment will be used.
+  //
+  // 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 containing Mojo handles).
+  Array Clone() const {
+    Array result;
+    result.is_null_ = is_null_;
+    result.vec_ = internal::Clone(vec_);
+    return result;
+  }
+
+  // Indicates whether the contents of this array are equal to |other|. A null
+  // array is only equal to another null array. If the element type defines an
+  // Equals() method, it will be used; otherwise == operator will be used.
+  bool Equals(const Array& other) const {
+    if (is_null() != other.is_null())
+      return false;
+    return internal::Equals(vec_, other.vec_);
+  }
+
+ private:
+  typedef std::vector<T> Array::*Testable;
+
+ public:
+  operator Testable() const { return is_null_ ? 0 : &Array::vec_; }
+
+ private:
+  // Forbid the == and != operators explicitly, otherwise Array will be
+  // converted to Testable to do == or != comparison.
+  template <typename U>
+  bool operator==(const Array<U>& other) const = delete;
+  template <typename U>
+  bool operator!=(const Array<U>& other) const = delete;
+
+  void Take(Array* other) {
+    operator=(nullptr);
+    Swap(other);
+  }
+
+  std::vector<T> vec_;
+  bool is_null_;
+
+  DISALLOW_COPY_AND_ASSIGN(Array);
+};
+
+// A |TypeConverter| that will create an |Array<T>| containing a copy of the
+// contents of an |std::vector<E>|, using |TypeConverter<T, E>| to copy each
+// element. The returned array will always be non-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 std::move(result);
+  }
+};
+
+// A |TypeConverter| that will create an |std::vector<E>| containing a copy of
+// the contents of an |Array<T>|, using |TypeConverter<E, T>| to copy each
+// element. If the input array is null, the output vector will be empty.
+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;
+  }
+};
+
+// A |TypeConverter| that will create an |Array<T>| containing a copy of the
+// contents of an |std::set<E>|, using |TypeConverter<T, E>| to copy each
+// element. The returned array will always be non-null.
+template <typename T, typename E>
+struct TypeConverter<Array<T>, std::set<E>> {
+  static Array<T> Convert(const std::set<E>& input) {
+    Array<T> result;
+    for (auto i : input)
+      result.push_back(TypeConverter<T, E>::Convert(i));
+    return std::move(result);
+  }
+};
+
+// A |TypeConverter| that will create an |std::set<E>| containing a copy of
+// the contents of an |Array<T>|, using |TypeConverter<E, T>| to copy each
+// element. If the input array is null, the output set will be empty.
+template <typename E, typename T>
+struct TypeConverter<std::set<E>, Array<T>> {
+  static std::set<E> Convert(const Array<T>& input) {
+    std::set<E> result;
+    if (!input.is_null()) {
+      for (size_t i = 0; i < input.size(); ++i)
+        result.insert(TypeConverter<E, T>::Convert(input[i]));
+    }
+    return result;
+  }
+};
+
+// Less than operator to allow Arrays as keys in std maps and sets.
+template <typename T>
+inline bool operator<(const Array<T>& a, const Array<T>& b) {
+  if (a.is_null())
+    return !b.is_null();
+  if (b.is_null())
+    return false;
+  return a.storage() < b.storage();
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
diff --git a/mojo/public/cpp/bindings/array_traits.h b/mojo/public/cpp/bindings/array_traits.h
new file mode 100644
index 0000000..366573d
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_H_
+
+namespace mojo {
+
+// This must be specialized for any type |T| to be serialized/deserialized as
+// a mojom array.
+//
+// Usually you would like to do a partial specialization for a container (e.g.
+// vector) template. Imagine you want to specialize it for Container<>, you need
+// to implement:
+//
+//   template <typename T>
+//   struct ArrayTraits<Container<T>> {
+//     using Element = T;
+//     // These two statements are optional. Use them if you'd like to serialize
+//     // a container that supports iterators but does not support O(1) random
+//     // access and so GetAt(...) would be expensive.
+//     // using Iterator = T::iterator;
+//     // using ConstIterator = T::const_iterator;
+//
+//     // These two methods are optional. Please see comments in struct_traits.h
+//     static bool IsNull(const Container<T>& input);
+//     static void SetToNull(Container<T>* output);
+//
+//     static size_t GetSize(const Container<T>& input);
+//
+//     // These two methods are optional. They are used to access the
+//     // underlying storage of the array to speed up copy of POD types.
+//     static T* GetData(Container<T>& input);
+//     static const T* GetData(const Container<T>& input);
+//
+//     // The following six methods are optional if the GetAt(...) methods are
+//     // implemented. These methods specify how to read the elements of
+//     // Container in some sequential order specified by the iterator.
+//     //
+//     // Acquires an iterator positioned at the first element in the container.
+//     static ConstIterator GetBegin(const Container<T>& input);
+//     static Iterator GetBegin(Container<T>& input);
+//
+//     // Advances |iterator| to the next position within the container.
+//     static void AdvanceIterator(ConstIterator& iterator);
+//     static void AdvanceIterator(Iterator& iterator);
+//
+//     // Returns a reference to the value at the current position of
+//     // |iterator|.
+//     static const T& GetValue(ConstIterator& iterator);
+//     static T& GetValue(Iterator& iterator);
+//
+//     // These two methods are optional if the iterator methods are
+//     // implemented.
+//     static T& GetAt(Container<T>& input, size_t index);
+//     static const T& GetAt(const Container<T>& input, size_t index);
+//
+//     // Returning false results in deserialization failure and causes the
+//     // message pipe receiving it to be disconnected.
+//     static bool Resize(Container<T>& input, size_t size);
+//   };
+//
+template <typename T>
+struct ArrayTraits;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/array_traits_carray.h b/mojo/public/cpp/bindings/array_traits_carray.h
new file mode 100644
index 0000000..ffcf9d5
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits_carray.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_CARRAY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_CARRAY_H_
+
+#include "mojo/public/cpp/bindings/array_traits.h"
+
+namespace mojo {
+
+template <typename T>
+struct CArray {
+  CArray() : size(0), max_size(0), data(nullptr) {}
+  CArray(size_t size, size_t max_size, T* data)
+      : size(size), max_size(max_size), data(data) {}
+  size_t size;
+  const size_t max_size;
+  T* data;
+};
+
+template <typename T>
+struct ArrayTraits<CArray<T>> {
+  using Element = T;
+
+  static bool IsNull(const CArray<T>& input) { return !input.data; }
+
+  static void SetToNull(CArray<T>* output) { output->data = nullptr; }
+
+  static size_t GetSize(const CArray<T>& input) { return input.size; }
+
+  static T* GetData(CArray<T>& input) { return input.data; }
+
+  static const T* GetData(const CArray<T>& input) { return input.data; }
+
+  static T& GetAt(CArray<T>& input, size_t index) { return input.data[index]; }
+
+  static const T& GetAt(const CArray<T>& input, size_t index) {
+    return input.data[index];
+  }
+
+  static bool Resize(CArray<T>& input, size_t size) {
+    if (size > input.max_size)
+      return false;
+
+    input.size = size;
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_CARRAY_H_
diff --git a/mojo/public/cpp/bindings/array_traits_standard.h b/mojo/public/cpp/bindings/array_traits_standard.h
new file mode 100644
index 0000000..862de6b
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits_standard.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_STANDARD_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STANDARD_H_
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/array_traits.h"
+
+namespace mojo {
+
+template <typename T>
+struct ArrayTraits<Array<T>> {
+  using Element = T;
+
+  static bool IsNull(const Array<T>& input) { return input.is_null(); }
+  static void SetToNull(Array<T>* output) { *output = nullptr; }
+
+  static size_t GetSize(const Array<T>& input) { return input.size(); }
+
+  static T* GetData(Array<T>& input) { return &input.front(); }
+
+  static const T* GetData(const Array<T>& input) { return &input.front(); }
+
+  static typename Array<T>::RefType GetAt(Array<T>& input, size_t index) {
+    return input[index];
+  }
+
+  static typename Array<T>::ConstRefType GetAt(const Array<T>& input,
+                                               size_t index) {
+    return input[index];
+  }
+
+  static bool Resize(Array<T>& input, size_t size) {
+    input.resize(size);
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STANDARD_H_
diff --git a/mojo/public/cpp/bindings/array_traits_stl.h b/mojo/public/cpp/bindings/array_traits_stl.h
new file mode 100644
index 0000000..9054a92
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits_stl.h
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_STL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array_traits.h"
+
+namespace mojo {
+
+template <typename T>
+struct ArrayTraits<std::vector<T>> {
+  using Element = T;
+
+  static bool IsNull(const std::vector<T>& input) {
+    // std::vector<> is always converted to non-null mojom array.
+    return false;
+  }
+
+  static void SetToNull(std::vector<T>* output) {
+    // std::vector<> doesn't support null state. Set it to empty instead.
+    output->clear();
+  }
+
+  static size_t GetSize(const std::vector<T>& input) { return input.size(); }
+
+  static T* GetData(std::vector<T>& input) { return input.data(); }
+
+  static const T* GetData(const std::vector<T>& input) { return input.data(); }
+
+  static typename std::vector<T>::reference GetAt(std::vector<T>& input,
+                                                  size_t index) {
+    return input[index];
+  }
+
+  static typename std::vector<T>::const_reference GetAt(
+      const std::vector<T>& input,
+      size_t index) {
+    return input[index];
+  }
+
+  static bool Resize(std::vector<T>& input, size_t size) {
+    if (input.size() != size) {
+      // This is a hack to make compilers for Mac and Android happy. They
+      // currently don't allow resizing types like
+      // std::vector<std::vector<MoveOnlyType>>.
+      // Because the deserialization code doesn't care about the original
+      // contents of |input|, we discard them directly.
+      std::vector<T> temp(size);
+      input.swap(temp);
+    }
+
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_
diff --git a/mojo/public/cpp/bindings/array_traits_wtf.h b/mojo/public/cpp/bindings/array_traits_wtf.h
new file mode 100644
index 0000000..7e773fc
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits_wtf.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_WTF_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_H_
+
+#include "mojo/public/cpp/bindings/array_traits.h"
+#include "mojo/public/cpp/bindings/wtf_array.h"
+
+namespace mojo {
+
+template <typename U>
+struct ArrayTraits<WTFArray<U>> {
+  using Element = U;
+
+  static bool IsNull(const WTFArray<U>& input) { return input.is_null(); }
+  static void SetToNull(WTFArray<U>* output) { *output = nullptr; }
+
+  static size_t GetSize(const WTFArray<U>& input) { return input.size(); }
+
+  static U* GetData(WTFArray<U>& input) { return &input.front(); }
+
+  static const U* GetData(const WTFArray<U>& input) { return &input.front(); }
+
+  static U& GetAt(WTFArray<U>& input, size_t index) { return input[index]; }
+
+  static const U& GetAt(const WTFArray<U>& input, size_t index) {
+    return input[index];
+  }
+
+  static bool Resize(WTFArray<U>& input, size_t size) {
+    input.resize(size);
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_H_
diff --git a/mojo/public/cpp/bindings/array_traits_wtf_vector.h b/mojo/public/cpp/bindings/array_traits_wtf_vector.h
new file mode 100644
index 0000000..6e20735
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits_wtf_vector.h
@@ -0,0 +1,47 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_WTF_VECTOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_VECTOR_H_
+
+#include "mojo/public/cpp/bindings/array_traits.h"
+#include "third_party/WebKit/Source/wtf/Vector.h"
+
+namespace mojo {
+
+template <typename U>
+struct ArrayTraits<WTF::Vector<U>> {
+  using Element = U;
+
+  static bool IsNull(const WTF::Vector<U>& input) {
+    // WTF::Vector<> is always converted to non-null mojom array.
+    return false;
+  }
+
+  static void SetToNull(WTF::Vector<U>* output) {
+    // WTF::Vector<> doesn't support null state. Set it to empty instead.
+    output->clear();
+  }
+
+  static size_t GetSize(const WTF::Vector<U>& input) { return input.size(); }
+
+  static U* GetData(WTF::Vector<U>& input) { return input.data(); }
+
+  static const U* GetData(const WTF::Vector<U>& input) { return input.data(); }
+
+  static U& GetAt(WTF::Vector<U>& input, size_t index) { return input[index]; }
+
+  static const U& GetAt(const WTF::Vector<U>& input, size_t index) {
+    return input[index];
+  }
+
+  static bool Resize(WTF::Vector<U>& input, size_t size) {
+    input.resize(size);
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_VECTOR_H_
diff --git a/mojo/public/cpp/bindings/associated_binding.h b/mojo/public/cpp/bindings/associated_binding.h
new file mode 100644
index 0000000..1da5009
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_binding.h
@@ -0,0 +1,175 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_BINDING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_BINDING_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+namespace mojo {
+
+// Represents the implementation side of an associated interface. It is similar
+// to Binding, except that it doesn't own a message pipe handle.
+//
+// When you bind this class to a request, optionally you can specify a
+// base::SingleThreadTaskRunner. This task runner must belong to the same
+// thread. It will be used to dispatch incoming method calls and connection
+// error notification. It is useful when you attach multiple task runners to a
+// single thread for the purposes of task scheduling. Please note that incoming
+// synchrounous method calls may not be run from this task runner, when they
+// reenter outgoing synchrounous calls on the same thread.
+template <typename Interface>
+class AssociatedBinding {
+ public:
+  // Constructs an incomplete associated binding that will use the
+  // implementation |impl|. It may be completed with a subsequent call to the
+  // |Bind| method. Does not take ownership of |impl|, which must outlive this
+  // object.
+  explicit AssociatedBinding(Interface* impl) : impl_(impl) {
+    stub_.set_sink(impl_);
+  }
+
+  // Constructs a completed associated binding of |impl|. The output |ptr_info|
+  // should be passed through the message pipe endpoint referred to by
+  // |associated_group| to setup the corresponding asssociated interface
+  // pointer. |impl| must outlive this object.
+  AssociatedBinding(Interface* impl,
+                    AssociatedInterfacePtrInfo<Interface>* ptr_info,
+                    AssociatedGroup* associated_group,
+                    scoped_refptr<base::SingleThreadTaskRunner> runner =
+                        base::ThreadTaskRunnerHandle::Get())
+      : AssociatedBinding(impl) {
+    Bind(ptr_info, associated_group, std::move(runner));
+  }
+
+  // Constructs a completed associated binding of |impl|. |impl| must outlive
+  // the binding.
+  AssociatedBinding(Interface* impl,
+                    AssociatedInterfaceRequest<Interface> request,
+                    scoped_refptr<base::SingleThreadTaskRunner> runner =
+                        base::ThreadTaskRunnerHandle::Get())
+      : AssociatedBinding(impl) {
+    Bind(std::move(request), std::move(runner));
+  }
+
+  ~AssociatedBinding() {}
+
+  // Creates an associated inteface and sets up this object as the
+  // implementation side. The output |ptr_info| should be passed through the
+  // message pipe endpoint referred to by |associated_group| to setup the
+  // corresponding asssociated interface pointer.
+  void Bind(AssociatedInterfacePtrInfo<Interface>* ptr_info,
+            AssociatedGroup* associated_group,
+            scoped_refptr<base::SingleThreadTaskRunner> runner =
+                base::ThreadTaskRunnerHandle::Get()) {
+    AssociatedInterfaceRequest<Interface> request;
+    associated_group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_PTR,
+                                                ptr_info, &request);
+    Bind(std::move(request), std::move(runner));
+  }
+
+  // Sets up this object as the implementation side of an associated interface.
+  void Bind(AssociatedInterfaceRequest<Interface> request,
+            scoped_refptr<base::SingleThreadTaskRunner> runner =
+                base::ThreadTaskRunnerHandle::Get()) {
+    ScopedInterfaceEndpointHandle handle = request.PassHandle();
+
+    DCHECK(handle.is_local())
+        << "The AssociatedInterfaceRequest is supposed to be used at the "
+        << "other side of the message pipe.";
+
+    if (!handle.is_valid() || !handle.is_local()) {
+      endpoint_client_.reset();
+      return;
+    }
+
+    endpoint_client_.reset(new InterfaceEndpointClient(
+        std::move(handle), &stub_,
+        base::WrapUnique(new typename Interface::RequestValidator_()),
+        Interface::HasSyncMethods_, std::move(runner)));
+    endpoint_client_->set_connection_error_handler(
+        base::Bind(&AssociatedBinding::RunConnectionErrorHandler,
+                   base::Unretained(this)));
+
+    stub_.serialization_context()->group_controller =
+        endpoint_client_->group_controller();
+  }
+
+  // Closes the associated interface. Puts this object into a state where it can
+  // be rebound.
+  void Close() {
+    DCHECK(endpoint_client_);
+    endpoint_client_.reset();
+    connection_error_handler_.Reset();
+  }
+
+  // Unbinds and returns the associated interface request so it can be
+  // used in another context, such as on another thread or with a different
+  // implementation. Puts this object into a state where it can be rebound.
+  AssociatedInterfaceRequest<Interface> Unbind() {
+    DCHECK(endpoint_client_);
+
+    AssociatedInterfaceRequest<Interface> request;
+    request.Bind(endpoint_client_->PassHandle());
+
+    endpoint_client_.reset();
+    connection_error_handler_.Reset();
+
+    return request;
+  }
+
+  // Sets an error handler that will be called if a connection error occurs.
+  //
+  // This method may only be called after this AssociatedBinding has been bound
+  // to a message pipe. The error handler will be reset when this
+  // AssociatedBinding is unbound or closed.
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    DCHECK(is_bound());
+    connection_error_handler_ = error_handler;
+  }
+
+  // Returns the interface implementation that was previously specified.
+  Interface* impl() { return impl_; }
+
+  // Indicates whether the associated binding has been completed.
+  bool is_bound() const { return !!endpoint_client_; }
+
+  // Returns the associated group that this object belongs to. Returns null if
+  // the object is not bound.
+  AssociatedGroup* associated_group() {
+    return endpoint_client_ ? endpoint_client_->associated_group() : nullptr;
+  }
+
+ private:
+  void RunConnectionErrorHandler() {
+    if (!connection_error_handler_.is_null())
+      connection_error_handler_.Run();
+  }
+
+  std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
+
+  typename Interface::Stub_ stub_;
+  Interface* impl_;
+  base::Closure connection_error_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssociatedBinding);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_BINDING_H_
diff --git a/mojo/public/cpp/bindings/associated_group.h b/mojo/public/cpp/bindings/associated_group.h
new file mode 100644
index 0000000..836c0d6
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_group.h
@@ -0,0 +1,90 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_
+
+#include <utility>
+
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+namespace mojo {
+
+class AssociatedGroupController;
+
+// AssociatedGroup refers to all the interface endpoints running at one end of a
+// message pipe. It is used to create associated interfaces for that message
+// pipe.
+// It is thread safe and cheap to make copies.
+class AssociatedGroup {
+ public:
+  // Configuration used by CreateAssociatedInterface(). Please see the comments
+  // of that method for more details.
+  enum AssociatedInterfaceConfig { WILL_PASS_PTR, WILL_PASS_REQUEST };
+
+  AssociatedGroup();
+  AssociatedGroup(const AssociatedGroup& other);
+
+  ~AssociatedGroup();
+
+  AssociatedGroup& operator=(const AssociatedGroup& other);
+
+  // |config| indicates whether |ptr_info| or |request| will be sent to the
+  // remote side of the message pipe.
+  //
+  // NOTE: If |config| is |WILL_PASS_REQUEST|, you will want to bind |ptr_info|
+  // to a local AssociatedInterfacePtr to make calls. However, there is one
+  // restriction: the pointer should NOT be used to make calls before |request|
+  // is sent. Violating that will cause the message pipe to be closed. On the
+  // other hand, as soon as |request| is sent, the pointer is usable. There is
+  // no need to wait until |request| is bound to an implementation at the remote
+  // side.
+  template <typename T>
+  void CreateAssociatedInterface(
+      AssociatedInterfaceConfig config,
+      AssociatedInterfacePtrInfo<T>* ptr_info,
+      AssociatedInterfaceRequest<T>* request) {
+    ScopedInterfaceEndpointHandle local;
+    ScopedInterfaceEndpointHandle remote;
+    CreateEndpointHandlePair(&local, &remote);
+
+    if (!local.is_valid() || !remote.is_valid()) {
+      *ptr_info = AssociatedInterfacePtrInfo<T>();
+      *request = AssociatedInterfaceRequest<T>();
+      return;
+    }
+
+    if (config == WILL_PASS_PTR) {
+      ptr_info->set_handle(std::move(remote));
+
+      // The implementation is local, therefore set the version according to
+      // the interface definition that this code is built against.
+      ptr_info->set_version(T::Version_);
+      request->Bind(std::move(local));
+    } else {
+      ptr_info->set_handle(std::move(local));
+
+      // The implementation is remote, we don't know about its actual version
+      // yet.
+      ptr_info->set_version(0u);
+      request->Bind(std::move(remote));
+    }
+  }
+
+ private:
+  friend class AssociatedGroupController;
+
+  void CreateEndpointHandlePair(
+      ScopedInterfaceEndpointHandle* local_endpoint,
+      ScopedInterfaceEndpointHandle* remote_endpoint);
+
+  scoped_refptr<AssociatedGroupController> controller_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_
diff --git a/mojo/public/cpp/bindings/associated_group_controller.h b/mojo/public/cpp/bindings/associated_group_controller.h
new file mode 100644
index 0000000..0ab8253
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_group_controller.h
@@ -0,0 +1,85 @@
+// Copyright 2016 The Chromium Authors. All 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_ASSOCIATED_GROUP_CONTROLLER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_CONTROLLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+namespace mojo {
+
+class AssociatedGroup;
+class InterfaceEndpointClient;
+class InterfaceEndpointController;
+
+// An internal interface used to manage endpoints within an associated group.
+class AssociatedGroupController :
+    public base::RefCountedDeleteOnMessageLoop<AssociatedGroupController> {
+ public:
+  explicit AssociatedGroupController(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  // Creates a pair of interface endpoint handles. The method generates a new
+  // interface ID and assigns it to the two handles. |local_endpoint| is used
+  // locally; while |remote_endpoint| is sent over the message pipe.
+  virtual void CreateEndpointHandlePair(
+      ScopedInterfaceEndpointHandle* local_endpoint,
+      ScopedInterfaceEndpointHandle* remote_endpoint) = 0;
+
+  // Creates an interface endpoint handle from a given interface ID. The handle
+  // is used locally.
+  // Typically, this method is used to (1) create an endpoint handle for the
+  // master interface; or (2) create an endpoint handle on receiving an
+  // interface ID from the message pipe.
+  virtual ScopedInterfaceEndpointHandle CreateLocalEndpointHandle(
+      InterfaceId id) = 0;
+
+  // Closes an interface endpoint handle.
+  virtual void CloseEndpointHandle(InterfaceId id, bool is_local) = 0;
+
+  // Attaches a client to the specified endpoint to send and receive messages.
+  // The returned object is still owned by the controller. It must only be used
+  // on the same thread as this call, and only before the client is detached
+  // using DetachEndpointClient().
+  virtual InterfaceEndpointController* AttachEndpointClient(
+      const ScopedInterfaceEndpointHandle& handle,
+      InterfaceEndpointClient* endpoint_client,
+      scoped_refptr<base::SingleThreadTaskRunner> runner) = 0;
+
+  // Detaches the client attached to the specified endpoint. It must be called
+  // on the same thread as the corresponding AttachEndpointClient() call.
+  virtual void DetachEndpointClient(
+      const ScopedInterfaceEndpointHandle& handle) = 0;
+
+  // Raises an error on the underlying message pipe. It disconnects the pipe
+  // and notifies all interfaces running on this pipe.
+  virtual void RaiseError() = 0;
+
+  std::unique_ptr<AssociatedGroup> CreateAssociatedGroup();
+
+ protected:
+  friend class base::RefCountedDeleteOnMessageLoop<AssociatedGroupController>;
+  friend class base::DeleteHelper<AssociatedGroupController>;
+
+  // Creates a new ScopedInterfaceEndpointHandle associated with this
+  // controller.
+  ScopedInterfaceEndpointHandle CreateScopedInterfaceEndpointHandle(
+      InterfaceId id,
+      bool is_local);
+
+  virtual ~AssociatedGroupController();
+
+  DISALLOW_COPY_AND_ASSIGN(AssociatedGroupController);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_CONTROLLER_H_
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr.h b/mojo/public/cpp/bindings/associated_interface_ptr.h
new file mode 100644
index 0000000..10494ce
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_interface_ptr.h
@@ -0,0 +1,208 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h"
+
+namespace mojo {
+
+// Represents the client side of an associated interface. It is similar to
+// InterfacePtr, except that it doesn't own a message pipe handle.
+template <typename Interface>
+class AssociatedInterfacePtr {
+ public:
+  // Constructs an unbound AssociatedInterfacePtr.
+  AssociatedInterfacePtr() {}
+  AssociatedInterfacePtr(decltype(nullptr)) {}
+
+  AssociatedInterfacePtr(AssociatedInterfacePtr&& other) {
+    internal_state_.Swap(&other.internal_state_);
+  }
+
+  AssociatedInterfacePtr& operator=(AssociatedInterfacePtr&& other) {
+    reset();
+    internal_state_.Swap(&other.internal_state_);
+    return *this;
+  }
+
+  // Assigning nullptr to this class causes it to closes the associated
+  // interface (if any) and returns the pointer to the unbound state.
+  AssociatedInterfacePtr& operator=(decltype(nullptr)) {
+    reset();
+    return *this;
+  }
+
+  ~AssociatedInterfacePtr() {}
+
+  // Sets up this object as the client side of an associated interface.
+  // Calling with an invalid |info| has the same effect as reset(). In this
+  // case, the AssociatedInterfacePtr is not considered as bound.
+  //
+  // |runner| must belong to the same thread. It will be used to dispatch all
+  // callbacks and connection error notification. It is useful when you attach
+  // multiple task runners to a single thread for the purposes of task
+  // scheduling.
+  //
+  // NOTE: Please see the comments of
+  // AssociatedGroup.CreateAssociatedInterface() about when you can use this
+  // object to make calls.
+  void Bind(AssociatedInterfacePtrInfo<Interface> info,
+            scoped_refptr<base::SingleThreadTaskRunner> runner =
+                base::ThreadTaskRunnerHandle::Get()) {
+    reset();
+
+    bool is_local = info.handle().is_local();
+
+    DCHECK(is_local) << "The AssociatedInterfacePtrInfo is supposed to be used "
+                        "at the other side of the message pipe.";
+
+    if (info.is_valid() && is_local)
+      internal_state_.Bind(std::move(info), std::move(runner));
+  }
+
+  bool is_bound() const { return internal_state_.is_bound(); }
+
+  Interface* get() const { return internal_state_.instance(); }
+
+  // Functions like a pointer to Interface. Must already be bound.
+  Interface* operator->() const { return get(); }
+  Interface& operator*() const { return *get(); }
+
+  // Returns the version number of the interface that the remote side supports.
+  uint32_t version() const { return internal_state_.version(); }
+
+  // Returns the internal interface ID of this associated interface.
+  uint32_t interface_id() const { return internal_state_.interface_id(); }
+
+  // Queries the max version that the remote side supports. On completion, the
+  // result will be returned as the input of |callback|. The version number of
+  // this object will also be updated.
+  void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
+    internal_state_.QueryVersion(callback);
+  }
+
+  // If the remote side doesn't support the specified version, it will close the
+  // associated interface asynchronously. This does nothing if it's already
+  // known that the remote side supports the specified version, i.e., if
+  // |version <= this->version()|.
+  //
+  // After calling RequireVersion() with a version not supported by the remote
+  // side, all subsequent calls to interface methods will be ignored.
+  void RequireVersion(uint32_t version) {
+    internal_state_.RequireVersion(version);
+  }
+
+  // Closes the associated interface (if any) and returns the pointer to the
+  // unbound state.
+  void reset() {
+    State doomed;
+    internal_state_.Swap(&doomed);
+  }
+
+  // Indicates whether an error has been encountered. If true, method calls made
+  // on this interface will be dropped (and may already have been dropped).
+  bool encountered_error() const { return internal_state_.encountered_error(); }
+
+  // Registers a handler to receive error notifications.
+  //
+  // This method may only be called after the AssociatedInterfacePtr has been
+  // bound.
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    internal_state_.set_connection_error_handler(error_handler);
+  }
+
+  // Unbinds and returns the associated interface pointer information which
+  // could be used to setup an AssociatedInterfacePtr again. This method may be
+  // used to move the proxy to a different thread.
+  //
+  // It is an error to call PassInterface() while there are pending responses.
+  // TODO: fix this restriction, it's not always obvious when there is a
+  // pending response.
+  AssociatedInterfacePtrInfo<Interface> PassInterface() {
+    DCHECK(!internal_state_.has_pending_callbacks());
+    State state;
+    internal_state_.Swap(&state);
+
+    return state.PassInterface();
+  }
+
+  // Returns the associated group that this object belongs to. Returns null if
+  // the object is not bound.
+  AssociatedGroup* associated_group() {
+    return internal_state_.associated_group();
+  }
+
+  // DO NOT USE. Exposed only for internal use and for testing.
+  internal::AssociatedInterfacePtrState<Interface>* internal_state() {
+    return &internal_state_;
+  }
+
+  // Allow AssociatedInterfacePtr<> to be used in boolean expressions, but not
+  // implicitly convertible to a real bool (which is dangerous).
+ private:
+  // TODO(dcheng): Use an explicit conversion operator.
+  typedef internal::AssociatedInterfacePtrState<Interface>
+      AssociatedInterfacePtr::*Testable;
+
+ public:
+  operator Testable() const {
+    return internal_state_.is_bound() ? &AssociatedInterfacePtr::internal_state_
+                                      : nullptr;
+  }
+
+ private:
+  // Forbid the == and != operators explicitly, otherwise AssociatedInterfacePtr
+  // will be converted to Testable to do == or != comparison.
+  template <typename T>
+  bool operator==(const AssociatedInterfacePtr<T>& other) const = delete;
+  template <typename T>
+  bool operator!=(const AssociatedInterfacePtr<T>& other) const = delete;
+
+  typedef internal::AssociatedInterfacePtrState<Interface> State;
+  mutable State internal_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtr);
+};
+
+// Creates an associated interface. The output |ptr| should be used locally
+// while the returned request should be passed through the message pipe endpoint
+// referred to by |associated_group| to setup the corresponding asssociated
+// interface implementation at the remote side.
+//
+// NOTE: |ptr| should NOT be used to make calls before the request is sent.
+// Violating that will cause the message pipe to be closed. On the other hand,
+// as soon as the request is sent, |ptr| is usable. There is no need to wait
+// until the request is bound to an implementation at the remote side.
+template <typename Interface>
+AssociatedInterfaceRequest<Interface> GetProxy(
+    AssociatedInterfacePtr<Interface>* ptr,
+    AssociatedGroup* group,
+    scoped_refptr<base::SingleThreadTaskRunner> runner =
+        base::ThreadTaskRunnerHandle::Get()) {
+  AssociatedInterfaceRequest<Interface> request;
+  AssociatedInterfacePtrInfo<Interface> ptr_info;
+  group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_REQUEST,
+                                   &ptr_info, &request);
+
+  ptr->Bind(std::move(ptr_info), std::move(runner));
+  return request;
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr_info.h b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
new file mode 100644
index 0000000..bfb3297
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
@@ -0,0 +1,76 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_INFO_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_INFO_H_
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+namespace mojo {
+
+// AssociatedInterfacePtrInfo stores necessary information to construct an
+// associated interface pointer. It is similar to InterfacePtrInfo except that
+// it doesn't own a message pipe handle.
+template <typename Interface>
+class AssociatedInterfacePtrInfo {
+ public:
+  AssociatedInterfacePtrInfo() : version_(0u) {}
+
+  AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other)
+      : handle_(std::move(other.handle_)), version_(other.version_) {
+    other.version_ = 0u;
+  }
+
+  AssociatedInterfacePtrInfo(ScopedInterfaceEndpointHandle handle,
+                             uint32_t version)
+      : handle_(std::move(handle)), version_(version) {}
+
+  ~AssociatedInterfacePtrInfo() {}
+
+  AssociatedInterfacePtrInfo& operator=(AssociatedInterfacePtrInfo&& other) {
+    if (this != &other) {
+      handle_ = std::move(other.handle_);
+      version_ = other.version_;
+      other.version_ = 0u;
+    }
+
+    return *this;
+  }
+
+  bool is_valid() const { return handle_.is_valid(); }
+
+  ScopedInterfaceEndpointHandle PassHandle() {
+    return std::move(handle_);
+  }
+  const ScopedInterfaceEndpointHandle& handle() const { return handle_; }
+  void set_handle(ScopedInterfaceEndpointHandle handle) {
+    handle_ = std::move(handle);
+  }
+
+  uint32_t version() const { return version_; }
+  void set_version(uint32_t version) { version_ = version; }
+
+  bool Equals(const AssociatedInterfacePtrInfo& other) const {
+    if (this == &other)
+      return true;
+
+    // Now that the two refer to different objects, they are equivalent if
+    // and only if they are both invalid.
+    return !is_valid() && !other.is_valid();
+  }
+
+ private:
+  ScopedInterfaceEndpointHandle handle_;
+  uint32_t version_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtrInfo);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_INFO_H_
diff --git a/mojo/public/cpp/bindings/associated_interface_request.h b/mojo/public/cpp/bindings/associated_interface_request.h
new file mode 100644
index 0000000..30fcd16
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_interface_request.h
@@ -0,0 +1,85 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
+
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+namespace mojo {
+
+// AssociatedInterfaceRequest represents an associated interface request. It is
+// similar to InterfaceRequest except that it doesn't own a message pipe handle.
+template <typename Interface>
+class AssociatedInterfaceRequest {
+ public:
+  // Constructs an empty AssociatedInterfaceRequest, representing that the
+  // client is not requesting an implementation of Interface.
+  AssociatedInterfaceRequest() {}
+  AssociatedInterfaceRequest(decltype(nullptr)) {}
+
+  // Takes the interface endpoint handle from another
+  // AssociatedInterfaceRequest.
+  AssociatedInterfaceRequest(AssociatedInterfaceRequest&& other) {
+    handle_ = std::move(other.handle_);
+  }
+  AssociatedInterfaceRequest& operator=(AssociatedInterfaceRequest&& other) {
+    if (this != &other)
+      handle_ = std::move(other.handle_);
+    return *this;
+  }
+
+  // Assigning to nullptr resets the AssociatedInterfaceRequest to an empty
+  // state, closing the interface endpoint handle currently bound to it (if
+  // any).
+  AssociatedInterfaceRequest& operator=(decltype(nullptr)) {
+    handle_.reset();
+    return *this;
+  }
+
+  // Indicates whether the request currently contains a valid interface endpoint
+  // handle.
+  bool is_pending() const { return handle_.is_valid(); }
+
+  void Bind(ScopedInterfaceEndpointHandle handle) {
+    handle_ = std::move(handle);
+  }
+
+  ScopedInterfaceEndpointHandle PassHandle() {
+    return std::move(handle_);
+  }
+
+  const ScopedInterfaceEndpointHandle& handle() const { return handle_; }
+
+  bool Equals(const AssociatedInterfaceRequest& other) const {
+    if (this == &other)
+      return true;
+
+    // Now that the two refer to different objects, they are equivalent if
+    // and only if they are both invalid.
+    return !is_pending() && !other.is_pending();
+  }
+
+ private:
+  ScopedInterfaceEndpointHandle handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssociatedInterfaceRequest);
+};
+
+// Makes an AssociatedInterfaceRequest bound to the specified associated
+// endpoint.
+template <typename Interface>
+AssociatedInterfaceRequest<Interface> MakeAssociatedRequest(
+    ScopedInterfaceEndpointHandle handle) {
+  AssociatedInterfaceRequest<Interface> request;
+  request.Bind(std::move(handle));
+  return request;
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
diff --git a/mojo/public/cpp/bindings/binding.h b/mojo/public/cpp/bindings/binding.h
new file mode 100644
index 0000000..8c9ee2f
--- /dev/null
+++ b/mojo/public/cpp/bindings/binding.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
+
+#include <utility>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/binding_state.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class AssociatedGroup;
+
+// Represents the binding of an interface implementation to a message pipe.
+// When the |Binding| object is destroyed, the binding between the message pipe
+// and the interface is torn down and the message pipe is closed, leaving the
+// interface implementation in an unbound state.
+//
+// Example:
+//
+//   #include "foo.mojom.h"
+//
+//   class FooImpl : public Foo {
+//    public:
+//     explicit FooImpl(InterfaceRequest<Foo> request)
+//         : binding_(this, std::move(request)) {}
+//
+//     // Foo implementation here.
+//
+//    private:
+//     Binding<Foo> binding_;
+//   };
+//
+//   class MyFooFactory : public InterfaceFactory<Foo> {
+//    public:
+//     void Create(..., InterfaceRequest<Foo> request) override {
+//       auto f = new FooImpl(std::move(request));
+//       // Do something to manage the lifetime of |f|. Use StrongBinding<> to
+//       // delete FooImpl on connection errors.
+//     }
+//   };
+//
+// This class is thread hostile while bound to a message pipe. All calls to this
+// class must be from the thread that bound it. The interface implementation's
+// methods will be called from the thread that bound this. If a Binding is not
+// bound to a message pipe, it may be bound or destroyed on any thread.
+//
+// When you bind this class to a message pipe, optionally you can specify a
+// base::SingleThreadTaskRunner. This task runner must belong to the same
+// thread. It will be used to dispatch incoming method calls and connection
+// error notification. It is useful when you attach multiple task runners to a
+// single thread for the purposes of task scheduling. Please note that incoming
+// synchrounous method calls may not be run from this task runner, when they
+// reenter outgoing synchrounous calls on the same thread.
+template <typename Interface>
+class Binding {
+ public:
+  // Constructs an incomplete binding that will use the implementation |impl|.
+  // The binding may be completed with a subsequent call to the |Bind| method.
+  // Does not take ownership of |impl|, which must outlive the binding.
+  explicit Binding(Interface* impl) : internal_state_(impl) {}
+
+  // Constructs a completed binding of message pipe |handle| to implementation
+  // |impl|. Does not take ownership of |impl|, which must outlive the binding.
+  Binding(Interface* impl,
+          ScopedMessagePipeHandle handle,
+          scoped_refptr<base::SingleThreadTaskRunner> runner =
+              base::ThreadTaskRunnerHandle::Get())
+      : Binding(impl) {
+    Bind(std::move(handle), std::move(runner));
+  }
+
+  // Constructs a completed binding of |impl| to a new message pipe, passing the
+  // client end to |ptr|, which takes ownership of it. The caller is expected to
+  // pass |ptr| on to the client of the service. Does not take ownership of any
+  // of the parameters. |impl| must outlive the binding. |ptr| only needs to
+  // last until the constructor returns.
+  Binding(Interface* impl,
+          InterfacePtr<Interface>* ptr,
+          scoped_refptr<base::SingleThreadTaskRunner> runner =
+              base::ThreadTaskRunnerHandle::Get())
+      : Binding(impl) {
+    Bind(ptr, std::move(runner));
+  }
+
+  // Constructs a completed binding of |impl| to the message pipe endpoint in
+  // |request|, taking ownership of the endpoint. Does not take ownership of
+  // |impl|, which must outlive the binding.
+  Binding(Interface* impl,
+          InterfaceRequest<Interface> request,
+          scoped_refptr<base::SingleThreadTaskRunner> runner =
+              base::ThreadTaskRunnerHandle::Get())
+      : Binding(impl) {
+    Bind(request.PassMessagePipe(), std::move(runner));
+  }
+
+  // Tears down the binding, closing the message pipe and leaving the interface
+  // implementation unbound.
+  ~Binding() {}
+
+  // Returns an InterfacePtr bound to one end of a pipe whose other end is
+  // bound to |this|.
+  InterfacePtr<Interface> CreateInterfacePtrAndBind(
+      scoped_refptr<base::SingleThreadTaskRunner> runner =
+          base::ThreadTaskRunnerHandle::Get()) {
+    InterfacePtr<Interface> interface_ptr;
+    Bind(&interface_ptr, std::move(runner));
+    return interface_ptr;
+  }
+
+  // Completes a binding that was constructed with only an interface
+  // implementation. Takes ownership of |handle| and binds it to the previously
+  // specified implementation.
+  void Bind(ScopedMessagePipeHandle handle,
+            scoped_refptr<base::SingleThreadTaskRunner> runner =
+                base::ThreadTaskRunnerHandle::Get()) {
+    internal_state_.Bind(std::move(handle), std::move(runner));
+  }
+
+  // Completes a binding that was constructed with only an interface
+  // implementation by creating a new message pipe, binding one end of it to the
+  // previously specified implementation, and passing the other to |ptr|, which
+  // takes ownership of it. The caller is expected to pass |ptr| on to the
+  // eventual client of the service. Does not take ownership of |ptr|.
+  void Bind(InterfacePtr<Interface>* ptr,
+            scoped_refptr<base::SingleThreadTaskRunner> runner =
+                base::ThreadTaskRunnerHandle::Get()) {
+    MessagePipe pipe;
+    ptr->Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0),
+                                          Interface::Version_),
+              runner);
+    Bind(std::move(pipe.handle1), std::move(runner));
+  }
+
+  // Completes a binding that was constructed with only an interface
+  // implementation by removing the message pipe endpoint from |request| and
+  // binding it to the previously specified implementation.
+  void Bind(InterfaceRequest<Interface> request,
+            scoped_refptr<base::SingleThreadTaskRunner> runner =
+                base::ThreadTaskRunnerHandle::Get()) {
+    Bind(request.PassMessagePipe(), std::move(runner));
+  }
+
+  // Whether there are any associated interfaces running on the pipe currently.
+  bool HasAssociatedInterfaces() const {
+    return internal_state_.HasAssociatedInterfaces();
+  }
+
+  // Stops processing incoming messages until
+  // ResumeIncomingMethodCallProcessing(), or WaitForIncomingMethodCall().
+  // Outgoing messages are still sent.
+  //
+  // No errors are detected on the message pipe while paused.
+  //
+  // This method may only be called if the object has been bound to a message
+  // pipe and there are no associated interfaces running.
+  void PauseIncomingMethodCallProcessing() {
+    CHECK(!HasAssociatedInterfaces());
+    internal_state_.PauseIncomingMethodCallProcessing();
+  }
+  void ResumeIncomingMethodCallProcessing() {
+    internal_state_.ResumeIncomingMethodCallProcessing();
+  }
+
+  // Blocks the calling thread until either a call arrives on the previously
+  // bound message pipe, the deadline is exceeded, or an error occurs. Returns
+  // true if a method was successfully read and dispatched.
+  //
+  // This method may only be called if the object has been bound to a message
+  // pipe and there are no associated interfaces running.
+  bool WaitForIncomingMethodCall(
+      MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) {
+    CHECK(!HasAssociatedInterfaces());
+    return internal_state_.WaitForIncomingMethodCall(deadline);
+  }
+
+  // Closes the message pipe that was previously bound. Put this object into a
+  // state where it can be rebound to a new pipe.
+  void Close() { internal_state_.Close(); }
+
+  // Unbinds the underlying pipe from this binding and returns it so it can be
+  // used in another context, such as on another thread or with a different
+  // implementation. Put this object into a state where it can be rebound to a
+  // new pipe.
+  //
+  // This method may only be called if the object has been bound to a message
+  // pipe and there are no associated interfaces running.
+  //
+  // TODO(yzshen): For now, users need to make sure there is no one holding
+  // on to associated interface endpoint handles at both sides of the
+  // message pipe in order to call this method. We need a way to forcefully
+  // invalidate associated interface endpoint handles.
+  InterfaceRequest<Interface> Unbind() {
+    CHECK(!HasAssociatedInterfaces());
+    return internal_state_.Unbind();
+  }
+
+  // Sets an error handler that will be called if a connection error occurs on
+  // the bound message pipe.
+  //
+  // This method may only be called after this Binding has been bound to a
+  // message pipe. The error handler will be reset when this Binding is unbound
+  // or closed.
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    DCHECK(is_bound());
+    internal_state_.set_connection_error_handler(error_handler);
+  }
+
+  // Returns the interface implementation that was previously specified. Caller
+  // does not take ownership.
+  Interface* impl() { return internal_state_.impl(); }
+
+  // Indicates whether the binding has been completed (i.e., whether a message
+  // pipe has been bound to the implementation).
+  bool is_bound() const { return internal_state_.is_bound(); }
+
+  // Returns the value of the handle currently bound to this Binding which can
+  // be used to make explicit Wait/WaitMany calls. Requires that the Binding be
+  // bound. Ownership of the handle is retained by the Binding, it is not
+  // transferred to the caller.
+  MessagePipeHandle handle() const { return internal_state_.handle(); }
+
+  // Returns the associated group that this object belongs to. Returns null if:
+  //   - this object is not bound; or
+  //   - the interface doesn't have methods to pass associated interface
+  //     pointers or requests.
+  AssociatedGroup* associated_group() {
+    return internal_state_.associated_group();
+  }
+
+  // Exposed for testing, should not generally be used.
+  void EnableTestingMode() { internal_state_.EnableTestingMode(); }
+
+ private:
+  internal::BindingState<Interface, Interface::PassesAssociatedKinds_>
+      internal_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(Binding);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
diff --git a/mojo/public/cpp/bindings/binding_set.h b/mojo/public/cpp/bindings/binding_set.h
new file mode 100644
index 0000000..b1baca6
--- /dev/null
+++ b/mojo/public/cpp/bindings/binding_set.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mojo {
+
+// Use this class to manage a set of bindings, which are automatically destroyed
+// and removed from the set when the pipe they are bound to is disconnected.
+template <typename Interface>
+class BindingSet {
+ public:
+  BindingSet() {}
+  ~BindingSet() { CloseAllBindings(); }
+
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    error_handler_ = error_handler;
+  }
+
+  void AddBinding(Interface* impl, InterfaceRequest<Interface> request) {
+    auto binding = new Element(impl, std::move(request));
+    binding->set_connection_error_handler(
+        base::Bind(&BindingSet::OnConnectionError, base::Unretained(this)));
+    bindings_.push_back(binding->GetWeakPtr());
+  }
+
+  // Returns an InterfacePtr bound to one end of a pipe whose other end is
+  // bound to |this|.
+  InterfacePtr<Interface> CreateInterfacePtrAndBind(Interface* impl) {
+    InterfacePtr<Interface> interface_ptr;
+    AddBinding(impl, GetProxy(&interface_ptr));
+    return interface_ptr;
+  }
+
+  void CloseAllBindings() {
+    for (const auto& it : bindings_) {
+      if (it) {
+        it->Close();
+        delete it.get();
+      }
+    }
+    bindings_.clear();
+  }
+
+  bool empty() const { return bindings_.empty(); }
+
+ private:
+  class Element {
+   public:
+    Element(Interface* impl, InterfaceRequest<Interface> request)
+        : binding_(impl, std::move(request)), weak_ptr_factory_(this) {
+      binding_.set_connection_error_handler(
+          base::Bind(&Element::OnConnectionError, base::Unretained(this)));
+    }
+
+    ~Element() {}
+
+    void set_connection_error_handler(const base::Closure& error_handler) {
+      error_handler_ = error_handler;
+    }
+
+    base::WeakPtr<Element> GetWeakPtr() {
+      return weak_ptr_factory_.GetWeakPtr();
+    }
+
+    void Close() { binding_.Close(); }
+
+    void OnConnectionError() {
+      base::Closure error_handler = error_handler_;
+      delete this;
+      if (!error_handler.is_null())
+        error_handler.Run();
+    }
+
+   private:
+    Binding<Interface> binding_;
+    base::Closure error_handler_;
+    base::WeakPtrFactory<Element> weak_ptr_factory_;
+
+    DISALLOW_COPY_AND_ASSIGN(Element);
+  };
+
+  void OnConnectionError() {
+    // Clear any deleted bindings.
+    bindings_.erase(std::remove_if(bindings_.begin(), bindings_.end(),
+                                   [](const base::WeakPtr<Element>& p) {
+                                     return p.get() == nullptr;
+                                   }),
+                    bindings_.end());
+
+    if (!error_handler_.is_null())
+      error_handler_.Run();
+  }
+
+  base::Closure error_handler_;
+  std::vector<base::WeakPtr<Element>> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(BindingSet);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
new file mode 100644
index 0000000..d14ad17
--- /dev/null
+++ b/mojo/public/cpp/bindings/connector.h
@@ -0,0 +1,211 @@
+// 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_CONNECTOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_CONNECTOR_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/sync_handle_watcher.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/watcher.h"
+
+namespace base {
+class Lock;
+}
+
+namespace mojo {
+
+// 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.
+//   - Sending messages can be configured to be thread safe (please see comments
+//     of the constructor). Other than that, the object should only be accessed
+//     on the creating thread.
+class Connector : public MessageReceiver {
+ public:
+  enum ConnectorConfig {
+    // Connector::Accept() is only called from a single thread.
+    SINGLE_THREADED_SEND,
+    // Connector::Accept() is allowed to be called from multiple threads.
+    MULTI_THREADED_SEND
+  };
+
+  // The Connector takes ownership of |message_pipe|.
+  Connector(ScopedMessagePipeHandle message_pipe,
+            ConnectorConfig config,
+            scoped_refptr<base::SingleThreadTaskRunner> runner);
+  ~Connector() override;
+
+  // 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) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    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) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    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_connection_error_handler(const base::Closure& error_handler) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    connection_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 {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return error_;
+  }
+
+  // Closes the pipe. The connector is put into a quiescent state.
+  //
+  // Please note that this method shouldn't be called unless it results from an
+  // explicit request of the user of bindings (e.g., the user sets an
+  // InterfacePtr to null or closes a Binding).
+  void CloseMessagePipe();
+
+  // Releases the pipe. Connector is put into a quiescent state.
+  ScopedMessagePipeHandle PassMessagePipe();
+
+  // Enters the error state. The upper layer may do this for unrecoverable
+  // issues such as invalid messages are received. If a connection error handler
+  // has been set, it will be called asynchronously.
+  //
+  // It is a no-op if the connector is already in the error state or there isn't
+  // a bound message pipe. Otherwise, it closes the message pipe, which notifies
+  // the other end and also prevents potential danger (say, the caller raises
+  // an error because it believes the other end is malicious). In order to
+  // appear to the user that the connector still binds to a message pipe, it
+  // creates a new message pipe, closes one end and binds to the other.
+  void RaiseError();
+
+  // Is the connector bound to a MessagePipe handle?
+  bool is_valid() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return message_pipe_.is_valid();
+  }
+
+  // Waits for the next message on the pipe, blocking until one arrives,
+  // |deadline| elapses, or an error happens. Returns |true| if a message has
+  // been delivered, |false| otherwise.
+  bool WaitForIncomingMessage(MojoDeadline deadline);
+
+  // See Binding for details of pause/resume.
+  void PauseIncomingMethodCallProcessing();
+  void ResumeIncomingMethodCallProcessing();
+
+  // MessageReceiver implementation:
+  bool Accept(Message* message) override;
+
+  MessagePipeHandle handle() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return message_pipe_.get();
+  }
+
+  // Allows |message_pipe_| to be watched while others perform sync handle
+  // watching on the same thread. Please see comments of
+  // SyncHandleWatcher::AllowWokenUpBySyncWatchOnSameThread().
+  void AllowWokenUpBySyncWatchOnSameThread();
+
+  // Watches |message_pipe_| (as well as other handles registered to be watched
+  // together) synchronously.
+  // This method:
+  //   - returns true when |should_stop| is set to true;
+  //   - return false when any error occurs, including |message_pipe_| being
+  //     closed.
+  bool SyncWatch(const bool* should_stop);
+
+  // Whether currently the control flow is inside the sync handle watcher
+  // callback.
+  bool during_sync_handle_watcher_callback() const {
+    return sync_handle_watcher_callback_count_ > 0;
+  }
+
+  base::SingleThreadTaskRunner* task_runner() const {
+    return task_runner_.get();
+  }
+
+ private:
+  // Callback of mojo::Watcher.
+  void OnWatcherHandleReady(MojoResult result);
+  // Callback of SyncHandleWatcher.
+  void OnSyncHandleWatcherHandleReady(MojoResult result);
+  void OnHandleReadyInternal(MojoResult result);
+
+  void WaitToReadMore();
+
+  // Returns false if |this| was destroyed during message dispatch.
+  WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result);
+
+  // |this| can be destroyed during message dispatch.
+  void ReadAllAvailableMessages();
+
+  // If |force_pipe_reset| is true, this method replaces the existing
+  // |message_pipe_| with a dummy message pipe handle (whose peer is closed).
+  // If |force_async_handler| is true, |connection_error_handler_| is called
+  // asynchronously.
+  void HandleError(bool force_pipe_reset, bool force_async_handler);
+
+  // Cancels any calls made to |waiter_|.
+  void CancelWait();
+
+  void EnsureSyncWatcherExists();
+
+  base::Closure connection_error_handler_;
+
+  ScopedMessagePipeHandle message_pipe_;
+  MessageReceiver* incoming_receiver_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  Watcher handle_watcher_;
+
+  bool error_;
+  bool drop_writes_;
+  bool enforce_errors_from_incoming_receiver_;
+
+  bool paused_;
+
+  // If sending messages is allowed from multiple threads, |lock_| is used to
+  // protect modifications to |message_pipe_| and |drop_writes_|.
+  std::unique_ptr<base::Lock> lock_;
+
+  std::unique_ptr<SyncHandleWatcher> sync_watcher_;
+  bool allow_woken_up_by_others_;
+  // If non-zero, currently the control flow is inside the sync handle watcher
+  // callback.
+  size_t sync_handle_watcher_callback_count_;
+
+  base::ThreadChecker thread_checker_;
+
+  // Create a single weak ptr and use it everywhere, to avoid the malloc/free
+  // cost of creating a new weak ptr whenever it is needed.
+  base::WeakPtr<Connector> weak_self_;
+  base::WeakPtrFactory<Connector> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(Connector);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_CONNECTOR_H_
diff --git a/mojo/public/cpp/bindings/enum_traits.h b/mojo/public/cpp/bindings/enum_traits.h
new file mode 100644
index 0000000..2c528f3
--- /dev/null
+++ b/mojo/public/cpp/bindings/enum_traits.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All 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_ENUM_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ENUM_TRAITS_H_
+
+namespace mojo {
+
+// This must be specialized for any type |T| to be serialized/deserialized as a
+// mojom enum |MojomType|. Each specialization needs to implement:
+//
+//   template <>
+//   struct EnumTraits<MojomType, T> {
+//     static MojomType ToMojom(T input);
+//
+//     // Returning false results in deserialization failure and causes the
+//     // message pipe receiving it to be disconnected.
+//     static bool FromMojom(MojomType input, T* output);
+//   };
+//
+template <typename MojomType, typename T>
+struct EnumTraits;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_ENUM_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h
new file mode 100644
index 0000000..9dc40a2
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_endpoint_client.h
@@ -0,0 +1,155 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ENDPOINT_CLIENT_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ENDPOINT_CLIENT_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+namespace mojo {
+
+class AssociatedGroup;
+class AssociatedGroupController;
+class InterfaceEndpointController;
+
+// InterfaceEndpointClient handles message sending and receiving of an interface
+// endpoint, either the implementation side or the client side.
+// It should only be accessed and destructed on the creating thread.
+class InterfaceEndpointClient : public MessageReceiverWithResponder {
+ public:
+  // |receiver| is okay to be null. If it is not null, it must outlive this
+  // object.
+  InterfaceEndpointClient(ScopedInterfaceEndpointHandle handle,
+                          MessageReceiverWithResponderStatus* receiver,
+                          std::unique_ptr<MessageFilter> payload_validator,
+                          bool expect_sync_requests,
+                          scoped_refptr<base::SingleThreadTaskRunner> runner);
+  ~InterfaceEndpointClient() override;
+
+  // Sets the error handler to receive notifications when an error is
+  // encountered.
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    error_handler_ = error_handler;
+  }
+
+  // Returns true if an error was encountered.
+  bool encountered_error() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return encountered_error_;
+  }
+
+  // Returns true if this endpoint has any pending callbacks.
+  bool has_pending_responders() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return !async_responders_.empty() || !sync_responses_.empty();
+  }
+
+  AssociatedGroupController* group_controller() const {
+    return handle_.group_controller();
+  }
+  AssociatedGroup* associated_group();
+  uint32_t interface_id() const;
+
+  // After this call the object is in an invalid state and shouldn't be reused.
+  ScopedInterfaceEndpointHandle PassHandle();
+
+  // Raises an error on the underlying message pipe. It disconnects the pipe
+  // and notifies all interfaces running on this pipe.
+  void RaiseError();
+
+  // MessageReceiverWithResponder implementation:
+  bool Accept(Message* message) override;
+  bool AcceptWithResponder(Message* message,
+                           MessageReceiver* responder) override;
+
+  // The following methods are called by the router. They must be called
+  // outside of the router's lock.
+
+  // NOTE: |message| must have passed message header validation.
+  bool HandleIncomingMessage(Message* message);
+  void NotifyError();
+
+ private:
+  // Maps from the id of a response to the MessageReceiver that handles the
+  // response.
+  using AsyncResponderMap =
+      std::map<uint64_t, std::unique_ptr<MessageReceiver>>;
+
+  struct SyncResponseInfo {
+   public:
+    explicit SyncResponseInfo(bool* in_response_received);
+    ~SyncResponseInfo();
+
+    std::unique_ptr<Message> response;
+
+    // Points to a stack-allocated variable.
+    bool* response_received;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(SyncResponseInfo);
+  };
+
+  using SyncResponseMap = std::map<uint64_t, std::unique_ptr<SyncResponseInfo>>;
+
+  // Used as the sink for |payload_validator_| and forwards messages to
+  // HandleValidatedMessage().
+  class HandleIncomingMessageThunk : public MessageReceiver {
+   public:
+    explicit HandleIncomingMessageThunk(InterfaceEndpointClient* owner);
+    ~HandleIncomingMessageThunk() override;
+
+    // MessageReceiver implementation:
+    bool Accept(Message* message) override;
+
+   private:
+    InterfaceEndpointClient* const owner_;
+
+    DISALLOW_COPY_AND_ASSIGN(HandleIncomingMessageThunk);
+  };
+
+  bool HandleValidatedMessage(Message* message);
+
+  ScopedInterfaceEndpointHandle handle_;
+  std::unique_ptr<AssociatedGroup> associated_group_;
+  InterfaceEndpointController* controller_;
+
+  MessageReceiverWithResponderStatus* const incoming_receiver_;
+  std::unique_ptr<MessageFilter> payload_validator_;
+  HandleIncomingMessageThunk thunk_;
+
+  AsyncResponderMap async_responders_;
+  SyncResponseMap sync_responses_;
+
+  uint64_t next_request_id_;
+
+  base::Closure error_handler_;
+  bool encountered_error_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  base::ThreadChecker thread_checker_;
+
+  base::WeakPtrFactory<InterfaceEndpointClient> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterfaceEndpointClient);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ENDPOINT_CLIENT_H_
diff --git a/mojo/public/cpp/bindings/interface_endpoint_controller.h b/mojo/public/cpp/bindings/interface_endpoint_controller.h
new file mode 100644
index 0000000..8d99d4a
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_endpoint_controller.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All 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_ENDPOINT_CONTROLLER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ENDPOINT_CONTROLLER_H_
+
+namespace mojo {
+
+class Message;
+
+// A control interface exposed by AssociatedGroupController for interface
+// endpoints.
+class InterfaceEndpointController {
+ public:
+  virtual ~InterfaceEndpointController() {}
+
+  virtual bool SendMessage(Message* message) = 0;
+
+  // Allows the interface endpoint to watch for incoming sync messages while
+  // others perform sync handle watching on the same thread. Please see comments
+  // of SyncHandleWatcher::AllowWokenUpBySyncWatchOnSameThread().
+  virtual void AllowWokenUpBySyncWatchOnSameThread() = 0;
+
+  // Watches the interface endpoint for incoming sync messages. (It also watches
+  // other other handles registered to be watched together.)
+  // This method:
+  //   - returns true when |should_stop| is set to true;
+  //   - return false otherwise, including
+  //     MultiplexRouter::DetachEndpointClient() being called for the same
+  //     interface endpoint.
+  virtual bool SyncWatch(const bool* should_stop) = 0;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ENDPOINT_CONTROLLER_H_
diff --git a/mojo/public/cpp/bindings/interface_id.h b/mojo/public/cpp/bindings/interface_id.h
new file mode 100644
index 0000000..53475d6
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_id.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ID_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ID_H_
+
+#include <stdint.h>
+
+namespace mojo {
+
+// The size of the type matters because it is directly used in messages.
+using InterfaceId = uint32_t;
+
+// IDs of associated interface can be generated at both sides of the message
+// pipe. In order to avoid collision, the highest bit is used as namespace bit:
+// at the side where the client-side of the master interface lives, IDs are
+// generated with the namespace bit set to 1; at the opposite side IDs are
+// generated with the namespace bit set to 0.
+const uint32_t kInterfaceIdNamespaceMask = 0x80000000;
+
+const InterfaceId kMasterInterfaceId = 0x00000000;
+const InterfaceId kInvalidInterfaceId = 0xFFFFFFFF;
+
+inline bool IsMasterInterfaceId(InterfaceId id) {
+  return id == kMasterInterfaceId;
+}
+
+inline bool IsValidInterfaceId(InterfaceId id) {
+  return id != kInvalidInterfaceId;
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ID_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..edcb9bf
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/callback_forward.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/lib/interface_ptr_state.h"
+
+namespace mojo {
+
+class AssociatedGroup;
+
+// A pointer to a local proxy of a remote Interface implementation. Uses a
+// message pipe to communicate with the remote implementation, and automatically
+// closes the pipe and deletes the proxy on destruction. The pointer must be
+// bound to a message pipe before the interface methods can be called.
+//
+// This class is thread hostile, as is the local proxy it manages, while bound
+// to a message pipe. All calls to this class or the proxy should be from the
+// same thread that bound it. If you need to move the proxy to a different
+// thread, extract the InterfacePtrInfo (containing just the message pipe and
+// any version information) using PassInterface(), pass it to a different
+// thread, and create and bind a new InterfacePtr from that thread. If an
+// InterfacePtr is not bound to a message pipe, it may be bound or destroyed on
+// any thread.
+template <typename Interface>
+class InterfacePtr {
+ public:
+  // Constructs an unbound InterfacePtr.
+  InterfacePtr() {}
+  InterfacePtr(decltype(nullptr)) {}
+
+  // Takes over the binding of another InterfacePtr.
+  InterfacePtr(InterfacePtr&& other) {
+    internal_state_.Swap(&other.internal_state_);
+  }
+
+  // Takes over the binding of another InterfacePtr, and closes any message pipe
+  // already bound to this pointer.
+  InterfacePtr& operator=(InterfacePtr&& other) {
+    reset();
+    internal_state_.Swap(&other.internal_state_);
+    return *this;
+  }
+
+  // Assigning nullptr to this class causes it to close the currently bound
+  // message pipe (if any) and returns the pointer to the unbound state.
+  InterfacePtr& operator=(decltype(nullptr)) {
+    reset();
+    return *this;
+  }
+
+  // Closes the bound message pipe (if any) on destruction.
+  ~InterfacePtr() {}
+
+  // Binds the InterfacePtr to a remote implementation of Interface.
+  //
+  // Calling with an invalid |info| (containing an invalid message pipe handle)
+  // has the same effect as reset(). In this case, the InterfacePtr is not
+  // considered as bound.
+  //
+  // |runner| must belong to the same thread. It will be used to dispatch all
+  // callbacks and connection error notification. It is useful when you attach
+  // multiple task runners to a single thread for the purposes of task
+  // scheduling.
+  void Bind(InterfacePtrInfo<Interface> info,
+            scoped_refptr<base::SingleThreadTaskRunner> runner =
+                base::ThreadTaskRunnerHandle::Get()) {
+    reset();
+    if (info.is_valid())
+      internal_state_.Bind(std::move(info), std::move(runner));
+  }
+
+  // Returns whether or not this InterfacePtr is bound to a message pipe.
+  bool is_bound() const { return internal_state_.is_bound(); }
+
+  // Returns a raw pointer to the local proxy. Caller does not take ownership.
+  // Note that the local proxy is thread hostile, as stated above.
+  Interface* get() const { return internal_state_.instance(); }
+
+  // Functions like a pointer to Interface. Must already be bound.
+  Interface* operator->() const { return get(); }
+  Interface& operator*() const { return *get(); }
+
+  // Returns the version number of the interface that the remote side supports.
+  uint32_t version() const { return internal_state_.version(); }
+
+  // Queries the max version that the remote side supports. On completion, the
+  // result will be returned as the input of |callback|. The version number of
+  // this interface pointer will also be updated.
+  void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
+    internal_state_.QueryVersion(callback);
+  }
+
+  // If the remote side doesn't support the specified version, it will close its
+  // end of the message pipe asynchronously. This does nothing if it's already
+  // known that the remote side supports the specified version, i.e., if
+  // |version <= this->version()|.
+  //
+  // After calling RequireVersion() with a version not supported by the remote
+  // side, all subsequent calls to interface methods will be ignored.
+  void RequireVersion(uint32_t version) {
+    internal_state_.RequireVersion(version);
+  }
+
+  // Closes the bound message pipe (if any) and returns the pointer to the
+  // unbound state.
+  void reset() {
+    State doomed;
+    internal_state_.Swap(&doomed);
+  }
+
+  // Whether there are any associated interfaces running on the pipe currently.
+  bool HasAssociatedInterfaces() const {
+    return internal_state_.HasAssociatedInterfaces();
+  }
+
+  // Indicates whether the message pipe has encountered an error. If true,
+  // method calls made on this interface will be dropped (and may already have
+  // been dropped).
+  bool encountered_error() const { return internal_state_.encountered_error(); }
+
+  // Registers a handler to receive error notifications. The handler will be
+  // called from the thread that owns this InterfacePtr.
+  //
+  // This method may only be called after the InterfacePtr has been bound to a
+  // message pipe.
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    internal_state_.set_connection_error_handler(error_handler);
+  }
+
+  // Unbinds the InterfacePtr and returns the information which could be used
+  // to setup an InterfacePtr again. This method may be used to move the proxy
+  // to a different thread (see class comments for details).
+  //
+  // It is an error to call PassInterface() while:
+  //   - there are pending responses; or
+  //     TODO: fix this restriction, it's not always obvious when there is a
+  //     pending response.
+  //   - there are associated interfaces running.
+  //     TODO(yzshen): For now, users need to make sure there is no one holding
+  //     on to associated interface endpoint handles at both sides of the
+  //     message pipe in order to call this method. We need a way to forcefully
+  //     invalidate associated interface endpoint handles.
+  InterfacePtrInfo<Interface> PassInterface() {
+    CHECK(!HasAssociatedInterfaces());
+    CHECK(!internal_state_.has_pending_callbacks());
+    State state;
+    internal_state_.Swap(&state);
+
+    return state.PassInterface();
+  }
+
+  // Returns the associated group that this object belongs to. Returns null if:
+  //   - this object is not bound; or
+  //   - the interface doesn't have methods to pass associated interface
+  //     pointers or requests.
+  AssociatedGroup* associated_group() {
+    return internal_state_.associated_group();
+  }
+
+  bool Equals(const InterfacePtr& other) const {
+    if (this == &other)
+      return true;
+
+    // Now that the two refer to different objects, they are equivalent if
+    // and only if they are both null.
+    return !(*this) && !other;
+  }
+
+  // DO NOT USE. Exposed only for internal use and for testing.
+  internal::InterfacePtrState<Interface, Interface::PassesAssociatedKinds_>*
+  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:
+  // TODO(dcheng): Use an explicit conversion operator.
+  typedef internal::InterfacePtrState<Interface,
+                                      Interface::PassesAssociatedKinds_>
+      InterfacePtr::*Testable;
+
+ public:
+  operator Testable() const {
+    return internal_state_.is_bound() ? &InterfacePtr::internal_state_
+                                      : nullptr;
+  }
+
+ private:
+  // Forbid the == and != operators explicitly, otherwise InterfacePtr will be
+  // converted to Testable to do == or != comparison.
+  template <typename T>
+  bool operator==(const InterfacePtr<T>& other) const = delete;
+  template <typename T>
+  bool operator!=(const InterfacePtr<T>& other) const = delete;
+
+  typedef internal::InterfacePtrState<Interface,
+                                      Interface::PassesAssociatedKinds_> State;
+  mutable State internal_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterfacePtr);
+};
+
+// If |info| is valid (containing a valid message pipe handle), returns an
+// InterfacePtr bound to it. Otherwise, returns an unbound InterfacePtr.
+template <typename Interface>
+InterfacePtr<Interface> MakeProxy(
+    InterfacePtrInfo<Interface> info,
+    scoped_refptr<base::SingleThreadTaskRunner> runner =
+        base::ThreadTaskRunnerHandle::Get()) {
+  InterfacePtr<Interface> ptr;
+  if (info.is_valid())
+    ptr.Bind(std::move(info), std::move(runner));
+  return std::move(ptr);
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/bindings/interface_ptr_info.h b/mojo/public/cpp/bindings/interface_ptr_info.h
new file mode 100644
index 0000000..0b2d808
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_ptr_info.h
@@ -0,0 +1,63 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_INFO_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_INFO_H_
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace mojo {
+
+// InterfacePtrInfo stores necessary information to communicate with a remote
+// interface implementation, which could be used to construct an InterfacePtr.
+template <typename Interface>
+class InterfacePtrInfo {
+ public:
+  InterfacePtrInfo() : version_(0u) {}
+
+  InterfacePtrInfo(ScopedMessagePipeHandle handle, uint32_t version)
+      : handle_(std::move(handle)), version_(version) {}
+
+  InterfacePtrInfo(InterfacePtrInfo&& other)
+      : handle_(std::move(other.handle_)), version_(other.version_) {
+    other.version_ = 0u;
+  }
+
+  ~InterfacePtrInfo() {}
+
+  InterfacePtrInfo& operator=(InterfacePtrInfo&& other) {
+    if (this != &other) {
+      handle_ = std::move(other.handle_);
+      version_ = other.version_;
+      other.version_ = 0u;
+    }
+
+    return *this;
+  }
+
+  bool is_valid() const { return handle_.is_valid(); }
+
+  ScopedMessagePipeHandle PassHandle() { return std::move(handle_); }
+  const ScopedMessagePipeHandle& handle() const { return handle_; }
+  void set_handle(ScopedMessagePipeHandle handle) {
+    handle_ = std::move(handle);
+  }
+
+  uint32_t version() const { return version_; }
+  void set_version(uint32_t version) { version_ = version; }
+
+ private:
+  ScopedMessagePipeHandle handle_;
+  uint32_t version_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterfacePtrInfo);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_INFO_H_
diff --git a/mojo/public/cpp/bindings/interface_ptr_set.h b/mojo/public/cpp/bindings/interface_ptr_set.h
new file mode 100644
index 0000000..d4b2046
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_ptr_set.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_INTERFACE_PTR_SET_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_SET_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface, template <typename> class Ptr>
+class PtrSet {
+ public:
+  PtrSet() {}
+  ~PtrSet() { CloseAll(); }
+
+  void AddPtr(Ptr<Interface> ptr) {
+    auto weak_interface_ptr = new Element(std::move(ptr));
+    ptrs_.push_back(weak_interface_ptr->GetWeakPtr());
+    ClearNullPtrs();
+  }
+
+  template <typename FunctionType>
+  void ForAllPtrs(FunctionType function) {
+    for (const auto& it : ptrs_) {
+      if (it)
+        function(it->get());
+    }
+    ClearNullPtrs();
+  }
+
+  void CloseAll() {
+    for (const auto& it : ptrs_) {
+      if (it)
+        it->Close();
+    }
+    ptrs_.clear();
+  }
+
+ private:
+  class Element {
+   public:
+    explicit Element(Ptr<Interface> ptr)
+        : ptr_(std::move(ptr)), weak_ptr_factory_(this) {
+      ptr_.set_connection_error_handler(base::Bind(&DeleteElement, this));
+    }
+
+    ~Element() {}
+
+    void Close() { ptr_.reset(); }
+
+    Interface* get() { return ptr_.get(); }
+
+    base::WeakPtr<Element> GetWeakPtr() {
+      return weak_ptr_factory_.GetWeakPtr();
+    }
+
+   private:
+    static void DeleteElement(Element* element) { delete element; }
+
+    Ptr<Interface> ptr_;
+    base::WeakPtrFactory<Element> weak_ptr_factory_;
+
+    DISALLOW_COPY_AND_ASSIGN(Element);
+  };
+
+  void ClearNullPtrs() {
+    ptrs_.erase(std::remove_if(ptrs_.begin(), ptrs_.end(),
+                               [](const base::WeakPtr<Element>& p) {
+                                 return p.get() == nullptr;
+                               }),
+                ptrs_.end());
+  }
+
+  std::vector<base::WeakPtr<Element>> ptrs_;
+};
+
+}  // namespace internal
+
+template <typename Interface>
+using InterfacePtrSet = internal::PtrSet<Interface, InterfacePtr>;
+
+template <typename Interface>
+using AssociatedInterfacePtrSet =
+    internal::PtrSet<Interface, AssociatedInterfacePtr>;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_SET_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..fc23aec
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_request.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
+
+#include <utility>
+
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+
+namespace mojo {
+
+// Represents a request from a remote client for an implementation of Interface
+// over a specified message pipe. The implementor of the interface should
+// remove the message pipe by calling PassMessagePipe() and bind it to the
+// implementation. If this is not done, the InterfaceRequest will automatically
+// close the pipe on destruction. Can also represent the absence of a request
+// if the client did not provide a message pipe.
+template <typename Interface>
+class InterfaceRequest {
+ public:
+  // Constructs an empty InterfaceRequest, representing that the client is not
+  // requesting an implementation of Interface.
+  InterfaceRequest() {}
+  InterfaceRequest(decltype(nullptr)) {}
+
+  // Takes the message pipe from another InterfaceRequest.
+  InterfaceRequest(InterfaceRequest&& other) {
+    handle_ = std::move(other.handle_);
+  }
+  InterfaceRequest& operator=(InterfaceRequest&& other) {
+    handle_ = std::move(other.handle_);
+    return *this;
+  }
+
+  // Assigning to nullptr resets the InterfaceRequest to an empty state,
+  // closing the message pipe currently bound to it (if any).
+  InterfaceRequest& operator=(decltype(nullptr)) {
+    handle_.reset();
+    return *this;
+  }
+
+  // Binds the request to a message pipe over which Interface is to be
+  // requested.  If the request is already bound to a message pipe, the current
+  // message pipe will be closed.
+  void Bind(ScopedMessagePipeHandle handle) { handle_ = std::move(handle); }
+
+  // Indicates whether the request currently contains a valid message pipe.
+  bool is_pending() const { return handle_.is_valid(); }
+
+  // Removes the message pipe from the request and returns it.
+  ScopedMessagePipeHandle PassMessagePipe() { return std::move(handle_); }
+
+  bool Equals(const InterfaceRequest& other) const {
+    if (this == &other)
+      return true;
+
+    // Now that the two refer to different objects, they are equivalent if
+    // and only if they are both invalid.
+    return !is_pending() && !other.is_pending();
+  }
+
+ private:
+  ScopedMessagePipeHandle handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterfaceRequest);
+};
+
+// Makes an InterfaceRequest bound to the specified message pipe. If |handle|
+// is empty or invalid, the resulting InterfaceRequest will represent the
+// absence of a request.
+template <typename Interface>
+InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) {
+  InterfaceRequest<Interface> request;
+  request.Bind(std::move(handle));
+  return std::move(request);
+}
+
+// Creates a new message pipe over which Interface is to be served. Binds the
+// specified InterfacePtr to one end of the message pipe, and returns an
+// InterfaceRequest bound to the other. The InterfacePtr should be passed to
+// the client, and the InterfaceRequest should be passed to whatever will
+// provide the implementation. The implementation should typically be bound to
+// the InterfaceRequest using the Binding or StrongBinding classes. The client
+// may begin to issue calls even before an implementation has been bound, since
+// messages sent over the pipe will just queue up until they are consumed by
+// the implementation.
+//
+// Example #1: Requesting a remote implementation of an interface.
+// ===============================================================
+//
+// Given the following interface:
+//
+//   interface Database {
+//     OpenTable(Table& table);
+//   }
+//
+// The client would have code similar to the following:
+//
+//   DatabasePtr database = ...;  // Connect to database.
+//   TablePtr table;
+//   database->OpenTable(GetProxy(&table));
+//
+// Upon return from GetProxy, |table| is ready to have methods called on it.
+//
+// Example #2: Registering a local implementation with a remote service.
+// =====================================================================
+//
+// Given the following interface
+//   interface Collector {
+//     RegisterSource(Source source);
+//   }
+//
+// The client would have code similar to the following:
+//
+//   CollectorPtr collector = ...;  // Connect to Collector.
+//   SourcePtr source;
+//   InterfaceRequest<Source> source_request = GetProxy(&source);
+//   collector->RegisterSource(std::move(source));
+//   CreateSource(std::move(source_request));  // Create implementation locally.
+//
+template <typename Interface>
+InterfaceRequest<Interface> GetProxy(
+    InterfacePtr<Interface>* ptr,
+    scoped_refptr<base::SingleThreadTaskRunner> runner =
+        base::ThreadTaskRunnerHandle::Get()) {
+  MessagePipe pipe;
+  ptr->Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u),
+            std::move(runner));
+  return MakeRequest<Interface>(std::move(pipe.handle1));
+}
+
+// Fuses an InterfaceRequest<T> endpoint with an InterfacePtrInfo<T> endpoint.
+// Returns |true| on success or |false| on failure.
+template <typename Interface>
+bool FuseInterface(InterfaceRequest<Interface> request,
+                   InterfacePtrInfo<Interface> proxy_info) {
+  MojoResult result = FuseMessagePipes(request.PassMessagePipe(),
+                                       proxy_info.PassHandle());
+  return result == MOJO_RESULT_OK;
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
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..dd24eac
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_internal.cc
@@ -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.
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+
+#include <stddef.h>
+#include <stdint.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;
+}
+
+}  // 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..ba6d16e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_internal.h
@@ -0,0 +1,367 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <new>
+
+#include "base/logging.h"
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename K, typename V>
+class Map_Data;
+
+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 {
+  using StorageType = T;
+  using Ref = T&;
+  using ConstRef = const T&;
+
+  static const uint32_t kMaxNumElements =
+      (std::numeric_limits<uint32_t>::max() - sizeof(ArrayHeader)) /
+      sizeof(StorageType);
+
+  static uint32_t GetStorageSize(uint32_t num_elements) {
+    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];
+  }
+};
+
+// 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 = std::numeric_limits<uint32_t>::max();
+
+  using StorageType = uint8_t;
+  using Ref = BitRef;
+  using ConstRef = bool;
+
+  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;
+  }
+};
+
+// What follows is code to support the serialization/validation of
+// Array_Data<T>. There are four interesting cases: arrays of primitives,
+// arrays of handles/interfaces, arrays of objects and arrays of unions.
+// Arrays of objects are represented as arrays of pointers to objects. Arrays
+// of unions are inlined so they are not pointers, but comparing with primitives
+// they require more work for serialization/validation.
+//
+// TODO(yzshen): Validation code should be organzied in a way similar to
+// Serializer<>, or merged into it. It should be templatized with the mojo
+// wrapper type instead of the data type, that way we can use MojomTypeTraits
+// to determine the categories.
+
+template <typename T, bool is_union, bool is_handle_or_interface>
+struct ArraySerializationHelper;
+
+template <typename T>
+struct ArraySerializationHelper<T, false, false> {
+  using ElementType = typename ArrayDataTraits<T>::StorageType;
+
+  static bool ValidateElements(const ArrayHeader* header,
+                               const ElementType* elements,
+                               ValidationContext* validation_context,
+                               const ContainerValidateParams* validate_params) {
+    DCHECK(!validate_params->element_is_nullable)
+        << "Primitive type should be non-nullable";
+    DCHECK(!validate_params->element_validate_params)
+        << "Primitive type should not have array validate params";
+
+    if (!validate_params->validate_enum_func)
+      return true;
+
+    // Enum validation.
+    for (uint32_t i = 0; i < header->num_elements; ++i) {
+      if (!validate_params->validate_enum_func(elements[i], validation_context))
+        return false;
+    }
+    return true;
+  }
+};
+
+template <typename T>
+struct ArraySerializationHelper<T, false, true> {
+  using ElementType = typename ArrayDataTraits<T>::StorageType;
+
+  static bool ValidateElements(const ArrayHeader* header,
+                               const ElementType* elements,
+                               ValidationContext* validation_context,
+                               const ContainerValidateParams* validate_params) {
+    DCHECK(!validate_params->element_validate_params)
+        << "Handle or interface type should not have array validate params";
+
+    for (uint32_t i = 0; i < header->num_elements; ++i) {
+      if (!validate_params->element_is_nullable &&
+          !IsHandleOrInterfaceValid(elements[i])) {
+        static const ValidationError kError =
+            std::is_same<T, Interface_Data>::value ||
+                    std::is_same<T, Handle_Data>::value
+                ? VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE
+                : VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID;
+        ReportValidationError(
+            validation_context, kError,
+            MakeMessageWithArrayIndex(
+                "invalid handle or interface ID in array expecting valid "
+                "handles or interface IDs",
+                header->num_elements, i)
+                .c_str());
+        return false;
+      }
+      if (!ValidateHandleOrInterface(elements[i], validation_context))
+        return false;
+    }
+    return true;
+  }
+};
+
+template <typename T>
+struct ArraySerializationHelper<Pointer<T>, false, false> {
+  using ElementType = typename ArrayDataTraits<Pointer<T>>::StorageType;
+
+  static bool ValidateElements(const ArrayHeader* header,
+                               const ElementType* elements,
+                               ValidationContext* validation_context,
+                               const ContainerValidateParams* validate_params) {
+    for (uint32_t i = 0; i < header->num_elements; ++i) {
+      if (!validate_params->element_is_nullable && !elements[i].offset) {
+        ReportValidationError(
+            validation_context,
+            VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+            MakeMessageWithArrayIndex("null in array expecting valid pointers",
+                                      header->num_elements,
+                                      i).c_str());
+        return false;
+      }
+      if (!ValidateCaller<T>::Run(elements[i], validation_context,
+                                  validate_params->element_validate_params)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+ private:
+  template <typename U,
+            bool is_array_or_map = IsSpecializationOf<Array_Data, U>::value ||
+                                   IsSpecializationOf<Map_Data, U>::value>
+  struct ValidateCaller {
+    static bool Run(const Pointer<U>& data,
+                    ValidationContext* validation_context,
+                    const ContainerValidateParams* validate_params) {
+      DCHECK(!validate_params)
+          << "Struct type should not have array validate params";
+
+      return ValidateStruct(data, validation_context);
+    }
+  };
+
+  template <typename U>
+  struct ValidateCaller<U, true> {
+    static bool Run(const Pointer<U>& data,
+                    ValidationContext* validation_context,
+                    const ContainerValidateParams* validate_params) {
+      return ValidateContainer(data, validation_context, validate_params);
+    }
+  };
+};
+
+template <typename U>
+struct ArraySerializationHelper<U, true, false> {
+  using ElementType = typename ArrayDataTraits<U>::StorageType;
+
+  static bool ValidateElements(const ArrayHeader* header,
+                               const ElementType* elements,
+                               ValidationContext* validation_context,
+                               const ContainerValidateParams* validate_params) {
+    for (uint32_t i = 0; i < header->num_elements; ++i) {
+      if (!validate_params->element_is_nullable && elements[i].is_null()) {
+        ReportValidationError(
+            validation_context,
+            VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+            MakeMessageWithArrayIndex("null in array expecting valid unions",
+                                      header->num_elements, i)
+                .c_str());
+        return false;
+      }
+      if (!ValidateInlinedUnion(elements[i], validation_context))
+        return false;
+    }
+    return true;
+  }
+};
+
+template <typename T>
+class Array_Data {
+ public:
+  using Traits = ArrayDataTraits<T>;
+  using StorageType = typename Traits::StorageType;
+  using Ref = typename Traits::Ref;
+  using ConstRef = typename Traits::ConstRef;
+  using Helper = ArraySerializationHelper<
+      T,
+      IsUnionDataType<T>::value,
+      std::is_same<T, AssociatedInterface_Data>::value ||
+          std::is_same<T, AssociatedInterfaceRequest_Data>::value ||
+          std::is_same<T, Interface_Data>::value ||
+          std::is_same<T, Handle_Data>::value>;
+  using Element = T;
+
+  // 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));
+  }
+
+  static bool Validate(const void* data,
+                       ValidationContext* validation_context,
+                       const ContainerValidateParams* validate_params) {
+    if (!data)
+      return true;
+    if (!IsAligned(data)) {
+      ReportValidationError(validation_context,
+                            VALIDATION_ERROR_MISALIGNED_OBJECT);
+      return false;
+    }
+    if (!validation_context->IsValidRange(data, sizeof(ArrayHeader))) {
+      ReportValidationError(validation_context,
+                            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_context,
+                            VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
+      return false;
+    }
+    if (validate_params->expected_num_elements != 0 &&
+        header->num_elements != validate_params->expected_num_elements) {
+      ReportValidationError(
+          validation_context,
+          VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+          MakeMessageWithExpectedArraySize(
+              "fixed-size array has wrong number of elements",
+              header->num_elements,
+              validate_params->expected_num_elements).c_str());
+      return false;
+    }
+    if (!validation_context->ClaimMemory(data, header->num_bytes)) {
+      ReportValidationError(validation_context,
+                            VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+      return false;
+    }
+
+    const Array_Data<T>* object = static_cast<const Array_Data<T>*>(data);
+    return Helper::ValidateElements(&object->header_, object->storage(),
+                                    validation_context, validate_params);
+  }
+
+  size_t size() const { return header_.num_elements; }
+
+  Ref at(size_t offset) {
+    DCHECK(offset < static_cast<size_t>(header_.num_elements));
+    return Traits::ToRef(storage(), offset);
+  }
+
+  ConstRef at(size_t offset) const {
+    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));
+  }
+
+ private:
+  Array_Data(uint32_t num_bytes, uint32_t num_elements) {
+    header_.num_bytes = num_bytes;
+    header_.num_elements = num_elements;
+  }
+  ~Array_Data() = delete;
+
+  internal::ArrayHeader header_;
+
+  // Elements of type internal::ArrayDataTraits<T>::StorageType follow.
+};
+static_assert(sizeof(Array_Data<char>) == 8, "Bad sizeof(Array_Data)");
+
+// UTF-8 encoded
+using String_Data = Array_Data<char>;
+
+}  // 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..5db27a5
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -0,0 +1,548 @@
+// Copyright 2014 The Chromium Authors. All 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 <stddef.h>
+#include <string.h>  // For |memcpy()|.
+
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/map.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Traits,
+          typename MaybeConstUserType,
+          bool HasGetBegin =
+              HasGetBeginMethod<Traits, MaybeConstUserType>::value>
+class ArrayIterator {};
+
+// Used as the UserTypeIterator template parameter of ArraySerializer.
+template <typename Traits, typename MaybeConstUserType>
+class ArrayIterator<Traits, MaybeConstUserType, true> {
+ public:
+  using IteratorType = decltype(
+      CallGetBeginIfExists<Traits>(std::declval<MaybeConstUserType&>()));
+
+  explicit ArrayIterator(MaybeConstUserType& input)
+      : input_(input), iter_(CallGetBeginIfExists<Traits>(input)) {}
+  ~ArrayIterator() {}
+
+  size_t GetSize() const { return Traits::GetSize(input_); }
+
+  using GetNextResult =
+      decltype(Traits::GetValue(std::declval<IteratorType&>()));
+  GetNextResult GetNext() {
+    auto& value = Traits::GetValue(iter_);
+    Traits::AdvanceIterator(iter_);
+    return value;
+  }
+
+  using GetDataIfExistsResult = decltype(
+      CallGetDataIfExists<Traits>(std::declval<MaybeConstUserType&>()));
+  GetDataIfExistsResult GetDataIfExists() {
+    return CallGetDataIfExists<Traits>(input_);
+  }
+
+ private:
+  MaybeConstUserType& input_;
+  IteratorType iter_;
+};
+
+// Used as the UserTypeIterator template parameter of ArraySerializer.
+template <typename Traits, typename MaybeConstUserType>
+class ArrayIterator<Traits, MaybeConstUserType, false> {
+ public:
+  explicit ArrayIterator(MaybeConstUserType& input) : input_(input), iter_(0) {}
+  ~ArrayIterator() {}
+
+  size_t GetSize() const { return Traits::GetSize(input_); }
+
+  using GetNextResult =
+      decltype(Traits::GetAt(std::declval<MaybeConstUserType&>(), 0));
+  GetNextResult GetNext() {
+    DCHECK_LT(iter_, Traits::GetSize(input_));
+    return Traits::GetAt(input_, iter_++);
+  }
+
+  using GetDataIfExistsResult = decltype(
+      CallGetDataIfExists<Traits>(std::declval<MaybeConstUserType&>()));
+  GetDataIfExistsResult GetDataIfExists() {
+    return CallGetDataIfExists<Traits>(input_);
+  }
+
+ private:
+  MaybeConstUserType& input_;
+  size_t iter_;
+};
+
+// ArraySerializer is also used to serialize map keys and values. Therefore, it
+// has a UserTypeIterator parameter which is an adaptor for reading to hide the
+// difference between ArrayTraits and MapTraits.
+template <typename MojomType,
+          typename MaybeConstUserType,
+          typename UserTypeIterator,
+          typename EnableType = void>
+struct ArraySerializer;
+
+// Handles serialization and deserialization of arrays of pod types.
+template <typename MojomType,
+          typename MaybeConstUserType,
+          typename UserTypeIterator>
+struct ArraySerializer<
+    MojomType,
+    MaybeConstUserType,
+    UserTypeIterator,
+    typename std::enable_if<BelongsTo<typename MojomType::Element,
+                                      MojomTypeCategory::POD>::value>::type> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Data = typename MojomTypeTraits<MojomType>::Data;
+  using DataElement = typename Data::Element;
+  using Element = typename MojomType::Element;
+  using Traits = ArrayTraits<UserType>;
+
+  static_assert(std::is_same<Element, DataElement>::value,
+                "Incorrect array serializer");
+  static_assert(std::is_same<Element, typename Traits::Element>::value,
+                "Incorrect array serializer");
+
+  static size_t GetSerializedSize(UserTypeIterator* input,
+                                  SerializationContext* context) {
+    return sizeof(Data) + Align(input->GetSize() * sizeof(DataElement));
+  }
+
+  static void SerializeElements(UserTypeIterator* input,
+                                Buffer* buf,
+                                Data* output,
+                                const ContainerValidateParams* validate_params,
+                                SerializationContext* context) {
+    DCHECK(!validate_params->element_is_nullable)
+        << "Primitive type should be non-nullable";
+    DCHECK(!validate_params->element_validate_params)
+        << "Primitive type should not have array validate params";
+
+    size_t size = input->GetSize();
+    if (size == 0)
+      return;
+
+    auto data = input->GetDataIfExists();
+    if (data) {
+      memcpy(output->storage(), data, size * sizeof(DataElement));
+    } else {
+      for (size_t i = 0; i < size; ++i)
+        output->at(i) = input->GetNext();
+    }
+  }
+
+  static bool DeserializeElements(Data* input,
+                                  UserType* output,
+                                  SerializationContext* context) {
+    if (!Traits::Resize(*output, input->size()))
+      return false;
+    ArrayIterator<Traits, UserType> iterator(*output);
+    if (input->size()) {
+      auto data = iterator.GetDataIfExists();
+      if (data) {
+        memcpy(data, input->storage(), input->size() * sizeof(DataElement));
+      } else {
+        for (size_t i = 0; i < input->size(); ++i)
+          iterator.GetNext() = input->at(i);
+      }
+    }
+    return true;
+  }
+};
+
+// Handles serialization and deserialization of arrays of enum types.
+template <typename MojomType,
+          typename MaybeConstUserType,
+          typename UserTypeIterator>
+struct ArraySerializer<
+    MojomType,
+    MaybeConstUserType,
+    UserTypeIterator,
+    typename std::enable_if<BelongsTo<typename MojomType::Element,
+                                      MojomTypeCategory::ENUM>::value>::type> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Data = typename MojomTypeTraits<MojomType>::Data;
+  using DataElement = typename Data::Element;
+  using Element = typename MojomType::Element;
+  using Traits = ArrayTraits<UserType>;
+
+  static_assert(sizeof(Element) == sizeof(DataElement),
+                "Incorrect array serializer");
+
+  static size_t GetSerializedSize(UserTypeIterator* input,
+                                  SerializationContext* context) {
+    return sizeof(Data) + Align(input->GetSize() * sizeof(DataElement));
+  }
+
+  static void SerializeElements(UserTypeIterator* input,
+                                Buffer* buf,
+                                Data* output,
+                                const ContainerValidateParams* validate_params,
+                                SerializationContext* context) {
+    DCHECK(!validate_params->element_is_nullable)
+        << "Primitive type should be non-nullable";
+    DCHECK(!validate_params->element_validate_params)
+        << "Primitive type should not have array validate params";
+
+    size_t size = input->GetSize();
+    for (size_t i = 0; i < size; ++i)
+      Serialize<Element>(input->GetNext(), output->storage() + i);
+  }
+
+  static bool DeserializeElements(Data* input,
+                                  UserType* output,
+                                  SerializationContext* context) {
+    if (!Traits::Resize(*output, input->size()))
+      return false;
+    ArrayIterator<Traits, UserType> iterator(*output);
+    for (size_t i = 0; i < input->size(); ++i) {
+      if (!Deserialize<Element>(input->at(i), &iterator.GetNext()))
+        return false;
+    }
+    return true;
+  }
+};
+
+// Serializes and deserializes arrays of bools.
+template <typename MojomType,
+          typename MaybeConstUserType,
+          typename UserTypeIterator>
+struct ArraySerializer<MojomType,
+                       MaybeConstUserType,
+                       UserTypeIterator,
+                       typename std::enable_if<BelongsTo<
+                           typename MojomType::Element,
+                           MojomTypeCategory::BOOLEAN>::value>::type> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = ArrayTraits<UserType>;
+  using Data = typename MojomTypeTraits<MojomType>::Data;
+
+  static_assert(std::is_same<bool, typename Traits::Element>::value,
+                "Incorrect array serializer");
+
+  static size_t GetSerializedSize(UserTypeIterator* input,
+                                  SerializationContext* context) {
+    return sizeof(Data) + Align((input->GetSize() + 7) / 8);
+  }
+
+  static void SerializeElements(UserTypeIterator* input,
+                                Buffer* buf,
+                                Data* output,
+                                const ContainerValidateParams* validate_params,
+                                SerializationContext* context) {
+    DCHECK(!validate_params->element_is_nullable)
+        << "Primitive type should be non-nullable";
+    DCHECK(!validate_params->element_validate_params)
+        << "Primitive type should not have array validate params";
+
+    size_t size = input->GetSize();
+    for (size_t i = 0; i < size; ++i)
+      output->at(i) = input->GetNext();
+  }
+  static bool DeserializeElements(Data* input,
+                                  UserType* output,
+                                  SerializationContext* context) {
+    if (!Traits::Resize(*output, input->size()))
+      return false;
+    ArrayIterator<Traits, UserType> iterator(*output);
+    for (size_t i = 0; i < input->size(); ++i)
+      iterator.GetNext() = input->at(i);
+    return true;
+  }
+};
+
+// Serializes and deserializes arrays of handles or interfaces.
+template <typename MojomType,
+          typename MaybeConstUserType,
+          typename UserTypeIterator>
+struct ArraySerializer<
+    MojomType,
+    MaybeConstUserType,
+    UserTypeIterator,
+    typename std::enable_if<
+        BelongsTo<typename MojomType::Element,
+                  MojomTypeCategory::ASSOCIATED_INTERFACE |
+                      MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST |
+                      MojomTypeCategory::HANDLE |
+                      MojomTypeCategory::INTERFACE |
+                      MojomTypeCategory::INTERFACE_REQUEST>::value>::type> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Data = typename MojomTypeTraits<MojomType>::Data;
+  using Element = typename MojomType::Element;
+  using Traits = ArrayTraits<UserType>;
+
+  static_assert(std::is_same<Element, typename Traits::Element>::value,
+                "Incorrect array serializer");
+
+  static size_t GetSerializedSize(UserTypeIterator* input,
+                                  SerializationContext* context) {
+    return sizeof(Data) +
+           Align(input->GetSize() * sizeof(typename Data::Element));
+  }
+
+  static void SerializeElements(UserTypeIterator* input,
+                                Buffer* buf,
+                                Data* output,
+                                const ContainerValidateParams* validate_params,
+                                SerializationContext* context) {
+    DCHECK(!validate_params->element_validate_params)
+        << "Handle or interface type should not have array validate params";
+
+    size_t size = input->GetSize();
+    for (size_t i = 0; i < size; ++i) {
+      Serialize<Element>(input->GetNext(), &output->at(i), context);
+
+      static const ValidationError kError =
+          BelongsTo<Element,
+                    MojomTypeCategory::ASSOCIATED_INTERFACE |
+                        MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST>::value
+              ? VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID
+              : VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE;
+      MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+          !validate_params->element_is_nullable &&
+              !IsHandleOrInterfaceValid(output->at(i)),
+          kError,
+          MakeMessageWithArrayIndex("invalid handle or interface ID in array "
+                                    "expecting valid handles or interface IDs",
+                                    size, i));
+    }
+  }
+  static bool DeserializeElements(Data* input,
+                                  UserType* output,
+                                  SerializationContext* context) {
+    if (!Traits::Resize(*output, input->size()))
+      return false;
+    ArrayIterator<Traits, UserType> iterator(*output);
+    for (size_t i = 0; i < input->size(); ++i) {
+      bool result =
+          Deserialize<Element>(&input->at(i), &iterator.GetNext(), context);
+      DCHECK(result);
+    }
+    return true;
+  }
+};
+
+// This template must only apply to pointer mojo entity (strings, structs,
+// arrays and maps).
+template <typename MojomType,
+          typename MaybeConstUserType,
+          typename UserTypeIterator>
+struct ArraySerializer<MojomType,
+                       MaybeConstUserType,
+                       UserTypeIterator,
+                       typename std::enable_if<BelongsTo<
+                           typename MojomType::Element,
+                           MojomTypeCategory::ARRAY | MojomTypeCategory::MAP |
+                               MojomTypeCategory::STRING |
+                               MojomTypeCategory::STRUCT>::value>::type> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Data = typename MojomTypeTraits<MojomType>::Data;
+  using Element = typename MojomType::Element;
+  using DataElementPtr = typename MojomTypeTraits<Element>::Data*;
+  using Traits = ArrayTraits<UserType>;
+
+  static size_t GetSerializedSize(UserTypeIterator* input,
+                                  SerializationContext* context) {
+    size_t element_count = input->GetSize();
+    size_t size = sizeof(Data) + element_count * sizeof(typename Data::Element);
+    for (size_t i = 0; i < element_count; ++i)
+      size += PrepareToSerialize<Element>(input->GetNext(), context);
+    return size;
+  }
+
+  static void SerializeElements(UserTypeIterator* input,
+                                Buffer* buf,
+                                Data* output,
+                                const ContainerValidateParams* validate_params,
+                                SerializationContext* context) {
+    size_t size = input->GetSize();
+    for (size_t i = 0; i < size; ++i) {
+      DataElementPtr data_ptr;
+      SerializeCaller<Element>::Run(input->GetNext(), buf, &data_ptr,
+                                    validate_params->element_validate_params,
+                                    context);
+      output->at(i).Set(data_ptr);
+      MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+          !validate_params->element_is_nullable && !data_ptr,
+          VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+          MakeMessageWithArrayIndex("null in array expecting valid pointers",
+                                    size, i));
+    }
+  }
+  static bool DeserializeElements(Data* input,
+                                  UserType* output,
+                                  SerializationContext* context) {
+    if (!Traits::Resize(*output, input->size()))
+      return false;
+    ArrayIterator<Traits, UserType> iterator(*output);
+    for (size_t i = 0; i < input->size(); ++i) {
+      if (!Deserialize<Element>(input->at(i).Get(), &iterator.GetNext(),
+                                context))
+        return false;
+    }
+    return true;
+  }
+
+ private:
+  template <typename T,
+            bool is_array_or_map = BelongsTo<T,
+                                             MojomTypeCategory::ARRAY |
+                                                 MojomTypeCategory::MAP>::value>
+  struct SerializeCaller {
+    template <typename InputElementType>
+    static void Run(InputElementType&& input,
+                    Buffer* buf,
+                    DataElementPtr* output,
+                    const ContainerValidateParams* validate_params,
+                    SerializationContext* context) {
+      Serialize<T>(std::forward<InputElementType>(input), buf, output, context);
+    }
+  };
+
+  template <typename T>
+  struct SerializeCaller<T, true> {
+    template <typename InputElementType>
+    static void Run(InputElementType&& input,
+                    Buffer* buf,
+                    DataElementPtr* output,
+                    const ContainerValidateParams* validate_params,
+                    SerializationContext* context) {
+      Serialize<T>(std::forward<InputElementType>(input), buf, output,
+                   validate_params, context);
+    }
+  };
+};
+
+// Handles serialization and deserialization of arrays of unions.
+template <typename MojomType,
+          typename MaybeConstUserType,
+          typename UserTypeIterator>
+struct ArraySerializer<
+    MojomType,
+    MaybeConstUserType,
+    UserTypeIterator,
+    typename std::enable_if<BelongsTo<typename MojomType::Element,
+                                      MojomTypeCategory::UNION>::value>::type> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Data = typename MojomTypeTraits<MojomType>::Data;
+  using Element = typename MojomType::Element;
+  using Traits = ArrayTraits<UserType>;
+
+  static_assert(std::is_same<typename MojomType::Element,
+                             typename Traits::Element>::value,
+                "Incorrect array serializer");
+
+  static size_t GetSerializedSize(UserTypeIterator* input,
+                                  SerializationContext* context) {
+    size_t element_count = input->GetSize();
+    size_t size = sizeof(Data);
+    for (size_t i = 0; i < element_count; ++i) {
+      // Call with |inlined| set to false, so that it will account for both the
+      // data in the union and the space in the array used to hold the union.
+      size += PrepareToSerialize<Element>(input->GetNext(), false, context);
+    }
+    return size;
+  }
+
+  static void SerializeElements(UserTypeIterator* input,
+                                Buffer* buf,
+                                Data* output,
+                                const ContainerValidateParams* validate_params,
+                                SerializationContext* context) {
+    size_t size = input->GetSize();
+    for (size_t i = 0; i < size; ++i) {
+      typename Data::Element* result = output->storage() + i;
+      Serialize<Element>(input->GetNext(), buf, &result, true, context);
+      MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+          !validate_params->element_is_nullable && output->at(i).is_null(),
+          VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+          MakeMessageWithArrayIndex("null in array expecting valid unions",
+                                    size, i));
+    }
+  }
+
+  static bool DeserializeElements(Data* input,
+                                  UserType* output,
+                                  SerializationContext* context) {
+    if (!Traits::Resize(*output, input->size()))
+      return false;
+    ArrayIterator<Traits, UserType> iterator(*output);
+    for (size_t i = 0; i < input->size(); ++i) {
+      if (!Deserialize<Element>(&input->at(i), &iterator.GetNext(), context))
+        return false;
+    }
+    return true;
+  }
+};
+
+template <typename Element, typename MaybeConstUserType>
+struct Serializer<Array<Element>, MaybeConstUserType> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = ArrayTraits<UserType>;
+  using Impl = ArraySerializer<Array<Element>,
+                               MaybeConstUserType,
+                               ArrayIterator<Traits, MaybeConstUserType>>;
+  using Data = typename MojomTypeTraits<Array<Element>>::Data;
+
+  static size_t PrepareToSerialize(MaybeConstUserType& input,
+                                   SerializationContext* context) {
+    if (CallIsNullIfExists<Traits>(input))
+      return 0;
+    ArrayIterator<Traits, MaybeConstUserType> iterator(input);
+    return Impl::GetSerializedSize(&iterator, context);
+  }
+
+  static void Serialize(MaybeConstUserType& input,
+                        Buffer* buf,
+                        Data** output,
+                        const ContainerValidateParams* validate_params,
+                        SerializationContext* context) {
+    if (!CallIsNullIfExists<Traits>(input)) {
+      MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+          validate_params->expected_num_elements != 0 &&
+              Traits::GetSize(input) != validate_params->expected_num_elements,
+          internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+          internal::MakeMessageWithExpectedArraySize(
+              "fixed-size array has wrong number of elements",
+              Traits::GetSize(input), validate_params->expected_num_elements));
+      Data* result = Data::New(Traits::GetSize(input), buf);
+      if (result) {
+        ArrayIterator<Traits, MaybeConstUserType> iterator(input);
+        Impl::SerializeElements(&iterator, buf, result, validate_params,
+                                context);
+      }
+      *output = result;
+    } else {
+      *output = nullptr;
+    }
+  }
+
+  static bool Deserialize(Data* input,
+                          UserType* output,
+                          SerializationContext* context) {
+    if (!input)
+      return CallSetToNullIfExists<Traits>(output);
+    return Impl::DeserializeElements(input, output, context);
+  }
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/associated_group.cc b/mojo/public/cpp/bindings/lib/associated_group.cc
new file mode 100644
index 0000000..a9c53b5
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/associated_group.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/associated_group.h"
+
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
+
+namespace mojo {
+
+AssociatedGroup::AssociatedGroup() {}
+
+AssociatedGroup::AssociatedGroup(const AssociatedGroup& other)
+    : controller_(other.controller_) {}
+
+AssociatedGroup::~AssociatedGroup() {}
+
+AssociatedGroup& AssociatedGroup::operator=(const AssociatedGroup& other) {
+  if (this == &other)
+    return *this;
+
+  controller_ = other.controller_;
+  return *this;
+}
+
+void AssociatedGroup::CreateEndpointHandlePair(
+    ScopedInterfaceEndpointHandle* local_endpoint,
+    ScopedInterfaceEndpointHandle* remote_endpoint) {
+  if (!controller_)
+    return;
+
+  controller_->CreateEndpointHandlePair(local_endpoint, remote_endpoint);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/associated_group_controller.cc b/mojo/public/cpp/bindings/lib/associated_group_controller.cc
new file mode 100644
index 0000000..42db9b3
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/associated_group_controller.cc
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All 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/associated_group_controller.h"
+
+#include "mojo/public/cpp/bindings/associated_group.h"
+
+namespace mojo {
+
+AssociatedGroupController::AssociatedGroupController(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : base::RefCountedDeleteOnMessageLoop<AssociatedGroupController>(
+          task_runner) {}
+
+AssociatedGroupController::~AssociatedGroupController() {}
+
+std::unique_ptr<AssociatedGroup>
+AssociatedGroupController::CreateAssociatedGroup() {
+  std::unique_ptr<AssociatedGroup> group(new AssociatedGroup);
+  group->controller_ = this;
+  return group;
+}
+
+ScopedInterfaceEndpointHandle
+AssociatedGroupController::CreateScopedInterfaceEndpointHandle(
+    InterfaceId id,
+    bool is_local) {
+  return ScopedInterfaceEndpointHandle(id, is_local, this);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
new file mode 100644
index 0000000..c7f74fb
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
@@ -0,0 +1,146 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ASSOCIATED_INTERFACE_PTR_STATE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ASSOCIATED_INTERFACE_PTR_STATE_H_
+
+#include <stdint.h>
+
+#include <algorithm>  // For |std::swap()|.
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface>
+class AssociatedInterfacePtrState {
+ public:
+  AssociatedInterfacePtrState() : version_(0u) {}
+
+  ~AssociatedInterfacePtrState() {
+    endpoint_client_.reset();
+    proxy_.reset();
+  }
+
+  Interface* instance() {
+    // This will be null if the object is not bound.
+    return proxy_.get();
+  }
+
+  uint32_t version() const { return version_; }
+
+  uint32_t interface_id() const {
+    DCHECK(is_bound());
+    return endpoint_client_->interface_id();
+  }
+
+  void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
+    // Do a static cast in case the interface contains methods with the same
+    // name. It is safe to capture |this| because the callback won't be run
+    // after this object goes away.
+    static_cast<ControlMessageProxy*>(proxy_.get())
+        ->QueryVersion(base::Bind(&AssociatedInterfacePtrState::OnQueryVersion,
+                                  base::Unretained(this), callback));
+  }
+
+  void RequireVersion(uint32_t version) {
+    if (version <= version_)
+      return;
+
+    version_ = version;
+    // Do a static cast in case the interface contains methods with the same
+    // name.
+    static_cast<ControlMessageProxy*>(proxy_.get())->RequireVersion(version);
+  }
+
+  void Swap(AssociatedInterfacePtrState* other) {
+    using std::swap;
+    swap(other->endpoint_client_, endpoint_client_);
+    swap(other->proxy_, proxy_);
+    swap(other->version_, version_);
+  }
+
+  void Bind(AssociatedInterfacePtrInfo<Interface> info,
+            scoped_refptr<base::SingleThreadTaskRunner> runner) {
+    DCHECK(!endpoint_client_);
+    DCHECK(!proxy_);
+    DCHECK_EQ(0u, version_);
+    DCHECK(info.is_valid());
+
+    version_ = info.version();
+    endpoint_client_.reset(new InterfaceEndpointClient(
+        info.PassHandle(), nullptr,
+        base::WrapUnique(new typename Interface::ResponseValidator_()), false,
+        std::move(runner)));
+    proxy_.reset(new Proxy(endpoint_client_.get()));
+    proxy_->serialization_context()->group_controller =
+        endpoint_client_->group_controller();
+  }
+
+  // After this method is called, the object is in an invalid state and
+  // shouldn't be reused.
+  AssociatedInterfacePtrInfo<Interface> PassInterface() {
+    ScopedInterfaceEndpointHandle handle = endpoint_client_->PassHandle();
+    endpoint_client_.reset();
+    proxy_.reset();
+    return AssociatedInterfacePtrInfo<Interface>(std::move(handle), version_);
+  }
+
+  bool is_bound() const { return !!endpoint_client_; }
+
+  bool encountered_error() const {
+    return endpoint_client_ ? endpoint_client_->encountered_error() : false;
+  }
+
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    DCHECK(endpoint_client_);
+    endpoint_client_->set_connection_error_handler(error_handler);
+  }
+
+  // Returns true if bound and awaiting a response to a message.
+  bool has_pending_callbacks() const {
+    return endpoint_client_ && endpoint_client_->has_pending_responders();
+  }
+
+  AssociatedGroup* associated_group() {
+    return endpoint_client_ ? endpoint_client_->associated_group() : nullptr;
+  }
+
+ private:
+  using Proxy = typename Interface::Proxy_;
+
+  void OnQueryVersion(const base::Callback<void(uint32_t)>& callback,
+                      uint32_t version) {
+    version_ = version;
+    callback.Run(version);
+  }
+
+  std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
+  std::unique_ptr<Proxy> proxy_;
+
+  uint32_t version_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtrState);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_ASSOCIATED_INTERFACE_PTR_STATE_H_
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h
new file mode 100644
index 0000000..c8d3e83
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -0,0 +1,249 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/bindings/message_header_validator.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface, bool use_multiplex_router>
+class BindingState;
+
+// Uses a single-threaded, dedicated router. If |Interface| doesn't have any
+// methods to pass associated interface pointers or requests, there won't be
+// multiple interfaces running on the underlying message pipe. In that case, we
+// can use this specialization to reduce cost.
+template <typename Interface>
+class BindingState<Interface, false> {
+ public:
+  explicit BindingState(Interface* impl) : impl_(impl) {
+    stub_.set_sink(impl_);
+  }
+
+  ~BindingState() { Close(); }
+
+  void Bind(ScopedMessagePipeHandle handle,
+            scoped_refptr<base::SingleThreadTaskRunner> runner) {
+    DCHECK(!router_);
+    internal::FilterChain filters;
+    filters.Append<MessageHeaderValidator>(Interface::Name_);
+    filters.Append<typename Interface::RequestValidator_>();
+
+    router_ =
+        new internal::Router(std::move(handle), std::move(filters),
+                             Interface::HasSyncMethods_, std::move(runner));
+    router_->set_incoming_receiver(&stub_);
+    router_->set_connection_error_handler(
+        base::Bind(&BindingState::RunConnectionErrorHandler,
+                   base::Unretained(this)));
+  }
+
+  bool HasAssociatedInterfaces() const { return false; }
+
+  void PauseIncomingMethodCallProcessing() {
+    DCHECK(router_);
+    router_->PauseIncomingMethodCallProcessing();
+  }
+  void ResumeIncomingMethodCallProcessing() {
+    DCHECK(router_);
+    router_->ResumeIncomingMethodCallProcessing();
+  }
+
+  bool WaitForIncomingMethodCall(
+      MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) {
+    DCHECK(router_);
+    return router_->WaitForIncomingMessage(deadline);
+  }
+
+  void Close() {
+    if (!router_)
+      return;
+
+    router_->CloseMessagePipe();
+    DestroyRouter();
+  }
+
+  InterfaceRequest<Interface> Unbind() {
+    InterfaceRequest<Interface> request =
+        MakeRequest<Interface>(router_->PassMessagePipe());
+    DestroyRouter();
+    return std::move(request);
+  }
+
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    DCHECK(is_bound());
+    connection_error_handler_ = error_handler;
+  }
+
+  Interface* impl() { return impl_; }
+
+  bool is_bound() const { return !!router_; }
+
+  MessagePipeHandle handle() const {
+    DCHECK(is_bound());
+    return router_->handle();
+  }
+
+  AssociatedGroup* associated_group() { return nullptr; }
+
+  void EnableTestingMode() {
+    DCHECK(is_bound());
+    router_->EnableTestingMode();
+  }
+
+ private:
+  void DestroyRouter() {
+    router_->set_connection_error_handler(base::Closure());
+    delete router_;
+    router_ = nullptr;
+    connection_error_handler_.Reset();
+  }
+
+  void RunConnectionErrorHandler() {
+    if (!connection_error_handler_.is_null())
+      connection_error_handler_.Run();
+  }
+
+  internal::Router* router_ = nullptr;
+  typename Interface::Stub_ stub_;
+  Interface* impl_;
+  base::Closure connection_error_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(BindingState);
+};
+
+// Uses a multiplexing router. If |Interface| has methods to pass associated
+// interface pointers or requests, this specialization should be used.
+template <typename Interface>
+class BindingState<Interface, true> {
+ public:
+  explicit BindingState(Interface* impl) : impl_(impl) {
+    stub_.set_sink(impl_);
+  }
+
+  ~BindingState() { Close(); }
+
+  void Bind(ScopedMessagePipeHandle handle,
+            scoped_refptr<base::SingleThreadTaskRunner> runner) {
+    DCHECK(!router_);
+
+    router_ = new internal::MultiplexRouter(false, std::move(handle), runner);
+    router_->SetMasterInterfaceName(Interface::Name_);
+    stub_.serialization_context()->group_controller = router_;
+
+    endpoint_client_.reset(new InterfaceEndpointClient(
+        router_->CreateLocalEndpointHandle(kMasterInterfaceId),
+        &stub_, base::WrapUnique(new typename Interface::RequestValidator_()),
+        Interface::HasSyncMethods_, std::move(runner)));
+
+    endpoint_client_->set_connection_error_handler(
+        base::Bind(&BindingState::RunConnectionErrorHandler,
+                   base::Unretained(this)));
+  }
+
+  bool HasAssociatedInterfaces() const {
+    return router_ ? router_->HasAssociatedEndpoints() : false;
+  }
+
+  void PauseIncomingMethodCallProcessing() {
+    DCHECK(router_);
+    router_->PauseIncomingMethodCallProcessing();
+  }
+  void ResumeIncomingMethodCallProcessing() {
+    DCHECK(router_);
+    router_->ResumeIncomingMethodCallProcessing();
+  }
+
+  bool WaitForIncomingMethodCall(
+      MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) {
+    DCHECK(router_);
+    return router_->WaitForIncomingMessage(deadline);
+  }
+
+  void Close() {
+    if (!router_)
+      return;
+
+    endpoint_client_.reset();
+    router_->CloseMessagePipe();
+    router_ = nullptr;
+    connection_error_handler_.Reset();
+  }
+
+  InterfaceRequest<Interface> Unbind() {
+    endpoint_client_.reset();
+    InterfaceRequest<Interface> request =
+        MakeRequest<Interface>(router_->PassMessagePipe());
+    router_ = nullptr;
+    connection_error_handler_.Reset();
+    return request;
+  }
+
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    DCHECK(is_bound());
+    connection_error_handler_ = error_handler;
+  }
+
+  Interface* impl() { return impl_; }
+
+  bool is_bound() const { return !!router_; }
+
+  MessagePipeHandle handle() const {
+    DCHECK(is_bound());
+    return router_->handle();
+  }
+
+  AssociatedGroup* associated_group() {
+    return endpoint_client_ ? endpoint_client_->associated_group() : nullptr;
+  }
+
+  void EnableTestingMode() {
+    DCHECK(is_bound());
+    router_->EnableTestingMode();
+  }
+
+ private:
+  void RunConnectionErrorHandler() {
+    if (!connection_error_handler_.is_null())
+      connection_error_handler_.Run();
+  }
+
+  scoped_refptr<internal::MultiplexRouter> router_;
+  std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
+
+  typename Interface::Stub_ stub_;
+  Interface* impl_;
+  base::Closure connection_error_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(BindingState);
+};
+
+}  // namesapce internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_
diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.cc b/mojo/public/cpp/bindings/lib/bindings_internal.cc
new file mode 100644
index 0000000..a3bdb1f
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.cc
@@ -0,0 +1,47 @@
+// Copyright 2016 The Chromium Authors. All 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_internal.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);
+  DCHECK(p_obj > p_slot);
+
+  *offset = static_cast<uint64_t>(p_obj - p_slot);
+}
+
+}  // namespace internal
+}  // namespace mojo
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..b37d872
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -0,0 +1,334 @@
+// 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 <stdint.h>
+
+#include <functional>
+
+#include "base/template_util.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+template <typename T>
+class Array;
+
+template <typename T>
+class AssociatedInterfacePtrInfo;
+
+template <typename T>
+class AssociatedInterfaceRequest;
+
+template <typename T>
+class InterfacePtr;
+
+template <typename T>
+class InterfaceRequest;
+
+template <typename K, typename V>
+class Map;
+
+class String;
+
+template <typename T>
+class StructPtr;
+
+template <typename T>
+class InlinedStructPtr;
+
+namespace internal {
+
+// Please note that this is a different value than |mojo::kInvalidHandleValue|,
+// which is the "decoded" invalid handle.
+const uint32_t kEncodedInvalidHandleValue = static_cast<uint32_t>(-1);
+
+// A serialized union always takes 16 bytes:
+//   4-byte size + 4-byte tag + 8-byte payload.
+const uint32_t kUnionDataSize = 16;
+
+template <typename T>
+class Array_Data;
+
+template <typename K, typename V>
+class Map_Data;
+
+using String_Data = Array_Data<char>;
+
+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.
+inline const void* DecodePointer(const uint64_t* offset) {
+  if (!*offset)
+    return nullptr;
+  return reinterpret_cast<const char*>(offset) + *offset;
+}
+
+#pragma pack(push, 1)
+
+struct StructHeader {
+  uint32_t num_bytes;
+  uint32_t version;
+};
+static_assert(sizeof(StructHeader) == 8, "Bad sizeof(StructHeader)");
+
+struct ArrayHeader {
+  uint32_t num_bytes;
+  uint32_t num_elements;
+};
+static_assert(sizeof(ArrayHeader) == 8, "Bad_sizeof(ArrayHeader)");
+
+template <typename T>
+struct Pointer {
+  using BaseType = T;
+
+  void Set(T* ptr) { EncodePointer(ptr, &offset); }
+  const T* Get() const { return static_cast<const T*>(DecodePointer(&offset)); }
+  T* Get() {
+    return static_cast<T*>(const_cast<void*>(DecodePointer(&offset)));
+  }
+
+  bool is_null() const { return offset == 0; }
+
+  uint64_t offset;
+};
+static_assert(sizeof(Pointer<char>) == 8, "Bad_sizeof(Pointer)");
+
+struct Handle_Data {
+  Handle_Data() = default;
+  explicit Handle_Data(uint32_t value) : value(value) {}
+
+  bool is_valid() const { return value != kEncodedInvalidHandleValue; }
+
+  uint32_t value;
+};
+static_assert(sizeof(Handle_Data) == 4, "Bad_sizeof(Handle_Data)");
+
+struct Interface_Data {
+  Handle_Data handle;
+  uint32_t version;
+};
+static_assert(sizeof(Interface_Data) == 8, "Bad_sizeof(Interface_Data)");
+
+struct AssociatedInterface_Data {
+  InterfaceId interface_id;
+  uint32_t version;
+};
+static_assert(sizeof(AssociatedInterface_Data) == 8,
+              "Bad_sizeof(AssociatedInterface_Data)");
+
+struct AssociatedInterfaceRequest_Data {
+  InterfaceId interface_id;
+};
+static_assert(sizeof(AssociatedInterfaceRequest_Data) == 4,
+              "Bad_sizeof(AssociatedInterfaceRequest_Data)");
+
+#pragma pack(pop)
+
+template <typename T>
+T FetchAndReset(T* ptr) {
+  T temp = *ptr;
+  *ptr = T();
+  return temp;
+}
+
+template <typename T>
+struct IsUnionDataType {
+ private:
+  template <typename U>
+  static YesType Test(const typename U::MojomUnionDataType*);
+
+  template <typename U>
+  static NoType Test(...);
+
+  EnsureTypeIsComplete<T> check_t_;
+
+ public:
+  static const bool value =
+      sizeof(Test<T>(0)) == sizeof(YesType) && !IsConst<T>::value;
+};
+
+enum class MojomTypeCategory : uint32_t {
+  ARRAY = 1 << 0,
+  ASSOCIATED_INTERFACE = 1 << 1,
+  ASSOCIATED_INTERFACE_REQUEST = 1 << 2,
+  BOOLEAN = 1 << 3,
+  ENUM = 1 << 4,
+  HANDLE = 1 << 5,
+  INTERFACE = 1 << 6,
+  INTERFACE_REQUEST = 1 << 7,
+  MAP = 1 << 8,
+  // POD except boolean and enum.
+  POD = 1 << 9,
+  STRING = 1 << 10,
+  STRUCT = 1 << 11,
+  UNION = 1 << 12
+};
+
+inline constexpr MojomTypeCategory operator&(MojomTypeCategory x,
+                                             MojomTypeCategory y) {
+  return static_cast<MojomTypeCategory>(static_cast<uint32_t>(x) &
+                                        static_cast<uint32_t>(y));
+}
+
+inline constexpr MojomTypeCategory operator|(MojomTypeCategory x,
+                                             MojomTypeCategory y) {
+  return static_cast<MojomTypeCategory>(static_cast<uint32_t>(x) |
+                                        static_cast<uint32_t>(y));
+}
+
+template <typename T, bool is_enum = std::is_enum<T>::value>
+struct MojomTypeTraits {
+  using Data = T;
+  using DataAsArrayElement = Data;
+
+  static const MojomTypeCategory category = MojomTypeCategory::POD;
+};
+
+template <typename T>
+struct MojomTypeTraits<Array<T>, false> {
+  using Data = Array_Data<typename MojomTypeTraits<T>::DataAsArrayElement>;
+  using DataAsArrayElement = Pointer<Data>;
+
+  static const MojomTypeCategory category = MojomTypeCategory::ARRAY;
+};
+
+template <typename T>
+struct MojomTypeTraits<AssociatedInterfacePtrInfo<T>, false> {
+  using Data = AssociatedInterface_Data;
+  using DataAsArrayElement = Data;
+
+  static const MojomTypeCategory category =
+      MojomTypeCategory::ASSOCIATED_INTERFACE;
+};
+
+template <typename T>
+struct MojomTypeTraits<AssociatedInterfaceRequest<T>, false> {
+  using Data = AssociatedInterfaceRequest_Data;
+  using DataAsArrayElement = Data;
+
+  static const MojomTypeCategory category =
+      MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST;
+};
+
+template <>
+struct MojomTypeTraits<bool, false> {
+  using Data = bool;
+  using DataAsArrayElement = Data;
+
+  static const MojomTypeCategory category = MojomTypeCategory::BOOLEAN;
+};
+
+template <typename T>
+struct MojomTypeTraits<T, true> {
+  using Data = int32_t;
+  using DataAsArrayElement = Data;
+
+  static const MojomTypeCategory category = MojomTypeCategory::ENUM;
+};
+
+template <typename T>
+struct MojomTypeTraits<ScopedHandleBase<T>, false> {
+  using Data = Handle_Data;
+  using DataAsArrayElement = Data;
+
+  static const MojomTypeCategory category = MojomTypeCategory::HANDLE;
+};
+
+template <typename T>
+struct MojomTypeTraits<InterfacePtr<T>, false> {
+  using Data = Interface_Data;
+  using DataAsArrayElement = Data;
+
+  static const MojomTypeCategory category = MojomTypeCategory::INTERFACE;
+};
+
+template <typename T>
+struct MojomTypeTraits<InterfaceRequest<T>, false> {
+  using Data = Handle_Data;
+  using DataAsArrayElement = Data;
+
+  static const MojomTypeCategory category =
+      MojomTypeCategory::INTERFACE_REQUEST;
+};
+
+template <typename K, typename V>
+struct MojomTypeTraits<Map<K, V>, false> {
+  using Data = Map_Data<typename MojomTypeTraits<K>::DataAsArrayElement,
+                        typename MojomTypeTraits<V>::DataAsArrayElement>;
+  using DataAsArrayElement = Pointer<Data>;
+
+  static const MojomTypeCategory category = MojomTypeCategory::MAP;
+};
+
+template <>
+struct MojomTypeTraits<String, false> {
+  using Data = String_Data;
+  using DataAsArrayElement = Pointer<Data>;
+
+  static const MojomTypeCategory category = MojomTypeCategory::STRING;
+};
+
+template <typename T>
+struct MojomTypeTraits<StructPtr<T>, false> {
+  using Data = typename T::Data_;
+  using DataAsArrayElement =
+      typename std::conditional<IsUnionDataType<Data>::value,
+                                Data,
+                                Pointer<Data>>::type;
+
+  static const MojomTypeCategory category = IsUnionDataType<Data>::value
+                                                ? MojomTypeCategory::UNION
+                                                : MojomTypeCategory::STRUCT;
+};
+
+template <typename T>
+struct MojomTypeTraits<InlinedStructPtr<T>, false> {
+  using Data = typename T::Data_;
+  using DataAsArrayElement =
+      typename std::conditional<IsUnionDataType<Data>::value,
+                                Data,
+                                Pointer<Data>>::type;
+
+  static const MojomTypeCategory category = IsUnionDataType<Data>::value
+                                                ? MojomTypeCategory::UNION
+                                                : MojomTypeCategory::STRUCT;
+};
+
+template <typename T, MojomTypeCategory categories>
+struct BelongsTo {
+  static const bool value =
+      static_cast<uint32_t>(MojomTypeTraits<T>::category & categories) != 0;
+};
+
+template <typename T>
+struct EnumHashImpl {
+  static_assert(std::is_enum<T>::value, "Incorrect hash function.");
+
+  size_t operator()(T input) const {
+    using UnderlyingType = typename base::underlying_type<T>::type;
+    return std::hash<UnderlyingType>()(static_cast<UnderlyingType>(input));
+  }
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_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/clone_equals_util.h b/mojo/public/cpp/bindings/lib/clone_equals_util.h
new file mode 100644
index 0000000..f7bd898
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/clone_equals_util.h
@@ -0,0 +1,161 @@
+// Copyright 2016 The Chromium Authors. All 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_CLONE_EQUALS_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_
+
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+#include "base/optional.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+struct HasCloneMethod {
+  template <typename U>
+  static char Test(decltype(&U::Clone));
+  template <typename U>
+  static int Test(...);
+  static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+  EnsureTypeIsComplete<T> check_t_;
+};
+
+template <typename T, bool has_clone_method = HasCloneMethod<T>::value>
+struct CloneTraits;
+
+template <typename T>
+T Clone(const T& input);
+
+template <typename T>
+struct CloneTraits<T, true> {
+  static T Clone(const T& input) { return input.Clone(); }
+};
+
+template <typename T>
+struct CloneTraits<T, false> {
+  static T Clone(const T& input) { return input; }
+};
+
+template <typename T>
+struct CloneTraits<base::Optional<T>, false> {
+  static base::Optional<T> Clone(const base::Optional<T>& input) {
+    if (!input)
+      return base::nullopt;
+
+    return base::Optional<T>(internal::Clone(*input));
+  }
+};
+
+template <typename T>
+struct CloneTraits<std::vector<T>, false> {
+  static std::vector<T> Clone(const std::vector<T>& input) {
+    std::vector<T> result;
+    result.reserve(input.size());
+    for (const auto& element : input)
+      result.push_back(internal::Clone(element));
+
+    return result;
+  }
+};
+
+template <typename K, typename V>
+struct CloneTraits<std::unordered_map<K, V>, false> {
+  static std::unordered_map<K, V> Clone(const std::unordered_map<K, V>& input) {
+    std::unordered_map<K, V> result;
+    for (const auto& element : input) {
+      result.insert(std::make_pair(internal::Clone(element.first),
+                                   internal::Clone(element.second)));
+    }
+    return result;
+  }
+};
+
+template <typename T>
+T Clone(const T& input) {
+  return CloneTraits<T>::Clone(input);
+};
+
+template <typename T>
+struct HasEqualsMethod {
+  template <typename U>
+  static char Test(decltype(&U::Equals));
+  template <typename U>
+  static int Test(...);
+  static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+  EnsureTypeIsComplete<T> check_t_;
+};
+
+template <typename T, bool has_equals_method = HasEqualsMethod<T>::value>
+struct EqualsTraits;
+
+template <typename T>
+bool Equals(const T& a, const T& b);
+
+template <typename T>
+struct EqualsTraits<T, true> {
+  static bool Equals(const T& a, const T& b) { return a.Equals(b); }
+};
+
+template <typename T>
+struct EqualsTraits<T, false> {
+  static bool Equals(const T& a, const T& b) { return a == b; }
+};
+
+template <typename T>
+struct EqualsTraits<base::Optional<T>, false> {
+  static bool Equals(const base::Optional<T>& a, const base::Optional<T>& b) {
+    if (!a && !b)
+      return true;
+    if (!a || !b)
+      return false;
+
+    return internal::Equals(*a, *b);
+  }
+};
+
+template <typename T>
+struct EqualsTraits<std::vector<T>, false> {
+  static bool Equals(const std::vector<T>& a, const std::vector<T>& b) {
+    if (a.size() != b.size())
+      return false;
+    for (size_t i = 0; i < a.size(); ++i) {
+      if (!internal::Equals(a[i], b[i]))
+        return false;
+    }
+    return true;
+  }
+};
+
+template <typename K, typename V>
+struct EqualsTraits<std::unordered_map<K, V>, false> {
+  static bool Equals(const std::unordered_map<K, V>& a,
+                     const std::unordered_map<K, V>& b) {
+    if (a.size() != b.size())
+      return false;
+    for (const auto& element : a) {
+      auto iter = b.find(element.first);
+      if (iter == b.end() || !internal::Equals(element.second, iter->second))
+        return false;
+    }
+    return true;
+  }
+};
+
+template <typename T>
+bool Equals(const T& a, const T& b) {
+  return EqualsTraits<T>::Equals(a, b);
+}
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_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..1bb38f0
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -0,0 +1,355 @@
+// 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/connector.h"
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/cpp/bindings/sync_handle_watcher.h"
+
+namespace mojo {
+
+namespace {
+
+// Similar to base::AutoLock, except that it does nothing if |lock| passed into
+// the constructor is null.
+class MayAutoLock {
+ public:
+  explicit MayAutoLock(base::Lock* lock) : lock_(lock) {
+    if (lock_)
+      lock_->Acquire();
+  }
+
+  ~MayAutoLock() {
+    if (lock_) {
+      lock_->AssertAcquired();
+      lock_->Release();
+    }
+  }
+
+ private:
+  base::Lock* lock_;
+  DISALLOW_COPY_AND_ASSIGN(MayAutoLock);
+};
+
+}  // namespace
+
+// ----------------------------------------------------------------------------
+
+Connector::Connector(ScopedMessagePipeHandle message_pipe,
+                     ConnectorConfig config,
+                     scoped_refptr<base::SingleThreadTaskRunner> runner)
+    : message_pipe_(std::move(message_pipe)),
+      incoming_receiver_(nullptr),
+      task_runner_(std::move(runner)),
+      handle_watcher_(task_runner_),
+      error_(false),
+      drop_writes_(false),
+      enforce_errors_from_incoming_receiver_(true),
+      paused_(false),
+      lock_(config == MULTI_THREADED_SEND ? new base::Lock : nullptr),
+      allow_woken_up_by_others_(false),
+      sync_handle_watcher_callback_count_(0),
+      weak_factory_(this) {
+  weak_self_ = weak_factory_.GetWeakPtr();
+  // 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() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  CancelWait();
+}
+
+void Connector::CloseMessagePipe() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  CancelWait();
+  MayAutoLock locker(lock_.get());
+  message_pipe_.reset();
+}
+
+ScopedMessagePipeHandle Connector::PassMessagePipe() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  CancelWait();
+  MayAutoLock locker(lock_.get());
+  return std::move(message_pipe_);
+}
+
+void Connector::RaiseError() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  HandleError(true, true);
+}
+
+bool Connector::WaitForIncomingMessage(MojoDeadline deadline) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (error_)
+    return false;
+
+  ResumeIncomingMethodCallProcessing();
+
+  MojoResult rv =
+      Wait(message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE, deadline, nullptr);
+  if (rv == MOJO_RESULT_SHOULD_WAIT || rv == MOJO_RESULT_DEADLINE_EXCEEDED)
+    return false;
+  if (rv != MOJO_RESULT_OK) {
+    // Users that call WaitForIncomingMessage() should expect their code to be
+    // re-entered, so we call the error handler synchronously.
+    HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false);
+    return false;
+  }
+  ignore_result(ReadSingleMessage(&rv));
+  return (rv == MOJO_RESULT_OK);
+}
+
+void Connector::PauseIncomingMethodCallProcessing() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (paused_)
+    return;
+
+  paused_ = true;
+  CancelWait();
+}
+
+void Connector::ResumeIncomingMethodCallProcessing() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!paused_)
+    return;
+
+  paused_ = false;
+  WaitToReadMore();
+}
+
+bool Connector::Accept(Message* message) {
+  DCHECK(lock_ || thread_checker_.CalledOnValidThread());
+
+  // It shouldn't hurt even if |error_| may be changed by a different thread at
+  // the same time. The outcome is that we may write into |message_pipe_| after
+  // encountering an error, which should be fine.
+  if (error_)
+    return false;
+
+  MayAutoLock locker(lock_.get());
+
+  if (!message_pipe_.is_valid() || drop_writes_)
+    return true;
+
+  MojoResult rv =
+      WriteMessageNew(message_pipe_.get(), message->TakeMojoMessage(),
+                      MOJO_WRITE_MESSAGE_FLAG_NONE);
+
+  switch (rv) {
+    case MOJO_RESULT_OK:
+      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 |DCHECK()|. (But, until
+      // crbug.com/389666, etc. are resolved, this will make tests fail quickly
+      // rather than hanging.)
+      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;
+}
+
+void Connector::AllowWokenUpBySyncWatchOnSameThread() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  allow_woken_up_by_others_ = true;
+
+  EnsureSyncWatcherExists();
+  sync_watcher_->AllowWokenUpBySyncWatchOnSameThread();
+}
+
+bool Connector::SyncWatch(const bool* should_stop) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (error_)
+    return false;
+
+  ResumeIncomingMethodCallProcessing();
+
+  EnsureSyncWatcherExists();
+  return sync_watcher_->SyncWatch(should_stop);
+}
+
+void Connector::OnWatcherHandleReady(MojoResult result) {
+  OnHandleReadyInternal(result);
+}
+
+void Connector::OnSyncHandleWatcherHandleReady(MojoResult result) {
+  base::WeakPtr<Connector> weak_self(weak_self_);
+
+  sync_handle_watcher_callback_count_++;
+  OnHandleReadyInternal(result);
+  // At this point, this object might have been deleted.
+  if (weak_self)
+    sync_handle_watcher_callback_count_--;
+}
+
+void Connector::OnHandleReadyInternal(MojoResult result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (result != MOJO_RESULT_OK) {
+    HandleError(result != MOJO_RESULT_FAILED_PRECONDITION, false);
+    return;
+  }
+  ReadAllAvailableMessages();
+  // At this point, this object might have been deleted. Return.
+}
+
+void Connector::WaitToReadMore() {
+  CHECK(!paused_);
+  DCHECK(!handle_watcher_.IsWatching());
+
+  MojoResult rv = handle_watcher_.Start(
+      message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+      base::Bind(&Connector::OnWatcherHandleReady,
+                 base::Unretained(this)));
+
+  if (rv != MOJO_RESULT_OK) {
+    // If the watch failed because the handle is invalid or its conditions can
+    // no longer be met, we signal the error asynchronously to avoid reentry.
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&Connector::OnWatcherHandleReady, weak_self_, rv));
+  }
+
+  if (allow_woken_up_by_others_) {
+    EnsureSyncWatcherExists();
+    sync_watcher_->AllowWokenUpBySyncWatchOnSameThread();
+  }
+}
+
+bool Connector::ReadSingleMessage(MojoResult* read_result) {
+  CHECK(!paused_);
+
+  bool receiver_result = false;
+
+  // Detect if |this| was destroyed during message dispatch. Allow for the
+  // possibility of re-entering ReadMore() through message dispatch.
+  base::WeakPtr<Connector> weak_self = weak_self_;
+
+  Message message;
+  const MojoResult rv = ReadMessage(message_pipe_.get(), &message);
+  *read_result = rv;
+
+  if (rv == MOJO_RESULT_OK) {
+    receiver_result =
+        incoming_receiver_ && incoming_receiver_->Accept(&message);
+  }
+
+  if (!weak_self)
+    return false;
+
+  if (rv == MOJO_RESULT_SHOULD_WAIT)
+    return true;
+
+  if (rv != MOJO_RESULT_OK) {
+    HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false);
+    return false;
+  }
+
+  if (enforce_errors_from_incoming_receiver_ && !receiver_result) {
+    HandleError(true, false);
+    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 (paused_)
+      return;
+
+    if (rv == MOJO_RESULT_SHOULD_WAIT)
+      break;
+  }
+}
+
+void Connector::CancelWait() {
+  handle_watcher_.Cancel();
+  sync_watcher_.reset();
+}
+
+void Connector::HandleError(bool force_pipe_reset, bool force_async_handler) {
+  if (error_ || !message_pipe_.is_valid())
+    return;
+
+  if (paused_) {
+    // Enforce calling the error handler asynchronously if the user has paused
+    // receiving messages. We need to wait until the user starts receiving
+    // messages again.
+    force_async_handler = true;
+  }
+
+  if (!force_pipe_reset && force_async_handler)
+    force_pipe_reset = true;
+
+  if (force_pipe_reset) {
+    CancelWait();
+    MayAutoLock locker(lock_.get());
+    message_pipe_.reset();
+    MessagePipe dummy_pipe;
+    message_pipe_ = std::move(dummy_pipe.handle0);
+  } else {
+    CancelWait();
+  }
+
+  if (force_async_handler) {
+    if (!paused_)
+      WaitToReadMore();
+  } else {
+    error_ = true;
+    if (!connection_error_handler_.is_null())
+      connection_error_handler_.Run();
+  }
+}
+
+void Connector::EnsureSyncWatcherExists() {
+  if (sync_watcher_)
+    return;
+  sync_watcher_.reset(new SyncHandleWatcher(
+      message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+      base::Bind(&Connector::OnSyncHandleWatcherHandleReady,
+                 base::Unretained(this))));
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.cc b/mojo/public/cpp/bindings/lib/control_message_handler.cc
new file mode 100644
index 0000000..9f44e88
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc
@@ -0,0 +1,84 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <utility>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+// static
+bool ControlMessageHandler::IsControlMessage(const Message* message) {
+  return message->header()->name == kRunMessageId ||
+         message->header()->name == kRunOrClosePipeMessageId;
+}
+
+ControlMessageHandler::ControlMessageHandler(uint32_t interface_version)
+    : interface_version_(interface_version) {
+}
+
+ControlMessageHandler::~ControlMessageHandler() {
+}
+
+bool ControlMessageHandler::Accept(Message* message) {
+  if (message->header()->name == kRunOrClosePipeMessageId)
+    return RunOrClosePipe(message);
+
+  NOTREACHED();
+  return false;
+}
+
+bool ControlMessageHandler::AcceptWithResponder(
+    Message* message,
+    MessageReceiverWithStatus* responder) {
+  if (message->header()->name == kRunMessageId)
+    return Run(message, responder);
+
+  NOTREACHED();
+  return false;
+}
+
+bool ControlMessageHandler::Run(Message* message,
+                                MessageReceiverWithStatus* responder) {
+  RunResponseMessageParamsPtr response_params_ptr(
+      RunResponseMessageParams::New());
+  response_params_ptr->reserved0 = 16u;
+  response_params_ptr->reserved1 = 0u;
+  response_params_ptr->query_version_result = QueryVersionResult::New();
+  response_params_ptr->query_version_result->version = interface_version_;
+
+  size_t size = PrepareToSerialize<RunResponseMessageParamsPtr>(
+      response_params_ptr, &context_);
+  ResponseMessageBuilder builder(kRunMessageId, size, message->request_id());
+
+  RunResponseMessageParams_Data* response_params = nullptr;
+  Serialize<RunResponseMessageParamsPtr>(response_params_ptr, builder.buffer(),
+                                         &response_params, &context_);
+  bool ok = responder->Accept(builder.message());
+  ALLOW_UNUSED_LOCAL(ok);
+  delete responder;
+
+  return true;
+}
+
+bool ControlMessageHandler::RunOrClosePipe(Message* message) {
+  RunOrClosePipeMessageParams_Data* params =
+      reinterpret_cast<RunOrClosePipeMessageParams_Data*>(
+          message->mutable_payload());
+  RunOrClosePipeMessageParamsPtr params_ptr;
+  Deserialize<RunOrClosePipeMessageParamsPtr>(params, &params_ptr, &context_);
+
+  return interface_version_ >= params_ptr->require_version->version;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.h b/mojo/public/cpp/bindings/lib/control_message_handler.h
new file mode 100644
index 0000000..13b5aa6
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.h
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_HANDLER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_HANDLER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/serialization_context.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace internal {
+
+// Handlers for request messages defined in interface_control_messages.mojom.
+class ControlMessageHandler : public MessageReceiverWithResponderStatus {
+ public:
+  static bool IsControlMessage(const Message* message);
+
+  explicit ControlMessageHandler(uint32_t interface_version);
+  ~ControlMessageHandler() override;
+
+  // Call the following methods only if IsControlMessage() returned true.
+  bool Accept(Message* message) override;
+  // Takes ownership of |responder|.
+  bool AcceptWithResponder(Message* message,
+                           MessageReceiverWithStatus* responder) override;
+
+ private:
+  bool Run(Message* message, MessageReceiverWithStatus* responder);
+  bool RunOrClosePipe(Message* message);
+
+  uint32_t interface_version_;
+  SerializationContext context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ControlMessageHandler);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_HANDLER_H_
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
new file mode 100644
index 0000000..7af409d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
@@ -0,0 +1,111 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+using RunCallback = base::Callback<void(QueryVersionResultPtr)>;
+
+class RunResponseForwardToCallback : public MessageReceiver {
+ public:
+  RunResponseForwardToCallback(const RunCallback& callback)
+      : callback_(callback) {}
+  bool Accept(Message* message) override;
+
+ private:
+  RunCallback callback_;
+  DISALLOW_COPY_AND_ASSIGN(RunResponseForwardToCallback);
+};
+
+bool RunResponseForwardToCallback::Accept(Message* message) {
+  RunResponseMessageParams_Data* params =
+      reinterpret_cast<RunResponseMessageParams_Data*>(
+          message->mutable_payload());
+  RunResponseMessageParamsPtr params_ptr;
+  SerializationContext context;
+  Deserialize<RunResponseMessageParamsPtr>(params, &params_ptr, &context);
+
+  callback_.Run(std::move(params_ptr->query_version_result));
+  return true;
+}
+
+void SendRunMessage(MessageReceiverWithResponder* receiver,
+                    QueryVersionPtr query_version,
+                    const RunCallback& callback,
+                    SerializationContext* context) {
+  RunMessageParamsPtr params_ptr(RunMessageParams::New());
+  params_ptr->reserved0 = 16u;
+  params_ptr->reserved1 = 0u;
+  params_ptr->query_version = std::move(query_version);
+
+  size_t size = PrepareToSerialize<RunMessageParamsPtr>(params_ptr, context);
+  RequestMessageBuilder builder(kRunMessageId, size);
+
+  RunMessageParams_Data* params = nullptr;
+  Serialize<RunMessageParamsPtr>(params_ptr, builder.buffer(), &params,
+                                 context);
+  MessageReceiver* responder = new RunResponseForwardToCallback(callback);
+  if (!receiver->AcceptWithResponder(builder.message(), responder))
+    delete responder;
+}
+
+void SendRunOrClosePipeMessage(MessageReceiverWithResponder* receiver,
+                               RequireVersionPtr require_version,
+                               SerializationContext* context) {
+  RunOrClosePipeMessageParamsPtr params_ptr(RunOrClosePipeMessageParams::New());
+  params_ptr->reserved0 = 16u;
+  params_ptr->reserved1 = 0u;
+  params_ptr->require_version = std::move(require_version);
+
+  size_t size =
+      PrepareToSerialize<RunOrClosePipeMessageParamsPtr>(params_ptr, context);
+  MessageBuilder builder(kRunOrClosePipeMessageId, size);
+
+  RunOrClosePipeMessageParams_Data* params = nullptr;
+  Serialize<RunOrClosePipeMessageParamsPtr>(params_ptr, builder.buffer(),
+                                            &params, context);
+  bool ok = receiver->Accept(builder.message());
+  ALLOW_UNUSED_LOCAL(ok);
+}
+
+void RunVersionCallback(const base::Callback<void(uint32_t)>& callback,
+                        QueryVersionResultPtr query_version_result) {
+  callback.Run(query_version_result->version);
+}
+
+}  // namespace
+
+ControlMessageProxy::ControlMessageProxy(MessageReceiverWithResponder* receiver)
+    : receiver_(receiver) {
+}
+
+void ControlMessageProxy::QueryVersion(
+    const base::Callback<void(uint32_t)>& callback) {
+  SendRunMessage(receiver_, QueryVersion::New(),
+                 base::Bind(&RunVersionCallback, callback), &context_);
+}
+
+void ControlMessageProxy::RequireVersion(uint32_t version) {
+  RequireVersionPtr require_version(RequireVersion::New());
+  require_version->version = version;
+  SendRunOrClosePipeMessage(receiver_, std::move(require_version), &context_);
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.h b/mojo/public/cpp/bindings/lib/control_message_proxy.h
new file mode 100644
index 0000000..5ec6ddc
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_PROXY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_PROXY_H_
+
+#include <stdint.h>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/serialization_context.h"
+
+namespace mojo {
+
+class MessageReceiverWithResponder;
+
+namespace internal {
+
+// Proxy for request messages defined in interface_control_messages.mojom.
+class ControlMessageProxy {
+ public:
+  // Doesn't take ownership of |receiver|. It must outlive this object.
+  explicit ControlMessageProxy(MessageReceiverWithResponder* receiver);
+
+  void QueryVersion(const base::Callback<void(uint32_t)>& callback);
+  void RequireVersion(uint32_t version);
+
+ protected:
+  // Not owned.
+  MessageReceiverWithResponder* receiver_;
+  SerializationContext context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ControlMessageProxy);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_PROXY_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..899bac1
--- /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 "base/logging.h"
+
+namespace mojo {
+namespace internal {
+
+FilterChain::FilterChain(MessageReceiver* sink) : sink_(sink) {
+}
+
+FilterChain::FilterChain(FilterChain&& other) : sink_(other.sink_) {
+  other.sink_ = nullptr;
+  filters_.swap(other.filters_);
+}
+
+FilterChain& FilterChain::operator=(FilterChain&& other) {
+  std::swap(sink_, other.sink_);
+  filters_.swap(other.filters_);
+  return *this;
+}
+
+FilterChain::~FilterChain() {
+  for (std::vector<MessageFilter*>::iterator iter = filters_.begin();
+       iter != filters_.end();
+       ++iter) {
+    delete *iter;
+  }
+}
+
+void FilterChain::SetSink(MessageReceiver* sink) {
+  DCHECK(!sink_);
+  sink_ = sink;
+  if (!filters_.empty())
+    filters_.back()->set_sink(sink);
+}
+
+MessageReceiver* FilterChain::GetHead() {
+  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..447be3d
--- /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 <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+namespace internal {
+
+class FilterChain {
+ public:
+  // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+  // this object is alive.
+  explicit FilterChain(MessageReceiver* sink = nullptr);
+
+  FilterChain(FilterChain&& other);
+  FilterChain& operator=(FilterChain&& other);
+  ~FilterChain();
+
+  template <typename FilterType, typename... Args>
+  inline void Append(Args&&... args);
+
+  // 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.
+  // TODO(dcheng): Use unique_ptr.
+  std::vector<MessageFilter*> filters_;
+
+  MessageReceiver* sink_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilterChain);
+};
+
+template <typename FilterType, typename... Args>
+inline void FilterChain::Append(Args&&... args) {
+  FilterType* filter = new FilterType(std::forward<Args>(args)..., 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..50b8a21
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.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/public/cpp/bindings/lib/fixed_buffer.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+
+namespace mojo {
+namespace internal {
+
+FixedBuffer::FixedBuffer() : ptr_(nullptr), cursor_(0), size_(0) {}
+
+void FixedBuffer::Initialize(void* memory, size_t size) {
+  DCHECK(size == internal::Align(size));
+
+  ptr_ = static_cast<char*>(memory);
+  cursor_ = 0;
+  size_ = size;
+}
+
+void* FixedBuffer::Allocate(size_t delta) {
+  delta = internal::Align(delta);
+
+  if (delta == 0 || delta > size_ - cursor_) {
+    NOTREACHED();
+    return nullptr;
+  }
+
+  char* result = ptr_ + cursor_;
+  cursor_ += delta;
+
+  return result;
+}
+
+FixedBufferForTesting::FixedBufferForTesting(size_t size) {
+  size_ = internal::Align(size);
+  // Use calloc here to ensure all message memory is zero'd out.
+  ptr_ = static_cast<char*>(calloc(size_, 1));
+}
+
+FixedBufferForTesting::~FixedBufferForTesting() {
+  free(ptr_);
+}
+
+void* FixedBufferForTesting::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..9a5704b
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/buffer.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:
+  FixedBuffer();
+
+  // |size| should be aligned using internal::Align.
+  void Initialize(void* memory, size_t size);
+
+  size_t size() const { return size_; }
+
+  // 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.
+  void* Allocate(size_t num_bytes) override;
+
+ protected:
+  char* ptr_;
+  size_t cursor_;
+  size_t size_;
+
+  DISALLOW_COPY_AND_ASSIGN(FixedBuffer);
+};
+
+class FixedBufferForTesting : public FixedBuffer {
+ public:
+  explicit FixedBufferForTesting(size_t size);
+  ~FixedBufferForTesting() override;
+
+  // 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:
+  DISALLOW_COPY_AND_ASSIGN(FixedBufferForTesting);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
new file mode 100644
index 0000000..344c2ca
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All 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_HANDLE_INTERFACE_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_HANDLE_INTERFACE_SERIALIZATION_H_
+
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/serialization_context.h"
+#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+struct Serializer<AssociatedInterfacePtrInfo<T>,
+                  AssociatedInterfacePtrInfo<T>> {
+  static void Serialize(AssociatedInterfacePtrInfo<T>& input,
+                        AssociatedInterface_Data* output,
+                        SerializationContext* context) {
+    DCHECK(!input.handle().is_valid() || !input.handle().is_local());
+    DCHECK_EQ(input.handle().group_controller(),
+              context->group_controller.get());
+    output->version = input.version();
+    output->interface_id = input.PassHandle().release();
+  }
+
+  static bool Deserialize(AssociatedInterface_Data* input,
+                          AssociatedInterfacePtrInfo<T>* output,
+                          SerializationContext* context) {
+    output->set_handle(context->group_controller->CreateLocalEndpointHandle(
+        FetchAndReset(&input->interface_id)));
+    output->set_version(input->version);
+    return true;
+  }
+};
+
+template <typename T>
+struct Serializer<AssociatedInterfaceRequest<T>,
+                  AssociatedInterfaceRequest<T>> {
+  static void Serialize(AssociatedInterfaceRequest<T>& input,
+                        AssociatedInterfaceRequest_Data* output,
+                        SerializationContext* context) {
+    DCHECK(!input.handle().is_valid() || !input.handle().is_local());
+    DCHECK_EQ(input.handle().group_controller(),
+              context->group_controller.get());
+    output->interface_id = input.PassHandle().release();
+  }
+
+  static bool Deserialize(AssociatedInterfaceRequest_Data* input,
+                          AssociatedInterfaceRequest<T>* output,
+                          SerializationContext* context) {
+    output->Bind(context->group_controller->CreateLocalEndpointHandle(
+        FetchAndReset(&input->interface_id)));
+    return true;
+  }
+};
+
+template <typename T>
+struct Serializer<InterfacePtr<T>, InterfacePtr<T>> {
+  static void Serialize(InterfacePtr<T>& input,
+                        Interface_Data* output,
+                        SerializationContext* context) {
+    InterfacePtrInfo<T> info = input.PassInterface();
+    output->handle = context->handles.AddHandle(info.PassHandle().release());
+    output->version = info.version();
+  }
+
+  static bool Deserialize(Interface_Data* input,
+                          InterfacePtr<T>* output,
+                          SerializationContext* context) {
+    output->Bind(InterfacePtrInfo<T>(
+        context->handles.TakeHandleAs<mojo::MessagePipeHandle>(input->handle),
+        input->version));
+    return true;
+  }
+};
+
+template <typename T>
+struct Serializer<InterfaceRequest<T>, InterfaceRequest<T>> {
+  static void Serialize(InterfaceRequest<T>& input,
+                        Handle_Data* output,
+                        SerializationContext* context) {
+    *output = context->handles.AddHandle(input.PassMessagePipe().release());
+  }
+
+  static bool Deserialize(Handle_Data* input,
+                          InterfaceRequest<T>* output,
+                          SerializationContext* context) {
+    output->Bind(context->handles.TakeHandleAs<MessagePipeHandle>(*input));
+    return true;
+  }
+};
+
+template <typename T>
+struct Serializer<ScopedHandleBase<T>, ScopedHandleBase<T>> {
+  static void Serialize(ScopedHandleBase<T>& input,
+                        Handle_Data* output,
+                        SerializationContext* context) {
+    *output = context->handles.AddHandle(input.release());
+  }
+
+  static bool Deserialize(Handle_Data* input,
+                          ScopedHandleBase<T>* output,
+                          SerializationContext* context) {
+    *output = context->handles.TakeHandleAs<T>(*input);
+    return true;
+  }
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_HANDLE_INTERFACE_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
new file mode 100644
index 0000000..e1f388a
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
@@ -0,0 +1,314 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_controller.h"
+#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
+
+namespace mojo {
+
+// ----------------------------------------------------------------------------
+
+namespace {
+
+void DCheckIfInvalid(const base::WeakPtr<InterfaceEndpointClient>& client,
+                   const std::string& message) {
+  bool is_valid = client && !client->encountered_error();
+  DCHECK(!is_valid) << message;
+}
+
+// When receiving an incoming message which expects a repsonse,
+// InterfaceEndpointClient creates a ResponderThunk object and passes it to the
+// incoming message receiver. When the receiver finishes processing the message,
+// it can provide a response using this object.
+class ResponderThunk : public MessageReceiverWithStatus {
+ public:
+  explicit ResponderThunk(
+      const base::WeakPtr<InterfaceEndpointClient>& endpoint_client,
+      scoped_refptr<base::SingleThreadTaskRunner> runner)
+      : endpoint_client_(endpoint_client),
+        accept_was_invoked_(false),
+        task_runner_(std::move(runner)) {}
+  ~ResponderThunk() override {
+    if (!accept_was_invoked_) {
+      // The Mojo application handled a message that was expecting a response
+      // but did not send a response.
+      // We raise an error to signal the calling application that an error
+      // condition occurred. Without this the calling application would have no
+      // way of knowing it should stop waiting for a response.
+      if (task_runner_->RunsTasksOnCurrentThread()) {
+        // Please note that even if this code is run from a different task
+        // runner on the same thread as |task_runner_|, it is okay to directly
+        // call InterfaceEndpointClient::RaiseError(), because it will raise
+        // error from the correct task runner asynchronously.
+        if (endpoint_client_) {
+          endpoint_client_->RaiseError();
+        }
+      } else {
+        task_runner_->PostTask(
+            FROM_HERE,
+            base::Bind(&InterfaceEndpointClient::RaiseError, endpoint_client_));
+      }
+    }
+  }
+
+  // MessageReceiver implementation:
+  bool Accept(Message* message) override {
+    DCHECK(task_runner_->RunsTasksOnCurrentThread());
+    accept_was_invoked_ = true;
+    DCHECK(message->has_flag(Message::kFlagIsResponse));
+
+    bool result = false;
+
+    if (endpoint_client_)
+      result = endpoint_client_->Accept(message);
+
+    return result;
+  }
+
+  // MessageReceiverWithStatus implementation:
+  bool IsValid() override {
+    DCHECK(task_runner_->RunsTasksOnCurrentThread());
+    return endpoint_client_ && !endpoint_client_->encountered_error();
+  }
+
+  void DCheckInvalid(const std::string& message) override {
+    if (task_runner_->RunsTasksOnCurrentThread()) {
+      DCheckIfInvalid(endpoint_client_, message);
+    } else {
+      task_runner_->PostTask(
+          FROM_HERE, base::Bind(&DCheckIfInvalid, endpoint_client_, message));
+    }
+ }
+
+ private:
+  base::WeakPtr<InterfaceEndpointClient> endpoint_client_;
+  bool accept_was_invoked_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(ResponderThunk);
+};
+
+}  // namespace
+
+// ----------------------------------------------------------------------------
+
+InterfaceEndpointClient::SyncResponseInfo::SyncResponseInfo(
+    bool* in_response_received)
+    : response_received(in_response_received) {}
+
+InterfaceEndpointClient::SyncResponseInfo::~SyncResponseInfo() {}
+
+// ----------------------------------------------------------------------------
+
+InterfaceEndpointClient::HandleIncomingMessageThunk::HandleIncomingMessageThunk(
+    InterfaceEndpointClient* owner)
+    : owner_(owner) {}
+
+InterfaceEndpointClient::HandleIncomingMessageThunk::
+    ~HandleIncomingMessageThunk() {}
+
+bool InterfaceEndpointClient::HandleIncomingMessageThunk::Accept(
+    Message* message) {
+  return owner_->HandleValidatedMessage(message);
+}
+
+// ----------------------------------------------------------------------------
+
+InterfaceEndpointClient::InterfaceEndpointClient(
+    ScopedInterfaceEndpointHandle handle,
+    MessageReceiverWithResponderStatus* receiver,
+    std::unique_ptr<MessageFilter> payload_validator,
+    bool expect_sync_requests,
+    scoped_refptr<base::SingleThreadTaskRunner> runner)
+    : handle_(std::move(handle)),
+      incoming_receiver_(receiver),
+      payload_validator_(std::move(payload_validator)),
+      thunk_(this),
+      next_request_id_(1),
+      encountered_error_(false),
+      task_runner_(std::move(runner)),
+      weak_ptr_factory_(this) {
+  DCHECK(handle_.is_valid());
+  DCHECK(handle_.is_local());
+
+  // TODO(yzshen): the way to use validator (or message filter in general)
+  // directly is a little awkward.
+  payload_validator_->set_sink(&thunk_);
+
+  controller_ = handle_.group_controller()->AttachEndpointClient(
+      handle_, this, task_runner_);
+  if (expect_sync_requests)
+    controller_->AllowWokenUpBySyncWatchOnSameThread();
+}
+
+InterfaceEndpointClient::~InterfaceEndpointClient() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  handle_.group_controller()->DetachEndpointClient(handle_);
+}
+
+AssociatedGroup* InterfaceEndpointClient::associated_group() {
+  if (!associated_group_)
+    associated_group_ = handle_.group_controller()->CreateAssociatedGroup();
+  return associated_group_.get();
+}
+
+uint32_t InterfaceEndpointClient::interface_id() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return handle_.id();
+}
+
+ScopedInterfaceEndpointHandle InterfaceEndpointClient::PassHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!has_pending_responders());
+
+  if (!handle_.is_valid())
+    return ScopedInterfaceEndpointHandle();
+
+  controller_ = nullptr;
+  handle_.group_controller()->DetachEndpointClient(handle_);
+
+  return std::move(handle_);
+}
+
+void InterfaceEndpointClient::RaiseError() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  handle_.group_controller()->RaiseError();
+}
+
+bool InterfaceEndpointClient::Accept(Message* message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(controller_);
+  DCHECK(!message->has_flag(Message::kFlagExpectsResponse));
+
+  if (encountered_error_)
+    return false;
+
+  return controller_->SendMessage(message);
+}
+
+bool InterfaceEndpointClient::AcceptWithResponder(Message* message,
+                                                  MessageReceiver* responder) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(controller_);
+  DCHECK(message->has_flag(Message::kFlagExpectsResponse));
+
+  if (encountered_error_)
+    return false;
+
+  // 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);
+
+  bool is_sync = message->has_flag(Message::kFlagIsSync);
+  if (!controller_->SendMessage(message))
+    return false;
+
+  if (!is_sync) {
+    // We assume ownership of |responder|.
+    async_responders_[request_id] = base::WrapUnique(responder);
+    return true;
+  }
+
+  SyncCallRestrictions::AssertSyncCallAllowed();
+
+  bool response_received = false;
+  std::unique_ptr<MessageReceiver> sync_responder(responder);
+  sync_responses_.insert(std::make_pair(
+      request_id, base::WrapUnique(new SyncResponseInfo(&response_received))));
+
+  base::WeakPtr<InterfaceEndpointClient> weak_self =
+      weak_ptr_factory_.GetWeakPtr();
+  controller_->SyncWatch(&response_received);
+  // Make sure that this instance hasn't been destroyed.
+  if (weak_self) {
+    DCHECK(ContainsKey(sync_responses_, request_id));
+    auto iter = sync_responses_.find(request_id);
+    DCHECK_EQ(&response_received, iter->second->response_received);
+    if (response_received) {
+      std::unique_ptr<Message> response = std::move(iter->second->response);
+      ignore_result(sync_responder->Accept(response.get()));
+    }
+    sync_responses_.erase(iter);
+  }
+
+  // Return true means that we take ownership of |responder|.
+  return true;
+}
+
+bool InterfaceEndpointClient::HandleIncomingMessage(Message* message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  return payload_validator_->Accept(message);
+}
+
+void InterfaceEndpointClient::NotifyError() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (encountered_error_)
+    return;
+  encountered_error_ = true;
+  if (!error_handler_.is_null())
+    error_handler_.Run();
+}
+
+bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) {
+  DCHECK_EQ(handle_.id(), message->interface_id());
+
+  if (message->has_flag(Message::kFlagExpectsResponse)) {
+    if (!incoming_receiver_)
+      return false;
+
+    MessageReceiverWithStatus* responder =
+        new ResponderThunk(weak_ptr_factory_.GetWeakPtr(), task_runner_);
+    bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
+    if (!ok)
+      delete responder;
+    return ok;
+  } else if (message->has_flag(Message::kFlagIsResponse)) {
+    uint64_t request_id = message->request_id();
+
+    if (message->has_flag(Message::kFlagIsSync)) {
+      auto it = sync_responses_.find(request_id);
+      if (it == sync_responses_.end())
+        return false;
+      it->second->response.reset(new Message());
+      message->MoveTo(it->second->response.get());
+      *it->second->response_received = true;
+      return true;
+    }
+
+    auto it = async_responders_.find(request_id);
+    if (it == async_responders_.end())
+      return false;
+    std::unique_ptr<MessageReceiver> responder = std::move(it->second);
+    async_responders_.erase(it);
+    return responder->Accept(message);
+  } else {
+    if (!incoming_receiver_)
+      return false;
+
+    return incoming_receiver_->Accept(message);
+  }
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
new file mode 100644
index 0000000..584933e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -0,0 +1,348 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_STATE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_STATE_H_
+
+#include <stdint.h>
+
+#include <algorithm>  // For |std::swap()|.
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/bindings/message_header_validator.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface, bool use_multiplex_router>
+class InterfacePtrState;
+
+// Uses a single-threaded, dedicated router. If |Interface| doesn't have any
+// methods to pass associated interface pointers or requests, there won't be
+// multiple interfaces running on the underlying message pipe. In that case, we
+// can use this specialization to reduce cost.
+template <typename Interface>
+class InterfacePtrState<Interface, false> {
+ public:
+  InterfacePtrState() : proxy_(nullptr), router_(nullptr), version_(0u) {}
+
+  ~InterfacePtrState() {
+    // Destruction order matters here. We delete |proxy_| first, even though
+    // |router_| may have a reference to it, so that destructors for any request
+    // callbacks still pending can interact with the InterfacePtr.
+    delete proxy_;
+    delete router_;
+  }
+
+  Interface* instance() {
+    ConfigureProxyIfNecessary();
+
+    // This will be null if the object is not bound.
+    return proxy_;
+  }
+
+  uint32_t version() const { return version_; }
+
+  void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
+    ConfigureProxyIfNecessary();
+
+    // Do a static cast in case the interface contains methods with the same
+    // name. It is safe to capture |this| because the callback won't be run
+    // after this object goes away.
+    static_cast<ControlMessageProxy*>(proxy_)->QueryVersion(
+        base::Bind(&InterfacePtrState::OnQueryVersion, base::Unretained(this),
+                   callback));
+  }
+
+  void RequireVersion(uint32_t version) {
+    ConfigureProxyIfNecessary();
+
+    if (version <= version_)
+      return;
+
+    version_ = version;
+    // Do a static cast in case the interface contains methods with the same
+    // name.
+    static_cast<ControlMessageProxy*>(proxy_)->RequireVersion(version);
+  }
+
+  void Swap(InterfacePtrState* other) {
+    using std::swap;
+    swap(other->proxy_, proxy_);
+    swap(other->router_, router_);
+    handle_.swap(other->handle_);
+    runner_.swap(other->runner_);
+    swap(other->version_, version_);
+  }
+
+  void Bind(InterfacePtrInfo<Interface> info,
+            scoped_refptr<base::SingleThreadTaskRunner> runner) {
+    DCHECK(!proxy_);
+    DCHECK(!router_);
+    DCHECK(!handle_.is_valid());
+    DCHECK_EQ(0u, version_);
+    DCHECK(info.is_valid());
+
+    handle_ = info.PassHandle();
+    version_ = info.version();
+    runner_ = std::move(runner);
+  }
+
+  bool HasAssociatedInterfaces() const { return false; }
+
+  // After this method is called, the object is in an invalid state and
+  // shouldn't be reused.
+  InterfacePtrInfo<Interface> PassInterface() {
+    return InterfacePtrInfo<Interface>(
+        router_ ? router_->PassMessagePipe() : std::move(handle_), version_);
+  }
+
+  bool is_bound() const { return handle_.is_valid() || router_; }
+
+  bool encountered_error() const {
+    return router_ ? router_->encountered_error() : false;
+  }
+
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    ConfigureProxyIfNecessary();
+
+    DCHECK(router_);
+    router_->set_connection_error_handler(error_handler);
+  }
+
+  // Returns true if bound and awaiting a response to a message.
+  bool has_pending_callbacks() const {
+    return router_ && router_->has_pending_responders();
+  }
+
+  AssociatedGroup* associated_group() { return nullptr; }
+
+  void EnableTestingMode() {
+    ConfigureProxyIfNecessary();
+    router_->EnableTestingMode();
+  }
+
+ private:
+  using Proxy = typename Interface::Proxy_;
+
+  void ConfigureProxyIfNecessary() {
+    // The proxy has been configured.
+    if (proxy_) {
+      DCHECK(router_);
+      return;
+    }
+    // The object hasn't been bound.
+    if (!handle_.is_valid())
+      return;
+
+    FilterChain filters;
+    filters.Append<MessageHeaderValidator>(Interface::Name_);
+    filters.Append<typename Interface::ResponseValidator_>();
+
+    router_ = new Router(std::move(handle_), std::move(filters), false,
+                         std::move(runner_));
+
+    proxy_ = new Proxy(router_);
+  }
+
+  void OnQueryVersion(const base::Callback<void(uint32_t)>& callback,
+                      uint32_t version) {
+    version_ = version;
+    callback.Run(version);
+  }
+
+  Proxy* proxy_;
+  Router* router_;
+
+  // |proxy_| and |router_| are not initialized until read/write with the
+  // message pipe handle is needed. |handle_| is valid between the Bind() call
+  // and the initialization of |proxy_| and |router_|.
+  ScopedMessagePipeHandle handle_;
+  scoped_refptr<base::SingleThreadTaskRunner> runner_;
+
+  uint32_t version_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterfacePtrState);
+};
+
+// Uses a multiplexing router. If |Interface| has methods to pass associated
+// interface pointers or requests, this specialization should be used.
+template <typename Interface>
+class InterfacePtrState<Interface, true> {
+ public:
+  InterfacePtrState() : version_(0u) {}
+
+  ~InterfacePtrState() {
+    endpoint_client_.reset();
+    proxy_.reset();
+    if (router_)
+      router_->CloseMessagePipe();
+  }
+
+  Interface* instance() {
+    ConfigureProxyIfNecessary();
+
+    // This will be null if the object is not bound.
+    return proxy_.get();
+  }
+
+  uint32_t version() const { return version_; }
+
+  void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
+    ConfigureProxyIfNecessary();
+
+
+    // Do a static cast in case the interface contains methods with the same
+    // name. It is safe to capture |this| because the callback won't be run
+    // after this object goes away.
+    static_cast<ControlMessageProxy*>(proxy_.get())->QueryVersion(
+        base::Bind(&InterfacePtrState::OnQueryVersion, base::Unretained(this),
+                   callback));
+  }
+
+  void RequireVersion(uint32_t version) {
+    ConfigureProxyIfNecessary();
+
+    if (version <= version_)
+      return;
+
+    version_ = version;
+    // Do a static cast in case the interface contains methods with the same
+    // name.
+    static_cast<ControlMessageProxy*>(proxy_.get())->RequireVersion(version);
+  }
+
+  void Swap(InterfacePtrState* other) {
+    using std::swap;
+    swap(other->router_, router_);
+    swap(other->endpoint_client_, endpoint_client_);
+    swap(other->proxy_, proxy_);
+    handle_.swap(other->handle_);
+    runner_.swap(other->runner_);
+    swap(other->version_, version_);
+  }
+
+  void Bind(InterfacePtrInfo<Interface> info,
+            scoped_refptr<base::SingleThreadTaskRunner> runner) {
+    DCHECK(!router_);
+    DCHECK(!endpoint_client_);
+    DCHECK(!proxy_);
+    DCHECK(!handle_.is_valid());
+    DCHECK_EQ(0u, version_);
+    DCHECK(info.is_valid());
+
+    handle_ = info.PassHandle();
+    version_ = info.version();
+    runner_ = std::move(runner);
+  }
+
+  bool HasAssociatedInterfaces() const {
+    return router_ ? router_->HasAssociatedEndpoints() : false;
+  }
+
+  // After this method is called, the object is in an invalid state and
+  // shouldn't be reused.
+  InterfacePtrInfo<Interface> PassInterface() {
+    endpoint_client_.reset();
+    proxy_.reset();
+    return InterfacePtrInfo<Interface>(
+        router_ ? router_->PassMessagePipe() : std::move(handle_), version_);
+  }
+
+  bool is_bound() const { return handle_.is_valid() || endpoint_client_; }
+
+  bool encountered_error() const {
+    return endpoint_client_ ? endpoint_client_->encountered_error() : false;
+  }
+
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    ConfigureProxyIfNecessary();
+
+    DCHECK(endpoint_client_);
+    endpoint_client_->set_connection_error_handler(error_handler);
+  }
+
+  // Returns true if bound and awaiting a response to a message.
+  bool has_pending_callbacks() const {
+    return endpoint_client_ && endpoint_client_->has_pending_responders();
+  }
+
+  AssociatedGroup* associated_group() {
+    ConfigureProxyIfNecessary();
+    return endpoint_client_->associated_group();
+  }
+
+  void EnableTestingMode() {
+    ConfigureProxyIfNecessary();
+    router_->EnableTestingMode();
+  }
+
+ private:
+  using Proxy = typename Interface::Proxy_;
+
+  void ConfigureProxyIfNecessary() {
+    // The proxy has been configured.
+    if (proxy_) {
+      DCHECK(router_);
+      DCHECK(endpoint_client_);
+      return;
+    }
+    // The object hasn't been bound.
+    if (!handle_.is_valid())
+      return;
+
+    router_ = new MultiplexRouter(true, std::move(handle_), runner_);
+    router_->SetMasterInterfaceName(Interface::Name_);
+    endpoint_client_.reset(new InterfaceEndpointClient(
+        router_->CreateLocalEndpointHandle(kMasterInterfaceId), nullptr,
+        base::WrapUnique(new typename Interface::ResponseValidator_()), false,
+        std::move(runner_)));
+    proxy_.reset(new Proxy(endpoint_client_.get()));
+    proxy_->serialization_context()->group_controller =
+        endpoint_client_->group_controller();
+  }
+
+  void OnQueryVersion(const base::Callback<void(uint32_t)>& callback,
+                      uint32_t version) {
+    version_ = version;
+    callback.Run(version);
+  }
+
+  scoped_refptr<MultiplexRouter> router_;
+
+  std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
+  std::unique_ptr<Proxy> proxy_;
+
+  // |router_| (as well as other members above) is not initialized until
+  // read/write with the message pipe handle is needed. |handle_| is valid
+  // between the Bind() call and the initialization of |router_|.
+  ScopedMessagePipeHandle handle_;
+  scoped_refptr<base::SingleThreadTaskRunner> runner_;
+
+  uint32_t version_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterfacePtrState);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_STATE_H_
diff --git a/mojo/public/cpp/bindings/lib/map_data_internal.h b/mojo/public/cpp/bindings/lib/map_data_internal.h
new file mode 100644
index 0000000..f8e3d29
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/map_data_internal.h
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All 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_MAP_DATA_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_DATA_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+
+namespace mojo {
+namespace internal {
+
+// Map serializes into a struct which has two arrays as struct fields, the keys
+// and the values.
+template <typename Key, typename Value>
+class Map_Data {
+ public:
+  static Map_Data* New(Buffer* buf) {
+    return new (buf->Allocate(sizeof(Map_Data))) Map_Data();
+  }
+
+  // |validate_params| must have non-null |key_validate_params| and
+  // |element_validate_params| members.
+  static bool Validate(const void* data,
+                       ValidationContext* validation_context,
+                       const ContainerValidateParams* validate_params) {
+    if (!data)
+      return true;
+
+    if (!ValidateStructHeaderAndClaimMemory(data, validation_context))
+      return false;
+
+    const Map_Data* object = static_cast<const Map_Data*>(data);
+    if (object->header_.num_bytes != sizeof(Map_Data) ||
+        object->header_.version != 0) {
+      ReportValidationError(validation_context,
+                            VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+      return false;
+    }
+
+    if (!ValidatePointerNonNullable(
+            object->keys, "null key array in map struct", validation_context) ||
+        !ValidateContainer(object->keys, validation_context,
+                           validate_params->key_validate_params)) {
+      return false;
+    }
+
+    if (!ValidatePointerNonNullable(object->values,
+                                    "null value array in map struct",
+                                    validation_context) ||
+        !ValidateContainer(object->values, validation_context,
+                           validate_params->element_validate_params)) {
+      return false;
+    }
+
+    if (object->keys.Get()->size() != object->values.Get()->size()) {
+      ReportValidationError(validation_context,
+                            VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP);
+      return false;
+    }
+
+    return true;
+  }
+
+  StructHeader header_;
+
+  Pointer<Array_Data<Key>> keys;
+  Pointer<Array_Data<Value>> values;
+
+ private:
+  Map_Data() {
+    header_.num_bytes = sizeof(*this);
+    header_.version = 0;
+  }
+  ~Map_Data() = delete;
+};
+static_assert(sizeof(Map_Data<char, char>) == 24, "Bad sizeof(Map_Data)");
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_DATA_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/map_serialization.h b/mojo/public/cpp/bindings/lib/map_serialization.h
new file mode 100644
index 0000000..c28b835
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/map_serialization.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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_
+
+#include <type_traits>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/array_serialization.h"
+#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
+#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
+#include "mojo/public/cpp/bindings/map.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename MaybeConstUserType>
+class MapReaderBase {
+ public:
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = MapTraits<UserType>;
+  using MaybeConstIterator =
+      decltype(Traits::GetBegin(std::declval<MaybeConstUserType&>()));
+
+  explicit MapReaderBase(MaybeConstUserType& input)
+      : input_(input), iter_(Traits::GetBegin(input_)) {}
+  ~MapReaderBase() {}
+
+  size_t GetSize() const { return Traits::GetSize(input_); }
+
+  // Return null because key or value elements are not stored continuously in
+  // memory.
+  void* GetDataIfExists() { return nullptr; }
+
+ protected:
+  MaybeConstUserType& input_;
+  MaybeConstIterator iter_;
+};
+
+// Used as the UserTypeReader template parameter of ArraySerializer.
+template <typename MaybeConstUserType>
+class MapKeyReader : public MapReaderBase<MaybeConstUserType> {
+ public:
+  using Base = MapReaderBase<MaybeConstUserType>;
+  using Traits = typename Base::Traits;
+
+  explicit MapKeyReader(MaybeConstUserType& input) : Base(input) {}
+  ~MapKeyReader() {}
+
+  const typename Traits::Key& GetNext() {
+    const typename Traits::Key& key = Traits::GetKey(this->iter_);
+    Traits::AdvanceIterator(this->iter_);
+    return key;
+  }
+};
+
+// Used as the UserTypeReader template parameter of ArraySerializer.
+template <typename MaybeConstUserType>
+class MapValueReader : public MapReaderBase<MaybeConstUserType> {
+ public:
+  using Base = MapReaderBase<MaybeConstUserType>;
+  using Traits = typename Base::Traits;
+  using MaybeConstIterator = typename Base::MaybeConstIterator;
+
+  explicit MapValueReader(MaybeConstUserType& input) : Base(input) {}
+  ~MapValueReader() {}
+
+  using GetNextResult =
+      decltype(Traits::GetValue(std::declval<MaybeConstIterator&>()));
+  GetNextResult GetNext() {
+    GetNextResult value = Traits::GetValue(this->iter_);
+    Traits::AdvanceIterator(this->iter_);
+    return value;
+  }
+};
+
+template <typename Key, typename Value, typename MaybeConstUserType>
+struct Serializer<Map<Key, Value>, MaybeConstUserType> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = MapTraits<UserType>;
+  using UserKey = typename Traits::Key;
+  using UserValue = typename Traits::Value;
+  using Data = typename MojomTypeTraits<Map<Key, Value>>::Data;
+  using KeyArraySerializer = ArraySerializer<Array<Key>,
+                                             std::vector<UserKey>,
+                                             MapKeyReader<MaybeConstUserType>>;
+  using ValueArraySerializer =
+      ArraySerializer<Array<Value>,
+                      std::vector<UserValue>,
+                      MapValueReader<MaybeConstUserType>>;
+
+  static size_t PrepareToSerialize(MaybeConstUserType& input,
+                                   SerializationContext* context) {
+    if (CallIsNullIfExists<Traits>(input))
+      return 0;
+
+    size_t struct_overhead = sizeof(Data);
+    MapKeyReader<MaybeConstUserType> key_reader(input);
+    size_t keys_size =
+        KeyArraySerializer::GetSerializedSize(&key_reader, context);
+    MapValueReader<MaybeConstUserType> value_reader(input);
+    size_t values_size =
+        ValueArraySerializer::GetSerializedSize(&value_reader, context);
+
+    return struct_overhead + keys_size + values_size;
+  }
+
+  static void Serialize(MaybeConstUserType& input,
+                        Buffer* buf,
+                        Data** output,
+                        const ContainerValidateParams* validate_params,
+                        SerializationContext* context) {
+    DCHECK(validate_params->key_validate_params);
+    DCHECK(validate_params->element_validate_params);
+    if (CallIsNullIfExists<Traits>(input)) {
+      *output = nullptr;
+      return;
+    }
+
+    auto result = Data::New(buf);
+    if (result) {
+      auto keys_ptr =
+          MojomTypeTraits<Array<Key>>::Data::New(Traits::GetSize(input), buf);
+      if (keys_ptr) {
+        MapKeyReader<MaybeConstUserType> key_reader(input);
+        KeyArraySerializer::SerializeElements(
+            &key_reader, buf, keys_ptr, validate_params->key_validate_params,
+            context);
+        result->keys.Set(keys_ptr);
+      }
+
+      auto values_ptr =
+          MojomTypeTraits<Array<Value>>::Data::New(Traits::GetSize(input), buf);
+      if (values_ptr) {
+        MapValueReader<MaybeConstUserType> value_reader(input);
+        ValueArraySerializer::SerializeElements(
+            &value_reader, buf, values_ptr,
+            validate_params->element_validate_params, context);
+        result->values.Set(values_ptr);
+      }
+    }
+    *output = result;
+  }
+
+  static bool Deserialize(Data* input,
+                          UserType* output,
+                          SerializationContext* context) {
+    if (!input)
+      return CallSetToNullIfExists<Traits>(output);
+
+    std::vector<UserKey> keys;
+    std::vector<UserValue> values;
+
+    if (!KeyArraySerializer::DeserializeElements(input->keys.Get(), &keys,
+                                                 context) ||
+        !ValueArraySerializer::DeserializeElements(input->values.Get(), &values,
+                                                   context)) {
+      return false;
+    }
+
+    DCHECK_EQ(keys.size(), values.size());
+    size_t size = keys.size();
+    Traits::SetToEmpty(output);
+
+    for (size_t i = 0; i < size; ++i) {
+      if (!Traits::Insert(*output, std::move(keys[i]), std::move(values[i])))
+        return false;
+    }
+    return true;
+  }
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_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..939e064
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -0,0 +1,125 @@
+// 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 <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+
+namespace mojo {
+
+Message::Message() {
+}
+
+Message::~Message() {
+  CloseHandles();
+}
+
+void Message::Initialize(size_t capacity, bool zero_initialized) {
+  DCHECK(!buffer_);
+  buffer_.reset(new internal::MessageBuffer(capacity, zero_initialized));
+}
+
+void Message::InitializeFromMojoMessage(ScopedMessageHandle message,
+                                        uint32_t num_bytes,
+                                        std::vector<Handle>* handles) {
+  DCHECK(!buffer_);
+  buffer_.reset(new internal::MessageBuffer(std::move(message), num_bytes));
+  handles_.swap(*handles);
+}
+
+void Message::MoveTo(Message* destination) {
+  DCHECK(this != destination);
+
+  // No copy needed.
+  std::swap(destination->buffer_, buffer_);
+  std::swap(destination->handles_, handles_);
+
+  CloseHandles();
+  handles_.clear();
+  buffer_.reset();
+}
+
+ScopedMessageHandle Message::TakeMojoMessage() {
+  if (handles_.empty())  // Fast path for the common case: No handles.
+    return buffer_->TakeMessage();
+
+  // Allocate a new message with space for the handles, then copy the buffer
+  // contents into it.
+  //
+  // TODO(rockot): We could avoid this copy by extending GetSerializedSize()
+  // behavior to collect handles. It's unoptimized for now because it's much
+  // more common to have messages with no handles.
+  ScopedMessageHandle new_message;
+  MojoResult rv = AllocMessage(
+      data_num_bytes(),
+      handles_.empty() ? nullptr
+                       : reinterpret_cast<const MojoHandle*>(handles_.data()),
+      handles_.size(),
+      MOJO_ALLOC_MESSAGE_FLAG_NONE,
+      &new_message);
+  CHECK_EQ(rv, MOJO_RESULT_OK);
+  handles_.clear();
+
+  void* new_buffer = nullptr;
+  rv = GetMessageBuffer(new_message.get(), &new_buffer);
+  CHECK_EQ(rv, MOJO_RESULT_OK);
+
+  memcpy(new_buffer, data(), data_num_bytes());
+  buffer_.reset();
+
+  return new_message;
+}
+
+void Message::NotifyBadMessage(const std::string& error) {
+  buffer_->NotifyBadMessage(error);
+}
+
+void Message::CloseHandles() {
+  for (std::vector<Handle>::iterator it = handles_.begin();
+       it != handles_.end(); ++it) {
+    if (it->is_valid())
+      CloseRaw(*it);
+  }
+}
+
+MojoResult ReadMessage(MessagePipeHandle handle, Message* message) {
+  MojoResult rv;
+
+  std::vector<Handle> handles;
+  ScopedMessageHandle mojo_message;
+  uint32_t num_bytes = 0, num_handles = 0;
+  rv = ReadMessageNew(handle,
+                      &mojo_message,
+                      &num_bytes,
+                      nullptr,
+                      &num_handles,
+                      MOJO_READ_MESSAGE_FLAG_NONE);
+  if (rv == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    DCHECK_GT(num_handles, 0u);
+    handles.resize(num_handles);
+    rv = ReadMessageNew(handle,
+                        &mojo_message,
+                        &num_bytes,
+                        reinterpret_cast<MojoHandle*>(handles.data()),
+                        &num_handles,
+                        MOJO_READ_MESSAGE_FLAG_NONE);
+  }
+
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  message->InitializeFromMojoMessage(
+      std::move(mojo_message), num_bytes, &handles);
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.cc b/mojo/public/cpp/bindings/lib/message_buffer.cc
new file mode 100644
index 0000000..af79cfd
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_buffer.cc
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All 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_buffer.h"
+
+#include <limits>
+
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+
+namespace mojo {
+namespace internal {
+
+MessageBuffer::MessageBuffer(size_t capacity, bool zero_initialized) {
+  DCHECK_LE(capacity, std::numeric_limits<uint32_t>::max());
+  data_num_bytes_ = static_cast<uint32_t>(capacity);
+
+  MojoResult rv = AllocMessage(capacity, nullptr, 0,
+                               MOJO_ALLOC_MESSAGE_FLAG_NONE, &message_);
+  CHECK_EQ(rv, MOJO_RESULT_OK);
+
+  if (capacity == 0) {
+    buffer_ = nullptr;
+  } else {
+    rv = GetMessageBuffer(message_.get(), &buffer_);
+    CHECK_EQ(rv, MOJO_RESULT_OK);
+
+    if (zero_initialized)
+      memset(buffer_, 0, capacity);
+  }
+}
+
+MessageBuffer::MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes) {
+  message_ = std::move(message);
+  data_num_bytes_ = num_bytes;
+
+  if (num_bytes == 0) {
+    buffer_ = nullptr;
+  } else {
+    MojoResult rv = GetMessageBuffer(message_.get(), &buffer_);
+    CHECK_EQ(rv, MOJO_RESULT_OK);
+  }
+}
+
+MessageBuffer::~MessageBuffer() {}
+
+void* MessageBuffer::Allocate(size_t delta) {
+  delta = internal::Align(delta);
+
+  DCHECK_LE(delta, static_cast<size_t>(data_num_bytes_));
+  DCHECK_GT(bytes_claimed_ + static_cast<uint32_t>(delta), bytes_claimed_);
+
+  uint32_t new_bytes_claimed = bytes_claimed_ + static_cast<uint32_t>(delta);
+  if (new_bytes_claimed > data_num_bytes_) {
+    NOTREACHED();
+    return nullptr;
+  }
+
+  char* start = static_cast<char*>(buffer_) + bytes_claimed_;
+  bytes_claimed_ = new_bytes_claimed;
+  return static_cast<void*>(start);
+}
+
+void MessageBuffer::NotifyBadMessage(const std::string& error) {
+  DCHECK(message_.is_valid());
+  MojoResult result = mojo::NotifyBadMessage(message_.get(), error);
+  DCHECK_EQ(result, MOJO_RESULT_OK);
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.h b/mojo/public/cpp/bindings/lib/message_buffer.h
new file mode 100644
index 0000000..0382131
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_buffer.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All 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_LIB_MESSAGE_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/system/message.h"
+
+namespace mojo {
+namespace internal {
+
+// A fixed-size Buffer implementation using a Mojo message object for storage.
+class MessageBuffer : public Buffer {
+ public:
+  // Initializes this buffer to carry a fixed byte capacity and no handles.
+  MessageBuffer(size_t capacity, bool zero_initialized);
+
+  // Initializes this buffer from an existing Mojo MessageHandle.
+  MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes);
+
+  ~MessageBuffer() override;
+
+  void* data() const { return buffer_; }
+  uint32_t data_num_bytes() const { return data_num_bytes_; }
+
+  // Buffer:
+  void* Allocate(size_t delta) override;
+
+  ScopedMessageHandle TakeMessage() { return std::move(message_); }
+
+  void NotifyBadMessage(const std::string& error);
+
+ private:
+  uint32_t data_num_bytes_ = 0;
+  ScopedMessageHandle message_;
+  void* buffer_;
+
+  uint32_t bytes_claimed_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageBuffer);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
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..4ffa180
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_builder.cc
@@ -0,0 +1,56 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/serialization_util.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) {
+  InitializeMessage(sizeof(MessageHeader) + payload_size);
+
+  MessageHeader* header;
+  Allocate(message_.buffer(), &header);
+  header->version = 0;
+  header->name = name;
+}
+
+MessageBuilder::~MessageBuilder() {
+}
+
+MessageBuilder::MessageBuilder() {}
+
+void MessageBuilder::InitializeMessage(size_t size) {
+  message_.Initialize(static_cast<uint32_t>(Align(size)),
+                      true /* zero_initialized */);
+}
+
+MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name,
+                                                         size_t payload_size,
+                                                         uint32_t flags,
+                                                         uint64_t request_id) {
+  InitializeMessage(sizeof(MessageHeaderWithRequestID) + payload_size);
+
+  MessageHeaderWithRequestID* header;
+  Allocate(message_.buffer(), &header);
+  header->version = 1;
+  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..a5a050f
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_builder.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+class Message;
+
+namespace internal {
+
+class MessageBuilder {
+ public:
+  MessageBuilder(uint32_t name, size_t payload_size);
+  ~MessageBuilder();
+
+  Buffer* buffer() { return message_.buffer(); }
+  Message* message() { return &message_; }
+
+ protected:
+  MessageBuilder();
+  void InitializeMessage(size_t size);
+
+  Message message_;
+
+  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,
+                                    Message::kFlagExpectsResponse,
+                                    0) {}
+
+  RequestMessageBuilder(uint32_t name,
+                        size_t payload_size,
+                        uint32_t extra_flags)
+      : MessageWithRequestIDBuilder(name,
+                                    payload_size,
+                                    Message::kFlagExpectsResponse | extra_flags,
+                                    0) {}
+};
+
+class ResponseMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+  ResponseMessageBuilder(uint32_t name,
+                         size_t payload_size,
+                         uint64_t request_id)
+      : MessageWithRequestIDBuilder(name,
+                                    payload_size,
+                                    Message::kFlagIsResponse,
+                                    request_id) {}
+
+  ResponseMessageBuilder(uint32_t name,
+                         size_t payload_size,
+                         uint64_t request_id,
+                         uint32_t extra_flags)
+      : MessageWithRequestIDBuilder(name,
+                                    payload_size,
+                                    Message::kFlagIsResponse | extra_flags,
+                                    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..10f7774
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.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/public/cpp/bindings/message_header_validator.h"
+
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+
+namespace mojo {
+namespace {
+
+bool IsValidMessageHeader(const internal::MessageHeader* header,
+                          internal::ValidationContext* validation_context) {
+  // 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->version == 0) {
+    if (header->num_bytes != sizeof(internal::MessageHeader)) {
+      internal::ReportValidationError(
+          validation_context,
+          internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+      return false;
+    }
+  } else if (header->version == 1) {
+    if (header->num_bytes != sizeof(internal::MessageHeaderWithRequestID)) {
+      internal::ReportValidationError(
+          validation_context,
+          internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+      return false;
+    }
+  } else if (header->version > 1) {
+    if (header->num_bytes < sizeof(internal::MessageHeaderWithRequestID)) {
+      internal::ReportValidationError(
+          validation_context,
+          internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+      return false;
+    }
+  }
+
+  // Validate flags (allow unknown bits):
+
+  // These flags require a RequestID.
+  if (header->version < 1 && ((header->flags & Message::kFlagExpectsResponse) ||
+                              (header->flags & Message::kFlagIsResponse))) {
+    internal::ReportValidationError(
+        validation_context,
+        internal::VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID);
+    return false;
+  }
+
+  // These flags are mutually exclusive.
+  if ((header->flags & Message::kFlagExpectsResponse) &&
+      (header->flags & Message::kFlagIsResponse)) {
+    internal::ReportValidationError(
+        validation_context,
+        internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+MessageHeaderValidator::MessageHeaderValidator(MessageReceiver* sink)
+    : MessageHeaderValidator("MessageHeaderValidator", sink) {}
+
+MessageHeaderValidator::MessageHeaderValidator(const std::string& description,
+                                               MessageReceiver* sink)
+    : MessageFilter(sink), description_(description) {
+}
+
+void MessageHeaderValidator::SetDescription(const std::string& description) {
+  description_ = description;
+}
+
+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.
+  internal::ValidationContext validation_context(
+      message->data(), message->data_num_bytes(), 0, message, description_);
+
+  if (!internal::ValidateStructHeaderAndClaimMemory(message->data(),
+                                                    &validation_context))
+    return false;
+
+  if (!IsValidMessageHeader(message->header(), &validation_context))
+    return false;
+
+  return sink_->Accept(message);
+}
+
+}  // namespace mojo
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..63edffd
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_internal.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_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+
+namespace mojo {
+namespace internal {
+
+#pragma pack(push, 1)
+
+struct MessageHeader : internal::StructHeader {
+  // Interface ID for identifying multiple interfaces running on the same
+  // message pipe.
+  uint32_t interface_id;
+  // Message name, which is scoped to the interface that the message belongs to.
+  uint32_t name;
+  // 0 or either of the enum values defined above.
+  uint32_t flags;
+  // Unused padding to make the struct size a multiple of 8 bytes.
+  uint32_t padding;
+};
+static_assert(sizeof(MessageHeader) == 24, "Bad sizeof(MessageHeader)");
+
+struct MessageHeaderWithRequestID : MessageHeader {
+  // Only used if either kFlagExpectsResponse or kFlagIsResponse is set in
+  // order to match responses with corresponding requests.
+  uint64_t request_id;
+};
+static_assert(sizeof(MessageHeaderWithRequestID) == 32,
+              "Bad sizeof(MessageHeaderWithRequestID)");
+
+#pragma pack(pop)
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
new file mode 100644
index 0000000..dcfbab1
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -0,0 +1,849 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_controller.h"
+#include "mojo/public/cpp/bindings/sync_handle_watcher.h"
+
+namespace mojo {
+namespace internal {
+
+// InterfaceEndpoint stores the information of an interface endpoint registered
+// with the router.
+// No one other than the router's |endpoints_| and |tasks_| should hold refs to
+// this object.
+class MultiplexRouter::InterfaceEndpoint
+    : public base::RefCounted<InterfaceEndpoint>,
+      public InterfaceEndpointController {
+ public:
+  InterfaceEndpoint(MultiplexRouter* router, InterfaceId id)
+      : router_(router),
+        id_(id),
+        closed_(false),
+        peer_closed_(false),
+        client_(nullptr),
+        event_signalled_(false) {}
+
+  // ---------------------------------------------------------------------------
+  // The following public methods are safe to call from any threads without
+  // locking.
+
+  InterfaceId id() const { return id_; }
+
+  // ---------------------------------------------------------------------------
+  // The following public methods are called under the router's lock.
+
+  bool closed() const { return closed_; }
+  void set_closed() {
+    router_->lock_.AssertAcquired();
+    closed_ = true;
+  }
+
+  bool peer_closed() const { return peer_closed_; }
+  void set_peer_closed() {
+    router_->lock_.AssertAcquired();
+    peer_closed_ = true;
+  }
+
+  base::SingleThreadTaskRunner* task_runner() const {
+    return task_runner_.get();
+  }
+
+  InterfaceEndpointClient* client() const { return client_; }
+
+  void AttachClient(InterfaceEndpointClient* client,
+                    scoped_refptr<base::SingleThreadTaskRunner> runner) {
+    router_->lock_.AssertAcquired();
+    DCHECK(!client_);
+    DCHECK(!closed_);
+    DCHECK(runner->BelongsToCurrentThread());
+
+    task_runner_ = std::move(runner);
+    client_ = client;
+  }
+
+  // This method must be called on the same thread as the corresponding
+  // AttachClient() call.
+  void DetachClient() {
+    router_->lock_.AssertAcquired();
+    DCHECK(client_);
+    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(!closed_);
+
+    task_runner_ = nullptr;
+    client_ = nullptr;
+    sync_watcher_.reset();
+  }
+
+  void SignalSyncMessageEvent() {
+    router_->lock_.AssertAcquired();
+    if (event_signalled_)
+      return;
+
+    EnsureEventMessagePipeExists();
+    event_signalled_ = true;
+    MojoResult result =
+        WriteMessageRaw(sync_message_event_sender_.get(), nullptr, 0, nullptr,
+                        0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+  }
+
+  // ---------------------------------------------------------------------------
+  // The following public methods (i.e., InterfaceEndpointController
+  // implementation) are called by the client on the same thread as the
+  // AttachClient() call. They are called outside of the router's lock.
+
+  bool SendMessage(Message* message) override {
+    DCHECK(task_runner_->BelongsToCurrentThread());
+    message->set_interface_id(id_);
+    return router_->connector_.Accept(message);
+  }
+
+  void AllowWokenUpBySyncWatchOnSameThread() override {
+    DCHECK(task_runner_->BelongsToCurrentThread());
+
+    EnsureSyncWatcherExists();
+    sync_watcher_->AllowWokenUpBySyncWatchOnSameThread();
+  }
+
+  bool SyncWatch(const bool* should_stop) override {
+    DCHECK(task_runner_->BelongsToCurrentThread());
+
+    EnsureSyncWatcherExists();
+    return sync_watcher_->SyncWatch(should_stop);
+  }
+
+ private:
+  friend class base::RefCounted<InterfaceEndpoint>;
+
+  ~InterfaceEndpoint() override {
+    router_->lock_.AssertAcquired();
+
+    DCHECK(!client_);
+    DCHECK(closed_);
+    DCHECK(peer_closed_);
+    DCHECK(!sync_watcher_);
+  }
+
+  void OnHandleReady(MojoResult result) {
+    DCHECK(task_runner_->BelongsToCurrentThread());
+    scoped_refptr<InterfaceEndpoint> self_protector(this);
+    scoped_refptr<MultiplexRouter> router_protector(router_);
+
+    // Because we never close |sync_message_event_{sender,receiver}_| before
+    // destruction or set a deadline, |result| should always be MOJO_RESULT_OK.
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+    bool reset_sync_watcher = false;
+    {
+      base::AutoLock locker(router_->lock_);
+
+      bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_);
+
+      if (!more_to_process)
+        ResetSyncMessageSignal();
+
+      // Currently there are no queued sync messages and the peer has closed so
+      // there won't be incoming sync messages in the future.
+      reset_sync_watcher = !more_to_process && peer_closed_;
+    }
+    if (reset_sync_watcher) {
+      // If a SyncWatch() call (or multiple ones) of this interface endpoint is
+      // on the call stack, resetting the sync watcher will allow it to exit
+      // when the call stack unwinds to that frame.
+      sync_watcher_.reset();
+    }
+  }
+
+  void EnsureSyncWatcherExists() {
+    DCHECK(task_runner_->BelongsToCurrentThread());
+    if (sync_watcher_)
+      return;
+
+    {
+      base::AutoLock locker(router_->lock_);
+      EnsureEventMessagePipeExists();
+
+      auto iter = router_->sync_message_tasks_.find(id_);
+      if (iter != router_->sync_message_tasks_.end() && !iter->second.empty())
+        SignalSyncMessageEvent();
+    }
+
+    sync_watcher_.reset(new SyncHandleWatcher(
+        sync_message_event_receiver_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+        base::Bind(&InterfaceEndpoint::OnHandleReady, base::Unretained(this))));
+  }
+
+  void EnsureEventMessagePipeExists() {
+    router_->lock_.AssertAcquired();
+
+    if (sync_message_event_receiver_.is_valid())
+      return;
+
+    MojoResult result = CreateMessagePipe(nullptr, &sync_message_event_sender_,
+                                          &sync_message_event_receiver_);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+  }
+
+  void ResetSyncMessageSignal() {
+    router_->lock_.AssertAcquired();
+
+    if (!event_signalled_)
+      return;
+
+    DCHECK(sync_message_event_receiver_.is_valid());
+    MojoResult result = ReadMessageRaw(sync_message_event_receiver_.get(),
+                                       nullptr, nullptr, nullptr, nullptr,
+                                       MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+    event_signalled_ = false;
+  }
+
+  // ---------------------------------------------------------------------------
+  // The following members are safe to access from any threads.
+
+  MultiplexRouter* const router_;
+  const InterfaceId id_;
+
+  // ---------------------------------------------------------------------------
+  // The following members are accessed under the router's lock.
+
+  // Whether the endpoint has been closed.
+  bool closed_;
+  // Whether the peer endpoint has been closed.
+  bool peer_closed_;
+
+  // The task runner on which |client_|'s methods can be called.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  // Not owned. It is null if no client is attached to this endpoint.
+  InterfaceEndpointClient* client_;
+
+  // A message pipe used as an event to signal that sync messages are available.
+  // The message pipe handles are initialized under the router's lock and remain
+  // unchanged afterwards. They may be accessed outside of the router's lock
+  // later.
+  ScopedMessagePipeHandle sync_message_event_sender_;
+  ScopedMessagePipeHandle sync_message_event_receiver_;
+  bool event_signalled_;
+
+  // ---------------------------------------------------------------------------
+  // The following members are only valid while a client is attached. They are
+  // used exclusively on the client's thread. They may be accessed outside of
+  // the router's lock.
+
+  std::unique_ptr<SyncHandleWatcher> sync_watcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterfaceEndpoint);
+};
+
+struct MultiplexRouter::Task {
+ public:
+  // Doesn't take ownership of |message| but takes its contents.
+  static std::unique_ptr<Task> CreateMessageTask(Message* message) {
+    Task* task = new Task(MESSAGE);
+    task->message.reset(new Message);
+    message->MoveTo(task->message.get());
+    return base::WrapUnique(task);
+  }
+  static std::unique_ptr<Task> CreateNotifyErrorTask(
+      InterfaceEndpoint* endpoint) {
+    Task* task = new Task(NOTIFY_ERROR);
+    task->endpoint_to_notify = endpoint;
+    return base::WrapUnique(task);
+  }
+
+  ~Task() {}
+
+  bool IsMessageTask() const { return type == MESSAGE; }
+  bool IsNotifyErrorTask() const { return type == NOTIFY_ERROR; }
+
+  std::unique_ptr<Message> message;
+  scoped_refptr<InterfaceEndpoint> endpoint_to_notify;
+
+  enum Type { MESSAGE, NOTIFY_ERROR };
+  Type type;
+
+ private:
+  explicit Task(Type in_type) : type(in_type) {}
+};
+
+MultiplexRouter::MultiplexRouter(
+    bool set_interface_id_namesapce_bit,
+    ScopedMessagePipeHandle message_pipe,
+    scoped_refptr<base::SingleThreadTaskRunner> runner)
+    : AssociatedGroupController(base::ThreadTaskRunnerHandle::Get()),
+      set_interface_id_namespace_bit_(set_interface_id_namesapce_bit),
+      header_validator_(this),
+      connector_(std::move(message_pipe),
+                 Connector::MULTI_THREADED_SEND,
+                 std::move(runner)),
+      control_message_handler_(this),
+      control_message_proxy_(&connector_),
+      next_interface_id_value_(1),
+      posted_to_process_tasks_(false),
+      encountered_error_(false),
+      testing_mode_(false) {
+  // Always participate in sync handle watching, because even if it doesn't
+  // expect sync requests during sync handle watching, it may still need to
+  // dispatch messages to associated endpoints on a different thread.
+  connector_.AllowWokenUpBySyncWatchOnSameThread();
+  connector_.set_incoming_receiver(&header_validator_);
+  connector_.set_connection_error_handler(
+      base::Bind(&MultiplexRouter::OnPipeConnectionError,
+                 base::Unretained(this)));
+}
+
+MultiplexRouter::~MultiplexRouter() {
+  base::AutoLock locker(lock_);
+
+  sync_message_tasks_.clear();
+  tasks_.clear();
+
+  for (auto iter = endpoints_.begin(); iter != endpoints_.end();) {
+    InterfaceEndpoint* endpoint = iter->second.get();
+    // Increment the iterator before calling UpdateEndpointStateMayRemove()
+    // because it may remove the corresponding value from the map.
+    ++iter;
+
+    DCHECK(endpoint->closed());
+    UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
+  }
+
+  DCHECK(endpoints_.empty());
+}
+
+void MultiplexRouter::SetMasterInterfaceName(const std::string& name) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  header_validator_.SetDescription(name + " [master] MessageHeaderValidator");
+  control_message_handler_.SetDescription(
+      name + " [master] PipeControlMessageHandler");
+}
+
+void MultiplexRouter::CreateEndpointHandlePair(
+    ScopedInterfaceEndpointHandle* local_endpoint,
+    ScopedInterfaceEndpointHandle* remote_endpoint) {
+  base::AutoLock locker(lock_);
+  uint32_t id = 0;
+  do {
+    if (next_interface_id_value_ >= kInterfaceIdNamespaceMask)
+      next_interface_id_value_ = 1;
+    id = next_interface_id_value_++;
+    if (set_interface_id_namespace_bit_)
+      id |= kInterfaceIdNamespaceMask;
+  } while (ContainsKey(endpoints_, id));
+
+  InterfaceEndpoint* endpoint = new InterfaceEndpoint(this, id);
+  endpoints_[id] = endpoint;
+  if (encountered_error_)
+    UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
+
+  *local_endpoint = CreateScopedInterfaceEndpointHandle(id, true);
+  *remote_endpoint = CreateScopedInterfaceEndpointHandle(id, false);
+}
+
+ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle(
+    InterfaceId id) {
+  if (!IsValidInterfaceId(id))
+    return ScopedInterfaceEndpointHandle();
+
+  base::AutoLock locker(lock_);
+  bool inserted = false;
+  InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted);
+  if (inserted) {
+    if (encountered_error_)
+      UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
+  } else {
+    // If the endpoint already exist, it is because we have received a
+    // notification that the peer endpoint has closed.
+    CHECK(!endpoint->closed());
+    CHECK(endpoint->peer_closed());
+  }
+  return CreateScopedInterfaceEndpointHandle(id, true);
+}
+
+void MultiplexRouter::CloseEndpointHandle(InterfaceId id, bool is_local) {
+  if (!IsValidInterfaceId(id))
+    return;
+
+  base::AutoLock locker(lock_);
+
+  if (!is_local) {
+    DCHECK(ContainsKey(endpoints_, id));
+    DCHECK(!IsMasterInterfaceId(id));
+
+    // We will receive a NotifyPeerEndpointClosed message from the other side.
+    control_message_proxy_.NotifyEndpointClosedBeforeSent(id);
+
+    return;
+  }
+
+  DCHECK(ContainsKey(endpoints_, id));
+  InterfaceEndpoint* endpoint = endpoints_[id].get();
+  DCHECK(!endpoint->client());
+  DCHECK(!endpoint->closed());
+  UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
+
+  if (!IsMasterInterfaceId(id))
+    control_message_proxy_.NotifyPeerEndpointClosed(id);
+
+  ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr);
+}
+
+InterfaceEndpointController* MultiplexRouter::AttachEndpointClient(
+    const ScopedInterfaceEndpointHandle& handle,
+    InterfaceEndpointClient* client,
+    scoped_refptr<base::SingleThreadTaskRunner> runner) {
+  const InterfaceId id = handle.id();
+
+  DCHECK(IsValidInterfaceId(id));
+  DCHECK(client);
+
+  base::AutoLock locker(lock_);
+  DCHECK(ContainsKey(endpoints_, id));
+
+  InterfaceEndpoint* endpoint = endpoints_[id].get();
+  endpoint->AttachClient(client, std::move(runner));
+
+  if (endpoint->peer_closed())
+    tasks_.push_back(Task::CreateNotifyErrorTask(endpoint));
+  ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr);
+
+  return endpoint;
+}
+
+void MultiplexRouter::DetachEndpointClient(
+    const ScopedInterfaceEndpointHandle& handle) {
+  const InterfaceId id = handle.id();
+
+  DCHECK(IsValidInterfaceId(id));
+
+  base::AutoLock locker(lock_);
+  DCHECK(ContainsKey(endpoints_, id));
+
+  InterfaceEndpoint* endpoint = endpoints_[id].get();
+  endpoint->DetachClient();
+}
+
+void MultiplexRouter::RaiseError() {
+  if (task_runner_->BelongsToCurrentThread()) {
+    connector_.RaiseError();
+  } else {
+    task_runner_->PostTask(FROM_HERE,
+                           base::Bind(&MultiplexRouter::RaiseError, this));
+  }
+}
+
+void MultiplexRouter::CloseMessagePipe() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  connector_.CloseMessagePipe();
+  // CloseMessagePipe() above won't trigger connection error handler.
+  // Explicitly call OnPipeConnectionError() so that associated endpoints will
+  // get notified.
+  OnPipeConnectionError();
+}
+
+bool MultiplexRouter::HasAssociatedEndpoints() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::AutoLock locker(lock_);
+
+  if (endpoints_.size() > 1)
+    return true;
+  if (endpoints_.size() == 0)
+    return false;
+
+  return !ContainsKey(endpoints_, kMasterInterfaceId);
+}
+
+void MultiplexRouter::EnableTestingMode() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::AutoLock locker(lock_);
+
+  testing_mode_ = true;
+  connector_.set_enforce_errors_from_incoming_receiver(false);
+}
+
+bool MultiplexRouter::Accept(Message* message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  scoped_refptr<MultiplexRouter> protector(this);
+  base::AutoLock locker(lock_);
+
+  ClientCallBehavior client_call_behavior =
+      connector_.during_sync_handle_watcher_callback()
+          ? ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES
+          : ALLOW_DIRECT_CLIENT_CALLS;
+
+  bool processed =
+      tasks_.empty() && ProcessIncomingMessage(message, client_call_behavior,
+                                               connector_.task_runner());
+
+  if (!processed) {
+    // Either the task queue is not empty or we cannot process the message
+    // directly. In both cases, there is no need to call ProcessTasks().
+    tasks_.push_back(Task::CreateMessageTask(message));
+    Task* task = tasks_.back().get();
+
+    if (task->message->has_flag(Message::kFlagIsSync)) {
+      InterfaceId id = task->message->interface_id();
+      sync_message_tasks_[id].push_back(task);
+      auto iter = endpoints_.find(id);
+      if (iter != endpoints_.end())
+        iter->second->SignalSyncMessageEvent();
+    }
+  } else if (!tasks_.empty()) {
+    // Processing the message may result in new tasks (for error notification)
+    // being added to the queue. In this case, we have to attempt to process the
+    // tasks.
+    ProcessTasks(client_call_behavior, connector_.task_runner());
+  }
+
+  // Always return true. If we see errors during message processing, we will
+  // explicitly call Connector::RaiseError() to disconnect the message pipe.
+  return true;
+}
+
+bool MultiplexRouter::OnPeerAssociatedEndpointClosed(InterfaceId id) {
+  lock_.AssertAcquired();
+
+  if (IsMasterInterfaceId(id))
+    return false;
+
+  InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr);
+
+  // It is possible that this endpoint has been set as peer closed. That is
+  // because when the message pipe is closed, all the endpoints are updated with
+  // PEER_ENDPOINT_CLOSED. We continue to process remaining tasks in the queue,
+  // as long as there are refs keeping the router alive. If there is a
+  // PeerAssociatedEndpointClosedEvent control message in the queue, we will get
+  // here and see that the endpoint has been marked as peer closed.
+  if (!endpoint->peer_closed()) {
+    if (endpoint->client())
+      tasks_.push_back(Task::CreateNotifyErrorTask(endpoint));
+    UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
+  }
+
+  // No need to trigger a ProcessTasks() because it is already on the stack.
+
+  return true;
+}
+
+bool MultiplexRouter::OnAssociatedEndpointClosedBeforeSent(InterfaceId id) {
+  lock_.AssertAcquired();
+
+  if (IsMasterInterfaceId(id))
+    return false;
+
+  InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr);
+  DCHECK(!endpoint->closed());
+  UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
+
+  control_message_proxy_.NotifyPeerEndpointClosed(id);
+
+  return true;
+}
+
+void MultiplexRouter::OnPipeConnectionError() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  scoped_refptr<MultiplexRouter> protector(this);
+  base::AutoLock locker(lock_);
+
+  encountered_error_ = true;
+
+  for (auto iter = endpoints_.begin(); iter != endpoints_.end();) {
+    InterfaceEndpoint* endpoint = iter->second.get();
+    // Increment the iterator before calling UpdateEndpointStateMayRemove()
+    // because it may remove the corresponding value from the map.
+    ++iter;
+
+    if (endpoint->client())
+      tasks_.push_back(Task::CreateNotifyErrorTask(endpoint));
+
+    UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
+  }
+
+  ProcessTasks(connector_.during_sync_handle_watcher_callback()
+                   ? ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES
+                   : ALLOW_DIRECT_CLIENT_CALLS,
+               connector_.task_runner());
+}
+
+void MultiplexRouter::ProcessTasks(
+    ClientCallBehavior client_call_behavior,
+    base::SingleThreadTaskRunner* current_task_runner) {
+  lock_.AssertAcquired();
+
+  if (posted_to_process_tasks_)
+    return;
+
+  while (!tasks_.empty()) {
+    std::unique_ptr<Task> task(std::move(tasks_.front()));
+    tasks_.pop_front();
+
+    InterfaceId id = kInvalidInterfaceId;
+    bool sync_message = task->IsMessageTask() && task->message &&
+                        task->message->has_flag(Message::kFlagIsSync);
+    if (sync_message) {
+      id = task->message->interface_id();
+      auto& sync_message_queue = sync_message_tasks_[id];
+      DCHECK_EQ(task.get(), sync_message_queue.front());
+      sync_message_queue.pop_front();
+    }
+
+    bool processed =
+        task->IsNotifyErrorTask()
+            ? ProcessNotifyErrorTask(task.get(), client_call_behavior,
+                                     current_task_runner)
+            : ProcessIncomingMessage(task->message.get(), client_call_behavior,
+                                     current_task_runner);
+
+    if (!processed) {
+      if (sync_message) {
+        auto& sync_message_queue = sync_message_tasks_[id];
+        sync_message_queue.push_front(task.get());
+      }
+      tasks_.push_front(std::move(task));
+      break;
+    } else {
+      if (sync_message) {
+        auto iter = sync_message_tasks_.find(id);
+        if (iter != sync_message_tasks_.end() && iter->second.empty())
+          sync_message_tasks_.erase(iter);
+      }
+    }
+  }
+}
+
+bool MultiplexRouter::ProcessFirstSyncMessageForEndpoint(InterfaceId id) {
+  lock_.AssertAcquired();
+
+  auto iter = sync_message_tasks_.find(id);
+  if (iter == sync_message_tasks_.end())
+    return false;
+
+  MultiplexRouter::Task* task = iter->second.front();
+  iter->second.pop_front();
+
+  DCHECK(task->IsMessageTask());
+  std::unique_ptr<Message> message(std::move(task->message));
+
+  // Note: after this call, |task| and  |iter| may be invalidated.
+  bool processed = ProcessIncomingMessage(
+      message.get(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, nullptr);
+  DCHECK(processed);
+
+  iter = sync_message_tasks_.find(id);
+  if (iter == sync_message_tasks_.end())
+    return false;
+
+  if (iter->second.empty()) {
+    sync_message_tasks_.erase(iter);
+    return false;
+  }
+
+  return true;
+}
+
+bool MultiplexRouter::ProcessNotifyErrorTask(
+    Task* task,
+    ClientCallBehavior client_call_behavior,
+    base::SingleThreadTaskRunner* current_task_runner) {
+  DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread());
+  lock_.AssertAcquired();
+  InterfaceEndpoint* endpoint = task->endpoint_to_notify.get();
+  if (!endpoint->client())
+    return true;
+
+  if (client_call_behavior != ALLOW_DIRECT_CLIENT_CALLS ||
+      endpoint->task_runner() != current_task_runner) {
+    MaybePostToProcessTasks(endpoint->task_runner());
+    return false;
+  }
+
+  DCHECK(endpoint->task_runner()->BelongsToCurrentThread());
+
+  InterfaceEndpointClient* client = endpoint->client();
+  {
+    // We must unlock before calling into |client| because it may call this
+    // object within NotifyError(). Holding the lock will lead to deadlock.
+    //
+    // It is safe to call into |client| without the lock. Because |client| is
+    // always accessed on the same thread, including DetachEndpointClient().
+    base::AutoUnlock unlocker(lock_);
+    client->NotifyError();
+  }
+  return true;
+}
+
+bool MultiplexRouter::ProcessIncomingMessage(
+    Message* message,
+    ClientCallBehavior client_call_behavior,
+    base::SingleThreadTaskRunner* current_task_runner) {
+  DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread());
+  lock_.AssertAcquired();
+
+  if (!message) {
+    // This is a sync message and has been processed during sync handle
+    // watching.
+    return true;
+  }
+
+  if (PipeControlMessageHandler::IsPipeControlMessage(message)) {
+    if (!control_message_handler_.Accept(message))
+      RaiseErrorInNonTestingMode();
+    return true;
+  }
+
+  InterfaceId id = message->interface_id();
+  DCHECK(IsValidInterfaceId(id));
+
+  bool inserted = false;
+  InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted);
+  if (inserted) {
+    // Currently, it is legitimate to receive messages for an endpoint
+    // that is not registered. For example, the endpoint is transferred in
+    // a message that is discarded. Once we add support to specify all
+    // enclosing endpoints in message header, we should be able to remove
+    // this.
+    UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
+
+    // It is also possible that this newly-inserted endpoint is the master
+    // endpoint. When the master InterfacePtr/Binding goes away, the message
+    // pipe is closed and we explicitly trigger a pipe connection error. The
+    // error updates all the endpoints, including the master endpoint, with
+    // PEER_ENDPOINT_CLOSED and removes the master endpoint from the
+    // registration. We continue to process remaining tasks in the queue, as
+    // long as there are refs keeping the router alive. If there are remaining
+    // messages for the master endpoint, we will get here.
+    if (!IsMasterInterfaceId(id))
+      control_message_proxy_.NotifyPeerEndpointClosed(id);
+    return true;
+  }
+
+  if (endpoint->closed())
+    return true;
+
+  if (!endpoint->client()) {
+    // We need to wait until a client is attached in order to dispatch further
+    // messages.
+    return false;
+  }
+
+  bool can_direct_call;
+  if (message->has_flag(Message::kFlagIsSync)) {
+    can_direct_call = client_call_behavior != NO_DIRECT_CLIENT_CALLS &&
+                      endpoint->task_runner()->BelongsToCurrentThread();
+  } else {
+    can_direct_call = client_call_behavior == ALLOW_DIRECT_CLIENT_CALLS &&
+                      endpoint->task_runner() == current_task_runner;
+  }
+
+  if (!can_direct_call) {
+    MaybePostToProcessTasks(endpoint->task_runner());
+    return false;
+  }
+
+  DCHECK(endpoint->task_runner()->BelongsToCurrentThread());
+
+  InterfaceEndpointClient* client = endpoint->client();
+  bool result = false;
+  {
+    // We must unlock before calling into |client| because it may call this
+    // object within HandleIncomingMessage(). Holding the lock will lead to
+    // deadlock.
+    //
+    // It is safe to call into |client| without the lock. Because |client| is
+    // always accessed on the same thread, including DetachEndpointClient().
+    base::AutoUnlock unlocker(lock_);
+    result = client->HandleIncomingMessage(message);
+  }
+  if (!result)
+    RaiseErrorInNonTestingMode();
+
+  return true;
+}
+
+void MultiplexRouter::MaybePostToProcessTasks(
+    base::SingleThreadTaskRunner* task_runner) {
+  lock_.AssertAcquired();
+  if (posted_to_process_tasks_)
+    return;
+
+  posted_to_process_tasks_ = true;
+  posted_to_task_runner_ = task_runner;
+  task_runner->PostTask(
+      FROM_HERE, base::Bind(&MultiplexRouter::LockAndCallProcessTasks, this));
+}
+
+void MultiplexRouter::LockAndCallProcessTasks() {
+  // There is no need to hold a ref to this class in this case because this is
+  // always called using base::Bind(), which holds a ref.
+  base::AutoLock locker(lock_);
+  posted_to_process_tasks_ = false;
+  scoped_refptr<base::SingleThreadTaskRunner> runner(
+      std::move(posted_to_task_runner_));
+  ProcessTasks(ALLOW_DIRECT_CLIENT_CALLS, runner.get());
+}
+
+void MultiplexRouter::UpdateEndpointStateMayRemove(
+    InterfaceEndpoint* endpoint,
+    EndpointStateUpdateType type) {
+  switch (type) {
+    case ENDPOINT_CLOSED:
+      endpoint->set_closed();
+      break;
+    case PEER_ENDPOINT_CLOSED:
+      endpoint->set_peer_closed();
+      // If the interface endpoint is performing a sync watch, this makes sure
+      // it is notified and eventually exits the sync watch.
+      endpoint->SignalSyncMessageEvent();
+      break;
+  }
+  if (endpoint->closed() && endpoint->peer_closed())
+    endpoints_.erase(endpoint->id());
+}
+
+void MultiplexRouter::RaiseErrorInNonTestingMode() {
+  lock_.AssertAcquired();
+  if (!testing_mode_)
+    RaiseError();
+}
+
+MultiplexRouter::InterfaceEndpoint* MultiplexRouter::FindOrInsertEndpoint(
+    InterfaceId id,
+    bool* inserted) {
+  lock_.AssertAcquired();
+  // Either |inserted| is nullptr or it points to a boolean initialized as
+  // false.
+  DCHECK(!inserted || !*inserted);
+
+  auto iter = endpoints_.find(id);
+  InterfaceEndpoint* endpoint;
+  if (iter == endpoints_.end()) {
+    endpoint = new InterfaceEndpoint(this, id);
+    endpoints_[id] = endpoint;
+    if (inserted)
+      *inserted = true;
+  } else {
+    endpoint = iter->second.get();
+  }
+
+  return endpoint;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
new file mode 100644
index 0000000..dc66e8e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -0,0 +1,240 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MULTIPLEX_ROUTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MULTIPLEX_ROUTER_H_
+
+#include <stdint.h>
+
+#include <deque>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
+#include "mojo/public/cpp/bindings/connector.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/message_header_validator.h"
+#include "mojo/public/cpp/bindings/pipe_control_message_handler.h"
+#include "mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h"
+#include "mojo/public/cpp/bindings/pipe_control_message_proxy.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace mojo {
+
+class AssociatedGroup;
+
+namespace internal {
+
+// MultiplexRouter supports routing messages for multiple interfaces over a
+// single message pipe.
+//
+// It is created on the thread where the master interface of the message pipe
+// lives. Although it is ref-counted, it is guarateed to be destructed on the
+// same thread.
+// Some public methods are only allowed to be called on the creating thread;
+// while the others are safe to call from any threads. Please see the method
+// comments for more details.
+class MultiplexRouter
+    : public MessageReceiver,
+      public AssociatedGroupController,
+      public PipeControlMessageHandlerDelegate {
+ public:
+  // If |set_interface_id_namespace_bit| is true, the interface IDs generated by
+  // this router will have the highest bit set.
+  MultiplexRouter(bool set_interface_id_namespace_bit,
+                  ScopedMessagePipeHandle message_pipe,
+                  scoped_refptr<base::SingleThreadTaskRunner> runner);
+
+  // Sets the master interface name for this router. Only used when reporting
+  // message header or control message validation errors.
+  void SetMasterInterfaceName(const std::string& name);
+
+  // ---------------------------------------------------------------------------
+  // The following public methods are safe to call from any threads.
+
+  // AssociatedGroupController implementation:
+  void CreateEndpointHandlePair(
+      ScopedInterfaceEndpointHandle* local_endpoint,
+      ScopedInterfaceEndpointHandle* remote_endpoint) override;
+  ScopedInterfaceEndpointHandle CreateLocalEndpointHandle(
+      InterfaceId id) override;
+  void CloseEndpointHandle(InterfaceId id, bool is_local) override;
+  InterfaceEndpointController* AttachEndpointClient(
+      const ScopedInterfaceEndpointHandle& handle,
+      InterfaceEndpointClient* endpoint_client,
+      scoped_refptr<base::SingleThreadTaskRunner> runner) override;
+  void DetachEndpointClient(
+      const ScopedInterfaceEndpointHandle& handle) override;
+  void RaiseError() override;
+
+  // ---------------------------------------------------------------------------
+  // The following public methods are called on the creating thread.
+
+  // Please note that this method shouldn't be called unless it results from an
+  // explicit request of the user of bindings (e.g., the user sets an
+  // InterfacePtr to null or closes a Binding).
+  void CloseMessagePipe();
+
+  // Extracts the underlying message pipe.
+  ScopedMessagePipeHandle PassMessagePipe() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    DCHECK(!HasAssociatedEndpoints());
+    return connector_.PassMessagePipe();
+  }
+
+  // Blocks the current thread until the first incoming message, or |deadline|.
+  bool WaitForIncomingMessage(MojoDeadline deadline) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return connector_.WaitForIncomingMessage(deadline);
+  }
+
+  // See Binding for details of pause/resume.
+  void PauseIncomingMethodCallProcessing() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    connector_.PauseIncomingMethodCallProcessing();
+  }
+  void ResumeIncomingMethodCallProcessing() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    connector_.ResumeIncomingMethodCallProcessing();
+  }
+
+  // Whether there are any associated interfaces running currently.
+  bool HasAssociatedEndpoints() const;
+
+  // Sets this object to testing mode.
+  // In testing mode, the object doesn't disconnect the underlying message pipe
+  // when it receives unexpected or invalid messages.
+  void EnableTestingMode();
+
+  // Is the router bound to a message pipe handle?
+  bool is_valid() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return connector_.is_valid();
+  }
+
+  // TODO(yzshen): consider removing this getter.
+  MessagePipeHandle handle() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return connector_.handle();
+  }
+
+ private:
+  class InterfaceEndpoint;
+  struct Task;
+
+  ~MultiplexRouter() override;
+
+  // MessageReceiver implementation:
+  bool Accept(Message* message) override;
+
+  // PipeControlMessageHandlerDelegate implementation:
+  bool OnPeerAssociatedEndpointClosed(InterfaceId id) override;
+  bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) override;
+
+  void OnPipeConnectionError();
+
+  // Specifies whether we are allowed to directly call into
+  // InterfaceEndpointClient (given that we are already on the same thread as
+  // the client).
+  enum ClientCallBehavior {
+    // Don't call any InterfaceEndpointClient methods directly.
+    NO_DIRECT_CLIENT_CALLS,
+    // Only call InterfaceEndpointClient::HandleIncomingMessage directly to
+    // handle sync messages.
+    ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES,
+    // Allow to call any InterfaceEndpointClient methods directly.
+    ALLOW_DIRECT_CLIENT_CALLS
+  };
+
+  // Processes enqueued tasks (incoming messages and error notifications).
+  // |current_task_runner| is only used when |client_call_behavior| is
+  // ALLOW_DIRECT_CLIENT_CALLS to determine whether we are on the right task
+  // runner to make client calls for async messages or connection error
+  // notifications.
+  //
+  // Note: Because calling into InterfaceEndpointClient may lead to destruction
+  // of this object, if direct calls are allowed, the caller needs to hold on to
+  // a ref outside of |lock_| before calling this method.
+  void ProcessTasks(ClientCallBehavior client_call_behavior,
+                    base::SingleThreadTaskRunner* current_task_runner);
+
+  // Processes the first queued sync message for the endpoint corresponding to
+  // |id|; returns whether there are more sync messages for that endpoint in the
+  // queue.
+  //
+  // This method is only used by enpoints during sync watching. Therefore, not
+  // all sync messages are handled by it.
+  bool ProcessFirstSyncMessageForEndpoint(InterfaceId id);
+
+  // Returns true to indicate that |task|/|message| has been processed.
+  bool ProcessNotifyErrorTask(
+      Task* task,
+      ClientCallBehavior client_call_behavior,
+      base::SingleThreadTaskRunner* current_task_runner);
+  bool ProcessIncomingMessage(
+      Message* message,
+      ClientCallBehavior client_call_behavior,
+      base::SingleThreadTaskRunner* current_task_runner);
+
+  void MaybePostToProcessTasks(base::SingleThreadTaskRunner* task_runner);
+  void LockAndCallProcessTasks();
+
+  // Updates the state of |endpoint|. If both the endpoint and its peer have
+  // been closed, removes it from |endpoints_|.
+  // NOTE: The method may invalidate |endpoint|.
+  enum EndpointStateUpdateType { ENDPOINT_CLOSED, PEER_ENDPOINT_CLOSED };
+  void UpdateEndpointStateMayRemove(InterfaceEndpoint* endpoint,
+                                    EndpointStateUpdateType type);
+
+  void RaiseErrorInNonTestingMode();
+
+  InterfaceEndpoint* FindOrInsertEndpoint(InterfaceId id, bool* inserted);
+
+  // Whether to set the namespace bit when generating interface IDs. Please see
+  // comments of kInterfaceIdNamespaceMask.
+  const bool set_interface_id_namespace_bit_;
+
+  MessageHeaderValidator header_validator_;
+  Connector connector_;
+
+  base::ThreadChecker thread_checker_;
+
+  // Protects the following members.
+  mutable base::Lock lock_;
+  PipeControlMessageHandler control_message_handler_;
+  PipeControlMessageProxy control_message_proxy_;
+
+  std::map<InterfaceId, scoped_refptr<InterfaceEndpoint>> endpoints_;
+  uint32_t next_interface_id_value_;
+
+  std::deque<std::unique_ptr<Task>> tasks_;
+  // It refers to tasks in |tasks_| and doesn't own any of them.
+  std::map<InterfaceId, std::deque<Task*>> sync_message_tasks_;
+
+  bool posted_to_process_tasks_;
+  scoped_refptr<base::SingleThreadTaskRunner> posted_to_task_runner_;
+
+  bool encountered_error_;
+
+  bool testing_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiplexRouter);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MULTIPLEX_ROUTER_H_
diff --git a/mojo/public/cpp/bindings/lib/native_enum_data.h b/mojo/public/cpp/bindings/lib/native_enum_data.h
new file mode 100644
index 0000000..dcafce2
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/native_enum_data.h
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All 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_NATIVE_ENUM_DATA_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_ENUM_DATA_H_
+
+namespace mojo {
+namespace internal {
+
+class ValidationContext;
+
+class NativeEnum_Data {
+ public:
+  static bool const kIsExtensible = true;
+
+  static bool IsKnownValue(int32_t value) { return false; }
+
+  static bool Validate(int32_t value,
+                       ValidationContext* validation_context) { return true; }
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_ENUM_DATA_H_
diff --git a/mojo/public/cpp/bindings/lib/native_enum_serialization.h b/mojo/public/cpp/bindings/lib/native_enum_serialization.h
new file mode 100644
index 0000000..4faf957
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/native_enum_serialization.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All 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_NATIVE_ENUM_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_ENUM_SERIALIZATION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "ipc/ipc_param_traits.h"
+#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
+#include "mojo/public/cpp/bindings/native_enum.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename MaybeConstUserType>
+struct NativeEnumSerializerImpl {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = IPC::ParamTraits<UserType>;
+
+  // IPC_ENUM_TRAITS* macros serialize enum as int, make sure that fits into
+  // mojo native-only enum.
+  static_assert(sizeof(NativeEnum) >= sizeof(int),
+                "Cannot store the serialization result in NativeEnum.");
+
+  static void Serialize(UserType input, int32_t* output) {
+    base::Pickle pickle;
+    Traits::Write(&pickle, input);
+
+    CHECK_GE(sizeof(int32_t), pickle.payload_size());
+    *output = 0;
+    memcpy(reinterpret_cast<char*>(output), pickle.payload(),
+           pickle.payload_size());
+  }
+
+  struct PickleData {
+    uint32_t payload_size;
+    int32_t value;
+  };
+  static_assert(sizeof(PickleData) == 8, "PickleData size mismatch.");
+
+  static bool Deserialize(int32_t input, UserType* output) {
+    PickleData data = {sizeof(int32_t), input};
+    base::Pickle pickle_view(reinterpret_cast<const char*>(&data),
+                             sizeof(PickleData));
+    base::PickleIterator iter(pickle_view);
+    return Traits::Read(&pickle_view, &iter, output);
+  }
+};
+
+struct UnmappedNativeEnumSerializerImpl {
+  static void Serialize(NativeEnum input, int32_t* output) {
+    *output = static_cast<int32_t>(input);
+  }
+  static bool Deserialize(int32_t input, NativeEnum* output) {
+    *output = static_cast<NativeEnum>(input);
+    return true;
+  }
+};
+
+template <>
+struct NativeEnumSerializerImpl<NativeEnum>
+    : public UnmappedNativeEnumSerializerImpl {};
+
+template <>
+struct NativeEnumSerializerImpl<const NativeEnum>
+    : public UnmappedNativeEnumSerializerImpl {};
+
+template <typename MaybeConstUserType>
+struct Serializer<NativeEnum, MaybeConstUserType>
+    : public NativeEnumSerializerImpl<MaybeConstUserType> {};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_ENUM_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/native_struct.cc b/mojo/public/cpp/bindings/lib/native_struct.cc
new file mode 100644
index 0000000..837b75a
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/native_struct.cc
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium Authors. All 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/native_struct.h"
+
+namespace mojo {
+
+// static
+NativeStructPtr NativeStruct::New() {
+  NativeStructPtr rv;
+  internal::StructHelper<NativeStruct>::Initialize(&rv);
+  return rv;
+}
+
+NativeStruct::NativeStruct() : data(nullptr) {}
+
+NativeStruct::~NativeStruct() {}
+
+NativeStructPtr NativeStruct::Clone() const {
+  NativeStructPtr rv(New());
+  rv->data = data.Clone();
+  return rv;
+}
+
+bool NativeStruct::Equals(const NativeStruct& other) const {
+  return data.Equals(other.data);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/native_struct_data.cc b/mojo/public/cpp/bindings/lib/native_struct_data.cc
new file mode 100644
index 0000000..0e5d245
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/native_struct_data.cc
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All 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/native_struct_data.h"
+
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+
+namespace mojo {
+namespace internal {
+
+// static
+bool NativeStruct_Data::Validate(const void* data,
+                                 ValidationContext* validation_context) {
+  const ContainerValidateParams data_validate_params(0, false, nullptr);
+  return Array_Data<uint8_t>::Validate(data, validation_context,
+                                       &data_validate_params);
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/native_struct_data.h b/mojo/public/cpp/bindings/lib/native_struct_data.h
new file mode 100644
index 0000000..5c58774
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/native_struct_data.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All 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_NATIVE_STRUCT_DATA_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_STRUCT_DATA_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+namespace internal {
+
+class Buffer;
+class ValidationContext;
+
+class NativeStruct_Data {
+ public:
+  static bool Validate(const void* data, ValidationContext* validation_context);
+
+  // Unlike normal structs, the memory layout is exactly the same as an array
+  // of uint8_t.
+  Array_Data<uint8_t> data;
+
+ private:
+  NativeStruct_Data() = delete;
+  ~NativeStruct_Data() = delete;
+};
+
+static_assert(sizeof(Array_Data<uint8_t>) == sizeof(NativeStruct_Data),
+              "Mismatched NativeStruct_Data and Array_Data<uint8_t> size");
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_STRUCT_DATA_H_
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
new file mode 100644
index 0000000..ac06059
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
@@ -0,0 +1,59 @@
+// Copyright 2016 The Chromium Authors. All 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/native_struct_serialization.h"
+
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+
+namespace mojo {
+namespace internal {
+
+// static
+size_t UnmappedNativeStructSerializerImpl::PrepareToSerialize(
+    const NativeStructPtr& input,
+    SerializationContext* context) {
+  if (!input)
+    return 0;
+  return internal::PrepareToSerialize<Array<uint8_t>>(input->data, context);
+}
+
+// static
+void UnmappedNativeStructSerializerImpl::Serialize(
+    const NativeStructPtr& input,
+    Buffer* buffer,
+    NativeStruct_Data** output,
+    SerializationContext* context) {
+  if (!input) {
+    *output = nullptr;
+    return;
+  }
+
+  Array_Data<uint8_t>* data = nullptr;
+  const ContainerValidateParams params(0, false, nullptr);
+  internal::Serialize<Array<uint8_t>>(input->data, buffer, &data, &params,
+                                      context);
+  *output = reinterpret_cast<NativeStruct_Data*>(data);
+}
+
+// static
+bool UnmappedNativeStructSerializerImpl::Deserialize(
+    NativeStruct_Data* input,
+    NativeStructPtr* output,
+    SerializationContext* context) {
+  Array_Data<uint8_t>* data = reinterpret_cast<Array_Data<uint8_t>*>(input);
+
+  NativeStructPtr result(NativeStruct::New());
+  if (!internal::Deserialize<Array<uint8_t>>(data, &result->data, context)) {
+    output = nullptr;
+    return false;
+  }
+  if (!result->data)
+    *output = nullptr;
+  else
+    result.Swap(output);
+  return true;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.h b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
new file mode 100644
index 0000000..e64b862
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
@@ -0,0 +1,132 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_STRUCT_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_STRUCT_SERIALIZATION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "ipc/ipc_param_traits.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
+#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/native_struct.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename MaybeConstUserType>
+struct NativeStructSerializerImpl {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = IPC::ParamTraits<UserType>;
+
+  static size_t PrepareToSerialize(MaybeConstUserType& value,
+                                   SerializationContext* context) {
+    base::PickleSizer sizer;
+    Traits::GetSize(&sizer, value);
+    return Align(sizer.payload_size() + sizeof(ArrayHeader));
+  }
+
+  static void Serialize(MaybeConstUserType& value,
+                        Buffer* buffer,
+                        NativeStruct_Data** out,
+                        SerializationContext* context) {
+    base::Pickle pickle;
+    Traits::Write(&pickle, value);
+
+#if DCHECK_IS_ON()
+    base::PickleSizer sizer;
+    Traits::GetSize(&sizer, value);
+    DCHECK_EQ(sizer.payload_size(), pickle.payload_size());
+#endif
+
+    size_t total_size = pickle.payload_size() + sizeof(ArrayHeader);
+    DCHECK_LT(total_size, std::numeric_limits<uint32_t>::max());
+
+    // Allocate a uint8 array, initialize its header, and copy the Pickle in.
+    ArrayHeader* header =
+        reinterpret_cast<ArrayHeader*>(buffer->Allocate(total_size));
+    header->num_bytes = static_cast<uint32_t>(total_size);
+    header->num_elements = static_cast<uint32_t>(pickle.payload_size());
+    memcpy(reinterpret_cast<char*>(header) + sizeof(ArrayHeader),
+           pickle.payload(), pickle.payload_size());
+
+    *out = reinterpret_cast<NativeStruct_Data*>(header);
+  }
+
+  static bool Deserialize(NativeStruct_Data* data,
+                          UserType* out,
+                          SerializationContext* context) {
+    if (!data)
+      return false;
+
+    // Construct a temporary base::Pickle view over the array data. Note that
+    // the Array_Data is laid out like this:
+    //
+    //   [num_bytes (4 bytes)] [num_elements (4 bytes)] [elements...]
+    //
+    // and base::Pickle expects to view data like this:
+    //
+    //   [payload_size (4 bytes)] [header bytes ...] [payload...]
+    //
+    // Because ArrayHeader's num_bytes includes the length of the header and
+    // Pickle's payload_size does not, we need to adjust the stored value
+    // momentarily so Pickle can view the data.
+    ArrayHeader* header = reinterpret_cast<ArrayHeader*>(data);
+    DCHECK_GE(header->num_bytes, sizeof(ArrayHeader));
+    header->num_bytes -= sizeof(ArrayHeader);
+
+    {
+      // Construct a view over the full Array_Data, including our hacked up
+      // header. Pickle will infer from this that the header is 8 bytes long,
+      // and the payload will contain all of the pickled bytes.
+      base::Pickle pickle_view(reinterpret_cast<const char*>(header),
+                               header->num_bytes + sizeof(ArrayHeader));
+      base::PickleIterator iter(pickle_view);
+      if (!Traits::Read(&pickle_view, &iter, out))
+        return false;
+    }
+
+    // Return the header to its original state.
+    header->num_bytes += sizeof(ArrayHeader);
+
+    return true;
+  }
+};
+
+struct UnmappedNativeStructSerializerImpl {
+  static size_t PrepareToSerialize(const NativeStructPtr& input,
+                                   SerializationContext* context);
+  static void Serialize(const NativeStructPtr& input,
+                        Buffer* buffer,
+                        NativeStruct_Data** output,
+                        SerializationContext* context);
+  static bool Deserialize(NativeStruct_Data* input,
+                          NativeStructPtr* output,
+                          SerializationContext* context);
+};
+
+template <>
+struct NativeStructSerializerImpl<NativeStructPtr>
+    : public UnmappedNativeStructSerializerImpl {};
+
+template <>
+struct NativeStructSerializerImpl<const NativeStructPtr>
+    : public UnmappedNativeStructSerializerImpl {};
+
+template <typename MaybeConstUserType>
+struct Serializer<NativeStructPtr, MaybeConstUserType>
+    : public NativeStructSerializerImpl<MaybeConstUserType> {};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_STRUCT_SERIALIZATION_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/pipe_control_message_handler.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
new file mode 100644
index 0000000..7ee9f8a
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
@@ -0,0 +1,84 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/pipe_control_message_handler.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+#include "mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h"
+#include "mojo/public/interfaces/bindings/pipe_control_messages.mojom.h"
+
+namespace mojo {
+
+PipeControlMessageHandler::PipeControlMessageHandler(
+    PipeControlMessageHandlerDelegate* delegate)
+    : delegate_(delegate) {}
+
+PipeControlMessageHandler::~PipeControlMessageHandler() {}
+
+void PipeControlMessageHandler::SetDescription(const std::string& description) {
+  description_ = description;
+}
+
+// static
+bool PipeControlMessageHandler::IsPipeControlMessage(const Message* message) {
+  return !IsValidInterfaceId(message->interface_id());
+}
+
+bool PipeControlMessageHandler::Accept(Message* message) {
+  if (!Validate(message))
+    return false;
+
+  if (message->name() == pipe_control::kRunOrClosePipeMessageId)
+    return RunOrClosePipe(message);
+
+  NOTREACHED();
+  return false;
+}
+
+bool PipeControlMessageHandler::Validate(Message* message) {
+  internal::ValidationContext validation_context(
+      message->data(), message->data_num_bytes(), 0, message, description_);
+
+  if (message->name() == pipe_control::kRunOrClosePipeMessageId) {
+    if (!internal::ValidateMessageIsRequestWithoutResponse(
+            message, &validation_context)) {
+      return false;
+    }
+    return internal::ValidateMessagePayload<
+        pipe_control::internal::RunOrClosePipeMessageParams_Data>(
+            message, &validation_context);
+  }
+
+  return false;
+}
+
+bool PipeControlMessageHandler::RunOrClosePipe(Message* message) {
+  pipe_control::internal::RunOrClosePipeMessageParams_Data* params =
+      reinterpret_cast<
+          pipe_control::internal::RunOrClosePipeMessageParams_Data*>(
+          message->mutable_payload());
+  pipe_control::RunOrClosePipeMessageParamsPtr params_ptr;
+  internal::Deserialize<pipe_control::RunOrClosePipeMessageParamsPtr>(
+      params, &params_ptr, &context_);
+
+  if (params_ptr->input->is_peer_associated_endpoint_closed_event()) {
+    return delegate_->OnPeerAssociatedEndpointClosed(
+        params_ptr->input->get_peer_associated_endpoint_closed_event()->id);
+  }
+  if (params_ptr->input->is_associated_endpoint_closed_before_sent_event()) {
+    return delegate_->OnAssociatedEndpointClosedBeforeSent(
+        params_ptr->input->get_associated_endpoint_closed_before_sent_event()
+            ->id);
+  }
+
+  DVLOG(1) << "Unsupported command in a RunOrClosePipe message pipe control "
+           << "message. Closing the pipe.";
+  return false;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
new file mode 100644
index 0000000..55ee64b
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
@@ -0,0 +1,74 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/pipe_control_message_proxy.h"
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/interfaces/bindings/pipe_control_messages.mojom.h"
+
+namespace mojo {
+namespace {
+
+void SendRunOrClosePipeMessage(MessageReceiver* receiver,
+                               pipe_control::RunOrClosePipeInputPtr input,
+                               internal::SerializationContext* context) {
+  pipe_control::RunOrClosePipeMessageParamsPtr params_ptr(
+      pipe_control::RunOrClosePipeMessageParams::New());
+  params_ptr->input = std::move(input);
+
+  size_t size =
+      internal::PrepareToSerialize<
+          pipe_control::RunOrClosePipeMessageParamsPtr>(params_ptr, context);
+  internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId,
+                                   size);
+
+  pipe_control::internal::RunOrClosePipeMessageParams_Data* params = nullptr;
+  internal::Serialize<pipe_control::RunOrClosePipeMessageParamsPtr>(
+      params_ptr, builder.buffer(), &params, context);
+  builder.message()->set_interface_id(kInvalidInterfaceId);
+  bool ok = receiver->Accept(builder.message());
+  // This return value may be ignored as !ok implies the underlying message pipe
+  // has encountered an error, which will be visible through other means.
+  ALLOW_UNUSED_LOCAL(ok);
+}
+
+}  // namespace
+
+PipeControlMessageProxy::PipeControlMessageProxy(MessageReceiver* receiver)
+    : receiver_(receiver) {}
+
+void PipeControlMessageProxy::NotifyPeerEndpointClosed(InterfaceId id) {
+  DCHECK(!IsMasterInterfaceId(id));
+  pipe_control::PeerAssociatedEndpointClosedEventPtr event(
+      pipe_control::PeerAssociatedEndpointClosedEvent::New());
+  event->id = id;
+
+  pipe_control::RunOrClosePipeInputPtr input(
+      pipe_control::RunOrClosePipeInput::New());
+  input->set_peer_associated_endpoint_closed_event(std::move(event));
+
+  SendRunOrClosePipeMessage(receiver_, std::move(input), &context_);
+}
+
+void PipeControlMessageProxy::NotifyEndpointClosedBeforeSent(InterfaceId id) {
+  DCHECK(!IsMasterInterfaceId(id));
+  pipe_control::AssociatedEndpointClosedBeforeSentEventPtr event(
+      pipe_control::AssociatedEndpointClosedBeforeSentEvent::New());
+  event->id = id;
+
+  pipe_control::RunOrClosePipeInputPtr input(
+      pipe_control::RunOrClosePipeInput::New());
+  input->set_associated_endpoint_closed_before_sent_event(std::move(event));
+
+  SendRunOrClosePipeMessage(receiver_, std::move(input), &context_);
+}
+
+}  // 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..8c1b77d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.cc
@@ -0,0 +1,323 @@
+// Copyright 2014 The Chromium Authors. All 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 <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+namespace {
+
+void DCheckIfInvalid(const base::WeakPtr<Router>& router,
+                   const std::string& message) {
+  bool is_valid = router && !router->encountered_error() && router->is_valid();
+  DCHECK(!is_valid) << message;
+}
+
+class ResponderThunk : public MessageReceiverWithStatus {
+ public:
+  explicit ResponderThunk(const base::WeakPtr<Router>& router,
+                          scoped_refptr<base::SingleThreadTaskRunner> runner)
+      : router_(router),
+        accept_was_invoked_(false),
+        task_runner_(std::move(runner)) {}
+  ~ResponderThunk() override {
+    if (!accept_was_invoked_) {
+      // The Mojo application handled a message that was expecting a response
+      // but did not send a response.
+      // We raise an error to signal the calling application that an error
+      // condition occurred. Without this the calling application would have no
+      // way of knowing it should stop waiting for a response.
+      if (task_runner_->RunsTasksOnCurrentThread()) {
+        // Please note that even if this code is run from a different task
+        // runner on the same thread as |task_runner_|, it is okay to directly
+        // call Router::RaiseError(), because it will raise error from the
+        // correct task runner asynchronously.
+        if (router_)
+          router_->RaiseError();
+      } else {
+        task_runner_->PostTask(FROM_HERE,
+                               base::Bind(&Router::RaiseError, router_));
+      }
+    }
+  }
+
+  // MessageReceiver implementation:
+  bool Accept(Message* message) override {
+    DCHECK(task_runner_->RunsTasksOnCurrentThread());
+    accept_was_invoked_ = true;
+    DCHECK(message->has_flag(Message::kFlagIsResponse));
+
+    bool result = false;
+
+    if (router_)
+      result = router_->Accept(message);
+
+    return result;
+  }
+
+  // MessageReceiverWithStatus implementation:
+  bool IsValid() override {
+    DCHECK(task_runner_->RunsTasksOnCurrentThread());
+    return router_ && !router_->encountered_error() && router_->is_valid();
+  }
+
+  void DCheckInvalid(const std::string& message) override {
+    if (task_runner_->RunsTasksOnCurrentThread()) {
+      DCheckIfInvalid(router_, message);
+    } else {
+      task_runner_->PostTask(FROM_HERE,
+                             base::Bind(&DCheckIfInvalid, router_, message));
+    }
+  }
+
+ private:
+  base::WeakPtr<Router> router_;
+  bool accept_was_invoked_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
+
+}  // namespace
+
+// ----------------------------------------------------------------------------
+
+Router::SyncResponseInfo::SyncResponseInfo(bool* in_response_received)
+    : response_received(in_response_received) {}
+
+Router::SyncResponseInfo::~SyncResponseInfo() {}
+
+// ----------------------------------------------------------------------------
+
+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,
+               bool expects_sync_requests,
+               scoped_refptr<base::SingleThreadTaskRunner> runner)
+    : thunk_(this),
+      filters_(std::move(filters)),
+      connector_(std::move(message_pipe),
+                 Connector::SINGLE_THREADED_SEND,
+                 std::move(runner)),
+      incoming_receiver_(nullptr),
+      next_request_id_(0),
+      testing_mode_(false),
+      pending_task_for_messages_(false),
+      encountered_error_(false),
+      weak_factory_(this) {
+  filters_.SetSink(&thunk_);
+  if (expects_sync_requests)
+    connector_.AllowWokenUpBySyncWatchOnSameThread();
+  connector_.set_incoming_receiver(filters_.GetHead());
+  connector_.set_connection_error_handler(
+      base::Bind(&Router::OnConnectionError, base::Unretained(this)));
+}
+
+Router::~Router() {}
+
+bool Router::Accept(Message* message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!message->has_flag(Message::kFlagExpectsResponse));
+  return connector_.Accept(message);
+}
+
+bool Router::AcceptWithResponder(Message* message, MessageReceiver* responder) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(message->has_flag(Message::kFlagExpectsResponse));
+
+  // 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_++;
+
+  bool is_sync = message->has_flag(Message::kFlagIsSync);
+  message->set_request_id(request_id);
+  if (!connector_.Accept(message))
+    return false;
+
+  if (!is_sync) {
+    // We assume ownership of |responder|.
+    async_responders_[request_id] = base::WrapUnique(responder);
+    return true;
+  }
+
+  SyncCallRestrictions::AssertSyncCallAllowed();
+
+  bool response_received = false;
+  std::unique_ptr<MessageReceiver> sync_responder(responder);
+  sync_responses_.insert(std::make_pair(
+      request_id, base::WrapUnique(new SyncResponseInfo(&response_received))));
+
+  base::WeakPtr<Router> weak_self = weak_factory_.GetWeakPtr();
+  connector_.SyncWatch(&response_received);
+  // Make sure that this instance hasn't been destroyed.
+  if (weak_self) {
+    DCHECK(ContainsKey(sync_responses_, request_id));
+    auto iter = sync_responses_.find(request_id);
+    DCHECK_EQ(&response_received, iter->second->response_received);
+    if (response_received) {
+      std::unique_ptr<Message> response = std::move(iter->second->response);
+      ignore_result(sync_responder->Accept(response.get()));
+    }
+    sync_responses_.erase(iter);
+  }
+
+  // Return true means that we take ownership of |responder|.
+  return true;
+}
+
+void Router::EnableTestingMode() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  testing_mode_ = true;
+  connector_.set_enforce_errors_from_incoming_receiver(false);
+}
+
+bool Router::HandleIncomingMessage(Message* message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  const bool during_sync_call =
+      connector_.during_sync_handle_watcher_callback();
+  if (!message->has_flag(Message::kFlagIsSync) &&
+      (during_sync_call || !pending_messages_.empty())) {
+    std::unique_ptr<Message> pending_message(new Message);
+    message->MoveTo(pending_message.get());
+    pending_messages_.push(std::move(pending_message));
+
+    if (!pending_task_for_messages_) {
+      pending_task_for_messages_ = true;
+      connector_.task_runner()->PostTask(
+          FROM_HERE, base::Bind(&Router::HandleQueuedMessages,
+                                weak_factory_.GetWeakPtr()));
+    }
+
+    return true;
+  }
+
+  return HandleMessageInternal(message);
+}
+
+void Router::HandleQueuedMessages() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(pending_task_for_messages_);
+
+  base::WeakPtr<Router> weak_self = weak_factory_.GetWeakPtr();
+  while (!pending_messages_.empty()) {
+    std::unique_ptr<Message> message(std::move(pending_messages_.front()));
+    pending_messages_.pop();
+
+    bool result = HandleMessageInternal(message.get());
+    if (!weak_self)
+      return;
+
+    if (!result && !testing_mode_) {
+      connector_.RaiseError();
+      break;
+    }
+  }
+
+  pending_task_for_messages_ = false;
+
+  // We may have already seen a connection error from the connector, but
+  // haven't notified the user because we want to process all the queued
+  // messages first. We should do it now.
+  if (connector_.encountered_error() && !encountered_error_)
+    OnConnectionError();
+}
+
+bool Router::HandleMessageInternal(Message* message) {
+  if (message->has_flag(Message::kFlagExpectsResponse)) {
+    if (!incoming_receiver_)
+      return false;
+
+    MessageReceiverWithStatus* responder = new ResponderThunk(
+        weak_factory_.GetWeakPtr(), connector_.task_runner());
+    bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
+    if (!ok)
+      delete responder;
+    return ok;
+
+  } else if (message->has_flag(Message::kFlagIsResponse)) {
+    uint64_t request_id = message->request_id();
+
+    if (message->has_flag(Message::kFlagIsSync)) {
+      auto it = sync_responses_.find(request_id);
+      if (it == sync_responses_.end()) {
+        DCHECK(testing_mode_);
+        return false;
+      }
+      it->second->response.reset(new Message());
+      message->MoveTo(it->second->response.get());
+      *it->second->response_received = true;
+      return true;
+    }
+
+    auto it = async_responders_.find(request_id);
+    if (it == async_responders_.end()) {
+      DCHECK(testing_mode_);
+      return false;
+    }
+    std::unique_ptr<MessageReceiver> responder = std::move(it->second);
+    async_responders_.erase(it);
+    return responder->Accept(message);
+  } else {
+    if (!incoming_receiver_)
+      return false;
+
+    return incoming_receiver_->Accept(message);
+  }
+}
+
+void Router::OnConnectionError() {
+  if (encountered_error_)
+    return;
+
+  if (!pending_messages_.empty()) {
+    // After all the pending messages are processed, we will check whether an
+    // error has been encountered and run the user's connection error handler
+    // if necessary.
+    DCHECK(pending_task_for_messages_);
+    return;
+  }
+
+  if (connector_.during_sync_handle_watcher_callback()) {
+    // We don't want the error handler to reenter an ongoing sync call.
+    connector_.task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&Router::OnConnectionError, weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  encountered_error_ = true;
+  if (!error_handler_.is_null())
+    error_handler_.Run();
+}
+
+// ----------------------------------------------------------------------------
+
+}  // 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..6dbe08d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <queue>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/connector.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+
+namespace mojo {
+namespace internal {
+
+// TODO(yzshen): Consider removing this class and use MultiplexRouter in all
+// cases. crbug.com/594244
+class Router : public MessageReceiverWithResponder {
+ public:
+  Router(ScopedMessagePipeHandle message_pipe,
+         FilterChain filters,
+         bool expects_sync_requests,
+         scoped_refptr<base::SingleThreadTaskRunner> runner);
+  ~Router() override;
+
+  // Sets the receiver to handle messages read from the message pipe that do
+  // not have the Message::kFlagIsResponse flag set.
+  void set_incoming_receiver(MessageReceiverWithResponderStatus* 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_connection_error_handler(const base::Closure& 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 {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return encountered_error_;
+  }
+
+  // Is the router bound to a MessagePipe handle?
+  bool is_valid() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return connector_.is_valid();
+  }
+
+  // Please note that this method shouldn't be called unless it results from an
+  // explicit request of the user of bindings (e.g., the user sets an
+  // InterfacePtr to null or closes a Binding).
+  void CloseMessagePipe() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    connector_.CloseMessagePipe();
+  }
+
+  ScopedMessagePipeHandle PassMessagePipe() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return connector_.PassMessagePipe();
+  }
+
+  void RaiseError() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    connector_.RaiseError();
+  }
+
+  // MessageReceiver implementation:
+  bool Accept(Message* message) override;
+  bool AcceptWithResponder(Message* message,
+                           MessageReceiver* responder) override;
+
+  // Blocks the current thread until the first incoming method call, i.e.,
+  // either a call to a client method or a callback method, or |deadline|.
+  bool WaitForIncomingMessage(MojoDeadline deadline) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return connector_.WaitForIncomingMessage(deadline);
+  }
+
+  // See Binding for details of pause/resume.
+  void PauseIncomingMethodCallProcessing() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    connector_.PauseIncomingMethodCallProcessing();
+  }
+  void ResumeIncomingMethodCallProcessing() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    connector_.ResumeIncomingMethodCallProcessing();
+  }
+
+  // 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();
+
+  MessagePipeHandle handle() const { return connector_.handle(); }
+
+  // Returns true if this Router has any pending callbacks.
+  bool has_pending_responders() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return !async_responders_.empty() || !sync_responses_.empty();
+  }
+
+ private:
+  // Maps from the id of a response to the MessageReceiver that handles the
+  // response.
+  using AsyncResponderMap =
+      std::map<uint64_t, std::unique_ptr<MessageReceiver>>;
+
+  struct SyncResponseInfo {
+   public:
+    explicit SyncResponseInfo(bool* in_response_received);
+    ~SyncResponseInfo();
+
+    std::unique_ptr<Message> response;
+
+    // Points to a stack-allocated variable.
+    bool* response_received;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(SyncResponseInfo);
+  };
+
+  using SyncResponseMap = std::map<uint64_t, std::unique_ptr<SyncResponseInfo>>;
+
+  class HandleIncomingMessageThunk : public MessageReceiver {
+   public:
+    HandleIncomingMessageThunk(Router* router);
+    ~HandleIncomingMessageThunk() override;
+
+    // MessageReceiver implementation:
+    bool Accept(Message* message) override;
+
+   private:
+    Router* router_;
+  };
+
+  bool HandleIncomingMessage(Message* message);
+  void HandleQueuedMessages();
+  bool HandleMessageInternal(Message* message);
+
+  void OnConnectionError();
+
+  HandleIncomingMessageThunk thunk_;
+  FilterChain filters_;
+  Connector connector_;
+  MessageReceiverWithResponderStatus* incoming_receiver_;
+  AsyncResponderMap async_responders_;
+  SyncResponseMap sync_responses_;
+  uint64_t next_request_id_;
+  bool testing_mode_;
+  std::queue<std::unique_ptr<Message>> pending_messages_;
+  // Whether a task has been posted to trigger processing of
+  // |pending_messages_|.
+  bool pending_task_for_messages_;
+  bool encountered_error_;
+  base::Closure error_handler_;
+  base::ThreadChecker thread_checker_;
+  base::WeakPtrFactory<Router> weak_factory_;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
diff --git a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
new file mode 100644
index 0000000..f54c3f7
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
+
+namespace mojo {
+
+ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle()
+    : ScopedInterfaceEndpointHandle(kInvalidInterfaceId, true, nullptr) {}
+
+ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
+    ScopedInterfaceEndpointHandle&& other)
+    : id_(other.id_), is_local_(other.is_local_) {
+  group_controller_.swap(other.group_controller_);
+  other.id_ = kInvalidInterfaceId;
+}
+
+ScopedInterfaceEndpointHandle::~ScopedInterfaceEndpointHandle() {
+  reset();
+}
+
+ScopedInterfaceEndpointHandle& ScopedInterfaceEndpointHandle::operator=(
+    ScopedInterfaceEndpointHandle&& other) {
+  reset();
+  swap(other);
+
+  return *this;
+}
+
+void ScopedInterfaceEndpointHandle::reset() {
+  if (!IsValidInterfaceId(id_))
+    return;
+
+  group_controller_->CloseEndpointHandle(id_, is_local_);
+
+  id_ = kInvalidInterfaceId;
+  is_local_ = true;
+  group_controller_ = nullptr;
+}
+
+void ScopedInterfaceEndpointHandle::swap(ScopedInterfaceEndpointHandle& other) {
+  using std::swap;
+  swap(other.id_, id_);
+  swap(other.is_local_, is_local_);
+  swap(other.group_controller_, group_controller_);
+}
+
+InterfaceId ScopedInterfaceEndpointHandle::release() {
+  InterfaceId result = id_;
+
+  id_ = kInvalidInterfaceId;
+  is_local_ = true;
+  group_controller_ = nullptr;
+
+  return result;
+}
+
+ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
+    InterfaceId id,
+    bool is_local,
+    scoped_refptr<AssociatedGroupController> group_controller)
+    : id_(id),
+      is_local_(is_local),
+      group_controller_(std::move(group_controller)) {
+  DCHECK(!IsValidInterfaceId(id) || group_controller_);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/serialization.h b/mojo/public/cpp/bindings/lib/serialization.h
new file mode 100644
index 0000000..6d7dd8e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/serialization.h
@@ -0,0 +1,109 @@
+// Copyright 2016 The Chromium Authors. All 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_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_H_
+
+#include <string.h>
+
+#include "mojo/public/cpp/bindings/array_traits_carray.h"
+#include "mojo/public/cpp/bindings/array_traits_standard.h"
+#include "mojo/public/cpp/bindings/array_traits_stl.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/handle_interface_serialization.h"
+#include "mojo/public/cpp/bindings/lib/map_serialization.h"
+#include "mojo/public/cpp/bindings/lib/native_enum_serialization.h"
+#include "mojo/public/cpp/bindings/lib/native_struct_serialization.h"
+#include "mojo/public/cpp/bindings/lib/string_serialization.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/map_traits_standard.h"
+#include "mojo/public/cpp/bindings/map_traits_stl.h"
+#include "mojo/public/cpp/bindings/string_traits_standard.h"
+#include "mojo/public/cpp/bindings/string_traits_stl.h"
+#include "mojo/public/cpp/bindings/string_traits_string16.h"
+#include "mojo/public/cpp/bindings/string_traits_string_piece.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename MojomType, typename DataArrayType, typename UserType>
+DataArrayType StructSerializeImpl(UserType* input) {
+  static_assert(BelongsTo<MojomType, MojomTypeCategory::STRUCT>::value,
+                "Unexpected type.");
+
+  SerializationContext context;
+  size_t size = PrepareToSerialize<MojomType>(*input, &context);
+  DCHECK_EQ(size, Align(size));
+
+  DataArrayType result(size);
+  if (size == 0)
+    return result;
+
+  void* result_buffer = &result.front();
+  // The serialization logic requires that the buffer is 8-byte aligned. If the
+  // result buffer is not properly aligned, we have to do an extra copy. In
+  // practice, this should never happen for mojo::Array (backed by std::vector).
+  bool need_copy = !IsAligned(result_buffer);
+
+  if (need_copy) {
+    // calloc sets the memory to all zero.
+    result_buffer = calloc(size, 1);
+    DCHECK(IsAligned(result_buffer));
+  }
+
+  FixedBuffer buffer;
+  buffer.Initialize(result_buffer, size);
+  typename MojomType::Struct::Data_* data = nullptr;
+  Serialize<MojomType>(*input, &buffer, &data, &context);
+
+  if (need_copy) {
+    memcpy(&result.front(), result_buffer, size);
+    free(result_buffer);
+  }
+
+  return result;
+}
+
+template <typename MojomType, typename DataArrayType, typename UserType>
+bool StructDeserializeImpl(const DataArrayType& input, UserType* output) {
+  static_assert(BelongsTo<MojomType, MojomTypeCategory::STRUCT>::value,
+                "Unexpected type.");
+  using DataType = typename MojomType::Struct::Data_;
+
+  if (input.is_null())
+    return false;
+
+  void* input_buffer =
+      input.empty()
+          ? nullptr
+          : const_cast<void*>(reinterpret_cast<const void*>(&input.front()));
+
+  // Please see comments in StructSerializeImpl.
+  bool need_copy = !IsAligned(input_buffer);
+
+  if (need_copy) {
+    input_buffer = malloc(input.size());
+    DCHECK(IsAligned(input_buffer));
+    memcpy(input_buffer, &input.front(), input.size());
+  }
+
+  ValidationContext validation_context(input_buffer, input.size(), 0);
+  bool result = false;
+  if (DataType::Validate(input_buffer, &validation_context)) {
+    auto data = reinterpret_cast<DataType*>(input_buffer);
+    SerializationContext context;
+    result = Deserialize<MojomType>(data, output, &context);
+  }
+
+  if (need_copy)
+    free(input_buffer);
+
+  return result;
+}
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.cc b/mojo/public/cpp/bindings/lib/serialization_context.cc
new file mode 100644
index 0000000..7fd80be
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/serialization_context.cc
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All 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/serialization_context.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+SerializedHandleVector::SerializedHandleVector() {}
+
+SerializedHandleVector::~SerializedHandleVector() {
+  for (auto handle : handles_) {
+    if (handle.is_valid()) {
+      MojoResult rv = MojoClose(handle.value());
+      DCHECK_EQ(rv, MOJO_RESULT_OK);
+    }
+  }
+}
+
+Handle_Data SerializedHandleVector::AddHandle(mojo::Handle handle) {
+  Handle_Data data;
+  if (!handle.is_valid()) {
+    data.value = kEncodedInvalidHandleValue;
+  } else {
+    DCHECK_LT(handles_.size(), std::numeric_limits<uint32_t>::max());
+    data.value = static_cast<uint32_t>(handles_.size());
+    handles_.push_back(handle);
+  }
+  return data;
+}
+
+mojo::Handle SerializedHandleVector::TakeHandle(
+    const Handle_Data& encoded_handle) {
+  if (!encoded_handle.is_valid())
+    return mojo::Handle();
+  DCHECK_LT(encoded_handle.value, handles_.size());
+  return FetchAndReset(&handles_[encoded_handle.value]);
+}
+
+void SerializedHandleVector::Swap(std::vector<mojo::Handle>* other) {
+  handles_.swap(*other);
+}
+
+SerializationContext::SerializationContext() {}
+
+SerializationContext::SerializationContext(
+    scoped_refptr<AssociatedGroupController> in_group_controller)
+    : group_controller(std::move(in_group_controller)) {}
+
+SerializationContext::~SerializationContext() {
+  DCHECK(!custom_contexts || custom_contexts->empty());
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.h b/mojo/public/cpp/bindings/lib/serialization_context.h
new file mode 100644
index 0000000..64d2a1a
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/serialization_context.h
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All 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_CONTEXT_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_CONTEXT_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <queue>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+class AssociatedGroupController;
+
+namespace internal {
+
+// A container for handles during serialization/deserialization.
+class SerializedHandleVector {
+ public:
+  SerializedHandleVector();
+  ~SerializedHandleVector();
+
+  size_t size() const { return handles_.size(); }
+
+  // Adds a handle to the handle list and returns its index for encoding.
+  Handle_Data AddHandle(mojo::Handle handle);
+
+  // Takes a handle from the list of serialized handle data.
+  mojo::Handle TakeHandle(const Handle_Data& encoded_handle);
+
+  // Takes a handle from the list of serialized handle data and returns it in
+  // |*out_handle| as a specific scoped handle type.
+  template <typename T>
+  ScopedHandleBase<T> TakeHandleAs(const Handle_Data& encoded_handle) {
+    return MakeScopedHandle(T(TakeHandle(encoded_handle).value()));
+  }
+
+  // Swaps all owned handles out with another Handle vector.
+  void Swap(std::vector<mojo::Handle>* other);
+
+ private:
+  // Handles are owned by this object.
+  std::vector<mojo::Handle> handles_;
+
+  DISALLOW_COPY_AND_ASSIGN(SerializedHandleVector);
+};
+
+// Context information for serialization/deserialization routines.
+struct SerializationContext {
+  SerializationContext();
+  explicit SerializationContext(
+      scoped_refptr<AssociatedGroupController> in_group_controller);
+
+  ~SerializationContext();
+
+  // Used to serialize/deserialize associated interface pointers and requests.
+  scoped_refptr<AssociatedGroupController> group_controller;
+
+  // Opaque context pointers returned by StringTraits::SetUpContext().
+  std::unique_ptr<std::queue<void*>> custom_contexts;
+
+  // Stashes handles encoded in a message by index.
+  SerializedHandleVector handles;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_CONTEXT_H_
diff --git a/mojo/public/cpp/bindings/lib/serialization_forward.h b/mojo/public/cpp/bindings/lib/serialization_forward.h
new file mode 100644
index 0000000..5bed126
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/serialization_forward.h
@@ -0,0 +1,122 @@
+// Copyright 2016 The Chromium Authors. All 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_SERIALIZATION_FORWARD_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_FORWARD_H_
+
+#include "base/optional.h"
+#include "mojo/public/cpp/bindings/array_traits.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/map_traits.h"
+#include "mojo/public/cpp/bindings/string_traits.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+// This file is included by serialization implementation files to avoid circular
+// includes.
+// Users of the serialization funtions should include serialization.h (and also
+// wtf_serialization.h if necessary).
+
+namespace mojo {
+namespace internal {
+
+template <typename MojomType, typename MaybeConstUserType>
+struct Serializer;
+
+template <typename T>
+struct IsOptionalWrapper {
+  static const bool value = IsSpecializationOf<
+      base::Optional,
+      typename std::remove_const<
+          typename std::remove_reference<T>::type>::type>::value;
+};
+
+// PrepareToSerialize() must be matched by a Serialize() for the same input
+// later. Moreover, within the same SerializationContext if PrepareToSerialize()
+// is called for |input_1|, ..., |input_n|, Serialize() must be called for
+// those objects in the exact same order.
+template <typename MojomType,
+          typename InputUserType,
+          typename... Args,
+          typename std::enable_if<
+              !IsOptionalWrapper<InputUserType>::value>::type* = nullptr>
+size_t PrepareToSerialize(InputUserType&& input, Args&&... args) {
+  return Serializer<MojomType,
+                    typename std::remove_reference<InputUserType>::type>::
+      PrepareToSerialize(std::forward<InputUserType>(input),
+                         std::forward<Args>(args)...);
+}
+
+template <typename MojomType,
+          typename InputUserType,
+          typename... Args,
+          typename std::enable_if<
+              !IsOptionalWrapper<InputUserType>::value>::type* = nullptr>
+void Serialize(InputUserType&& input, Args&&... args) {
+  Serializer<MojomType, typename std::remove_reference<InputUserType>::type>::
+      Serialize(std::forward<InputUserType>(input),
+                std::forward<Args>(args)...);
+}
+
+template <typename MojomType,
+          typename DataType,
+          typename InputUserType,
+          typename... Args,
+          typename std::enable_if<
+              !IsOptionalWrapper<InputUserType>::value>::type* = nullptr>
+bool Deserialize(DataType&& input, InputUserType* output, Args&&... args) {
+  return Serializer<MojomType, InputUserType>::Deserialize(
+      std::forward<DataType>(input), output, std::forward<Args>(args)...);
+}
+
+// Specialization that unwraps base::Optional<>.
+template <typename MojomType,
+          typename InputUserType,
+          typename... Args,
+          typename std::enable_if<
+              IsOptionalWrapper<InputUserType>::value>::type* = nullptr>
+size_t PrepareToSerialize(InputUserType&& input, Args&&... args) {
+  if (!input)
+    return 0;
+  return PrepareToSerialize<MojomType>(*input, std::forward<Args>(args)...);
+}
+
+template <typename MojomType,
+          typename InputUserType,
+          typename DataType,
+          typename... Args,
+          typename std::enable_if<
+              IsOptionalWrapper<InputUserType>::value>::type* = nullptr>
+void Serialize(InputUserType&& input,
+               Buffer* buffer,
+               DataType** output,
+               Args&&... args) {
+  if (!input) {
+    *output = nullptr;
+    return;
+  }
+  Serialize<MojomType>(*input, buffer, output, std::forward<Args>(args)...);
+}
+
+template <typename MojomType,
+          typename DataType,
+          typename InputUserType,
+          typename... Args,
+          typename std::enable_if<
+              IsOptionalWrapper<InputUserType>::value>::type* = nullptr>
+bool Deserialize(DataType&& input, InputUserType* output, Args&&... args) {
+  if (!input) {
+    *output = base::nullopt;
+    return true;
+  }
+  if (!*output)
+    output->emplace();
+  return Deserialize<MojomType>(std::forward<DataType>(input), &output->value(),
+                                std::forward<Args>(args)...);
+}
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_FORWARD_H_
diff --git a/mojo/public/cpp/bindings/lib/serialization_util.h b/mojo/public/cpp/bindings/lib/serialization_util.h
new file mode 100644
index 0000000..4820a01
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/serialization_util.h
@@ -0,0 +1,213 @@
+// 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_SERIALIZATION_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_UTIL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <queue>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/serialization_context.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+struct HasIsNullMethod {
+  template <typename U>
+  static char Test(decltype(U::IsNull)*);
+  template <typename U>
+  static int Test(...);
+  static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+  EnsureTypeIsComplete<T> check_t_;
+};
+
+template <
+    typename Traits,
+    typename UserType,
+    typename std::enable_if<HasIsNullMethod<Traits>::value>::type* = nullptr>
+bool CallIsNullIfExists(const UserType& input) {
+  return Traits::IsNull(input);
+}
+
+template <
+    typename Traits,
+    typename UserType,
+    typename std::enable_if<!HasIsNullMethod<Traits>::value>::type* = nullptr>
+bool CallIsNullIfExists(const UserType& input) {
+  return false;
+}
+template <typename T>
+struct HasSetToNullMethod {
+  template <typename U>
+  static char Test(decltype(U::SetToNull)*);
+  template <typename U>
+  static int Test(...);
+  static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+  EnsureTypeIsComplete<T> check_t_;
+};
+
+template <
+    typename Traits,
+    typename UserType,
+    typename std::enable_if<HasSetToNullMethod<Traits>::value>::type* = nullptr>
+bool CallSetToNullIfExists(UserType* output) {
+  Traits::SetToNull(output);
+  return true;
+}
+
+template <typename Traits,
+          typename UserType,
+          typename std::enable_if<!HasSetToNullMethod<Traits>::value>::type* =
+              nullptr>
+bool CallSetToNullIfExists(UserType* output) {
+  LOG(ERROR) << "A null value is received. But the Struct/Array/StringTraits "
+             << "class doesn't define a SetToNull() function and therefore is "
+             << "unable to deserialize the value.";
+  return false;
+}
+
+template <typename T>
+struct HasSetUpContextMethod {
+  template <typename U>
+  static char Test(decltype(U::SetUpContext)*);
+  template <typename U>
+  static int Test(...);
+  static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+  EnsureTypeIsComplete<T> check_t_;
+};
+
+template <typename Traits,
+          bool has_context = HasSetUpContextMethod<Traits>::value>
+struct CustomContextHelper;
+
+template <typename Traits>
+struct CustomContextHelper<Traits, true> {
+  template <typename MaybeConstUserType>
+  static void* SetUp(MaybeConstUserType& input, SerializationContext* context) {
+    void* custom_context = Traits::SetUpContext(input);
+    if (!context->custom_contexts)
+      context->custom_contexts.reset(new std::queue<void*>());
+    context->custom_contexts->push(custom_context);
+    return custom_context;
+  }
+
+  static void* GetNext(SerializationContext* context) {
+    void* custom_context = context->custom_contexts->front();
+    context->custom_contexts->pop();
+    return custom_context;
+  }
+
+  template <typename MaybeConstUserType>
+  static void TearDown(MaybeConstUserType& input, void* custom_context) {
+    Traits::TearDownContext(input, custom_context);
+  }
+};
+
+template <typename Traits>
+struct CustomContextHelper<Traits, false> {
+  template <typename MaybeConstUserType>
+  static void* SetUp(MaybeConstUserType& input, SerializationContext* context) {
+    return nullptr;
+  }
+
+  static void* GetNext(SerializationContext* context) { return nullptr; }
+
+  template <typename MaybeConstUserType>
+  static void TearDown(MaybeConstUserType& input, void* custom_context) {
+    DCHECK(!custom_context);
+  }
+};
+
+template <typename ReturnType, typename ParamType, typename InputUserType>
+ReturnType CallWithContext(ReturnType (*f)(ParamType, void*),
+                           InputUserType&& input,
+                           void* context) {
+  return f(std::forward<InputUserType>(input), context);
+}
+
+template <typename ReturnType, typename ParamType, typename InputUserType>
+ReturnType CallWithContext(ReturnType (*f)(ParamType),
+                           InputUserType&& input,
+                           void* context) {
+  return f(std::forward<InputUserType>(input));
+}
+
+template <typename T, typename MaybeConstUserType>
+struct HasGetBeginMethod {
+  template <typename U>
+  static char Test(decltype(U::GetBegin(std::declval<MaybeConstUserType&>()))*);
+  template <typename U>
+  static int Test(...);
+  static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+  EnsureTypeIsComplete<T> check_t_;
+};
+
+template <
+    typename Traits,
+    typename MaybeConstUserType,
+    typename std::enable_if<
+        HasGetBeginMethod<Traits, MaybeConstUserType>::value>::type* = nullptr>
+decltype(Traits::GetBegin(std::declval<MaybeConstUserType&>()))
+CallGetBeginIfExists(MaybeConstUserType& input) {
+  return Traits::GetBegin(input);
+}
+
+template <
+    typename Traits,
+    typename MaybeConstUserType,
+    typename std::enable_if<
+        !HasGetBeginMethod<Traits, MaybeConstUserType>::value>::type* = nullptr>
+size_t CallGetBeginIfExists(MaybeConstUserType& input) {
+  return 0;
+}
+
+template <typename T, typename MaybeConstUserType>
+struct HasGetDataMethod {
+  template <typename U>
+  static char Test(decltype(U::GetData(std::declval<MaybeConstUserType&>()))*);
+  template <typename U>
+  static int Test(...);
+  static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+  EnsureTypeIsComplete<T> check_t_;
+};
+
+template <
+    typename Traits,
+    typename MaybeConstUserType,
+    typename std::enable_if<
+        HasGetDataMethod<Traits, MaybeConstUserType>::value>::type* = nullptr>
+decltype(Traits::GetData(std::declval<MaybeConstUserType&>()))
+CallGetDataIfExists(MaybeConstUserType& input) {
+  return Traits::GetData(input);
+}
+
+template <
+    typename Traits,
+    typename MaybeConstUserType,
+    typename std::enable_if<
+        !HasGetDataMethod<Traits, MaybeConstUserType>::value>::type* = nullptr>
+void* CallGetDataIfExists(MaybeConstUserType& input) {
+  return nullptr;
+}
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_UTIL_H_
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..5e65891
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_serialization.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_BINDINGS_LIB_STRING_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
+
+#include <stddef.h>
+#include <string.h>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/string_traits.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename MaybeConstUserType>
+struct Serializer<String, MaybeConstUserType> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = StringTraits<UserType>;
+
+  static size_t PrepareToSerialize(MaybeConstUserType& input,
+                                   SerializationContext* context) {
+    if (CallIsNullIfExists<Traits>(input))
+      return 0;
+
+    void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
+    return Align(sizeof(String_Data) +
+                 CallWithContext(Traits::GetSize, input, custom_context));
+  }
+
+  static void Serialize(MaybeConstUserType& input,
+                        Buffer* buffer,
+                        String_Data** output,
+                        SerializationContext* context) {
+    if (CallIsNullIfExists<Traits>(input)) {
+      *output = nullptr;
+      return;
+    }
+
+    void* custom_context = CustomContextHelper<Traits>::GetNext(context);
+
+    String_Data* result = String_Data::New(
+        CallWithContext(Traits::GetSize, input, custom_context), buffer);
+    if (result) {
+      memcpy(result->storage(),
+             CallWithContext(Traits::GetData, input, custom_context),
+             CallWithContext(Traits::GetSize, input, custom_context));
+    }
+    *output = result;
+
+    CustomContextHelper<Traits>::TearDown(input, custom_context);
+  }
+
+  static bool Deserialize(String_Data* input,
+                          UserType* output,
+                          SerializationContext* context) {
+    if (!input)
+      return CallSetToNullIfExists<Traits>(output);
+    return Traits::Read(StringDataView(input), output);
+  }
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/string_traits_string16.cc b/mojo/public/cpp/bindings/lib/string_traits_string16.cc
new file mode 100644
index 0000000..95ff6cc
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_traits_string16.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All 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_traits_string16.h"
+
+#include <string>
+
+#include "base/strings/utf_string_conversions.h"
+
+namespace mojo {
+
+// static
+void* StringTraits<base::string16>::SetUpContext(const base::string16& input) {
+  return new std::string(base::UTF16ToUTF8(input));
+}
+
+// static
+void StringTraits<base::string16>::TearDownContext(const base::string16& input,
+                                                   void* context) {
+  delete static_cast<std::string*>(context);
+}
+
+// static
+size_t StringTraits<base::string16>::GetSize(const base::string16& input,
+                                             void* context) {
+  return static_cast<std::string*>(context)->size();
+}
+
+// static
+const char* StringTraits<base::string16>::GetData(const base::string16& input,
+                                                  void* context) {
+  return static_cast<std::string*>(context)->data();
+}
+
+// static
+bool StringTraits<base::string16>::Read(StringDataView input,
+                                        base::string16* output) {
+  return base::UTF8ToUTF16(input.storage(), input.size(), output);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
new file mode 100644
index 0000000..19fa907
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
@@ -0,0 +1,85 @@
+// Copyright 2016 The Chromium Authors. All 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_traits_wtf.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "third_party/WebKit/Source/wtf/text/StringUTF8Adaptor.h"
+
+namespace mojo {
+namespace {
+
+struct UTF8AdaptorInfo {
+  explicit UTF8AdaptorInfo(const WTF::String& input) : utf8_adaptor(input) {
+#if DCHECK_IS_ON()
+    original_size_in_bytes = static_cast<size_t>(input.sizeInBytes());
+#endif
+  }
+
+  ~UTF8AdaptorInfo() {}
+
+  WTF::StringUTF8Adaptor utf8_adaptor;
+
+#if DCHECK_IS_ON()
+  // For sanity check only.
+  size_t original_size_in_bytes;
+#endif
+};
+
+UTF8AdaptorInfo* ToAdaptor(const WTF::String& input, void* context) {
+  UTF8AdaptorInfo* adaptor = static_cast<UTF8AdaptorInfo*>(context);
+
+#if DCHECK_IS_ON()
+  DCHECK_EQ(adaptor->original_size_in_bytes,
+            static_cast<size_t>(input.sizeInBytes()));
+#endif
+  return adaptor;
+}
+
+}  // namespace
+
+// static
+void StringTraits<WTF::String>::SetToNull(WTF::String* output) {
+  if (output->isNull())
+    return;
+
+  WTF::String result;
+  output->swap(result);
+}
+
+// static
+void* StringTraits<WTF::String>::SetUpContext(const WTF::String& input) {
+  return new UTF8AdaptorInfo(input);
+}
+
+// static
+void StringTraits<WTF::String>::TearDownContext(const WTF::String& input,
+                                                void* context) {
+  delete ToAdaptor(input, context);
+}
+
+// static
+size_t StringTraits<WTF::String>::GetSize(const WTF::String& input,
+                                          void* context) {
+  return ToAdaptor(input, context)->utf8_adaptor.length();
+}
+
+// static
+const char* StringTraits<WTF::String>::GetData(const WTF::String& input,
+                                               void* context) {
+  return ToAdaptor(input, context)->utf8_adaptor.data();
+}
+
+// static
+bool StringTraits<WTF::String>::Read(StringDataView input,
+                                     WTF::String* output) {
+  WTF::String result = WTF::String::fromUTF8(input.storage(), input.size());
+  output->swap(result);
+  return true;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
new file mode 100644
index 0000000..3d864af
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
@@ -0,0 +1,91 @@
+// Copyright 2016 The Chromium Authors. All 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/sync_call_restrictions.h"
+
+#if ENABLE_SYNC_CALL_RESTRICTIONS
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "mojo/public/c/system/core.h"
+
+namespace mojo {
+
+namespace {
+
+class SyncCallSettings {
+ public:
+  static SyncCallSettings* current();
+
+  bool allowed() const {
+    return scoped_allow_count_ > 0 || system_defined_value_;
+  }
+
+  void IncreaseScopedAllowCount() { scoped_allow_count_++; }
+  void DecreaseScopedAllowCount() {
+    DCHECK_LT(0u, scoped_allow_count_);
+    scoped_allow_count_--;
+  }
+
+ private:
+  SyncCallSettings();
+  ~SyncCallSettings();
+
+  bool system_defined_value_ = true;
+  size_t scoped_allow_count_ = 0;
+};
+
+base::LazyInstance<base::ThreadLocalPointer<SyncCallSettings>>
+    g_sync_call_settings = LAZY_INSTANCE_INITIALIZER;
+
+// static
+SyncCallSettings* SyncCallSettings::current() {
+  SyncCallSettings* result = g_sync_call_settings.Pointer()->Get();
+  if (!result) {
+    result = new SyncCallSettings();
+    DCHECK_EQ(result, g_sync_call_settings.Pointer()->Get());
+  }
+  return result;
+}
+
+SyncCallSettings::SyncCallSettings() {
+  MojoResult result = MojoGetProperty(MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED,
+                                      &system_defined_value_);
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+
+  DCHECK(!g_sync_call_settings.Pointer()->Get());
+  g_sync_call_settings.Pointer()->Set(this);
+}
+
+SyncCallSettings::~SyncCallSettings() {
+  g_sync_call_settings.Pointer()->Set(nullptr);
+}
+
+}  // namespace
+
+// static
+void SyncCallRestrictions::AssertSyncCallAllowed() {
+  if (!SyncCallSettings::current()->allowed()) {
+      LOG(FATAL) << "Mojo sync calls are not allowed in this process because "
+                 << "they can lead to jank and deadlock. If you must make an "
+                 << "exception, please see "
+                 << "SyncCallRestrictions::ScopedAllowSyncCall and consult "
+                 << "mojo/OWNERS.";
+  }
+}
+
+// static
+void SyncCallRestrictions::IncreaseScopedAllowCount() {
+  SyncCallSettings::current()->IncreaseScopedAllowCount();
+}
+
+// static
+void SyncCallRestrictions::DecreaseScopedAllowCount() {
+  SyncCallSettings::current()->DecreaseScopedAllowCount();
+}
+
+}  // namespace mojo
+
+#endif  // ENABLE_SYNC_CALL_RESTRICTIONS
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
new file mode 100644
index 0000000..f6372d9
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
@@ -0,0 +1,113 @@
+// Copyright 2016 The Chromium Authors. All 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/sync_handle_registry.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/threading/thread_local.h"
+#include "mojo/public/c/system/core.h"
+
+namespace mojo {
+namespace {
+
+base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>
+    g_current_sync_handle_watcher = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// static
+scoped_refptr<SyncHandleRegistry> SyncHandleRegistry::current() {
+  scoped_refptr<SyncHandleRegistry> result(
+      g_current_sync_handle_watcher.Pointer()->Get());
+  if (!result) {
+    result = new SyncHandleRegistry();
+    DCHECK_EQ(result.get(), g_current_sync_handle_watcher.Pointer()->Get());
+  }
+  return result;
+}
+
+bool SyncHandleRegistry::RegisterHandle(const Handle& handle,
+                                        MojoHandleSignals handle_signals,
+                                        const HandleCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (ContainsKey(handles_, handle))
+    return false;
+
+  MojoResult result = MojoAddHandle(wait_set_handle_.get().value(),
+                                    handle.value(), handle_signals);
+  if (result != MOJO_RESULT_OK)
+    return false;
+
+  handles_[handle] = callback;
+  return true;
+}
+
+void SyncHandleRegistry::UnregisterHandle(const Handle& handle) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!ContainsKey(handles_, handle))
+    return;
+
+  MojoResult result =
+      MojoRemoveHandle(wait_set_handle_.get().value(), handle.value());
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+  handles_.erase(handle);
+}
+
+bool SyncHandleRegistry::WatchAllHandles(const bool* should_stop[],
+                                         size_t count) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  MojoResult result;
+  uint32_t num_ready_handles;
+  MojoHandle ready_handle;
+  MojoResult ready_handle_result;
+
+  scoped_refptr<SyncHandleRegistry> preserver(this);
+  while (true) {
+    for (size_t i = 0; i < count; ++i)
+      if (*should_stop[i])
+        return true;
+    do {
+      result = Wait(wait_set_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                    MOJO_DEADLINE_INDEFINITE, nullptr);
+      if (result != MOJO_RESULT_OK)
+        return false;
+
+      // TODO(yzshen): Theoretically it can reduce sync call re-entrancy if we
+      // give priority to the handle that is waiting for sync response.
+      num_ready_handles = 1;
+      result = MojoGetReadyHandles(wait_set_handle_.get().value(),
+                                   &num_ready_handles, &ready_handle,
+                                   &ready_handle_result, nullptr);
+      if (result != MOJO_RESULT_OK && result != MOJO_RESULT_SHOULD_WAIT)
+        return false;
+    } while (result == MOJO_RESULT_SHOULD_WAIT);
+
+    const auto iter = handles_.find(Handle(ready_handle));
+    iter->second.Run(ready_handle_result);
+  };
+
+  return false;
+}
+
+SyncHandleRegistry::SyncHandleRegistry() {
+  MojoHandle handle;
+  MojoResult result = MojoCreateWaitSet(&handle);
+  CHECK_EQ(MOJO_RESULT_OK, result);
+  wait_set_handle_.reset(Handle(handle));
+  CHECK(wait_set_handle_.is_valid());
+
+  DCHECK(!g_current_sync_handle_watcher.Pointer()->Get());
+  g_current_sync_handle_watcher.Pointer()->Set(this);
+}
+
+SyncHandleRegistry::~SyncHandleRegistry() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  g_current_sync_handle_watcher.Pointer()->Set(nullptr);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.h b/mojo/public/cpp/bindings/lib/sync_handle_registry.h
new file mode 100644
index 0000000..d6b8c38
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.h
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All 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_SYNC_HANDLE_REGISTRY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SYNC_HANDLE_REGISTRY_H_
+
+#include <unordered_map>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+// SyncHandleRegistry is a thread-local storage to register handles that want to
+// be watched together.
+//
+// This class is not thread safe.
+class SyncHandleRegistry : public base::RefCounted<SyncHandleRegistry> {
+ public:
+  // Returns a thread-local object.
+  static scoped_refptr<SyncHandleRegistry> current();
+
+  using HandleCallback = base::Callback<void(MojoResult)>;
+  bool RegisterHandle(const Handle& handle,
+                      MojoHandleSignals handle_signals,
+                      const HandleCallback& callback);
+
+  void UnregisterHandle(const Handle& handle);
+
+  // Waits on all the registered handles and runs callbacks synchronously for
+  // those ready handles.
+  // The method:
+  //   - returns true when any element of |should_stop| is set to true;
+  //   - returns false when any error occurs.
+  bool WatchAllHandles(const bool* should_stop[], size_t count);
+
+ private:
+  friend class base::RefCounted<SyncHandleRegistry>;
+
+  struct HandleHasher {
+    size_t operator()(const Handle& handle) const {
+      return std::hash<uint32_t>()(static_cast<uint32_t>(handle.value()));
+    }
+  };
+  using HandleMap = std::unordered_map<Handle, HandleCallback, HandleHasher>;
+
+  SyncHandleRegistry();
+  ~SyncHandleRegistry();
+
+  HandleMap handles_;
+
+  ScopedHandle wait_set_handle_;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncHandleRegistry);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_SYNC_HANDLE_REGISTRY_H_
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_watcher.cc b/mojo/public/cpp/bindings/lib/sync_handle_watcher.cc
new file mode 100644
index 0000000..92b91f4
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/sync_handle_watcher.cc
@@ -0,0 +1,76 @@
+// Copyright 2016 The Chromium Authors. All 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/sync_handle_watcher.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+
+SyncHandleWatcher::SyncHandleWatcher(
+    const Handle& handle,
+    MojoHandleSignals handle_signals,
+    const SyncHandleRegistry::HandleCallback& callback)
+    : handle_(handle),
+      handle_signals_(handle_signals),
+      callback_(callback),
+      registered_(false),
+      register_request_count_(0),
+      registry_(SyncHandleRegistry::current()),
+      destroyed_(new base::RefCountedData<bool>(false)) {}
+
+SyncHandleWatcher::~SyncHandleWatcher() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (registered_)
+    registry_->UnregisterHandle(handle_);
+
+  destroyed_->data = true;
+}
+
+void SyncHandleWatcher::AllowWokenUpBySyncWatchOnSameThread() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  IncrementRegisterCount();
+}
+
+bool SyncHandleWatcher::SyncWatch(const bool* should_stop) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  IncrementRegisterCount();
+  if (!registered_) {
+    DecrementRegisterCount();
+    return false;
+  }
+
+  // This object may be destroyed during the WatchAllHandles() call. So we have
+  // to preserve the boolean that WatchAllHandles uses.
+  auto destroyed = destroyed_;
+  const bool* should_stop_array[] = {should_stop, &destroyed->data};
+  bool result = registry_->WatchAllHandles(should_stop_array, 2);
+
+  // This object has been destroyed.
+  if (destroyed->data)
+    return false;
+
+  DecrementRegisterCount();
+  return result;
+}
+
+void SyncHandleWatcher::IncrementRegisterCount() {
+  register_request_count_++;
+  if (!registered_) {
+    registered_ =
+        registry_->RegisterHandle(handle_, handle_signals_, callback_);
+  }
+}
+
+void SyncHandleWatcher::DecrementRegisterCount() {
+  DCHECK_GT(register_request_count_, 0u);
+
+  register_request_count_--;
+  if (register_request_count_ == 0 && registered_) {
+    registry_->UnregisterHandle(handle_);
+    registered_ = false;
+  }
+}
+
+}  // namespace mojo
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..5151123
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/template_util.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_LIB_TEMPLATE_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
+
+#include <type_traits>
+
+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 <class T>
+struct IsPointer : FalseType {};
+template <class T>
+struct IsPointer<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 std::move() in a
+// destructive way.
+template <typename T>
+struct IsMoveOnlyType {
+  static const bool value = std::is_constructible<T, T&&>::value &&
+                            !std::is_constructible<T, const T&>::value;
+};
+
+// 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 T>
+struct EnsureTypeIsComplete {
+  // sizeof() cannot be applied to incomplete types, this line will fail
+  // compilation if T is forward declaration.
+  using CheckSize = char (*)[sizeof(T)];
+};
+
+template <typename Base, typename Derived>
+struct IsBaseOf {
+ private:
+  static Derived* CreateDerived();
+  static char(&Check(Base*))[1];
+  static char(&Check(...))[2];
+
+  EnsureTypeIsComplete<Base> check_base_;
+  EnsureTypeIsComplete<Derived> check_derived_;
+
+ public:
+  static bool const value = sizeof Check(CreateDerived()) == 1 &&
+                            !IsSame<Base const, void const>::value;
+};
+
+template <class T>
+struct RemovePointer {
+  typedef T type;
+};
+template <class T>
+struct RemovePointer<T*> {
+  typedef T type;
+};
+
+template <template <typename...> class Template, typename T>
+struct IsSpecializationOf : FalseType {};
+
+template <template <typename...> class Template, typename... Args>
+struct IsSpecializationOf<Template, Template<Args...>> : TrueType {};
+
+template <bool B, typename T, typename F>
+struct Conditional {
+  typedef T type;
+};
+
+template <typename T, typename F>
+struct Conditional<false, T, F> {
+  typedef F type;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/union_accessor.h b/mojo/public/cpp/bindings/lib/union_accessor.h
new file mode 100644
index 0000000..821aede
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/union_accessor.h
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_UNION_ACCESSOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_UNION_ACCESSOR_H_
+
+namespace mojo {
+namespace internal {
+
+// When serializing and deserializing Unions, it is necessary to access
+// the private fields and methods of the Union. This allows us to do that
+// without leaking those same fields and methods in the Union interface.
+// All Union wrappers are friends of this class allowing such access.
+template <typename U>
+class UnionAccessor {
+ public:
+  explicit UnionAccessor(U* u) : u_(u) {}
+
+  typename U::Union_* data() { return &(u_->data_); }
+
+  typename U::Tag* tag() { return &(u_->tag_); }
+
+  void SwitchActive(typename U::Tag new_tag) { u_->SwitchActive(new_tag); }
+
+ private:
+  U* u_;
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_UNION_ACCESSOR_H_
diff --git a/mojo/public/cpp/bindings/lib/validate_params.h b/mojo/public/cpp/bindings/lib/validate_params.h
new file mode 100644
index 0000000..c0ee8e0
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validate_params.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_PUBLIC_CPP_BINDINGS_LIB_VALIDATE_PARAMS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATE_PARAMS_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+
+namespace mojo {
+namespace internal {
+
+class ValidationContext;
+
+using ValidateEnumFunc = bool (*)(int32_t, ValidationContext*);
+
+class ContainerValidateParams {
+ public:
+  // Validates a map. A map is validated as a pair of arrays, one for the keys
+  // and one for the values. Both arguments must be non-null.
+  //
+  // ContainerValidateParams takes ownership of |in_key_validate params| and
+  // |in_element_validate params|.
+  ContainerValidateParams(ContainerValidateParams* in_key_validate_params,
+                          ContainerValidateParams* in_element_validate_params)
+      : key_validate_params(in_key_validate_params),
+        element_validate_params(in_element_validate_params) {
+    DCHECK(in_key_validate_params)
+        << "Map validate params require key validate params";
+    DCHECK(in_element_validate_params)
+        << "Map validate params require element validate params";
+  }
+
+  // Validates an array.
+  //
+  // ContainerValidateParams takes ownership of |in_element_validate params|.
+  ContainerValidateParams(uint32_t in_expected_num_elements,
+                          bool in_element_is_nullable,
+                          ContainerValidateParams* in_element_validate_params)
+      : expected_num_elements(in_expected_num_elements),
+        element_is_nullable(in_element_is_nullable),
+        element_validate_params(in_element_validate_params) {}
+
+  // Validates an array of enums.
+  ContainerValidateParams(uint32_t in_expected_num_elements,
+                          ValidateEnumFunc in_validate_enum_func)
+      : expected_num_elements(in_expected_num_elements),
+        validate_enum_func(in_validate_enum_func) {}
+
+  ~ContainerValidateParams() {
+    if (element_validate_params)
+      delete element_validate_params;
+    if (key_validate_params)
+      delete key_validate_params;
+  }
+
+  // If |expected_num_elements| is not 0, the array is expected to have exactly
+  // that number of elements.
+  uint32_t expected_num_elements = 0;
+
+  // Whether the elements are nullable.
+  bool element_is_nullable = false;
+
+  // Validation information for the map key array. May contain other
+  // ArrayValidateParams e.g. if the keys are strings.
+  ContainerValidateParams* key_validate_params = nullptr;
+
+  // For arrays: validation information for elements. It is either a pointer to
+  // another instance of ArrayValidateParams (if elements are arrays or maps),
+  // or nullptr.
+  //
+  // For maps: validation information for the whole value array. May contain
+  // other ArrayValidateParams e.g. if the values are arrays or maps.
+  ContainerValidateParams* element_validate_params = nullptr;
+
+  // Validation function for enum elements.
+  ValidateEnumFunc validate_enum_func = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ContainerValidateParams);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATE_PARAMS_H_
diff --git a/mojo/public/cpp/bindings/lib/validation_context.cc b/mojo/public/cpp/bindings/lib/validation_context.cc
new file mode 100644
index 0000000..a95e07e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_context.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/bindings/lib/validation_context.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+namespace internal {
+
+ValidationContext::ValidationContext(const void* data,
+                                     size_t data_num_bytes,
+                                     size_t num_handles,
+                                     Message* message,
+                                     const base::StringPiece& description)
+    : message_(message),
+      description_(description),
+      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.
+    NOTREACHED();
+    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.
+    NOTREACHED();
+    handle_end_ = 0;
+  }
+}
+
+ValidationContext::~ValidationContext() {
+}
+
+bool ValidationContext::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 ValidationContext::ClaimHandle(const Handle_Data& 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 ValidationContext::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 ValidationContext::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/validation_context.h b/mojo/public/cpp/bindings/lib/validation_context.h
new file mode 100644
index 0000000..6045ca8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_context.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_CPP_BINDINGS_LIB_VALIDATION_CONTEXT_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_CONTEXT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+
+namespace mojo {
+
+class Handle;
+class Message;
+
+namespace internal {
+
+// ValidationContext is used when validating object sizes, pointers and handle
+// indices in the payload of incoming messages.
+class ValidationContext {
+ public:
+  // [data, data + data_num_bytes) specifies the initial valid memory range.
+  // [0, num_handles) specifies the initial valid range of handle indices.
+  //
+  // If provided, |message| and |description| provide additional information
+  // to use when reporting validation errors. In addition if |message| is
+  // provided, the MojoNotifyBadMessage API will be used to notify the system of
+  // such errors.
+  ValidationContext(const void* data,
+                    size_t data_num_bytes,
+                    size_t num_handles,
+                    Message* message = nullptr,
+                    const base::StringPiece& description = "");
+
+  ~ValidationContext();
+
+  // 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_Data& 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;
+
+  Message* message() const { return message_; }
+  const base::StringPiece& description() const { return description_; }
+
+ private:
+  bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const;
+
+  Message* const message_;
+  const base::StringPiece description_;
+
+  // [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_;
+
+  DISALLOW_COPY_AND_ASSIGN(ValidationContext);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_CONTEXT_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..90652de
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_errors.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.
+
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+#include "base/strings/stringprintf.h"
+#include "mojo/public/cpp/bindings/message.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_ILLEGAL_INTERFACE_ID:
+      return "VALIDATION_ERROR_ILLEGAL_INTERFACE_ID";
+    case VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID:
+      return "VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID";
+    case VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS:
+      return "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS";
+    case VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID:
+      return "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID";
+    case VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD:
+      return "VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD";
+    case VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP:
+      return "VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP";
+    case VALIDATION_ERROR_UNKNOWN_UNION_TAG:
+      return "VALIDATION_ERROR_UNKNOWN_UNION_TAG";
+    case VALIDATION_ERROR_UNKNOWN_ENUM_VALUE:
+      return "VALIDATION_ERROR_UNKNOWN_ENUM_VALUE";
+    case VALIDATION_ERROR_DESERIALIZATION_FAILED:
+      return "VALIDATION_ERROR_DESERIALIZATION_FAILED";
+  }
+
+  return "Unknown error";
+}
+
+void ReportValidationError(ValidationContext* context,
+                           ValidationError error,
+                           const char* description) {
+  if (g_validation_error_observer) {
+    g_validation_error_observer->set_last_error(error);
+    return;
+  }
+
+  if (description) {
+    LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) << " ("
+               << description << ")";
+    if (context->message()) {
+      context->message()->NotifyBadMessage(
+          base::StringPrintf("Validation failed for %s [%s (%s)]",
+                             context->description().data(),
+                             ValidationErrorToString(error), description));
+    }
+  } else {
+    LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
+    if (context->message()) {
+      context->message()->NotifyBadMessage(
+          base::StringPrintf("Validation failed for %s [%s]",
+                             context->description().data(),
+                             ValidationErrorToString(error)));
+    }
+  }
+}
+
+ValidationErrorObserverForTesting::ValidationErrorObserverForTesting(
+    const base::Closure& callback)
+    : last_error_(VALIDATION_ERROR_NONE), callback_(callback) {
+  DCHECK(!g_validation_error_observer);
+  g_validation_error_observer = this;
+}
+
+ValidationErrorObserverForTesting::~ValidationErrorObserverForTesting() {
+  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) {
+  DCHECK(!g_serialization_warning_observer);
+  g_serialization_warning_observer = this;
+}
+
+SerializationWarningObserverForTesting::
+    ~SerializationWarningObserverForTesting() {
+  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..ec0aa27
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_errors.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_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.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 struct header.
+  // - |num_bytes| and |version| don't match.
+  // TODO(yzshen): Consider splitting it into two different error codes. Because
+  // the former indicates someone is misbehaving badly whereas the latter could
+  // be due to an inappropriately-modified .mojom file.
+  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,
+  // An interface ID is illegal.
+  VALIDATION_ERROR_ILLEGAL_INTERFACE_ID,
+  // A non-nullable interface ID field is set to invalid.
+  VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
+  // |flags| in the message header is invalid. The flags are either
+  // inconsistent with one another, inconsistent with other parts of the
+  // message, or unexpected for the message receiver.  For example the
+  // receiver is expecting a request message but the flags indicate that
+  // the message is a response message.
+  VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS,
+  // |flags| in the message header indicates that a request ID is required but
+  // there isn't one.
+  VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID,
+  // The |name| field in a message header contains an unexpected value.
+  VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD,
+  // Two parallel arrays which are supposed to represent a map have different
+  // lengths.
+  VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP,
+  // Attempted to deserialize a tagged union with an unknown tag.
+  VALIDATION_ERROR_UNKNOWN_UNION_TAG,
+  // A value of a non-extensible enum type is unknown.
+  VALIDATION_ERROR_UNKNOWN_ENUM_VALUE,
+  // Message deserialization failure, for example due to rejection by custom
+  // validation logic.
+  VALIDATION_ERROR_DESERIALIZATION_FAILED,
+};
+
+const char* ValidationErrorToString(ValidationError error);
+
+void ReportValidationError(ValidationContext* context,
+                           ValidationError error,
+                           const char* description = nullptr);
+
+// Only used by validation tests and when there is only one thread doing message
+// validation.
+class ValidationErrorObserverForTesting {
+ public:
+  explicit ValidationErrorObserverForTesting(const base::Closure& callback);
+  ~ValidationErrorObserverForTesting();
+
+  ValidationError last_error() const { return last_error_; }
+  void set_last_error(ValidationError error) {
+    last_error_ = error;
+    callback_.Run();
+  }
+
+ private:
+  ValidationError last_error_;
+  base::Closure callback_;
+
+  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_;
+
+  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)         \
+  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/lib/validation_util.cc b/mojo/public/cpp/bindings/lib/validation_util.cc
new file mode 100644
index 0000000..9e63521
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_util.cc
@@ -0,0 +1,252 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+bool ValidateEncodedPointer(const uint64_t* offset) {
+  // - Make sure |*offset| is no more than 32-bits.
+  // - Cast |offset| to uintptr_t so overflow behavior is well defined across
+  //   32-bit and 64-bit systems.
+  return *offset <= std::numeric_limits<uint32_t>::max() &&
+         (reinterpret_cast<uintptr_t>(offset) +
+              static_cast<uint32_t>(*offset) >=
+          reinterpret_cast<uintptr_t>(offset));
+}
+
+bool ValidateStructHeaderAndClaimMemory(const void* data,
+                                        ValidationContext* validation_context) {
+  if (!IsAligned(data)) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_MISALIGNED_OBJECT);
+    return false;
+  }
+  if (!validation_context->IsValidRange(data, sizeof(StructHeader))) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+    return false;
+  }
+
+  const StructHeader* header = static_cast<const StructHeader*>(data);
+
+  if (header->num_bytes < sizeof(StructHeader)) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+    return false;
+  }
+
+  if (!validation_context->ClaimMemory(data, header->num_bytes)) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+    return false;
+  }
+
+  return true;
+}
+
+bool ValidateUnionHeaderAndClaimMemory(const void* data,
+                                       bool inlined,
+                                       ValidationContext* validation_context) {
+  if (!IsAligned(data)) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_MISALIGNED_OBJECT);
+    return false;
+  }
+
+  // If the union is inlined in another structure its memory was already
+  // claimed.
+  // This ONLY applies to the union itself, NOT anything which the union points
+  // to.
+  if (!inlined && !validation_context->ClaimMemory(data, kUnionDataSize)) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+    return false;
+  }
+
+  return true;
+}
+
+bool ValidateMessageIsRequestWithoutResponse(
+    const Message* message,
+    ValidationContext* validation_context) {
+  if (message->has_flag(Message::kFlagIsResponse) ||
+      message->has_flag(Message::kFlagExpectsResponse)) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
+    return false;
+  }
+  return true;
+}
+
+bool ValidateMessageIsRequestExpectingResponse(
+    const Message* message,
+    ValidationContext* validation_context) {
+  if (message->has_flag(Message::kFlagIsResponse) ||
+      !message->has_flag(Message::kFlagExpectsResponse)) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
+    return false;
+  }
+  return true;
+}
+
+bool ValidateMessageIsResponse(const Message* message,
+                               ValidationContext* validation_context) {
+  if (message->has_flag(Message::kFlagExpectsResponse) ||
+      !message->has_flag(Message::kFlagIsResponse)) {
+    ReportValidationError(validation_context,
+                          VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
+    return false;
+  }
+  return true;
+}
+
+bool ValidateControlRequest(const Message* message,
+                            ValidationContext* validation_context) {
+  switch (message->header()->name) {
+    case kRunMessageId:
+      return ValidateMessageIsRequestExpectingResponse(message,
+                                                       validation_context) &&
+             ValidateMessagePayload<RunMessageParams_Data>(message,
+                                                           validation_context);
+    case kRunOrClosePipeMessageId:
+      return ValidateMessageIsRequestWithoutResponse(message,
+                                                     validation_context) &&
+          ValidateMessagePayload<RunOrClosePipeMessageParams_Data>(
+              message, validation_context);
+  }
+  return false;
+}
+
+bool ValidateControlResponse(const Message* message,
+                             ValidationContext* validation_context) {
+  if (!ValidateMessageIsResponse(message, validation_context))
+    return false;
+  switch (message->header()->name) {
+    case kRunMessageId:
+      return ValidateMessagePayload<RunResponseMessageParams_Data>(
+          message, validation_context);
+  }
+  return false;
+}
+
+bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input) {
+  return IsValidInterfaceId(input.interface_id);
+}
+
+bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input) {
+  return IsValidInterfaceId(input.interface_id);
+}
+
+bool IsHandleOrInterfaceValid(const Interface_Data& input) {
+  return input.handle.is_valid();
+}
+
+bool IsHandleOrInterfaceValid(const Handle_Data& input) {
+  return input.is_valid();
+}
+
+bool ValidateHandleOrInterfaceNonNullable(
+    const AssociatedInterface_Data& input,
+    const char* error_message,
+    ValidationContext* validation_context) {
+  if (IsHandleOrInterfaceValid(input))
+    return true;
+
+  ReportValidationError(validation_context,
+                        VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
+                        error_message);
+  return false;
+}
+
+bool ValidateHandleOrInterfaceNonNullable(
+    const AssociatedInterfaceRequest_Data& input,
+    const char* error_message,
+    ValidationContext* validation_context) {
+  if (IsHandleOrInterfaceValid(input))
+    return true;
+
+  ReportValidationError(validation_context,
+                        VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
+                        error_message);
+  return false;
+}
+
+bool ValidateHandleOrInterfaceNonNullable(
+    const Interface_Data& input,
+    const char* error_message,
+    ValidationContext* validation_context) {
+  if (IsHandleOrInterfaceValid(input))
+    return true;
+
+  ReportValidationError(validation_context,
+                        VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+                        error_message);
+  return false;
+}
+
+bool ValidateHandleOrInterfaceNonNullable(
+    const Handle_Data& input,
+    const char* error_message,
+    ValidationContext* validation_context) {
+  if (IsHandleOrInterfaceValid(input))
+    return true;
+
+  ReportValidationError(validation_context,
+                        VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+                        error_message);
+  return false;
+}
+
+bool ValidateHandleOrInterface(const AssociatedInterface_Data& input,
+                               ValidationContext* validation_context) {
+  if (!IsMasterInterfaceId(input.interface_id))
+    return true;
+
+  ReportValidationError(validation_context,
+                        VALIDATION_ERROR_ILLEGAL_INTERFACE_ID);
+  return false;
+}
+
+bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input,
+                               ValidationContext* validation_context) {
+  if (!IsMasterInterfaceId(input.interface_id))
+    return true;
+
+  ReportValidationError(validation_context,
+                        VALIDATION_ERROR_ILLEGAL_INTERFACE_ID);
+  return false;
+}
+
+bool ValidateHandleOrInterface(const Interface_Data& input,
+                               ValidationContext* validation_context) {
+  if (validation_context->ClaimHandle(input.handle))
+    return true;
+
+  ReportValidationError(validation_context, VALIDATION_ERROR_ILLEGAL_HANDLE);
+  return false;
+}
+
+bool ValidateHandleOrInterface(const Handle_Data& input,
+                               ValidationContext* validation_context) {
+  if (validation_context->ClaimHandle(input))
+    return true;
+
+  ReportValidationError(validation_context, VALIDATION_ERROR_ILLEGAL_HANDLE);
+  return false;
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/validation_util.h b/mojo/public/cpp/bindings/lib/validation_util.h
new file mode 100644
index 0000000..c883392
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_util.h
@@ -0,0 +1,170 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_UTIL_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace internal {
+
+// Checks whether decoding the pointer will overflow and produce a pointer
+// smaller than |offset|.
+bool ValidateEncodedPointer(const uint64_t* offset);
+
+template <typename T>
+bool ValidatePointer(const Pointer<T>& input,
+                     ValidationContext* validation_context) {
+  bool result = ValidateEncodedPointer(&input.offset);
+  if (!result)
+    ReportValidationError(validation_context, VALIDATION_ERROR_ILLEGAL_POINTER);
+
+  return result;
+}
+
+// Validates that |data| contains a valid struct header, in terms of alignment
+// and size (i.e., the |num_bytes| field of the header is sufficient for storing
+// the header itself). Besides, it checks that the memory range
+// [data, data + num_bytes) is not marked as occupied by other objects in
+// |validation_context|. On success, the memory range is marked as occupied.
+// Note: Does not verify |version| or that |num_bytes| is correct for the
+// claimed version.
+bool ValidateStructHeaderAndClaimMemory(const void* data,
+                                        ValidationContext* validation_context);
+
+// Validates that |data| contains a valid union header, in terms of alignment
+// and size. If not inlined, it checks that the memory range
+// [data, data + num_bytes) is not marked as occupied by other objects in
+// |validation_context|. On success, the memory range is marked as occupied.
+bool ValidateUnionHeaderAndClaimMemory(const void* data,
+                                       bool inlined,
+                                       ValidationContext* validation_context);
+
+// Validates that the message is a request which doesn't expect a response.
+bool ValidateMessageIsRequestWithoutResponse(
+    const Message* message,
+    ValidationContext* validation_context);
+
+// Validates that the message is a request expecting a response.
+bool ValidateMessageIsRequestExpectingResponse(
+    const Message* message,
+    ValidationContext* validation_context);
+
+// Validates that the message is a response.
+bool ValidateMessageIsResponse(const Message* message,
+                               ValidationContext* validation_context);
+
+// Validates that the message payload is a valid struct of type ParamsType.
+template <typename ParamsType>
+bool ValidateMessagePayload(const Message* message,
+                            ValidationContext* validation_context) {
+  return ParamsType::Validate(message->payload(), validation_context);
+}
+
+// The following methods validate control messages defined in
+// interface_control_messages.mojom.
+bool ValidateControlRequest(const Message* message,
+                            ValidationContext* validation_context);
+bool ValidateControlResponse(const Message* message,
+                             ValidationContext* validation_context);
+
+// The following Validate.*NonNullable() functions validate that the given
+// |input| is not null/invalid.
+template <typename T>
+bool ValidatePointerNonNullable(const T& input,
+                                const char* error_message,
+                                ValidationContext* validation_context) {
+  if (input.offset)
+    return true;
+
+  ReportValidationError(validation_context,
+                        VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+                        error_message);
+  return false;
+}
+
+template <typename T>
+bool ValidateInlinedUnionNonNullable(const T& input,
+                                     const char* error_message,
+                                     ValidationContext* validation_context) {
+  if (!input.is_null())
+    return true;
+
+  ReportValidationError(validation_context,
+                        VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+                        error_message);
+  return false;
+}
+
+bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input);
+bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input);
+bool IsHandleOrInterfaceValid(const Interface_Data& input);
+bool IsHandleOrInterfaceValid(const Handle_Data& input);
+
+bool ValidateHandleOrInterfaceNonNullable(
+    const AssociatedInterface_Data& input,
+    const char* error_message,
+    ValidationContext* validation_context);
+bool ValidateHandleOrInterfaceNonNullable(
+    const AssociatedInterfaceRequest_Data& input,
+    const char* error_message,
+    ValidationContext* validation_context);
+bool ValidateHandleOrInterfaceNonNullable(
+    const Interface_Data& input,
+    const char* error_message,
+    ValidationContext* validation_context);
+bool ValidateHandleOrInterfaceNonNullable(
+    const Handle_Data& input,
+    const char* error_message,
+    ValidationContext* validation_context);
+
+template <typename T>
+bool ValidateContainer(const Pointer<T>& input,
+                       ValidationContext* validation_context,
+                       const ContainerValidateParams* validate_params) {
+  return ValidatePointer(input, validation_context) &&
+         T::Validate(input.Get(), validation_context, validate_params);
+}
+
+template <typename T>
+bool ValidateStruct(const Pointer<T>& input,
+                    ValidationContext* validation_context) {
+  return ValidatePointer(input, validation_context) &&
+         T::Validate(input.Get(), validation_context);
+}
+
+template <typename T>
+bool ValidateInlinedUnion(const T& input,
+                          ValidationContext* validation_context) {
+  return T::Validate(&input, validation_context, true);
+}
+
+template <typename T>
+bool ValidateNonInlinedUnion(const Pointer<T>& input,
+                             ValidationContext* validation_context) {
+  return ValidatePointer(input, validation_context) &&
+         T::Validate(input.Get(), validation_context, false);
+}
+
+bool ValidateHandleOrInterface(const AssociatedInterface_Data& input,
+                               ValidationContext* validation_context);
+bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input,
+                               ValidationContext* validation_context);
+bool ValidateHandleOrInterface(const Interface_Data& input,
+                               ValidationContext* validation_context);
+bool ValidateHandleOrInterface(const Handle_Data& input,
+                               ValidationContext* validation_context);
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
new file mode 100644
index 0000000..edbf27b
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
@@ -0,0 +1,76 @@
+// Copyright 2016 The Chromium Authors. All 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_WTF_CLONE_EQUALS_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_CLONE_EQUALS_UTIL_H_
+
+#include <type_traits>
+
+#include "mojo/public/cpp/bindings/lib/clone_equals_util.h"
+#include "third_party/WebKit/Source/wtf/HashMap.h"
+#include "third_party/WebKit/Source/wtf/Optional.h"
+#include "third_party/WebKit/Source/wtf/Vector.h"
+#include "third_party/WebKit/Source/wtf/text/WTFString.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+struct CloneTraits<WTF::Vector<T>, false> {
+  static WTF::Vector<T> Clone(const WTF::Vector<T>& input) {
+    WTF::Vector<T> result;
+    result.reserveCapacity(input.size());
+    for (const auto& element : input)
+      result.append(internal::Clone(element));
+
+    return result;
+  }
+};
+
+template <typename K, typename V>
+struct CloneTraits<WTF::HashMap<K, V>, false> {
+  static WTF::HashMap<K, V> Clone(const WTF::HashMap<K, V>& input) {
+    WTF::HashMap<K, V> result;
+    auto input_end = input.end();
+    for (auto it = input.begin(); it != input_end; ++it)
+      result.add(internal::Clone(it->key), internal::Clone(it->value));
+    return result;
+  }
+};
+
+template <typename T>
+struct EqualsTraits<WTF::Vector<T>, false> {
+  static bool Equals(const WTF::Vector<T>& a, const WTF::Vector<T>& b) {
+    if (a.size() != b.size())
+      return false;
+    for (size_t i = 0; i < a.size(); ++i) {
+      if (!internal::Equals(a[i], b[i]))
+        return false;
+    }
+    return true;
+  }
+};
+
+template <typename K, typename V>
+struct EqualsTraits<WTF::HashMap<K, V>, false> {
+  static bool Equals(const WTF::HashMap<K, V>& a, const WTF::HashMap<K, V>& b) {
+    if (a.size() != b.size())
+      return false;
+
+    auto a_end = a.end();
+    auto b_end = b.end();
+
+    for (auto iter = a.begin(); iter != a_end; ++iter) {
+      auto b_iter = b.find(iter->key);
+      if (b_iter == b_end || !internal::Equals(iter->value, b_iter->value))
+        return false;
+    }
+    return true;
+  }
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_CLONE_EQUALS_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/wtf_serialization.h b/mojo/public/cpp/bindings/lib/wtf_serialization.h
new file mode 100644
index 0000000..132e19c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/wtf_serialization.h
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All 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_WTF_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_
+
+#include "mojo/public/cpp/bindings/array_traits_wtf.h"
+#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
+#include "mojo/public/cpp/bindings/map_traits_wtf.h"
+#include "mojo/public/cpp/bindings/map_traits_wtf_hash_map.h"
+#include "mojo/public/cpp/bindings/string_traits_wtf.h"
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/map.h b/mojo/public/cpp/bindings/map.h
new file mode 100644
index 0000000..d4c7952
--- /dev/null
+++ b/mojo/public/cpp/bindings/map.h
@@ -0,0 +1,304 @@
+// Copyright 2014 The Chromium Authors. All 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_MAP_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_H_
+
+#include <stddef.h>
+#include <map>
+#include <unordered_map>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+// A move-only map that can handle move-only values. Map has the following
+// characteristics:
+//   - The map itself can be null, and this is distinct from empty.
+//   - Keys must not be move-only.
+//   - The Key-type's "<" operator is used to sort the entries, and also is
+//     used to determine equality of the key values.
+//   - There can only be one entry per unique key.
+//   - Values of move-only types will be moved into the Map when they are added
+//     using the insert() method.
+template <typename K, typename V>
+class Map {
+ public:
+  using Key = K;
+  using Value = V;
+
+  // Map keys cannot be move only classes.
+  static_assert(!internal::IsMoveOnlyType<Key>::value,
+                "Map keys cannot be move only types.");
+
+  using Iterator = typename std::map<Key, Value>::iterator;
+  using ConstIterator = typename std::map<Key, Value>::const_iterator;
+
+  // Constructs an empty map.
+  Map() : is_null_(false) {}
+  // Constructs a null map.
+  Map(std::nullptr_t null_pointer) : is_null_(true) {}
+
+  // Constructs a non-null Map containing the specified |keys| mapped to the
+  // corresponding |values|.
+  Map(mojo::Array<Key> keys, mojo::Array<Value> values) : is_null_(false) {
+    DCHECK(keys.size() == values.size());
+    for (size_t i = 0; i < keys.size(); ++i)
+      map_.insert(std::make_pair(keys[i], std::move(values[i])));
+  }
+
+  ~Map() {}
+
+  Map(std::map<Key, Value>&& other) : map_(std::move(other)), is_null_(false) {}
+  Map(Map&& other) : is_null_(true) { Take(&other); }
+
+  Map& operator=(std::map<Key, Value>&& other) {
+    is_null_ = false;
+    map_ = std::move(other);
+    return *this;
+  }
+  Map& operator=(Map&& other) {
+    Take(&other);
+    return *this;
+  }
+
+  Map& operator=(std::nullptr_t null_pointer) {
+    is_null_ = true;
+    map_.clear();
+    return *this;
+  }
+
+  // Copies the contents of some other type of map into a new Map using a
+  // TypeConverter. A TypeConverter for std::map to Map is defined below.
+  template <typename U>
+  static Map From(const U& other) {
+    return TypeConverter<Map, U>::Convert(other);
+  }
+
+  // Copies the contents of the Map into some other type of map. A TypeConverter
+  // for Map to std::map is defined below.
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, Map>::Convert(*this);
+  }
+
+  // Indicates whether the map is null (which is distinct from empty).
+  bool is_null() const { return is_null_; }
+
+  // Indicates whether the map is empty (which is distinct from null).
+  bool empty() const { return map_.empty() && !is_null_; }
+
+  // Indicates the number of keys in the map, which will be zero if the map is
+  // null.
+  size_t size() const { return map_.size(); }
+
+  // Inserts a key-value pair into the map. Like std::map, this does not insert
+  // |value| if |key| is already a member of the map.
+  void insert(const Key& key, const Value& value) {
+    is_null_ = false;
+    map_.insert(std::make_pair(key, value));
+  }
+  void insert(const Key& key, Value&& value) {
+    is_null_ = false;
+    map_.insert(std::make_pair(key, std::move(value)));
+  }
+
+  // Returns a reference to the value associated with the specified key,
+  // crashing the process if the key is not present in the map.
+  Value& at(const Key& key) { return map_.at(key); }
+  const Value& at(const Key& key) const { return map_.at(key); }
+
+  // Returns a reference to the value associated with the specified key,
+  // creating a new entry if the key is not already present in the map. A
+  // newly-created value will be value-initialized (meaning that it will be
+  // initialized by the default constructor of the value type, if any, or else
+  // will be zero-initialized).
+  Value& operator[](const Key& key) {
+    is_null_ = false;
+    return map_[key];
+  }
+
+  // Sets the map to empty (even if previously it was null).
+  void SetToEmpty() {
+    is_null_ = false;
+    map_.clear();
+  }
+
+  // Returns a const reference to the std::map managed by this class. If this
+  // object is null, the return value will be an empty map.
+  const std::map<Key, Value>& storage() const { return map_; }
+
+  // Passes the underlying storage and resets this map to null.
+  std::map<Key, Value> PassStorage() {
+    is_null_ = true;
+    return std::move(map_);
+  }
+
+  operator const std::map<Key, Value>&() const { return map_; }
+
+  // Swaps the contents of this Map with another Map of the same type (including
+  // nullness).
+  void Swap(Map<Key, Value>* other) {
+    std::swap(is_null_, other->is_null_);
+    map_.swap(other->map_);
+  }
+
+  // Swaps the contents of this Map with an std::map containing keys and values
+  // of the same type. Since std::map cannot represent the null state, the
+  // std::map will be empty if Map is null. The Map will always be left in a
+  // non-null state.
+  void Swap(std::map<Key, Value>* other) {
+    is_null_ = false;
+    map_.swap(*other);
+  }
+
+  // Removes all contents from the Map and places them into parallel key/value
+  // arrays. Each key will be copied from the source to the destination, and
+  // values will be copied unless their type is designated move-only, in which
+  // case they will be moved. Either way, the Map will be left in a null state.
+  void DecomposeMapTo(mojo::Array<Key>* keys, mojo::Array<Value>* values) {
+    std::vector<Key> key_vector;
+    key_vector.reserve(map_.size());
+    std::vector<Value> value_vector;
+    value_vector.reserve(map_.size());
+
+    for (auto& entry : map_) {
+      key_vector.push_back(entry.first);
+      value_vector.push_back(std::move(entry.second));
+    }
+
+    map_.clear();
+    is_null_ = true;
+
+    keys->Swap(&key_vector);
+    values->Swap(&value_vector);
+  }
+
+  // Returns a new Map that contains a copy of the contents of this map. If the
+  // key/value type defines a Clone() method, it will be used; otherwise copy
+  // constructor/assignment will be used.
+  //
+  // Please note that calling this method will fail compilation if the key/value
+  // type cannot be cloned (which usually means that it is a Mojo handle type or
+  // a type containing Mojo handles).
+  Map Clone() const {
+    Map result;
+    result.is_null_ = is_null_;
+    for (auto it = map_.begin(); it != map_.end(); ++it) {
+      result.map_.insert(std::make_pair(internal::Clone(it->first),
+                                        internal::Clone(it->second)));
+    }
+    return result;
+  }
+
+  // Indicates whether the contents of this map are equal to those of another
+  // Map (including nullness). If the key/value type defines an Equals() method,
+  // it will be used; otherwise == operator will be used.
+  bool Equals(const Map& other) const {
+    if (is_null() != other.is_null())
+      return false;
+    if (size() != other.size())
+      return false;
+    auto i = begin();
+    auto j = other.begin();
+    while (i != end()) {
+      if (!internal::Equals(i->first, j->first))
+        return false;
+      if (!internal::Equals(i->second, j->second))
+        return false;
+      ++i;
+      ++j;
+    }
+    return true;
+  }
+
+  // Provide read-only iteration over map members in a way similar to STL
+  // collections.
+  ConstIterator begin() const { return map_.begin(); }
+  Iterator begin() { return map_.begin(); }
+
+  ConstIterator end() const { return map_.end(); }
+  Iterator end() { return map_.end(); }
+
+  // Returns the iterator pointing to the entry for |key|, if present, or else
+  // returns end().
+  ConstIterator find(const Key& key) const { return map_.find(key); }
+  Iterator find(const Key& key) { return map_.find(key); }
+
+ private:
+  typedef std::map<Key, Value> Map::*Testable;
+
+ public:
+  // The Map may be used in boolean expressions to determine if it is non-null,
+  // but is not implicitly convertible to an actual bool value (which would be
+  // dangerous).
+  operator Testable() const { return is_null_ ? 0 : &Map::map_; }
+
+ private:
+  // Forbid the == and != operators explicitly, otherwise Map will be converted
+  // to Testable to do == or != comparison.
+  template <typename T, typename U>
+  bool operator==(const Map<T, U>& other) const = delete;
+  template <typename T, typename U>
+  bool operator!=(const Map<T, U>& other) const = delete;
+
+  void Take(Map* other) {
+    operator=(nullptr);
+    Swap(other);
+  }
+
+  std::map<Key, Value> map_;
+  bool is_null_;
+
+  DISALLOW_COPY_AND_ASSIGN(Map);
+};
+
+// Copies the contents of an std::map to a new Map, optionally changing the
+// types of the keys and values along the way using TypeConverter.
+template <typename MojoKey,
+          typename MojoValue,
+          typename STLKey,
+          typename STLValue>
+struct TypeConverter<Map<MojoKey, MojoValue>, std::map<STLKey, STLValue>> {
+  static Map<MojoKey, MojoValue> Convert(
+      const std::map<STLKey, STLValue>& input) {
+    Map<MojoKey, MojoValue> result;
+    for (auto& pair : input) {
+      result.insert(TypeConverter<MojoKey, STLKey>::Convert(pair.first),
+                    TypeConverter<MojoValue, STLValue>::Convert(pair.second));
+    }
+    return result;
+  }
+};
+
+// Copies the contents of a Map to an std::map, optionally changing the types of
+// the keys and values along the way using TypeConverter.
+template <typename MojoKey,
+          typename MojoValue,
+          typename STLKey,
+          typename STLValue>
+struct TypeConverter<std::map<STLKey, STLValue>, Map<MojoKey, MojoValue>> {
+  static std::map<STLKey, STLValue> Convert(
+      const Map<MojoKey, MojoValue>& input) {
+    std::map<STLKey, STLValue> result;
+    if (!input.is_null()) {
+      for (auto it = input.begin(); it != input.end(); ++it) {
+        result.insert(std::make_pair(
+            TypeConverter<STLKey, MojoKey>::Convert(it->first),
+            TypeConverter<STLValue, MojoValue>::Convert(it->second)));
+      }
+    }
+    return result;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_H_
diff --git a/mojo/public/cpp/bindings/map_traits.h b/mojo/public/cpp/bindings/map_traits.h
new file mode 100644
index 0000000..01dd66d
--- /dev/null
+++ b/mojo/public/cpp/bindings/map_traits.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All 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_MAP_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_H_
+
+namespace mojo {
+
+// This must be specialized for any type |T| to be serialized/deserialized as
+// a mojom map.
+//
+// Usually you would like to do a partial specialization for a map template.
+// Imagine you want to specialize it for CustomMap<>, you need to implement:
+//
+//   template <typename K, typename V>
+//   struct MapTraits<CustomMap<K, V>> {
+//     using Key = K;
+//     using Value = V;
+//
+//     // These two methods are optional. Please see comments in struct_traits.h
+//     static bool IsNull(const CustomMap<K, V>& input);
+//     static void SetToNull(CustomMap<K, V>* output);
+//
+//     static size_t GetSize(const CustomMap<K, V>& input);
+//
+//     static CustomConstIterator GetBegin(const CustomMap<K, V>& input);
+//     static CustomIterator GetBegin(CustomMap<K, V>& input);
+//
+//     static void AdvanceIterator(CustomConstIterator& iterator);
+//     static void AdvanceIterator(CustomIterator& iterator);
+//
+//     static const K& GetKey(CustomIterator& iterator);
+//     static const K& GetKey(CustomConstIterator& iterator);
+//
+//     static V& GetValue(CustomIterator& iterator);
+//     static const V& GetValue(CustomConstIterator& iterator);
+//
+//     // Returning false results in deserialization failure and causes the
+//     // message pipe receiving it to be disconnected.
+//     static bool Insert(CustomMap<K, V>& input,
+//                        const K& key,
+//                        V&& value);
+//     static bool Insert(CustomMap<K, V>& input,
+//                        const K& key,
+//                        const V& value);
+//
+//     static void SetToEmpty(CustomMap<K, V>* output);
+//   };
+//
+template <typename T>
+struct MapTraits;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/map_traits_standard.h b/mojo/public/cpp/bindings/map_traits_standard.h
new file mode 100644
index 0000000..0c76890
--- /dev/null
+++ b/mojo/public/cpp/bindings/map_traits_standard.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All 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_MAP_TRAITS_STANDARD_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_
+
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/map_traits.h"
+
+namespace mojo {
+
+template <typename K, typename V>
+struct MapTraits<Map<K, V>> {
+  using Key = K;
+  using Value = V;
+  using Iterator = typename Map<K, V>::Iterator;
+  using ConstIterator = typename Map<K, V>::ConstIterator;
+
+  static bool IsNull(const Map<K, V>& input) { return input.is_null(); }
+  static void SetToNull(Map<K, V>* output) { *output = nullptr; }
+
+  static size_t GetSize(const Map<K, V>& input) { return input.size(); }
+
+  static ConstIterator GetBegin(const Map<K, V>& input) {
+    return input.begin();
+  }
+  static Iterator GetBegin(Map<K, V>& input) { return input.begin(); }
+
+  static void AdvanceIterator(ConstIterator& iterator) { iterator++; }
+  static void AdvanceIterator(Iterator& iterator) { iterator++; }
+
+  static const K& GetKey(Iterator& iterator) { return iterator->first; }
+  static const K& GetKey(ConstIterator& iterator) { return iterator->first; }
+
+  static V& GetValue(Iterator& iterator) { return iterator->second; }
+  static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
+
+  static bool Insert(Map<K, V>& input, const K& key, V&& value) {
+    input.insert(key, std::forward<V>(value));
+    return true;
+  }
+  static bool Insert(Map<K, V>& input, const K& key, const V& value) {
+    input.insert(key, value);
+    return true;
+  }
+
+  static void SetToEmpty(Map<K, V>* output) { output->SetToEmpty(); }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_
diff --git a/mojo/public/cpp/bindings/map_traits_stl.h b/mojo/public/cpp/bindings/map_traits_stl.h
new file mode 100644
index 0000000..ff79a20
--- /dev/null
+++ b/mojo/public/cpp/bindings/map_traits_stl.h
@@ -0,0 +1,113 @@
+// Copyright 2016 The Chromium Authors. All 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_MAP_TRAITS_STL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STL_H_
+
+#include <map>
+#include <unordered_map>
+
+#include "mojo/public/cpp/bindings/map_traits.h"
+
+namespace mojo {
+
+template <typename K, typename V>
+struct MapTraits<std::map<K, V>> {
+  using Key = K;
+  using Value = V;
+  using Iterator = typename std::map<K, V>::iterator;
+  using ConstIterator = typename std::map<K, V>::const_iterator;
+
+  static bool IsNull(const std::map<K, V>& input) {
+    // std::map<> is always converted to non-null mojom map.
+    return false;
+  }
+
+  static void SetToNull(std::map<K, V>* output) {
+    // std::map<> doesn't support null state. Set it to empty instead.
+    output->clear();
+  }
+
+  static size_t GetSize(const std::map<K, V>& input) { return input.size(); }
+
+  static ConstIterator GetBegin(const std::map<K, V>& input) {
+    return input.begin();
+  }
+  static Iterator GetBegin(std::map<K, V>& input) { return input.begin(); }
+
+  static void AdvanceIterator(ConstIterator& iterator) { iterator++; }
+  static void AdvanceIterator(Iterator& iterator) { iterator++; }
+
+  static const K& GetKey(Iterator& iterator) { return iterator->first; }
+  static const K& GetKey(ConstIterator& iterator) { return iterator->first; }
+
+  static V& GetValue(Iterator& iterator) { return iterator->second; }
+  static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
+
+  static bool Insert(std::map<K, V>& input, const K& key, V&& value) {
+    input.insert(std::make_pair(key, std::forward<V>(value)));
+    return true;
+  }
+  static bool Insert(std::map<K, V>& input, const K& key, const V& value) {
+    input.insert(std::make_pair(key, value));
+    return true;
+  }
+
+  static void SetToEmpty(std::map<K, V>* output) { output->clear(); }
+};
+
+template <typename K, typename V>
+struct MapTraits<std::unordered_map<K, V>> {
+  using Key = K;
+  using Value = V;
+  using Iterator = typename std::unordered_map<K, V>::iterator;
+  using ConstIterator = typename std::unordered_map<K, V>::const_iterator;
+
+  static bool IsNull(const std::unordered_map<K, V>& input) {
+    // std::unordered_map<> is always converted to non-null mojom map.
+    return false;
+  }
+
+  static void SetToNull(std::unordered_map<K, V>* output) {
+    // std::unordered_map<> doesn't support null state. Set it to empty instead.
+    output->clear();
+  }
+
+  static size_t GetSize(const std::unordered_map<K, V>& input) {
+    return input.size();
+  }
+
+  static ConstIterator GetBegin(const std::unordered_map<K, V>& input) {
+    return input.begin();
+  }
+  static Iterator GetBegin(std::unordered_map<K, V>& input) {
+    return input.begin();
+  }
+
+  static void AdvanceIterator(ConstIterator& iterator) { iterator++; }
+  static void AdvanceIterator(Iterator& iterator) { iterator++; }
+
+  static const K& GetKey(Iterator& iterator) { return iterator->first; }
+  static const K& GetKey(ConstIterator& iterator) { return iterator->first; }
+
+  static V& GetValue(Iterator& iterator) { return iterator->second; }
+  static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
+
+  static bool Insert(std::unordered_map<K, V>& input, const K& key, V&& value) {
+    input.insert(std::make_pair(key, std::forward<V>(value)));
+    return true;
+  }
+  static bool Insert(std::unordered_map<K, V>& input,
+                     const K& key,
+                     const V& value) {
+    input.insert(std::make_pair(key, value));
+    return true;
+  }
+
+  static void SetToEmpty(std::unordered_map<K, V>* output) { output->clear(); }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STL_H_
diff --git a/mojo/public/cpp/bindings/map_traits_wtf.h b/mojo/public/cpp/bindings/map_traits_wtf.h
new file mode 100644
index 0000000..805e4c9
--- /dev/null
+++ b/mojo/public/cpp/bindings/map_traits_wtf.h
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All 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_MAP_TRAITS_WTF_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/map_traits.h"
+#include "mojo/public/cpp/bindings/wtf_map.h"
+
+namespace mojo {
+
+template <typename K, typename V>
+struct MapTraits<WTFMap<K, V>> {
+  using Key = K;
+  using Value = V;
+  using Iterator = typename WTFMap<K, V>::Iterator;
+  using ConstIterator = typename WTFMap<K, V>::ConstIterator;
+
+  static bool IsNull(const WTFMap<K, V>& input) { return input.is_null(); }
+  static void SetToNull(WTFMap<K, V>* output) { *output = nullptr; }
+
+  static size_t GetSize(const WTFMap<K, V>& input) { return input.size(); }
+
+  static ConstIterator GetBegin(const WTFMap<K, V>& input) {
+    return input.begin();
+  }
+  static Iterator GetBegin(WTFMap<K, V>& input) { return input.begin(); }
+
+  static void AdvanceIterator(ConstIterator& iterator) { ++iterator; }
+  static void AdvanceIterator(Iterator& iterator) { ++iterator; }
+
+  static const K& GetKey(Iterator& iterator) { return iterator->key; }
+  static const K& GetKey(ConstIterator& iterator) { return iterator->key; }
+
+  static V& GetValue(Iterator& iterator) { return iterator->value; }
+  static const V& GetValue(ConstIterator& iterator) { return iterator->value; }
+
+  static bool Insert(WTFMap<K, V>& input, const K& key, V&& value) {
+    if (!WTFMap<K, V>::IsValidKey(key)) {
+      LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
+      return false;
+    }
+    input.insert(key, std::forward<V>(value));
+    return true;
+  }
+  static bool Insert(WTFMap<K, V>& input, const K& key, const V& value) {
+    if (!WTFMap<K, V>::IsValidKey(key)) {
+      LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
+      return false;
+    }
+    input.insert(key, value);
+    return true;
+  }
+
+  static void SetToEmpty(WTFMap<K, V>* output) { output->SetToEmpty(); }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
diff --git a/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h b/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h
new file mode 100644
index 0000000..4392201
--- /dev/null
+++ b/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All 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_MAP_TRAITS_WTF_HASH_MAP_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_HASH_MAP_H_
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/map_traits.h"
+#include "third_party/WebKit/Source/wtf/HashMap.h"
+
+namespace mojo {
+
+template <typename K, typename V>
+struct MapTraits<WTF::HashMap<K, V>> {
+  using Key = K;
+  using Value = V;
+  using Iterator = typename WTF::HashMap<K, V>::iterator;
+  using ConstIterator = typename WTF::HashMap<K, V>::const_iterator;
+
+  static bool IsNull(const WTF::HashMap<K, V>& input) {
+    // WTF::HashMap<> is always converted to non-null mojom map.
+    return false;
+  }
+
+  static void SetToNull(WTF::HashMap<K, V>* output) {
+    // WTF::HashMap<> doesn't support null state. Set it to empty instead.
+    output->clear();
+  }
+
+  static size_t GetSize(const WTF::HashMap<K, V>& input) {
+    return input.size();
+  }
+
+  static ConstIterator GetBegin(const WTF::HashMap<K, V>& input) {
+    return input.begin();
+  }
+  static Iterator GetBegin(WTF::HashMap<K, V>& input) { return input.begin(); }
+
+  static void AdvanceIterator(ConstIterator& iterator) { ++iterator; }
+  static void AdvanceIterator(Iterator& iterator) { ++iterator; }
+
+  static const K& GetKey(Iterator& iterator) { return iterator->key; }
+  static const K& GetKey(ConstIterator& iterator) { return iterator->key; }
+
+  static V& GetValue(Iterator& iterator) { return iterator->value; }
+  static const V& GetValue(ConstIterator& iterator) { return iterator->value; }
+
+  static bool Insert(WTF::HashMap<K, V>& input, const K& key, V&& value) {
+    if (!WTF::HashMap<K, V>::isValidKey(key)) {
+      LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
+      return false;
+    }
+    input.add(key, std::forward<V>(value));
+    return true;
+  }
+  static bool Insert(WTF::HashMap<K, V>& input, const K& key, const V& value) {
+    if (!WTF::HashMap<K, V>::isValidKey(key)) {
+      LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
+      return false;
+    }
+    input.add(key, value);
+    return true;
+  }
+
+  static void SetToEmpty(WTF::HashMap<K, V>* output) { output->clear(); }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_HASH_MAP_H_
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
new file mode 100644
index 0000000..e758432
--- /dev/null
+++ b/mojo/public/cpp/bindings/message.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/message_buffer.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/system/message.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:
+  static const uint32_t kFlagExpectsResponse = 1 << 0;
+  static const uint32_t kFlagIsResponse = 1 << 1;
+  static const uint32_t kFlagIsSync = 1 << 2;
+
+  Message();
+  ~Message();
+
+  // Initializes a Message with enough space for |capacity| bytes.
+  void Initialize(size_t capacity, bool zero_initialized);
+
+  // Initializes a Message from an existing Mojo MessageHandle.
+  void InitializeFromMojoMessage(ScopedMessageHandle message,
+                                 uint32_t num_bytes,
+                                 std::vector<Handle>* handles);
+
+  // Transfers data and handles to |destination|.
+  void MoveTo(Message* destination);
+
+  uint32_t data_num_bytes() const { return buffer_->data_num_bytes(); }
+
+  // Access the raw bytes of the message.
+  const uint8_t* data() const {
+    return static_cast<const uint8_t*>(buffer_->data());
+  }
+
+  uint8_t* mutable_data() { return static_cast<uint8_t*>(buffer_->data()); }
+
+  // Access the header.
+  const internal::MessageHeader* header() const {
+    return static_cast<const internal::MessageHeader*>(buffer_->data());
+  }
+
+  internal::MessageHeader* header() {
+    return const_cast<internal::MessageHeader*>(
+        static_cast<const Message*>(this)->header());
+  }
+
+  uint32_t interface_id() const { return header()->interface_id; }
+  void set_interface_id(uint32_t id) { header()->interface_id = id; }
+
+  uint32_t name() const { return header()->name; }
+  bool has_flag(uint32_t flag) const { return !!(header()->flags & flag); }
+
+  // Access the request_id field (if present).
+  bool has_request_id() const { return header()->version >= 1; }
+  uint64_t request_id() const {
+    DCHECK(has_request_id());
+    return static_cast<const internal::MessageHeaderWithRequestID*>(
+               header())->request_id;
+  }
+  void set_request_id(uint64_t request_id) {
+    DCHECK(has_request_id());
+    static_cast<internal::MessageHeaderWithRequestID*>(header())
+        ->request_id = request_id;
+  }
+
+  // Access the payload.
+  const uint8_t* payload() const { return data() + header()->num_bytes; }
+  uint8_t* mutable_payload() { return const_cast<uint8_t*>(payload()); }
+  uint32_t payload_num_bytes() const {
+    DCHECK(buffer_->data_num_bytes() >= header()->num_bytes);
+    size_t num_bytes = buffer_->data_num_bytes() - header()->num_bytes;
+    DCHECK(num_bytes <= std::numeric_limits<uint32_t>::max());
+    return static_cast<uint32_t>(num_bytes);
+  }
+
+  // Access the handles.
+  const std::vector<Handle>* handles() const { return &handles_; }
+  std::vector<Handle>* mutable_handles() { return &handles_; }
+
+  // Access the underlying Buffer interface.
+  internal::Buffer* buffer() { return buffer_.get(); }
+
+  // Takes a scoped MessageHandle which may be passed to |WriteMessageNew()| for
+  // transmission. Note that this invalidates this Message object, taking
+  // ownership of its internal storage and any attached handles.
+  ScopedMessageHandle TakeMojoMessage();
+
+  // Notifies the system that this message is "bad," in this case meaning it was
+  // rejected by bindings validation code.
+  void NotifyBadMessage(const std::string& error);
+
+ private:
+  void CloseHandles();
+
+  std::unique_ptr<internal::MessageBuffer> buffer_;
+  std::vector<Handle> handles_;
+
+  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) WARN_UNUSED_RESULT = 0;
+};
+
+class MessageReceiverWithResponder : public MessageReceiver {
+ public:
+  ~MessageReceiverWithResponder() override {}
+
+  // 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.
+  //
+  // TODO(yzshen): consider changing |responder| to
+  // std::unique_ptr<MessageReceiver>.
+  virtual bool AcceptWithResponder(Message* message, MessageReceiver* responder)
+      WARN_UNUSED_RESULT = 0;
+};
+
+// A MessageReceiver that is also able to provide status about the state
+// of the underlying MessagePipe to which it will be forwarding messages
+// received via the |Accept()| call.
+class MessageReceiverWithStatus : public MessageReceiver {
+ public:
+  ~MessageReceiverWithStatus() override {}
+
+  // Returns |true| if this MessageReceiver is currently bound to a MessagePipe,
+  // the pipe has not been closed, and the pipe has not encountered an error.
+  virtual bool IsValid() = 0;
+
+  // DCHECKs if this MessageReceiver is currently bound to a MessagePipe, the
+  // pipe has not been closed, and the pipe has not encountered an error.
+  // This function may be called on any thread.
+  virtual void DCheckInvalid(const std::string& message) = 0;
+};
+
+// An alternative to MessageReceiverWithResponder for cases in which it
+// is necessary for the implementor of this interface to know about the status
+// of the MessagePipe which will carry the responses.
+class MessageReceiverWithResponderStatus : public MessageReceiver {
+ public:
+  ~MessageReceiverWithResponderStatus() override {}
+
+  // A variant on Accept that registers a MessageReceiverWithStatus (known as
+  // the responder) to handle the response message generated from the given
+  // message. Any of the responder's methods (Accept or IsValid) 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.
+  //
+  // TODO(yzshen): consider changing |responder| to
+  // std::unique_ptr<MessageReceiver>.
+  virtual bool AcceptWithResponder(Message* message,
+                                   MessageReceiverWithStatus* responder)
+      WARN_UNUSED_RESULT = 0;
+};
+
+// Read a single message from the pipe. The caller should have created the
+// Message, but not called Initialize(). Returns MOJO_RESULT_SHOULD_WAIT if
+// the caller should wait on the handle to become readable. Returns
+// MOJO_RESULT_OK if the message was read successfully and should be
+// dispatched, otherwise returns an error code if something went wrong.
+//
+// NOTE: The message hasn't been validated and may be malformed!
+MojoResult ReadMessage(MessagePipeHandle handle, Message* message);
+
+}  // 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..638c53b
--- /dev/null
+++ b/mojo/public/cpp/bindings/message_filter.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_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+
+#include "mojo/public/cpp/bindings/message.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);
+  ~MessageFilter() override;
+
+  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);
+
+  bool Accept(Message* message) override;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
diff --git a/mojo/public/cpp/bindings/message_header_validator.h b/mojo/public/cpp/bindings/message_header_validator.h
new file mode 100644
index 0000000..3bcbd0a
--- /dev/null
+++ b/mojo/public/cpp/bindings/message_header_validator.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_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+
+class MessageHeaderValidator : public MessageFilter {
+ public:
+  explicit MessageHeaderValidator(MessageReceiver* sink = nullptr);
+  MessageHeaderValidator(const std::string& description,
+                         MessageReceiver* sink = nullptr);
+
+  // Sets the description associated with this validator. Used for reporting
+  // more detailed validation errors.
+  void SetDescription(const std::string& description);
+
+  bool Accept(Message* message) override;
+
+ private:
+  std::string description_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_
diff --git a/mojo/public/cpp/bindings/native_enum.h b/mojo/public/cpp/bindings/native_enum.h
new file mode 100644
index 0000000..08b43b7
--- /dev/null
+++ b/mojo/public/cpp/bindings/native_enum.h
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All 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_NATIVE_ENUM_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_ENUM_H_
+
+#include <functional>
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/native_enum_data.h"
+
+namespace mojo {
+
+// Native-only enums correspond to "[Native] enum Foo;" definitions in mojom.
+enum class NativeEnum : int32_t {};
+
+}  // namespace mojo
+
+namespace std {
+
+template <>
+struct hash<mojo::NativeEnum>
+    : public mojo::internal::EnumHashImpl<mojo::NativeEnum> {};
+
+}  // namespace std
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_NATIVE_ENUM_H_
diff --git a/mojo/public/cpp/bindings/native_struct.h b/mojo/public/cpp/bindings/native_struct.h
new file mode 100644
index 0000000..882c970
--- /dev/null
+++ b/mojo/public/cpp/bindings/native_struct.h
@@ -0,0 +1,47 @@
+// Copyright 2016 The Chromium Authors. All 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_NATIVE_STRUCT_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+class NativeStruct;
+using NativeStructPtr = StructPtr<NativeStruct>;
+
+// Native-only structs correspond to "[Native] struct Foo;" definitions in
+// mojom.
+class NativeStruct {
+ public:
+  using Data_ = internal::NativeStruct_Data;
+
+  static NativeStructPtr New();
+
+  template <typename U>
+  static NativeStructPtr From(const U& u) {
+    return TypeConverter<NativeStructPtr, U>::Convert(u);
+  }
+
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, NativeStruct>::Convert(*this);
+  }
+
+  NativeStruct();
+  ~NativeStruct();
+
+  NativeStructPtr Clone() const;
+  bool Equals(const NativeStruct& other) const;
+
+  Array<uint8_t> data;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_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..d8915cd
--- /dev/null
+++ b/mojo/public/cpp/bindings/no_interface.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_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.
+
+class NoInterfaceProxy;
+class NoInterfaceStub;
+
+class NoInterface {
+ public:
+  static const char* Name_;
+  typedef NoInterfaceProxy Proxy_;
+  typedef NoInterfaceStub Stub_;
+  typedef PassThroughFilter RequestValidator_;
+  typedef PassThroughFilter ResponseValidator_;
+  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; }
+  bool Accept(Message* message) override;
+  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/pipe_control_message_handler.h b/mojo/public/cpp/bindings/pipe_control_message_handler.h
new file mode 100644
index 0000000..b387b06
--- /dev/null
+++ b/mojo/public/cpp/bindings/pipe_control_message_handler.h
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/lib/serialization_context.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+
+class PipeControlMessageHandlerDelegate;
+
+// Handler for messages defined in pipe_control_messages.mojom.
+class PipeControlMessageHandler : public MessageReceiver {
+ public:
+  explicit PipeControlMessageHandler(
+      PipeControlMessageHandlerDelegate* delegate);
+  ~PipeControlMessageHandler() override;
+
+  // Sets the description for this handler. Used only when reporting validation
+  // errors.
+  void SetDescription(const std::string& description);
+
+  // NOTE: |message| must have passed message header validation.
+  static bool IsPipeControlMessage(const Message* message);
+
+  // MessageReceiver implementation:
+
+  // NOTE: |message| must:
+  //   - have passed message header validation; and
+  //   - be a pipe control message (i.e., IsPipeControlMessage() returns true).
+  // If the method returns false, the message pipe should be closed.
+  bool Accept(Message* message) override;
+
+ private:
+  // |message| must have passed message header validation.
+  bool Validate(Message* message);
+  bool RunOrClosePipe(Message* message);
+
+  std::string description_;
+  PipeControlMessageHandlerDelegate* const delegate_;
+  internal::SerializationContext context_;
+
+  DISALLOW_COPY_AND_ASSIGN(PipeControlMessageHandler);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_
diff --git a/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h
new file mode 100644
index 0000000..06f0695
--- /dev/null
+++ b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_
+
+#include "mojo/public/cpp/bindings/interface_id.h"
+
+namespace mojo {
+
+class PipeControlMessageHandlerDelegate {
+ public:
+  // The implementation of the following methods should return false if the
+  // notification is unexpected. In that case, the user of this delegate is
+  // expected to close the message pipe.
+  virtual bool OnPeerAssociatedEndpointClosed(InterfaceId id) = 0;
+  virtual bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) = 0;
+
+ protected:
+  virtual ~PipeControlMessageHandlerDelegate() {}
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_
diff --git a/mojo/public/cpp/bindings/pipe_control_message_proxy.h b/mojo/public/cpp/bindings/pipe_control_message_proxy.h
new file mode 100644
index 0000000..7f3e006
--- /dev/null
+++ b/mojo/public/cpp/bindings/pipe_control_message_proxy.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_PROXY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_PROXY_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/lib/serialization_context.h"
+
+namespace mojo {
+
+class MessageReceiver;
+
+// Proxy for request messages defined in pipe_control_messages.mojom.
+class PipeControlMessageProxy {
+ public:
+  // Doesn't take ownership of |receiver|. It must outlive this object.
+  explicit PipeControlMessageProxy(MessageReceiver* receiver);
+
+  void NotifyPeerEndpointClosed(InterfaceId id);
+  void NotifyEndpointClosedBeforeSent(InterfaceId id);
+
+ private:
+  // Not owned.
+  MessageReceiver* receiver_;
+  internal::SerializationContext context_;
+
+  DISALLOW_COPY_AND_ASSIGN(PipeControlMessageProxy);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_PROXY_H_
diff --git a/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h
new file mode 100644
index 0000000..1f45b0c
--- /dev/null
+++ b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+
+namespace mojo {
+
+class AssociatedGroupController;
+
+// ScopedInterfaceEndpointHandle refers to one end of an interface, either the
+// implementation side or the client side.
+class ScopedInterfaceEndpointHandle {
+ public:
+  // Creates an invalid endpoint handle.
+  ScopedInterfaceEndpointHandle();
+
+  ScopedInterfaceEndpointHandle(ScopedInterfaceEndpointHandle&& other);
+
+  ~ScopedInterfaceEndpointHandle();
+
+  ScopedInterfaceEndpointHandle& operator=(
+      ScopedInterfaceEndpointHandle&& other);
+
+  bool is_valid() const { return IsValidInterfaceId(id_); }
+
+  bool is_local() const { return is_local_; }
+
+  void reset();
+  void swap(ScopedInterfaceEndpointHandle& other);
+
+  // DO NOT USE METHODS BELOW THIS LINE. These are for internal use and testing.
+
+  InterfaceId id() const { return id_; }
+
+  AssociatedGroupController* group_controller() const {
+    return group_controller_.get();
+  }
+
+  // Releases the handle without closing it.
+  InterfaceId release();
+
+ private:
+  friend class AssociatedGroupController;
+
+  // This is supposed to be used by AssociatedGroupController only.
+  // |id| is the corresponding interface ID.
+  // If |is_local| is false, this handle is meant to be passed over a message
+  // pipe the remote side of the associated group.
+  ScopedInterfaceEndpointHandle(
+      InterfaceId id,
+      bool is_local,
+      scoped_refptr<AssociatedGroupController> group_controller);
+
+  InterfaceId id_;
+  bool is_local_;
+  scoped_refptr<AssociatedGroupController> group_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceEndpointHandle);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_
diff --git a/mojo/public/cpp/bindings/stl_converters.h b/mojo/public/cpp/bindings/stl_converters.h
new file mode 100644
index 0000000..92b2924
--- /dev/null
+++ b/mojo/public/cpp/bindings/stl_converters.h
@@ -0,0 +1,245 @@
+// Copyright 2016 The Chromium Authors. All 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_STL_CONVERTERS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
+
+#include <map>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/string.h"
+
+// Two functions are defined to facilitate conversion between
+// mojo::Array/Map/String and std::vector/map/string: mojo::UnwrapToSTLType()
+// recursively convert mojo types to STL types; mojo::WrapSTLType() does the
+// opposite. For example:
+//   mojo::Array<mojo::Map<mojo::String, mojo::Array<int32_t>>> mojo_obj;
+//
+//   std::vector<std::map<std::string, std::vector<int32_t>>> stl_obj =
+//       mojo::UnwrapToSTLType(std::move(mojo_obj));
+//
+//   mojo_obj = mojo::WrapSTLType(std::move(stl_obj));
+//
+// Notes:
+//   - The conversion moves as much contents as possible. The two functions both
+//     take an rvalue ref as input in order to avoid accidental copies.
+//   - Because std::vector/map/string cannot express null, UnwrapToSTLType()
+//     converts null mojo::Array/Map/String to empty.
+//   - The recursive conversion stops at any types that are not the types listed
+//     above. For example, unwrapping mojo::Array<StructContainingMojoMap> will
+//     result in std::vector<StructContainingMojoMap>. It won't convert
+//     mojo::Map inside the struct.
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+struct UnwrapTraits;
+
+template <typename T>
+struct UnwrapShouldGoDeeper {
+ public:
+  static const bool value =
+      !std::is_same<T, typename UnwrapTraits<T>::Type>::value;
+};
+
+template <typename T>
+struct UnwrapTraits {
+ public:
+  using Type = T;
+  static Type Unwrap(T input) { return input; }
+};
+
+template <typename T>
+struct UnwrapTraits<Array<T>> {
+ public:
+  using Type = std::vector<typename UnwrapTraits<T>::Type>;
+
+  static Type Unwrap(Array<T> input) {
+    return Helper<T>::Run(std::move(input));
+  }
+
+ private:
+  template <typename U, bool should_go_deeper = UnwrapShouldGoDeeper<U>::value>
+  struct Helper {};
+
+  template <typename U>
+  struct Helper<U, true> {
+   public:
+    static Type Run(Array<T> input) {
+      Type output;
+      output.reserve(input.size());
+      for (size_t i = 0; i < input.size(); ++i)
+        output.push_back(UnwrapTraits<T>::Unwrap(std::move(input[i])));
+      return output;
+    }
+  };
+
+  template <typename U>
+  struct Helper<U, false> {
+   public:
+    static Type Run(Array<T> input) { return input.PassStorage(); }
+  };
+};
+
+template <typename K, typename V>
+struct UnwrapTraits<Map<K, V>> {
+ public:
+  using Type =
+      std::map<typename UnwrapTraits<K>::Type, typename UnwrapTraits<V>::Type>;
+
+  static Type Unwrap(Map<K, V> input) {
+    return Helper<K, V>::Run(std::move(input));
+  }
+
+ private:
+  template <typename X,
+            typename Y,
+            bool should_go_deeper = UnwrapShouldGoDeeper<X>::value ||
+                                    UnwrapShouldGoDeeper<Y>::value>
+  struct Helper {};
+
+  template <typename X, typename Y>
+  struct Helper<X, Y, true> {
+   public:
+    static Type Run(Map<K, V> input) {
+      std::map<K, V> input_storage = input.PassStorage();
+      Type output;
+      for (auto& pair : input_storage) {
+        output.insert(
+            std::make_pair(UnwrapTraits<K>::Unwrap(pair.first),
+                           UnwrapTraits<V>::Unwrap(std::move(pair.second))));
+      }
+      return output;
+    }
+  };
+
+  template <typename X, typename Y>
+  struct Helper<X, Y, false> {
+   public:
+    static Type Run(Map<K, V> input) { return input.PassStorage(); }
+  };
+};
+
+template <>
+struct UnwrapTraits<String> {
+ public:
+  using Type = std::string;
+
+  static std::string Unwrap(const String& input) { return input; }
+};
+
+template <typename T>
+struct WrapTraits;
+
+template <typename T>
+struct WrapShouldGoDeeper {
+ public:
+  static const bool value =
+      !std::is_same<T, typename WrapTraits<T>::Type>::value;
+};
+
+template <typename T>
+struct WrapTraits {
+ public:
+  using Type = T;
+
+  static T Wrap(T input) { return input; }
+};
+
+template <typename T>
+struct WrapTraits<std::vector<T>> {
+ public:
+  using Type = Array<typename WrapTraits<T>::Type>;
+
+  static Type Wrap(std::vector<T> input) {
+    return Helper<T>::Run(std::move(input));
+  }
+
+ private:
+  template <typename U, bool should_go_deeper = WrapShouldGoDeeper<U>::value>
+  struct Helper {};
+
+  template <typename U>
+  struct Helper<U, true> {
+   public:
+    static Type Run(std::vector<T> input) {
+      std::vector<typename WrapTraits<T>::Type> output_storage;
+      output_storage.reserve(input.size());
+      for (auto& element : input)
+        output_storage.push_back(WrapTraits<T>::Wrap(std::move(element)));
+      return Type(std::move(output_storage));
+    }
+  };
+
+  template <typename U>
+  struct Helper<U, false> {
+   public:
+    static Type Run(std::vector<T> input) { return Type(std::move(input)); }
+  };
+};
+
+template <typename K, typename V>
+struct WrapTraits<std::map<K, V>> {
+ public:
+  using Type = Map<typename WrapTraits<K>::Type, typename WrapTraits<V>::Type>;
+
+  static Type Wrap(std::map<K, V> input) {
+    return Helper<K, V>::Run(std::move(input));
+  }
+
+ private:
+  template <typename X,
+            typename Y,
+            bool should_go_deeper =
+                WrapShouldGoDeeper<X>::value || WrapShouldGoDeeper<Y>::value>
+  struct Helper {};
+
+  template <typename X, typename Y>
+  struct Helper<X, Y, true> {
+   public:
+    static Type Run(std::map<K, V> input) {
+      Type output;
+      for (auto& pair : input) {
+        output.insert(WrapTraits<K>::Wrap(pair.first),
+                      WrapTraits<V>::Wrap(std::move(pair.second)));
+      }
+      return output;
+    }
+  };
+
+  template <typename X, typename Y>
+  struct Helper<X, Y, false> {
+   public:
+    static Type Run(std::map<K, V> input) { return Type(std::move(input)); }
+  };
+};
+
+template <>
+struct WrapTraits<std::string> {
+ public:
+  using Type = String;
+
+  static String Wrap(const std::string& input) { return input; }
+};
+
+}  // namespace internal
+
+template <typename T>
+typename internal::UnwrapTraits<T>::Type UnwrapToSTLType(T&& input) {
+  return internal::UnwrapTraits<T>::Unwrap(std::move(input));
+}
+
+template <typename T>
+typename internal::WrapTraits<T>::Type WrapSTLType(T&& input) {
+  return internal::WrapTraits<T>::Wrap(std::move(input));
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
diff --git a/mojo/public/cpp/bindings/string.h b/mojo/public/cpp/bindings/string.h
new file mode 100644
index 0000000..7cfd713
--- /dev/null
+++ b/mojo/public/cpp/bindings/string.h
@@ -0,0 +1,196 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+// A UTF-8 encoded character string that can be null. Provides functions that
+// are similar to std::string, along with access to the underlying std::string
+// object.
+class String {
+ public:
+  // Constructs an empty string.
+  String() : is_null_(false) {}
+  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) {}
+  String(const mojo::String& str)
+      : value_(str.value_), is_null_(str.is_null_) {}
+
+  template <size_t N>
+  String(const char chars[N])
+      : value_(chars, N - 1), is_null_(false) {}
+
+  String(std::string&& other) : value_(std::move(other)), is_null_(false) {}
+  String(String&& other) : is_null_(true) { Swap(&other); }
+
+  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 mojo::String& str) {
+    value_ = str.value_;
+    is_null_ = str.is_null_;
+    return *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;
+  }
+
+  String& operator=(std::string&& other) {
+    value_ = std::move(other);
+    is_null_ = false;
+    return *this;
+  }
+  String& operator=(String&& other) {
+    is_null_ = true;
+    value_.clear();
+    Swap(&other);
+    return *this;
+  }
+
+  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_; }
+
+  // Returns a const reference to the |std::string| managed by this class. If
+  // the string is null, this will be an empty std::string.
+  const std::string& storage() const { return value_; }
+
+  // Passes the underlying storage and resets this string to null.
+  std::string PassStorage() {
+    is_null_ = true;
+    return std::move(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();
+}
+
+inline bool operator<(const String& a, const String& b) {
+  if (a.is_null())
+    return !b.is_null();
+  if (b.is_null())
+    return false;
+
+  return a.get() < b.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]) {
+    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]) {
+    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/string_traits.h b/mojo/public/cpp/bindings/string_traits.h
new file mode 100644
index 0000000..a6ade6f
--- /dev/null
+++ b/mojo/public/cpp/bindings/string_traits.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+
+namespace mojo {
+
+// Access to the contents of a serialized string.
+class StringDataView {
+ public:
+  explicit StringDataView(internal::String_Data* data) : data_(data) {
+    DCHECK(data_);
+  }
+
+  const char* storage() const { return data_->storage(); }
+
+  size_t size() const { return data_->size(); }
+
+ private:
+  internal::String_Data* data_;
+};
+
+// This must be specialized for any type |T| to be serialized/deserialized as
+// a mojom string.
+//
+// Imagine you want to specialize it for CustomString, usually you need to
+// implement:
+//
+//   template <T>
+//   struct StringTraits<CustomString> {
+//     // These two methods are optional. Please see comments in struct_traits.h
+//     static bool IsNull(const CustomString& input);
+//     static void SetToNull(CustomString* output);
+//
+//     static size_t GetSize(const CustomString& input);
+//     static const char* GetData(const CustomString& input);
+//
+//     static bool Read(StringDataView input, CustomString* output);
+//   };
+//
+// In some cases, you may need to do conversion before you can return the size
+// and data as 8-bit characters for serialization. (For example, CustomString is
+// UTF-16 string). In that case, you can add two optional methods:
+//
+//   static void* SetUpContext(const CustomString& input);
+//   static void TearDownContext(const CustomString& input, void* context);
+//
+// And then you append a second parameter, void* context, to GetSize() and
+// GetData():
+//
+//   static size_t GetSize(const CustomString& input, void* context);
+//   static const char* GetData(const CustomString& input, void* context);
+//
+// If a CustomString instance is not null, the serialization code will call
+// SetUpContext() at the beginning, and pass the resulting context pointer to
+// GetSize()/GetData(). After serialization is done, it calls TearDownContext()
+// so that you can do any necessary cleanup.
+//
+template <typename T>
+struct StringTraits;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/string_traits_standard.h b/mojo/public/cpp/bindings/string_traits_standard.h
new file mode 100644
index 0000000..9b78d24
--- /dev/null
+++ b/mojo/public/cpp/bindings/string_traits_standard.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_STANDARD_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STANDARD_H_
+
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/string_traits.h"
+
+namespace mojo {
+
+template <>
+struct StringTraits<String> {
+  static bool IsNull(const String& input) { return input.is_null(); }
+  static void SetToNull(String* output) { *output = nullptr; }
+
+  static size_t GetSize(const String& input) { return input.size(); }
+
+  static const char* GetData(const String& input) { return input.data(); }
+
+  static bool Read(StringDataView input, String* output) {
+    String result(input.storage(), input.size());
+    result.Swap(output);
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STANDARD_H_
diff --git a/mojo/public/cpp/bindings/string_traits_stl.h b/mojo/public/cpp/bindings/string_traits_stl.h
new file mode 100644
index 0000000..f6cc8ad
--- /dev/null
+++ b/mojo/public/cpp/bindings/string_traits_stl.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_STL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STL_H_
+
+#include <string>
+
+#include "mojo/public/cpp/bindings/string_traits.h"
+
+namespace mojo {
+
+template <>
+struct StringTraits<std::string> {
+  static bool IsNull(const std::string& input) {
+    // std::string is always converted to non-null mojom string.
+    return false;
+  }
+
+  static void SetToNull(std::string* output) {
+    // std::string doesn't support null state. Set it to empty instead.
+    output->clear();
+  }
+
+  static size_t GetSize(const std::string& input) { return input.size(); }
+
+  static const char* GetData(const std::string& input) { return input.data(); }
+
+  static bool Read(StringDataView input, std::string* output) {
+    output->assign(input.storage(), input.size());
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STL_H_
diff --git a/mojo/public/cpp/bindings/string_traits_string16.h b/mojo/public/cpp/bindings/string_traits_string16.h
new file mode 100644
index 0000000..5a08908
--- /dev/null
+++ b/mojo/public/cpp/bindings/string_traits_string16.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_STRING16_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STRING16_H_
+
+#include "base/strings/string16.h"
+#include "mojo/public/cpp/bindings/string_traits.h"
+
+namespace mojo {
+
+template <>
+struct StringTraits<base::string16> {
+  static bool IsNull(const base::string16& input) {
+    // base::string16 is always converted to non-null mojom string.
+    return false;
+  }
+
+  static void SetToNull(base::string16* output) {
+    // Convert null to an "empty" base::string16.
+    output->clear();
+  }
+
+  static void* SetUpContext(const base::string16& input);
+  static void TearDownContext(const base::string16& input, void* context);
+
+  static size_t GetSize(const base::string16& input, void* context);
+  static const char* GetData(const base::string16& input, void* context);
+
+  static bool Read(StringDataView input, base::string16* output);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STRING16_H_
diff --git a/mojo/public/cpp/bindings/string_traits_string_piece.h b/mojo/public/cpp/bindings/string_traits_string_piece.h
new file mode 100644
index 0000000..af6be89
--- /dev/null
+++ b/mojo/public/cpp/bindings/string_traits_string_piece.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_STRING_PIECE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STRING_PIECE_H_
+
+#include "base/strings/string_piece.h"
+#include "mojo/public/cpp/bindings/string_traits.h"
+
+namespace mojo {
+
+template <>
+struct StringTraits<base::StringPiece> {
+  static bool IsNull(const base::StringPiece& input) {
+    // base::StringPiece is always converted to non-null mojom string. We could
+    // have let StringPiece containing a null data pointer map to null mojom
+    // string, but StringPiece::empty() returns true in this case. It seems
+    // confusing to mix the concept of empty and null strings, especially
+    // because they mean different things in mojom.
+    return false;
+  }
+
+  static void SetToNull(base::StringPiece* output) {
+    // Convert null to an "empty" base::StringPiece.
+    output->set(nullptr, 0);
+  }
+
+  static size_t GetSize(const base::StringPiece& input) { return input.size(); }
+
+  static const char* GetData(const base::StringPiece& input) {
+    return input.data();
+  }
+
+  static bool Read(StringDataView input, base::StringPiece* output) {
+    output->set(input.storage(), input.size());
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STRING_PIECE_H_
diff --git a/mojo/public/cpp/bindings/string_traits_wtf.h b/mojo/public/cpp/bindings/string_traits_wtf.h
new file mode 100644
index 0000000..238c2eb
--- /dev/null
+++ b/mojo/public/cpp/bindings/string_traits_wtf.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All 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_TRAITS_WTF_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_WTF_H_
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/string_traits.h"
+#include "third_party/WebKit/Source/wtf/text/WTFString.h"
+
+namespace mojo {
+
+template <>
+struct StringTraits<WTF::String> {
+  static bool IsNull(const WTF::String& input) { return input.isNull(); }
+  static void SetToNull(WTF::String* output);
+
+  static void* SetUpContext(const WTF::String& input);
+  static void TearDownContext(const WTF::String& input, void* context);
+
+  static size_t GetSize(const WTF::String& input, void* context);
+
+  static const char* GetData(const WTF::String& input, void* context);
+
+  static bool Read(StringDataView input, WTF::String* output);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_WTF_H_
diff --git a/mojo/public/cpp/bindings/strong_binding.h b/mojo/public/cpp/bindings/strong_binding.h
new file mode 100644
index 0000000..7fb7eea
--- /dev/null
+++ b/mojo/public/cpp/bindings/strong_binding.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/bindings/message_header_validator.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// This connects an interface implementation strongly to a pipe. When a
+// connection error is detected the implementation is deleted. Deleting the
+// connector also closes the pipe.
+//
+// Example of an implementation that is always bound strongly to a pipe
+//
+//   class StronglyBound : public Foo {
+//    public:
+//     explicit StronglyBound(InterfaceRequest<Foo> request)
+//         : binding_(this, std::move(request)) {}
+//
+//     // Foo implementation here
+//
+//    private:
+//     StrongBinding<Foo> binding_;
+//   };
+//
+//   class MyFooFactory : public InterfaceFactory<Foo> {
+//    public:
+//     void Create(..., InterfaceRequest<Foo> request) override {
+//       new StronglyBound(std::move(request));  // The binding now owns the
+//                                               // instance of StronglyBound.
+//     }
+//   };
+//
+// This class is thread hostile once it is bound to a message pipe. Until it is
+// bound, it may be bound or destroyed on any thread.
+template <typename Interface>
+class StrongBinding {
+ public:
+  explicit StrongBinding(Interface* impl) : binding_(impl) {}
+
+  StrongBinding(Interface* impl, ScopedMessagePipeHandle handle)
+      : StrongBinding(impl) {
+    Bind(std::move(handle));
+  }
+
+  StrongBinding(Interface* impl, InterfacePtr<Interface>* ptr)
+      : StrongBinding(impl) {
+    Bind(ptr);
+  }
+
+  StrongBinding(Interface* impl, InterfaceRequest<Interface> request)
+      : StrongBinding(impl) {
+    Bind(std::move(request));
+  }
+
+  ~StrongBinding() {}
+
+  void Bind(ScopedMessagePipeHandle handle) {
+    DCHECK(!binding_.is_bound());
+    binding_.Bind(std::move(handle));
+    binding_.set_connection_error_handler(
+        base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this)));
+  }
+
+  void Bind(InterfacePtr<Interface>* ptr) {
+    DCHECK(!binding_.is_bound());
+    binding_.Bind(ptr);
+    binding_.set_connection_error_handler(
+        base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this)));
+  }
+
+  void Bind(InterfaceRequest<Interface> request) {
+    DCHECK(!binding_.is_bound());
+    binding_.Bind(std::move(request));
+    binding_.set_connection_error_handler(
+        base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this)));
+  }
+
+  bool WaitForIncomingMethodCall() {
+    return binding_.WaitForIncomingMethodCall();
+  }
+
+  // Note: The error handler must not delete the interface implementation.
+  //
+  // This method may only be called after this StrongBinding has been bound to a
+  // message pipe.
+  void set_connection_error_handler(const base::Closure& error_handler) {
+    DCHECK(binding_.is_bound());
+    connection_error_handler_ = error_handler;
+  }
+
+  Interface* impl() { return binding_.impl(); }
+  // Exposed for testing, should not generally be used.
+  internal::Router* internal_router() { return binding_.internal_router(); }
+
+  void OnConnectionError() {
+    if (!connection_error_handler_.is_null())
+      connection_error_handler_.Run();
+    delete binding_.impl();
+  }
+
+ private:
+  base::Closure connection_error_handler_;
+  Binding<Interface> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(StrongBinding);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_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..92f2728
--- /dev/null
+++ b/mojo/public/cpp/bindings/struct_ptr.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_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
+
+#include <new>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Struct>
+class StructHelper {
+ public:
+  template <typename Ptr>
+  static void Initialize(Ptr* ptr) {
+    ptr->Initialize();
+  }
+};
+
+}  // namespace internal
+
+// Smart pointer wrapping a mojom structure with move-only semantics.
+template <typename S>
+class StructPtr {
+ public:
+  using Struct = S;
+
+  StructPtr() : ptr_(nullptr) {}
+  StructPtr(decltype(nullptr)) : ptr_(nullptr) {}
+
+  ~StructPtr() { delete ptr_; }
+
+  StructPtr& operator=(decltype(nullptr)) {
+    reset();
+    return *this;
+  }
+
+  StructPtr(StructPtr&& other) : ptr_(nullptr) { Take(&other); }
+  StructPtr& operator=(StructPtr&& other) {
+    Take(&other);
+    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 {
+    DCHECK(ptr_);
+    return *ptr_;
+  }
+  Struct* operator->() const {
+    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(); }
+
+  bool Equals(const StructPtr& other) const {
+    if (is_null() || other.is_null())
+      return is_null() && other.is_null();
+    return ptr_->Equals(*other.ptr_);
+  }
+
+ private:
+  // TODO(dcheng): Use an explicit conversion operator.
+  typedef Struct* StructPtr::*Testable;
+
+ public:
+  operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; }
+
+ private:
+  friend class internal::StructHelper<Struct>;
+
+  // Forbid the == and != operators explicitly, otherwise StructPtr will be
+  // converted to Testable to do == or != comparison.
+  template <typename T>
+  bool operator==(const StructPtr<T>& other) const = delete;
+  template <typename T>
+  bool operator!=(const StructPtr<T>& other) const = delete;
+
+  void Initialize() {
+    DCHECK(!ptr_);
+    ptr_ = new Struct();
+  }
+
+  void Take(StructPtr* other) {
+    reset();
+    Swap(other);
+  }
+
+  Struct* ptr_;
+
+  DISALLOW_COPY_AND_ASSIGN(StructPtr);
+};
+
+// Designed to be used when Struct is small and copyable.
+template <typename S>
+class InlinedStructPtr {
+ public:
+  using Struct = S;
+
+  InlinedStructPtr() : is_null_(true) {}
+  InlinedStructPtr(decltype(nullptr)) : is_null_(true) {}
+
+  ~InlinedStructPtr() {}
+
+  InlinedStructPtr& operator=(decltype(nullptr)) {
+    reset();
+    return *this;
+  }
+
+  InlinedStructPtr(InlinedStructPtr&& other) : is_null_(true) { Take(&other); }
+  InlinedStructPtr& operator=(InlinedStructPtr&& other) {
+    Take(&other);
+    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 {
+    DCHECK(!is_null_);
+    return value_;
+  }
+  Struct* operator->() const {
+    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();
+  }
+  bool Equals(const InlinedStructPtr& other) const {
+    if (is_null() || other.is_null())
+      return is_null() && other.is_null();
+    return value_.Equals(other.value_);
+  }
+
+ private:
+  // TODO(dcheng): Use an explicit conversion operator.
+  typedef Struct InlinedStructPtr::*Testable;
+
+ public:
+  operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; }
+
+ private:
+  friend class internal::StructHelper<Struct>;
+
+  // Forbid the == and != operators explicitly, otherwise InlinedStructPtr will
+  // be converted to Testable to do == or != comparison.
+  template <typename T>
+  bool operator==(const InlinedStructPtr<T>& other) const = delete;
+  template <typename T>
+  bool operator!=(const InlinedStructPtr<T>& other) const = delete;
+
+  void Initialize() { is_null_ = false; }
+
+  void Take(InlinedStructPtr* other) {
+    reset();
+    Swap(other);
+  }
+
+  mutable Struct value_;
+  bool is_null_;
+
+  DISALLOW_COPY_AND_ASSIGN(InlinedStructPtr);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
diff --git a/mojo/public/cpp/bindings/struct_traits.h b/mojo/public/cpp/bindings/struct_traits.h
new file mode 100644
index 0000000..0f0bea7
--- /dev/null
+++ b/mojo/public/cpp/bindings/struct_traits.h
@@ -0,0 +1,152 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRUCT_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_TRAITS_H_
+
+namespace mojo {
+
+// This must be specialized for any type |T| to be serialized/deserialized as
+// a mojom struct of type |MojomType|.
+//
+// Each specialization needs to implement a few things:
+//   1. Static getters for each field in the Mojom type. These should be
+//      of the form:
+//
+//        static <return type> <field name>(const T& input);
+//
+//      and should return a serializable form of the named field as extracted
+//      from |input|.
+//
+//      Serializable form of a field:
+//        Value or reference of the same type used in |MojomType|, or the
+//        following alternatives:
+//        - string:
+//          Value or reference of any type that has a StringTraits defined.
+//          Supported by default: base::StringPiece, std::string.
+//
+//        - array:
+//          Value or reference of any type that has an ArrayTraits defined.
+//          Supported by default: std::vector, WTF::Vector (in blink), CArray.
+//
+//        - map:
+//          Value or reference of any type that has a MapTraits defined.
+//          Supported by default: std::map.
+//
+//        - struct:
+//          Value or reference of any type that has a StructTraits defined.
+//
+//        - enum:
+//          Value of any type that has an EnumTraits defined.
+//
+//      During serialization, getters for string/struct/array/map/union fields
+//      are called twice (one for size calculation and one for actual
+//      serialization). If you want to return a value (as opposed to a
+//      reference) from these getters, you have to be sure that constructing and
+//      copying the returned object is really cheap.
+//
+//      Getters for fields of other types are called once.
+//
+//   2. A static Read() method to set the contents of a |T| instance from a
+//      |MojomType|DataView (e.g., if |MojomType| is test::Example, the data
+//      view will be test::ExampleDataView).
+//
+//        static bool Read(|MojomType|DataView data, T* output);
+//
+//      The generated |MojomType|DataView type provides a convenient,
+//      inexpensive view of a serialized struct's field data.
+//
+//      Returning false indicates invalid incoming data and causes the message
+//      pipe receiving it to be disconnected. Therefore, you can do custom
+//      validation for |T| in this method.
+//
+//   3. [Optional] A static IsNull() method indicating whether a given |T|
+//      instance is null:
+//
+//        static bool IsNull(const T& input);
+//
+//      If this method returns true, it is guaranteed that none of the getters
+//      (described in section 1) will be called for the same |input|. So you
+//      don't have to check whether |input| is null in those getters.
+//
+//      If it is not defined, |T| instances are always considered non-null.
+//
+//      [Optional] A static SetToNull() method to set the contents of a given
+//      |T| instance to null.
+//
+//        static void SetToNull(T* output);
+//
+//      When a null serialized struct is received, the deserialization code
+//      calls this method instead of Read().
+//
+//      NOTE: It is to set |*output|'s contents to a null state, not to set the
+//      |output| pointer itself to null. "Null state" means whatever state you
+//      think it makes sense to map a null serialized struct to.
+//
+//      If it is not defined, null is not allowed to be converted to |T|. In
+//      that case, an incoming null value is considered invalid and causes the
+//      message pipe to be disconnected.
+//
+//   4. [Optional] As mentioned above, getters for string/struct/array/map/union
+//      fields are called multiple times (twice to be exact). If you need to do
+//      some expensive calculation/conversion, you probably want to cache the
+//      result across multiple calls. You can introduce an arbitrary context
+//      object by adding two optional methods:
+//        static void* SetUpContext(const T& input);
+//        static void TearDownContext(const T& input, void* context);
+//
+//      And then you append a second parameter, void* context, to getters:
+//        static <return type> <field name>(const T& input, void* context);
+//
+//      If a T instance is not null, the serialization code will call
+//      SetUpContext() at the beginning, and pass the resulting context pointer
+//      to getters. After serialization is done, it calls TearDownContext() so
+//      that you can do any necessary cleanup.
+//
+// In the description above, methods having an |input| parameter define it as
+// const reference of T. Actually, it can be a non-const reference of T too.
+// E.g., if T contains Mojo handles or interfaces whose ownership needs to be
+// transferred. Correspondingly, it requies you to always give non-const T
+// reference/value to the Mojo bindings for serialization:
+//    - if T is used in the "type_mappings" section of a typemap config file,
+//      you need to declare it as pass-by-value:
+//        type_mappings = [ "MojomType=T(pass_by_value)" ]
+//    - if another type U's StructTraits has a getter for T, it needs to return
+//      non-const reference/value.
+//
+// EXAMPLE:
+//
+// Mojom definition:
+//   struct Bar {};
+//   struct Foo {
+//     int32 f_integer;
+//     string f_string;
+//     array<string> f_string_array;
+//     Bar f_bar;
+//   };
+//
+// StructTraits for Foo:
+//   template <>
+//   struct StructTraits<Foo, CustomFoo> {
+//     // Optional methods dealing with null:
+//     static bool IsNull(const CustomFoo& input);
+//     static void SetToNull(CustomFoo* output);
+//
+//     // Field getters:
+//     static int32_t f_integer(const CustomFoo& input);
+//     static const std::string& f_string(const CustomFoo& input);
+//     static const std::vector<std::string>& f_string_array(
+//         const CustomFoo& input);
+//     // Assuming there is a StructTraits<Bar, CustomBar> defined.
+//     static const CustomBar& f_bar(const CustomFoo& input);
+//
+//     static bool Read(FooDataView data, CustomFoo* output);
+//   };
+//
+template <typename MojomType, typename T>
+struct StructTraits;
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h
new file mode 100644
index 0000000..e8fc1c4
--- /dev/null
+++ b/mojo/public/cpp/bindings/sync_call_restrictions.h
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All 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_SYNC_CALL_RESTRICTIONS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_SYNC_CALL_RESTRICTIONS_H_
+
+#include "base/macros.h"
+#include "base/threading/thread_restrictions.h"
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_SYNC_CALL_RESTRICTIONS 1
+#else
+#define ENABLE_SYNC_CALL_RESTRICTIONS 0
+#endif
+
+namespace ui {
+class GpuService;
+}
+
+namespace views {
+class ClipboardMus;
+}
+
+namespace mojo {
+
+// In some processes, sync calls are disallowed. For example, in the browser
+// process we don't want any sync calls to child processes for performance,
+// security and stability reasons. SyncCallRestrictions helps to enforce such
+// rules.
+//
+// Before processing a sync call, the bindings call
+// SyncCallRestrictions::AssertSyncCallAllowed() to check whether sync calls are
+// allowed. By default, it is determined by the mojo system property
+// MOJO_PROPERTY_SYNC_CALL_ALLOWED. If the default setting says no but you have
+// a very compelling reason to disregard that (which should be very very rare),
+// you can override it by constructing a ScopedAllowSyncCall object, which
+// allows making sync calls on the current thread during its lifetime.
+class SyncCallRestrictions {
+ public:
+#if ENABLE_SYNC_CALL_RESTRICTIONS
+  // Checks whether the current thread is allowed to make sync calls, and causes
+  // a DCHECK if not.
+  static void AssertSyncCallAllowed();
+#else
+  // Inline the empty definitions of functions so that they can be compiled out.
+  static void AssertSyncCallAllowed() {}
+#endif
+
+ private:
+  // DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to mojo/OWNERS first.
+  // BEGIN ALLOWED USAGE.
+  friend class ui::GpuService;  // http://crbug.com/620058
+  // END ALLOWED USAGE.
+
+  // BEGIN USAGE THAT NEEDS TO BE FIXED.
+  // In the non-mus case, we called blocking OS functions in the ui::Clipboard
+  // implementation which weren't caught by sync call restrictions. Our blocking
+  // calls to mus, however, are.
+  friend class views::ClipboardMus;
+  // END USAGE THAT NEEDS TO BE FIXED.
+
+#if ENABLE_SYNC_CALL_RESTRICTIONS
+  static void IncreaseScopedAllowCount();
+  static void DecreaseScopedAllowCount();
+#else
+  static void IncreaseScopedAllowCount() {}
+  static void DecreaseScopedAllowCount() {}
+#endif
+
+  // If a process is configured to disallow sync calls in general, constructing
+  // a ScopedAllowSyncCall object temporarily allows making sync calls on the
+  // current thread. Doing this is almost always incorrect, which is why we
+  // limit who can use this through friend. If you find yourself needing to use
+  // this, talk to mojo/OWNERS.
+  class ScopedAllowSyncCall {
+   public:
+    ScopedAllowSyncCall() { IncreaseScopedAllowCount(); }
+    ~ScopedAllowSyncCall() { DecreaseScopedAllowCount(); }
+
+   private:
+#if ENABLE_SYNC_CALL_RESTRICTIONS
+    base::ThreadRestrictions::ScopedAllowWait allow_wait_;
+#endif
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedAllowSyncCall);
+  };
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SyncCallRestrictions);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_SYNC_CALL_RESTRICTIONS_H_
diff --git a/mojo/public/cpp/bindings/sync_handle_registry.h b/mojo/public/cpp/bindings/sync_handle_registry.h
new file mode 100644
index 0000000..6c0701e
--- /dev/null
+++ b/mojo/public/cpp/bindings/sync_handle_registry.h
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All 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_SYNC_HANDLE_REGISTRY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_SYNC_HANDLE_REGISTRY_H_
+
+#include <unordered_map>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// SyncHandleRegistry is a thread-local storage to register handles that want to
+// be watched together.
+//
+// This class is not thread safe.
+class SyncHandleRegistry : public base::RefCounted<SyncHandleRegistry> {
+ public:
+  // Returns a thread-local object.
+  static scoped_refptr<SyncHandleRegistry> current();
+
+  using HandleCallback = base::Callback<void(MojoResult)>;
+  bool RegisterHandle(const Handle& handle,
+                      MojoHandleSignals handle_signals,
+                      const HandleCallback& callback);
+
+  void UnregisterHandle(const Handle& handle);
+
+  // Waits on all the registered handles and runs callbacks synchronously for
+  // those ready handles.
+  // The method:
+  //   - returns true when any element of |should_stop| is set to true;
+  //   - returns false when any error occurs.
+  bool WatchAllHandles(const bool* should_stop[], size_t count);
+
+ private:
+  friend class base::RefCounted<SyncHandleRegistry>;
+
+  struct HandleHasher {
+    size_t operator()(const Handle& handle) const {
+      return std::hash<uint32_t>()(static_cast<uint32_t>(handle.value()));
+    }
+  };
+  using HandleMap = std::unordered_map<Handle, HandleCallback, HandleHasher>;
+
+  SyncHandleRegistry();
+  ~SyncHandleRegistry();
+
+  HandleMap handles_;
+
+  ScopedHandle wait_set_handle_;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncHandleRegistry);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_SYNC_HANDLE_REGISTRY_H_
diff --git a/mojo/public/cpp/bindings/sync_handle_watcher.h b/mojo/public/cpp/bindings/sync_handle_watcher.h
new file mode 100644
index 0000000..36b796b
--- /dev/null
+++ b/mojo/public/cpp/bindings/sync_handle_watcher.h
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Authors. All 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_SYNC_HANDLE_WATCHER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_SYNC_HANDLE_WATCHER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/sync_handle_registry.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// SyncHandleWatcher supports watching a handle synchronously. It also supports
+// registering the handle with a thread-local storage (SyncHandleRegistry), so
+// that when other SyncHandleWatcher instances on the same thread perform sync
+// handle watching, this handle will be watched together.
+//
+// SyncHandleWatcher is used for sync methods. While a sync call is waiting for
+// response, we would like to block the thread. On the other hand, we need
+// incoming sync method requests on the same thread to be able to reenter. We
+// also need master interface endpoints to continue dispatching messages for
+// associated endpoints on different threads.
+//
+// This class is not thread safe.
+class SyncHandleWatcher {
+ public:
+  // Note: |handle| must outlive this object.
+  SyncHandleWatcher(const Handle& handle,
+                    MojoHandleSignals handle_signals,
+                    const SyncHandleRegistry::HandleCallback& callback);
+
+  ~SyncHandleWatcher();
+
+  // Registers |handle_| with SyncHandleRegistry, so that when others perform
+  // sync handle watching on the same thread, |handle_| will be watched
+  // together.
+  void AllowWokenUpBySyncWatchOnSameThread();
+
+  // Waits on |handle_| plus all handles registered with SyncHandleRegistry and
+  // runs callbacks synchronously for those ready handles.
+  // This method:
+  //   - returns true when |should_stop| is set to true;
+  //   - return false when any error occurs, including this object being
+  //     destroyed during a callback.
+  bool SyncWatch(const bool* should_stop);
+
+ private:
+  void IncrementRegisterCount();
+  void DecrementRegisterCount();
+
+  const Handle handle_;
+  const MojoHandleSignals handle_signals_;
+  SyncHandleRegistry::HandleCallback callback_;
+
+  // Whether |handle_| has been registered with SyncHandleRegistry.
+  bool registered_;
+  // If non-zero, |handle_| should be registered with SyncHandleRegistry.
+  size_t register_request_count_;
+
+  scoped_refptr<SyncHandleRegistry> registry_;
+
+  scoped_refptr<base::RefCountedData<bool>> destroyed_;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncHandleWatcher);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_SYNC_HANDLE_WATCHER_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..4e38f15
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -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.
+
+import("../../../mojo_application.gni")
+
+source_set("tests") {
+  testonly = true
+
+  sources = [
+    "array_common_test.h",
+    "array_unittest.cc",
+    "associated_interface_unittest.cc",
+    "bind_task_runner_unittest.cc",
+    "binding_callback_unittest.cc",
+    "binding_unittest.cc",
+    "buffer_unittest.cc",
+    "connector_unittest.cc",
+    "constant_unittest.cc",
+    "container_test_util.cc",
+    "container_test_util.h",
+    "equals_unittest.cc",
+    "handle_passing_unittest.cc",
+    "interface_ptr_unittest.cc",
+    "map_common_test.h",
+    "map_unittest.cc",
+    "message_queue.cc",
+    "message_queue.h",
+    "multiplex_router_unittest.cc",
+    "request_response_unittest.cc",
+    "router_test_util.cc",
+    "router_test_util.h",
+    "router_unittest.cc",
+    "sample_service_unittest.cc",
+    "serialization_warning_unittest.cc",
+    "stl_converters_unittest.cc",
+    "string_unittest.cc",
+    "struct_unittest.cc",
+    "sync_method_unittest.cc",
+    "type_conversion_unittest.cc",
+    "union_unittest.cc",
+    "validation_context_unittest.cc",
+    "validation_unittest.cc",
+    "variant_test_util.h",
+  ]
+
+  deps = [
+    ":mojo_public_bindings_test_utils",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//mojo/public/interfaces/bindings/tests:test_associated_interfaces",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
+    "//mojo/public/interfaces/bindings/tests:test_struct_traits_interfaces",
+    "//testing/gtest",
+  ]
+
+  data = [
+    "//mojo/public/interfaces/bindings/tests/data/validation/",
+  ]
+
+  if (is_ios) {
+    assert_no_deps = [ "//third_party/WebKit/*" ]
+  } else {
+    sources += [
+      "pickle_unittest.cc",
+      "struct_traits_unittest.cc",
+    ]
+
+    deps += [ "//mojo/public/interfaces/bindings/tests:test_interfaces_blink" ]
+  }
+}
+
+if (!is_ios) {
+  source_set("for_blink_tests") {
+    testonly = true
+
+    sources = [
+      "array_common_test.h",
+      "container_test_util.cc",
+      "container_test_util.h",
+      "map_common_test.h",
+      "variant_test_util.h",
+      "wtf_array_unittest.cc",
+      "wtf_map_unittest.cc",
+      "wtf_types_unittest.cc",
+    ]
+
+    deps = [
+      "//mojo/public/cpp/bindings",
+      "//mojo/public/cpp/system",
+      "//mojo/public/interfaces/bindings/tests:test_interfaces",
+      "//mojo/public/interfaces/bindings/tests:test_wtf_types",
+      "//mojo/public/interfaces/bindings/tests:test_wtf_types_blink",
+      "//testing/gtest",
+    ]
+  }
+}
+
+source_set("struct_with_traits_impl") {
+  sources = [
+    "struct_with_traits_impl.cc",
+    "struct_with_traits_impl.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/public/cpp/system:system",
+  ]
+}
+
+source_set("perftests") {
+  testonly = true
+
+  sources = [
+    "bindings_perftest.cc",
+  ]
+
+  if (!is_ios) {
+    sources += [ "e2e_perftest.cc" ]
+  }
+
+  deps = [
+    "//base/test:test_support",
+    "//mojo/edk/test:test_support",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+    "//testing/gtest",
+  ]
+}
+
+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/array_common_test.h b/mojo/public/cpp/bindings/tests/array_common_test.h
new file mode 100644
index 0000000..6b4bcfb
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/array_common_test.h
@@ -0,0 +1,404 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <utility>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/tests/container_test_util.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+// Common tests for both mojo::Array and mojo::WTFArray.
+template <template <typename...> class ArrayType>
+class ArrayCommonTest {
+ public:
+  // Tests null and empty arrays.
+  static void NullAndEmpty() {
+    ArrayType<char> array0;
+    EXPECT_TRUE(array0.empty());
+    EXPECT_FALSE(array0.is_null());
+    array0 = nullptr;
+    EXPECT_TRUE(array0.is_null());
+    EXPECT_FALSE(array0.empty());
+
+    ArrayType<char> array1(nullptr);
+    EXPECT_TRUE(array1.is_null());
+    EXPECT_FALSE(array1.empty());
+    array1.SetToEmpty();
+    EXPECT_TRUE(array1.empty());
+    EXPECT_FALSE(array1.is_null());
+  }
+
+  // Tests that basic array operations work.
+  static void Basic() {
+    ArrayType<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 ArrayType<bool> operations work.
+  static void Bool() {
+    ArrayType<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 ArrayType<ScopedMessagePipeHandle> supports transferring
+  // handles.
+  static void Handle() {
+    MessagePipe pipe;
+    ArrayType<ScopedMessagePipeHandle> handles(2);
+    handles[0] = std::move(pipe.handle0);
+    handles[1].reset(pipe.handle1.release());
+
+    EXPECT_FALSE(pipe.handle0.is_valid());
+    EXPECT_FALSE(pipe.handle1.is_valid());
+
+    ArrayType<ScopedMessagePipeHandle> handles2 = std::move(handles);
+    EXPECT_TRUE(handles2[0].is_valid());
+    EXPECT_TRUE(handles2[1].is_valid());
+
+    ScopedMessagePipeHandle pipe_handle = std::move(handles2[0]);
+    EXPECT_TRUE(pipe_handle.is_valid());
+    EXPECT_FALSE(handles2[0].is_valid());
+  }
+
+  // Tests that ArrayType<ScopedMessagePipeHandle> supports closing handles.
+  static void HandlesAreClosed() {
+    MessagePipe pipe;
+    MojoHandle pipe0_value = pipe.handle0.get().value();
+    MojoHandle pipe1_value = pipe.handle0.get().value();
+
+    {
+      ArrayType<ScopedMessagePipeHandle> handles(2);
+      handles[0] = std::move(pipe.handle0);
+      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));
+  }
+
+  static void Clone() {
+    {
+      // Test POD.
+      ArrayType<int32_t> array(3);
+      for (size_t i = 0; i < array.size(); ++i)
+        array[i] = static_cast<int32_t>(i);
+
+      ArrayType<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.
+      ArrayType<String> array(2);
+      array[0] = "hello";
+      array[1] = "world";
+
+      ArrayType<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.
+      ArrayType<RectPtr> array(2);
+      array[1] = Rect::New();
+      array[1]->x = 1;
+      array[1]->y = 2;
+      array[1]->width = 3;
+      array[1]->height = 4;
+
+      ArrayType<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.
+      ArrayType<ArrayType<int8_t>> array(2);
+      array[0] = nullptr;
+      array[1] = ArrayType<int8_t>(2);
+      array[1][0] = 0;
+      array[1][1] = 1;
+
+      ArrayType<ArrayType<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.
+      ArrayType<ScopedMessagePipeHandle> array(10);
+      EXPECT_FALSE(array[0].is_valid());
+    }
+  }
+
+  static void Serialization_ArrayOfPOD() {
+    ArrayType<int32_t> array(4);
+    for (size_t i = 0; i < array.size(); ++i)
+      array[i] = static_cast<int32_t>(i);
+
+    size_t size =
+        mojo::internal::PrepareToSerialize<Array<int32_t>>(array, nullptr);
+    EXPECT_EQ(8U + 4 * 4U, size);
+
+    mojo::internal::FixedBufferForTesting buf(size);
+    mojo::internal::Array_Data<int32_t>* data;
+    mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
+    mojo::internal::Serialize<Array<int32_t>>(array, &buf, &data,
+                                              &validate_params, nullptr);
+
+    ArrayType<int32_t> array2;
+    mojo::internal::Deserialize<Array<int32_t>>(data, &array2, nullptr);
+
+    EXPECT_EQ(4U, array2.size());
+    for (size_t i = 0; i < array2.size(); ++i)
+      EXPECT_EQ(static_cast<int32_t>(i), array2[i]);
+  }
+
+  static void Serialization_EmptyArrayOfPOD() {
+    ArrayType<int32_t> array;
+    size_t size =
+        mojo::internal::PrepareToSerialize<Array<int32_t>>(array, nullptr);
+    EXPECT_EQ(8U, size);
+
+    mojo::internal::FixedBufferForTesting buf(size);
+    mojo::internal::Array_Data<int32_t>* data;
+    mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
+    mojo::internal::Serialize<Array<int32_t>>(array, &buf, &data,
+                                              &validate_params, nullptr);
+
+    ArrayType<int32_t> array2;
+    mojo::internal::Deserialize<Array<int32_t>>(data, &array2, nullptr);
+    EXPECT_EQ(0U, array2.size());
+  }
+
+  static void Serialization_ArrayOfArrayOfPOD() {
+    ArrayType<ArrayType<int32_t>> array(2);
+    for (size_t j = 0; j < array.size(); ++j) {
+      ArrayType<int32_t> inner(4);
+      for (size_t i = 0; i < inner.size(); ++i)
+        inner[i] = static_cast<int32_t>(i + (j * 10));
+      array[j] = std::move(inner);
+    }
+
+    size_t size = mojo::internal::PrepareToSerialize<Array<Array<int32_t>>>(
+        array, nullptr);
+    EXPECT_EQ(8U + 2 * 8U + 2 * (8U + 4 * 4U), size);
+
+    mojo::internal::FixedBufferForTesting buf(size);
+    typename mojo::internal::MojomTypeTraits<Array<Array<int32_t>>>::Data* data;
+    mojo::internal::ContainerValidateParams validate_params(
+        0, false,
+        new mojo::internal::ContainerValidateParams(0, false, nullptr));
+    mojo::internal::Serialize<Array<Array<int32_t>>>(array, &buf, &data,
+                                                     &validate_params, nullptr);
+
+    ArrayType<ArrayType<int32_t>> array2;
+    mojo::internal::Deserialize<Array<Array<int32_t>>>(data, &array2, nullptr);
+
+    EXPECT_EQ(2U, array2.size());
+    for (size_t j = 0; j < array2.size(); ++j) {
+      const ArrayType<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]);
+    }
+  }
+
+  static void Serialization_ArrayOfBool() {
+    ArrayType<bool> array(10);
+    for (size_t i = 0; i < array.size(); ++i)
+      array[i] = i % 2 ? true : false;
+
+    size_t size =
+        mojo::internal::PrepareToSerialize<Array<bool>>(array, nullptr);
+    EXPECT_EQ(8U + 8U, size);
+
+    mojo::internal::FixedBufferForTesting buf(size);
+    mojo::internal::Array_Data<bool>* data;
+    mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
+    mojo::internal::Serialize<Array<bool>>(array, &buf, &data, &validate_params,
+                                           nullptr);
+
+    ArrayType<bool> array2;
+    mojo::internal::Deserialize<Array<bool>>(data, &array2, nullptr);
+
+    EXPECT_EQ(10U, array2.size());
+    for (size_t i = 0; i < array2.size(); ++i)
+      EXPECT_EQ(i % 2 ? true : false, array2[i]);
+  }
+
+  static void Serialization_ArrayOfString() {
+    ArrayType<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 =
+        mojo::internal::PrepareToSerialize<Array<String>>(array, nullptr);
+    EXPECT_EQ(8U +            // array header
+                  10 * 8U +   // array payload (10 pointers)
+                  10 * (8U +  // string header
+                        8U),  // string length of 1 padded to 8
+              size);
+
+    mojo::internal::FixedBufferForTesting buf(size);
+    typename mojo::internal::MojomTypeTraits<Array<String>>::Data* data;
+    mojo::internal::ContainerValidateParams validate_params(
+        0, false,
+        new mojo::internal::ContainerValidateParams(0, false, nullptr));
+    mojo::internal::Serialize<Array<String>>(array, &buf, &data,
+                                             &validate_params, nullptr);
+
+    ArrayType<String> array2;
+    mojo::internal::Deserialize<Array<String>>(data, &array2, nullptr);
+
+    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]);
+    }
+  }
+
+  static void Resize_Copyable() {
+    ASSERT_EQ(0u, CopyableType::num_instances());
+    ArrayType<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 = nullptr;
+    EXPECT_EQ(0u, CopyableType::num_instances());
+    EXPECT_FALSE(array);
+    array.resize(0);
+    EXPECT_EQ(0u, CopyableType::num_instances());
+    EXPECT_TRUE(array);
+  }
+
+  static void Resize_MoveOnly() {
+    ASSERT_EQ(0u, MoveOnlyType::num_instances());
+    ArrayType<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 = nullptr;
+    EXPECT_EQ(0u, MoveOnlyType::num_instances());
+    EXPECT_FALSE(array);
+    array.resize(0);
+    EXPECT_EQ(0u, MoveOnlyType::num_instances());
+    EXPECT_TRUE(array);
+  }
+};
+
+#define ARRAY_COMMON_TEST(ArrayType, test_name) \
+  TEST_F(ArrayType##Test, test_name) {          \
+    ArrayCommonTest<ArrayType>::test_name();    \
+  }
+
+}  // namespace test
+}  // namespace mojo
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..0700bb1
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/array_unittest.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/public/cpp/bindings/array.h"
+
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/tests/array_common_test.h"
+#include "mojo/public/cpp/bindings/tests/container_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using ArrayTest = testing::Test;
+
+ARRAY_COMMON_TEST(Array, NullAndEmpty)
+ARRAY_COMMON_TEST(Array, Basic)
+ARRAY_COMMON_TEST(Array, Bool)
+ARRAY_COMMON_TEST(Array, Handle)
+ARRAY_COMMON_TEST(Array, HandlesAreClosed)
+ARRAY_COMMON_TEST(Array, Clone)
+ARRAY_COMMON_TEST(Array, Serialization_ArrayOfPOD)
+ARRAY_COMMON_TEST(Array, Serialization_EmptyArrayOfPOD)
+ARRAY_COMMON_TEST(Array, Serialization_ArrayOfArrayOfPOD)
+ARRAY_COMMON_TEST(Array, Serialization_ArrayOfBool)
+ARRAY_COMMON_TEST(Array, Serialization_ArrayOfString)
+ARRAY_COMMON_TEST(Array, Resize_Copyable)
+ARRAY_COMMON_TEST(Array, Resize_MoveOnly)
+
+TEST_F(ArrayTest, PushBack_Copyable) {
+  ASSERT_EQ(0u, CopyableType::num_instances());
+  Array<CopyableType> array(2);
+  array = nullptr;
+  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 = nullptr;
+  EXPECT_EQ(0u, CopyableType::num_instances());
+}
+
+TEST_F(ArrayTest, PushBack_MoveOnly) {
+  ASSERT_EQ(0u, MoveOnlyType::num_instances());
+  Array<MoveOnlyType> array(2);
+  array = nullptr;
+  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(std::move(value));
+    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(std::move(value));
+    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 = nullptr;
+  EXPECT_EQ(0u, MoveOnlyType::num_instances());
+}
+
+TEST_F(ArrayTest, MoveFromAndToSTLVector_Copyable) {
+  std::vector<CopyableType> vec1(1);
+  Array<CopyableType> arr(std::move(vec1));
+  ASSERT_EQ(1u, arr.size());
+  ASSERT_FALSE(arr[0].copied());
+
+  std::vector<CopyableType> vec2(arr.PassStorage());
+  ASSERT_EQ(1u, vec2.size());
+  ASSERT_FALSE(vec2[0].copied());
+
+  ASSERT_EQ(0u, arr.size());
+  ASSERT_TRUE(arr.is_null());
+}
+
+TEST_F(ArrayTest, MoveFromAndToSTLVector_MoveOnly) {
+  std::vector<MoveOnlyType> vec1(1);
+  Array<MoveOnlyType> arr(std::move(vec1));
+
+  ASSERT_EQ(1u, arr.size());
+
+  std::vector<MoveOnlyType> vec2(arr.PassStorage());
+  ASSERT_EQ(1u, vec2.size());
+
+  ASSERT_EQ(0u, arr.size());
+  ASSERT_TRUE(arr.is_null());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc b/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc
new file mode 100644
index 0000000..8f2eb08
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc
@@ -0,0 +1,594 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+#include "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using mojo::internal::MultiplexRouter;
+
+class IntegerSenderImpl : public IntegerSender {
+ public:
+  explicit IntegerSenderImpl(AssociatedInterfaceRequest<IntegerSender> request)
+      : binding_(this, std::move(request)) {}
+
+  ~IntegerSenderImpl() override {}
+
+  void set_notify_send_method_called(
+      const base::Callback<void(int32_t)>& callback) {
+    notify_send_method_called_ = callback;
+  }
+
+  void Echo(int32_t value, const EchoCallback& callback) override {
+    callback.Run(value);
+  }
+  void Send(int32_t value) override { notify_send_method_called_.Run(value); }
+
+  AssociatedBinding<IntegerSender>* binding() { return &binding_; }
+
+  void set_connection_error_handler(const base::Closure& handler) {
+    binding_.set_connection_error_handler(handler);
+  }
+
+ private:
+  AssociatedBinding<IntegerSender> binding_;
+  base::Callback<void(int32_t)> notify_send_method_called_;
+};
+
+class IntegerSenderConnectionImpl : public IntegerSenderConnection {
+ public:
+  explicit IntegerSenderConnectionImpl(
+      InterfaceRequest<IntegerSenderConnection> request)
+      : binding_(this, std::move(request)) {}
+
+  ~IntegerSenderConnectionImpl() override {}
+
+  void GetSender(AssociatedInterfaceRequest<IntegerSender> sender) override {
+    IntegerSenderImpl* sender_impl = new IntegerSenderImpl(std::move(sender));
+    sender_impl->set_connection_error_handler(
+        base::Bind(&DeleteSender, sender_impl));
+  }
+
+  void AsyncGetSender(const AsyncGetSenderCallback& callback) override {
+    AssociatedInterfaceRequest<IntegerSender> request;
+    IntegerSenderAssociatedPtrInfo ptr_info;
+    binding_.associated_group()->CreateAssociatedInterface(
+        AssociatedGroup::WILL_PASS_PTR, &ptr_info, &request);
+    GetSender(std::move(request));
+    callback.Run(std::move(ptr_info));
+  }
+
+  Binding<IntegerSenderConnection>* binding() { return &binding_; }
+
+ private:
+  static void DeleteSender(IntegerSenderImpl* sender) { delete sender; }
+
+  Binding<IntegerSenderConnection> binding_;
+};
+
+class AssociatedInterfaceTest : public testing::Test {
+ public:
+  AssociatedInterfaceTest() {}
+  ~AssociatedInterfaceTest() override { base::RunLoop().RunUntilIdle(); }
+
+  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
+
+  template <typename T>
+  AssociatedInterfacePtrInfo<T> EmulatePassingAssociatedPtrInfo(
+      AssociatedInterfacePtrInfo<T> ptr_info,
+      scoped_refptr<MultiplexRouter> target) {
+    ScopedInterfaceEndpointHandle handle = ptr_info.PassHandle();
+    CHECK(!handle.is_local());
+    return AssociatedInterfacePtrInfo<T>(
+        target->CreateLocalEndpointHandle(handle.release()),
+        ptr_info.version());
+  }
+
+  template <typename T>
+  AssociatedInterfaceRequest<T> EmulatePassingAssociatedRequest(
+      AssociatedInterfaceRequest<T> request,
+      scoped_refptr<MultiplexRouter> target) {
+    ScopedInterfaceEndpointHandle handle = request.PassHandle();
+    CHECK(!handle.is_local());
+    return MakeAssociatedRequest<T>(
+        target->CreateLocalEndpointHandle(handle.release()));
+  }
+
+  // Okay to call from any thread.
+  void QuitRunLoop(base::RunLoop* run_loop) {
+    if (loop_.task_runner()->BelongsToCurrentThread()) {
+      run_loop->Quit();
+    } else {
+      loop_.task_runner()->PostTask(
+          FROM_HERE,
+          base::Bind(&AssociatedInterfaceTest::QuitRunLoop,
+                     base::Unretained(this), base::Unretained(run_loop)));
+    }
+  }
+
+ private:
+  base::MessageLoop loop_;
+};
+
+void DoSetFlagAndRunClosure(bool* flag, const base::Closure& closure) {
+  *flag = true;
+  closure.Run();
+}
+
+void DoExpectValueSetFlagAndRunClosure(int32_t expected_value,
+                                       bool* flag,
+                                       const base::Closure& closure,
+                                       int32_t value) {
+  EXPECT_EQ(expected_value, value);
+  DoSetFlagAndRunClosure(flag, closure);
+}
+
+base::Closure SetFlagAndRunClosure(bool* flag, const base::Closure& closure) {
+  return base::Bind(&DoSetFlagAndRunClosure, flag, closure);
+}
+
+base::Callback<void(int32_t)> ExpectValueSetFlagAndRunClosure(
+    int32_t expected_value,
+    bool* flag,
+    const base::Closure& closure) {
+  return base::Bind(
+      &DoExpectValueSetFlagAndRunClosure, expected_value, flag, closure);
+}
+
+TEST_F(AssociatedInterfaceTest, InterfacesAtBothEnds) {
+  // Bind to the same pipe two associated interfaces, whose implementation lives
+  // at different ends. Test that the two don't interfere with each other.
+
+  MessagePipe pipe;
+  scoped_refptr<MultiplexRouter> router0(new MultiplexRouter(
+      true, std::move(pipe.handle0), base::ThreadTaskRunnerHandle::Get()));
+  scoped_refptr<MultiplexRouter> router1(new MultiplexRouter(
+      false, std::move(pipe.handle1), base::ThreadTaskRunnerHandle::Get()));
+
+  AssociatedInterfaceRequest<IntegerSender> request;
+  IntegerSenderAssociatedPtrInfo ptr_info;
+
+  router0->CreateAssociatedGroup()->CreateAssociatedInterface(
+      AssociatedGroup::WILL_PASS_PTR, &ptr_info, &request);
+  ptr_info = EmulatePassingAssociatedPtrInfo(std::move(ptr_info), router1);
+
+  IntegerSenderImpl impl0(std::move(request));
+  AssociatedInterfacePtr<IntegerSender> ptr0;
+  ptr0.Bind(std::move(ptr_info));
+
+  router0->CreateAssociatedGroup()->CreateAssociatedInterface(
+      AssociatedGroup::WILL_PASS_REQUEST, &ptr_info, &request);
+  request = EmulatePassingAssociatedRequest(std::move(request), router1);
+
+  IntegerSenderImpl impl1(std::move(request));
+  AssociatedInterfacePtr<IntegerSender> ptr1;
+  ptr1.Bind(std::move(ptr_info));
+
+  base::RunLoop run_loop, run_loop2;
+  bool ptr0_callback_run = false;
+  ptr0->Echo(123, ExpectValueSetFlagAndRunClosure(123, &ptr0_callback_run,
+                                                  run_loop.QuitClosure()));
+
+  bool ptr1_callback_run = false;
+  ptr1->Echo(456, ExpectValueSetFlagAndRunClosure(456, &ptr1_callback_run,
+                                                  run_loop2.QuitClosure()));
+
+  run_loop.Run();
+  run_loop2.Run();
+  EXPECT_TRUE(ptr0_callback_run);
+  EXPECT_TRUE(ptr1_callback_run);
+
+  bool ptr0_error_callback_run = false;
+  base::RunLoop run_loop3;
+  ptr0.set_connection_error_handler(
+      SetFlagAndRunClosure(&ptr0_error_callback_run, run_loop3.QuitClosure()));
+
+  impl0.binding()->Close();
+  run_loop3.Run();
+  EXPECT_TRUE(ptr0_error_callback_run);
+
+  bool impl1_error_callback_run = false;
+  base::RunLoop run_loop4;
+  impl1.binding()->set_connection_error_handler(
+      SetFlagAndRunClosure(&impl1_error_callback_run, run_loop4.QuitClosure()));
+
+  ptr1.reset();
+  run_loop4.Run();
+  EXPECT_TRUE(impl1_error_callback_run);
+}
+
+class TestSender {
+ public:
+  TestSender()
+      : sender_thread_("TestSender"),
+        next_sender_(nullptr),
+        max_value_to_send_(-1) {
+    sender_thread_.Start();
+  }
+
+  // The following three methods are called on the corresponding sender thread.
+  void SetUp(IntegerSenderAssociatedPtrInfo ptr_info,
+             TestSender* next_sender,
+             int32_t max_value_to_send) {
+    CHECK(sender_thread_.task_runner()->BelongsToCurrentThread());
+
+    ptr_.Bind(std::move(ptr_info));
+    next_sender_ = next_sender ? next_sender : this;
+    max_value_to_send_ = max_value_to_send;
+  }
+
+  void Send(int32_t value) {
+    CHECK(sender_thread_.task_runner()->BelongsToCurrentThread());
+
+    if (value > max_value_to_send_)
+      return;
+
+    ptr_->Send(value);
+
+    next_sender_->sender_thread()->task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&TestSender::Send, base::Unretained(next_sender_), ++value));
+  }
+
+  void TearDown() {
+    CHECK(sender_thread_.task_runner()->BelongsToCurrentThread());
+
+    ptr_.reset();
+  }
+
+  base::Thread* sender_thread() { return &sender_thread_; }
+
+ private:
+  base::Thread sender_thread_;
+  TestSender* next_sender_;
+  int32_t max_value_to_send_;
+
+  AssociatedInterfacePtr<IntegerSender> ptr_;
+};
+
+class TestReceiver {
+ public:
+  TestReceiver() : receiver_thread_("TestReceiver"), expected_calls_(0) {
+    receiver_thread_.Start();
+  }
+
+  void SetUp(AssociatedInterfaceRequest<IntegerSender> request0,
+             AssociatedInterfaceRequest<IntegerSender> request1,
+             size_t expected_calls,
+             const base::Closure& notify_finish) {
+    CHECK(receiver_thread_.task_runner()->BelongsToCurrentThread());
+
+    impl0_.reset(new IntegerSenderImpl(std::move(request0)));
+    impl0_->set_notify_send_method_called(
+        base::Bind(&TestReceiver::SendMethodCalled, base::Unretained(this)));
+    impl1_.reset(new IntegerSenderImpl(std::move(request1)));
+    impl1_->set_notify_send_method_called(
+        base::Bind(&TestReceiver::SendMethodCalled, base::Unretained(this)));
+
+    expected_calls_ = expected_calls;
+    notify_finish_ = notify_finish;
+  }
+
+  void TearDown() {
+    CHECK(receiver_thread_.task_runner()->BelongsToCurrentThread());
+
+    impl0_.reset();
+    impl1_.reset();
+  }
+
+  base::Thread* receiver_thread() { return &receiver_thread_; }
+  const std::vector<int32_t>& values() const { return values_; }
+
+ private:
+  void SendMethodCalled(int32_t value) {
+    values_.push_back(value);
+
+    if (values_.size() >= expected_calls_)
+      notify_finish_.Run();
+  }
+
+  base::Thread receiver_thread_;
+  size_t expected_calls_;
+
+  std::unique_ptr<IntegerSenderImpl> impl0_;
+  std::unique_ptr<IntegerSenderImpl> impl1_;
+
+  std::vector<int32_t> values_;
+
+  base::Closure notify_finish_;
+};
+
+class NotificationCounter {
+ public:
+  NotificationCounter(size_t total_count, const base::Closure& notify_finish)
+      : total_count_(total_count),
+        current_count_(0),
+        notify_finish_(notify_finish) {}
+
+  ~NotificationCounter() {}
+
+  // Okay to call from any thread.
+  void OnGotNotification() {
+    bool finshed = false;
+    {
+      base::AutoLock locker(lock_);
+      CHECK_LT(current_count_, total_count_);
+      current_count_++;
+      finshed = current_count_ == total_count_;
+    }
+
+    if (finshed)
+      notify_finish_.Run();
+  }
+
+ private:
+  base::Lock lock_;
+  const size_t total_count_;
+  size_t current_count_;
+  base::Closure notify_finish_;
+};
+
+TEST_F(AssociatedInterfaceTest, MultiThreadAccess) {
+  // Set up four associated interfaces on a message pipe. Use the inteface
+  // pointers on four threads in parallel; run the interface implementations on
+  // two threads. Test that multi-threaded access works.
+
+  const int32_t kMaxValue = 1000;
+  MessagePipe pipe;
+  scoped_refptr<MultiplexRouter> router0(new MultiplexRouter(
+      true, std::move(pipe.handle0), base::ThreadTaskRunnerHandle::Get()));
+  scoped_refptr<MultiplexRouter> router1(new MultiplexRouter(
+      false, std::move(pipe.handle1), base::ThreadTaskRunnerHandle::Get()));
+
+  AssociatedInterfaceRequest<IntegerSender> requests[4];
+  IntegerSenderAssociatedPtrInfo ptr_infos[4];
+
+  for (size_t i = 0; i < 4; ++i) {
+    router0->CreateAssociatedGroup()->CreateAssociatedInterface(
+        AssociatedGroup::WILL_PASS_PTR, &ptr_infos[i], &requests[i]);
+    ptr_infos[i] =
+        EmulatePassingAssociatedPtrInfo(std::move(ptr_infos[i]), router1);
+  }
+
+  TestSender senders[4];
+  for (size_t i = 0; i < 4; ++i) {
+    senders[i].sender_thread()->task_runner()->PostTask(
+        FROM_HERE, base::Bind(&TestSender::SetUp, base::Unretained(&senders[i]),
+                              base::Passed(&ptr_infos[i]), nullptr,
+                              kMaxValue * (i + 1) / 4));
+  }
+
+  base::RunLoop run_loop;
+  TestReceiver receivers[2];
+  NotificationCounter counter(
+      2, base::Bind(&AssociatedInterfaceTest::QuitRunLoop,
+                    base::Unretained(this), base::Unretained(&run_loop)));
+  for (size_t i = 0; i < 2; ++i) {
+    receivers[i].receiver_thread()->task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&TestReceiver::SetUp, base::Unretained(&receivers[i]),
+                   base::Passed(&requests[2 * i]),
+                   base::Passed(&requests[2 * i + 1]),
+                   static_cast<size_t>(kMaxValue / 2),
+                   base::Bind(&NotificationCounter::OnGotNotification,
+                              base::Unretained(&counter))));
+  }
+
+  for (size_t i = 0; i < 4; ++i) {
+    senders[i].sender_thread()->task_runner()->PostTask(
+        FROM_HERE, base::Bind(&TestSender::Send, base::Unretained(&senders[i]),
+                              kMaxValue * i / 4 + 1));
+  }
+
+  run_loop.Run();
+
+  for (size_t i = 0; i < 4; ++i) {
+    base::RunLoop run_loop;
+    senders[i].sender_thread()->task_runner()->PostTaskAndReply(
+        FROM_HERE,
+        base::Bind(&TestSender::TearDown, base::Unretained(&senders[i])),
+        base::Bind(&AssociatedInterfaceTest::QuitRunLoop,
+                   base::Unretained(this), base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+
+  for (size_t i = 0; i < 2; ++i) {
+    base::RunLoop run_loop;
+    receivers[i].receiver_thread()->task_runner()->PostTaskAndReply(
+        FROM_HERE,
+        base::Bind(&TestReceiver::TearDown, base::Unretained(&receivers[i])),
+        base::Bind(&AssociatedInterfaceTest::QuitRunLoop,
+                   base::Unretained(this), base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(static_cast<size_t>(kMaxValue / 2), receivers[0].values().size());
+  EXPECT_EQ(static_cast<size_t>(kMaxValue / 2), receivers[1].values().size());
+
+  std::vector<int32_t> all_values;
+  all_values.insert(all_values.end(), receivers[0].values().begin(),
+                    receivers[0].values().end());
+  all_values.insert(all_values.end(), receivers[1].values().begin(),
+                    receivers[1].values().end());
+
+  std::sort(all_values.begin(), all_values.end());
+  for (size_t i = 0; i < all_values.size(); ++i)
+    ASSERT_EQ(static_cast<int32_t>(i + 1), all_values[i]);
+}
+
+TEST_F(AssociatedInterfaceTest, FIFO) {
+  // Set up four associated interfaces on a message pipe. Use the inteface
+  // pointers on four threads; run the interface implementations on two threads.
+  // Take turns to make calls using the four pointers. Test that FIFO-ness is
+  // preserved.
+
+  const int32_t kMaxValue = 100;
+  MessagePipe pipe;
+  scoped_refptr<MultiplexRouter> router0(new MultiplexRouter(
+      true, std::move(pipe.handle0), base::ThreadTaskRunnerHandle::Get()));
+  scoped_refptr<MultiplexRouter> router1(new MultiplexRouter(
+      false, std::move(pipe.handle1), base::ThreadTaskRunnerHandle::Get()));
+
+  AssociatedInterfaceRequest<IntegerSender> requests[4];
+  IntegerSenderAssociatedPtrInfo ptr_infos[4];
+
+  for (size_t i = 0; i < 4; ++i) {
+    router0->CreateAssociatedGroup()->CreateAssociatedInterface(
+        AssociatedGroup::WILL_PASS_PTR, &ptr_infos[i], &requests[i]);
+    ptr_infos[i] =
+        EmulatePassingAssociatedPtrInfo(std::move(ptr_infos[i]), router1);
+  }
+
+  TestSender senders[4];
+  for (size_t i = 0; i < 4; ++i) {
+    senders[i].sender_thread()->task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&TestSender::SetUp, base::Unretained(&senders[i]),
+                   base::Passed(&ptr_infos[i]),
+                   base::Unretained(&senders[(i + 1) % 4]), kMaxValue));
+  }
+
+  base::RunLoop run_loop;
+  TestReceiver receivers[2];
+  NotificationCounter counter(
+      2, base::Bind(&AssociatedInterfaceTest::QuitRunLoop,
+                    base::Unretained(this), base::Unretained(&run_loop)));
+  for (size_t i = 0; i < 2; ++i) {
+    receivers[i].receiver_thread()->task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&TestReceiver::SetUp, base::Unretained(&receivers[i]),
+                   base::Passed(&requests[2 * i]),
+                   base::Passed(&requests[2 * i + 1]),
+                   static_cast<size_t>(kMaxValue / 2),
+                   base::Bind(&NotificationCounter::OnGotNotification,
+                              base::Unretained(&counter))));
+  }
+
+  senders[0].sender_thread()->task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&TestSender::Send, base::Unretained(&senders[0]), 1));
+
+  run_loop.Run();
+
+  for (size_t i = 0; i < 4; ++i) {
+    base::RunLoop run_loop;
+    senders[i].sender_thread()->task_runner()->PostTaskAndReply(
+        FROM_HERE,
+        base::Bind(&TestSender::TearDown, base::Unretained(&senders[i])),
+        base::Bind(&AssociatedInterfaceTest::QuitRunLoop,
+                   base::Unretained(this), base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+
+  for (size_t i = 0; i < 2; ++i) {
+    base::RunLoop run_loop;
+    receivers[i].receiver_thread()->task_runner()->PostTaskAndReply(
+        FROM_HERE,
+        base::Bind(&TestReceiver::TearDown, base::Unretained(&receivers[i])),
+        base::Bind(&AssociatedInterfaceTest::QuitRunLoop,
+                   base::Unretained(this), base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(static_cast<size_t>(kMaxValue / 2), receivers[0].values().size());
+  EXPECT_EQ(static_cast<size_t>(kMaxValue / 2), receivers[1].values().size());
+
+  for (size_t i = 0; i < 2; ++i) {
+    for (size_t j = 1; j < receivers[i].values().size(); ++j)
+      EXPECT_LT(receivers[i].values()[j - 1], receivers[i].values()[j]);
+  }
+}
+
+void CaptureInt32(int32_t* storage,
+                  const base::Closure& closure,
+                  int32_t value) {
+  *storage = value;
+  closure.Run();
+}
+
+void CaptureSenderPtrInfo(IntegerSenderAssociatedPtr* storage,
+                          const base::Closure& closure,
+                          IntegerSenderAssociatedPtrInfo info) {
+  storage->Bind(std::move(info));
+  closure.Run();
+}
+
+TEST_F(AssociatedInterfaceTest, PassAssociatedInterfaces) {
+  IntegerSenderConnectionPtr connection_ptr;
+  IntegerSenderConnectionImpl connection(GetProxy(&connection_ptr));
+
+  IntegerSenderAssociatedPtr sender0;
+  connection_ptr->GetSender(
+      GetProxy(&sender0, connection_ptr.associated_group()));
+
+  int32_t echoed_value = 0;
+  base::RunLoop run_loop;
+  sender0->Echo(123, base::Bind(&CaptureInt32, &echoed_value,
+                                run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_EQ(123, echoed_value);
+
+  IntegerSenderAssociatedPtr sender1;
+  base::RunLoop run_loop2;
+  connection_ptr->AsyncGetSender(
+      base::Bind(&CaptureSenderPtrInfo, &sender1, run_loop2.QuitClosure()));
+  run_loop2.Run();
+  EXPECT_TRUE(sender1);
+
+  base::RunLoop run_loop3;
+  sender1->Echo(456, base::Bind(&CaptureInt32, &echoed_value,
+                                run_loop3.QuitClosure()));
+  run_loop3.Run();
+  EXPECT_EQ(456, echoed_value);
+}
+
+TEST_F(AssociatedInterfaceTest, BindingWaitAndPauseWhenNoAssociatedInterfaces) {
+  IntegerSenderConnectionPtr connection_ptr;
+  IntegerSenderConnectionImpl connection(GetProxy(&connection_ptr));
+
+  IntegerSenderAssociatedPtr sender0;
+  connection_ptr->GetSender(
+      GetProxy(&sender0, connection_ptr.associated_group()));
+
+  EXPECT_FALSE(connection.binding()->HasAssociatedInterfaces());
+  // There are no associated interfaces running on the pipe yet. It is okay to
+  // pause.
+  connection.binding()->PauseIncomingMethodCallProcessing();
+  connection.binding()->ResumeIncomingMethodCallProcessing();
+
+  // There are no associated interfaces running on the pipe yet. It is okay to
+  // wait.
+  EXPECT_TRUE(connection.binding()->WaitForIncomingMethodCall());
+
+  // The previous wait has dispatched the GetSender request message, therefore
+  // an associated interface has been set up on the pipe. It is not allowed to
+  // wait or pause.
+  EXPECT_TRUE(connection.binding()->HasAssociatedInterfaces());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/bind_task_runner_unittest.cc b/mojo/public/cpp/bindings/tests/bind_task_runner_unittest.cc
new file mode 100644
index 0000000..dab64f0
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/bind_task_runner_unittest.cc
@@ -0,0 +1,395 @@
+// Copyright 2016 The Chromium Authors. 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/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class TestTaskRunner : public base::SingleThreadTaskRunner {
+ public:
+  TestTaskRunner()
+      : thread_id_(base::PlatformThread::CurrentRef()),
+        quit_called_(false),
+        task_ready_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                    base::WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+  bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
+                                  const base::Closure& task,
+                                  base::TimeDelta delay) override {
+    NOTREACHED();
+    return false;
+  }
+
+  bool PostDelayedTask(const tracked_objects::Location& from_here,
+                       const base::Closure& task,
+                       base::TimeDelta delay) override {
+    {
+      base::AutoLock locker(lock_);
+      tasks_.push(task);
+    }
+    task_ready_.Signal();
+    return true;
+  }
+  bool RunsTasksOnCurrentThread() const override {
+    return base::PlatformThread::CurrentRef() == thread_id_;
+  }
+
+  // Only quits when Quit() is called.
+  void Run() {
+    DCHECK(RunsTasksOnCurrentThread());
+    quit_called_ = false;
+
+    while (true) {
+      {
+        base::AutoLock locker(lock_);
+        while (!tasks_.empty()) {
+          auto task = tasks_.front();
+          tasks_.pop();
+
+          {
+            base::AutoUnlock unlocker(lock_);
+            task.Run();
+            if (quit_called_)
+              return;
+          }
+        }
+      }
+      task_ready_.Wait();
+    }
+  }
+
+  void Quit() {
+    DCHECK(RunsTasksOnCurrentThread());
+    quit_called_ = true;
+  }
+
+  // Waits until one task is ready and runs it.
+  void RunOneTask() {
+    DCHECK(RunsTasksOnCurrentThread());
+
+    while (true) {
+      {
+        base::AutoLock locker(lock_);
+        if (!tasks_.empty()) {
+          auto task = tasks_.front();
+          tasks_.pop();
+
+          {
+            base::AutoUnlock unlocker(lock_);
+            task.Run();
+            return;
+          }
+        }
+      }
+      task_ready_.Wait();
+    }
+  }
+
+ private:
+  ~TestTaskRunner() override {}
+
+  const base::PlatformThreadRef thread_id_;
+  bool quit_called_;
+  base::WaitableEvent task_ready_;
+
+  // Protect |tasks_|.
+  base::Lock lock_;
+  std::queue<base::Closure> tasks_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestTaskRunner);
+};
+
+template <typename BindingType, typename RequestType>
+class IntegerSenderImpl : public IntegerSender {
+ public:
+  IntegerSenderImpl(RequestType request,
+                    scoped_refptr<base::SingleThreadTaskRunner> runner)
+      : binding_(this, std::move(request), std::move(runner)) {}
+
+  ~IntegerSenderImpl() override {}
+
+  using EchoHandler = base::Callback<void(int32_t, const EchoCallback&)>;
+
+  void set_echo_handler(const EchoHandler& handler) { echo_handler_ = handler; }
+
+  void Echo(int32_t value, const EchoCallback& callback) override {
+    if (echo_handler_.is_null())
+      callback.Run(value);
+    else
+      echo_handler_.Run(value, callback);
+  }
+  void Send(int32_t value) override { NOTREACHED(); }
+
+  BindingType* binding() { return &binding_; }
+
+ private:
+  BindingType binding_;
+  EchoHandler echo_handler_;
+};
+
+class IntegerSenderConnectionImpl : public IntegerSenderConnection {
+ public:
+  using SenderType = IntegerSenderImpl<AssociatedBinding<IntegerSender>,
+                                       IntegerSenderAssociatedRequest>;
+
+  explicit IntegerSenderConnectionImpl(
+      IntegerSenderConnectionRequest request,
+      scoped_refptr<base::SingleThreadTaskRunner> runner,
+      scoped_refptr<base::SingleThreadTaskRunner> sender_runner)
+      : binding_(this, std::move(request), std::move(runner)),
+        sender_runner_(std::move(sender_runner)) {}
+
+  ~IntegerSenderConnectionImpl() override {}
+
+  void set_get_sender_notification(const base::Closure& notification) {
+    get_sender_notification_ = notification;
+  }
+  void GetSender(IntegerSenderAssociatedRequest sender) override {
+    sender_impl_.reset(new SenderType(std::move(sender), sender_runner_));
+    get_sender_notification_.Run();
+  }
+
+  void AsyncGetSender(const AsyncGetSenderCallback& callback) override {
+    NOTREACHED();
+  }
+
+  Binding<IntegerSenderConnection>* binding() { return &binding_; }
+
+  SenderType* sender_impl() { return sender_impl_.get(); }
+
+ private:
+  Binding<IntegerSenderConnection> binding_;
+  std::unique_ptr<SenderType> sender_impl_;
+  scoped_refptr<base::SingleThreadTaskRunner> sender_runner_;
+  base::Closure get_sender_notification_;
+};
+
+class BindTaskRunnerTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    binding_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
+    ptr_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
+
+    auto request = GetProxy(&ptr_, ptr_task_runner_);
+    impl_.reset(new ImplType(std::move(request), binding_task_runner_));
+  }
+
+  base::MessageLoop loop_;
+  scoped_refptr<TestTaskRunner> binding_task_runner_;
+  scoped_refptr<TestTaskRunner> ptr_task_runner_;
+
+  IntegerSenderPtr ptr_;
+  using ImplType =
+      IntegerSenderImpl<Binding<IntegerSender>, IntegerSenderRequest>;
+  std::unique_ptr<ImplType> impl_;
+};
+
+class AssociatedBindTaskRunnerTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    connection_binding_task_runner_ =
+        scoped_refptr<TestTaskRunner>(new TestTaskRunner);
+    connection_ptr_task_runner_ =
+        scoped_refptr<TestTaskRunner>(new TestTaskRunner);
+    sender_binding_task_runner_ =
+        scoped_refptr<TestTaskRunner>(new TestTaskRunner);
+    sender_ptr_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
+
+    auto connection_request =
+        GetProxy(&connection_ptr_, connection_ptr_task_runner_);
+    connection_impl_.reset(new IntegerSenderConnectionImpl(
+        std::move(connection_request), connection_binding_task_runner_,
+        sender_binding_task_runner_));
+
+    connection_impl_->set_get_sender_notification(
+        base::Bind(&AssociatedBindTaskRunnerTest::QuitTaskRunner,
+                   base::Unretained(this)));
+
+    connection_ptr_->GetSender(GetProxy(&sender_ptr_,
+                                        connection_ptr_.associated_group(),
+                                        sender_ptr_task_runner_));
+    connection_binding_task_runner_->Run();
+  }
+
+  void QuitTaskRunner() {
+    connection_binding_task_runner_->Quit();
+  }
+
+  base::MessageLoop loop_;
+  scoped_refptr<TestTaskRunner> connection_binding_task_runner_;
+  scoped_refptr<TestTaskRunner> connection_ptr_task_runner_;
+  scoped_refptr<TestTaskRunner> sender_binding_task_runner_;
+  scoped_refptr<TestTaskRunner> sender_ptr_task_runner_;
+
+  IntegerSenderConnectionPtr connection_ptr_;
+  std::unique_ptr<IntegerSenderConnectionImpl> connection_impl_;
+  IntegerSenderAssociatedPtr sender_ptr_;
+};
+
+void DoSetFlagAndQuitTaskRunner(bool* flag,
+                                scoped_refptr<TestTaskRunner> task_runner) {
+  *flag = true;
+  if (task_runner)
+    task_runner->Quit();
+}
+
+void DoExpectValueSetFlagAndQuitTaskRunner(
+    int32_t expected_value,
+    bool* flag,
+    scoped_refptr<TestTaskRunner> task_runner,
+    int32_t value) {
+  EXPECT_EQ(expected_value, value);
+  DoSetFlagAndQuitTaskRunner(flag, task_runner);
+}
+
+void DoExpectValueSetFlagForwardValueAndQuitTaskRunner(
+    int32_t expected_value,
+    bool* flag,
+    scoped_refptr<TestTaskRunner> task_runner,
+    int32_t value,
+    const IntegerSender::EchoCallback& callback) {
+  EXPECT_EQ(expected_value, value);
+  *flag = true;
+  callback.Run(value);
+  task_runner->Quit();
+}
+
+base::Closure SetFlagAndQuitTaskRunner(
+    bool* flag,
+    scoped_refptr<TestTaskRunner> task_runner) {
+  return base::Bind(&DoSetFlagAndQuitTaskRunner, flag, task_runner);
+}
+
+base::Callback<void(int32_t)> ExpectValueSetFlagAndQuitTaskRunner(
+    int32_t expected_value,
+    bool* flag,
+    scoped_refptr<TestTaskRunner> task_runner) {
+  return base::Bind(&DoExpectValueSetFlagAndQuitTaskRunner, expected_value,
+                    flag, task_runner);
+}
+
+TEST_F(BindTaskRunnerTest, MethodCall) {
+  bool echo_called = false;
+  impl_->set_echo_handler(
+      base::Bind(&DoExpectValueSetFlagForwardValueAndQuitTaskRunner,
+                 1024, &echo_called, binding_task_runner_));
+  bool echo_replied = false;
+  ptr_->Echo(1024, ExpectValueSetFlagAndQuitTaskRunner(1024, &echo_replied,
+                                                       ptr_task_runner_));
+  binding_task_runner_->Run();
+  EXPECT_TRUE(echo_called);
+  ptr_task_runner_->Run();
+  EXPECT_TRUE(echo_replied);
+}
+
+TEST_F(BindTaskRunnerTest, BindingConnectionError) {
+  bool connection_error_called = false;
+  impl_->binding()->set_connection_error_handler(
+      SetFlagAndQuitTaskRunner(&connection_error_called, binding_task_runner_));
+  ptr_.reset();
+  binding_task_runner_->Run();
+  EXPECT_TRUE(connection_error_called);
+}
+
+TEST_F(BindTaskRunnerTest, PtrConnectionError) {
+  bool connection_error_called = false;
+  ptr_.set_connection_error_handler(
+      SetFlagAndQuitTaskRunner(&connection_error_called, ptr_task_runner_));
+  impl_->binding()->Close();
+  ptr_task_runner_->Run();
+  EXPECT_TRUE(connection_error_called);
+}
+
+void ExpectValueSetFlagAndForward(int32_t expected_value,
+                                  bool* flag,
+                                  int32_t value,
+                                  const IntegerSender::EchoCallback& callback) {
+  EXPECT_EQ(expected_value, value);
+  *flag = true;
+  callback.Run(value);
+}
+
+TEST_F(AssociatedBindTaskRunnerTest, MethodCall) {
+  bool echo_called = false;
+  connection_impl_->sender_impl()->set_echo_handler(
+      base::Bind(&ExpectValueSetFlagAndForward, 1024, &echo_called));
+
+  bool echo_replied = false;
+  sender_ptr_->Echo(
+      1024, ExpectValueSetFlagAndQuitTaskRunner(1024, &echo_replied, nullptr));
+
+  // The Echo request first arrives at the master endpoint's task runner, and
+  // then is forwarded to the associated endpoint's task runner.
+  connection_binding_task_runner_->RunOneTask();
+  sender_binding_task_runner_->RunOneTask();
+  EXPECT_TRUE(echo_called);
+
+  // Similarly, the Echo response arrives at the master endpoint's task runner
+  // and then is forwarded to the associated endpoint's task runner.
+  connection_ptr_task_runner_->RunOneTask();
+  sender_ptr_task_runner_->RunOneTask();
+  EXPECT_TRUE(echo_replied);
+}
+
+TEST_F(AssociatedBindTaskRunnerTest, BindingConnectionError) {
+  bool sender_impl_error = false;
+  connection_impl_->sender_impl()->binding()->set_connection_error_handler(
+      SetFlagAndQuitTaskRunner(&sender_impl_error,
+                               sender_binding_task_runner_));
+  bool connection_impl_error = false;
+  connection_impl_->binding()->set_connection_error_handler(
+      SetFlagAndQuitTaskRunner(&connection_impl_error,
+                               connection_binding_task_runner_));
+  bool sender_ptr_error = false;
+  sender_ptr_.set_connection_error_handler(
+      SetFlagAndQuitTaskRunner(&sender_ptr_error, sender_ptr_task_runner_));
+  connection_ptr_.reset();
+  sender_ptr_task_runner_->Run();
+  EXPECT_TRUE(sender_ptr_error);
+  connection_binding_task_runner_->Run();
+  EXPECT_TRUE(connection_impl_error);
+  sender_binding_task_runner_->Run();
+  EXPECT_TRUE(sender_impl_error);
+}
+
+TEST_F(AssociatedBindTaskRunnerTest, PtrConnectionError) {
+  bool sender_impl_error = false;
+  connection_impl_->sender_impl()->binding()->set_connection_error_handler(
+      SetFlagAndQuitTaskRunner(&sender_impl_error,
+                               sender_binding_task_runner_));
+  bool connection_ptr_error = false;
+  connection_ptr_.set_connection_error_handler(
+      SetFlagAndQuitTaskRunner(&connection_ptr_error,
+                               connection_ptr_task_runner_));
+  bool sender_ptr_error = false;
+  sender_ptr_.set_connection_error_handler(
+      SetFlagAndQuitTaskRunner(&sender_ptr_error, sender_ptr_task_runner_));
+  connection_impl_->binding()->Close();
+  sender_binding_task_runner_->Run();
+  EXPECT_TRUE(sender_impl_error);
+  connection_ptr_task_runner_->Run();
+  EXPECT_TRUE(connection_ptr_error);
+  sender_ptr_task_runner_->Run();
+  EXPECT_TRUE(sender_ptr_error);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
new file mode 100644
index 0000000..02b082a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
@@ -0,0 +1,346 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "build/build_config.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// The tests in this file are designed to test the interaction between a
+// Callback and its associated Binding. If a Callback is deleted before
+// being used we DCHECK fail--unless the associated Binding has already
+// been closed or deleted. This contract must be explained to the Mojo
+// application developer. For example it is the developer's responsibility to
+// ensure that the Binding is destroyed before an unused Callback is destroyed.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mojo {
+namespace test {
+namespace {
+
+void SaveValue(int32_t* storage, const base::Closure& closure, int32_t value) {
+  *storage = value;
+  if (!closure.is_null())
+    closure.Run();
+}
+
+base::Callback<void(int32_t)> BindValueSaver(int32_t* last_value_seen,
+                                             const base::Closure& closure) {
+  return base::Bind(&SaveValue, last_value_seen, closure);
+}
+
+// An implementation of sample::Provider used on the server side.
+// It only implements one of the methods: EchoInt().
+// All it does is save the values and Callbacks it sees.
+class InterfaceImpl : public sample::Provider {
+ public:
+  InterfaceImpl()
+      : last_server_value_seen_(0),
+        callback_saved_(new EchoIntCallback) {}
+
+  ~InterfaceImpl() override {
+    if (callback_saved_) {
+      delete callback_saved_;
+    }
+  }
+
+  // Run's the callback previously saved from the last invocation
+  // of |EchoInt()|.
+  bool RunCallback() {
+    if (callback_saved_) {
+      callback_saved_->Run(last_server_value_seen_);
+      return true;
+    }
+    return false;
+  }
+
+  // Delete's the previously saved callback.
+  void DeleteCallback() {
+    delete callback_saved_;
+    callback_saved_ = nullptr;
+  }
+
+  // sample::Provider implementation
+
+  // Saves its two input values in member variables and does nothing else.
+  void EchoInt(int32_t x, const EchoIntCallback& callback) override {
+    last_server_value_seen_ = x;
+    *callback_saved_ = callback;
+    if (!closure_.is_null()) {
+      closure_.Run();
+      closure_.Reset();
+    }
+  }
+
+  void EchoString(const std::string& a,
+                  const EchoStringCallback& callback) override {
+    CHECK(false) << "Not implemented.";
+  }
+
+  void EchoStrings(const std::string& a,
+                   const std::string& b,
+                   const EchoStringsCallback& callback) override {
+    CHECK(false) << "Not implemented.";
+  }
+
+  void EchoMessagePipeHandle(
+      ScopedMessagePipeHandle a,
+      const EchoMessagePipeHandleCallback& callback) override {
+    CHECK(false) << "Not implemented.";
+  }
+
+  void EchoEnum(sample::Enum a, const EchoEnumCallback& callback) override {
+    CHECK(false) << "Not implemented.";
+  }
+
+  void resetLastServerValueSeen() { last_server_value_seen_ = 0; }
+
+  int32_t last_server_value_seen() const { return last_server_value_seen_; }
+
+  void set_closure(const base::Closure& closure) { closure_ = closure; }
+
+ private:
+  int32_t last_server_value_seen_;
+  EchoIntCallback* callback_saved_;
+  base::Closure closure_;
+};
+
+class BindingCallbackTest : public testing::Test {
+ public:
+  BindingCallbackTest() {}
+  ~BindingCallbackTest() override {}
+
+ protected:
+  int32_t last_client_callback_value_seen_;
+  sample::ProviderPtr interface_ptr_;
+
+  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
+
+ private:
+  base::MessageLoop loop_;
+};
+
+// Tests that the InterfacePtr and the Binding can communicate with each
+// other normally.
+TEST_F(BindingCallbackTest, Basic) {
+  // Create the ServerImpl and the Binding.
+  InterfaceImpl server_impl;
+  Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+  // Initialize the test values.
+  server_impl.resetLastServerValueSeen();
+  last_client_callback_value_seen_ = 0;
+
+  // Invoke the Echo method.
+  base::RunLoop run_loop, run_loop2;
+  server_impl.set_closure(run_loop.QuitClosure());
+  interface_ptr_->EchoInt(
+      7,
+      BindValueSaver(&last_client_callback_value_seen_,
+                     run_loop2.QuitClosure()));
+  run_loop.Run();
+
+  // Check that server saw the correct value, but the client has not yet.
+  EXPECT_EQ(7, server_impl.last_server_value_seen());
+  EXPECT_EQ(0, last_client_callback_value_seen_);
+
+  // Now run the Callback.
+  server_impl.RunCallback();
+  run_loop2.Run();
+
+  // Check that the client has now seen the correct value.
+  EXPECT_EQ(7, last_client_callback_value_seen_);
+
+  // Initialize the test values again.
+  server_impl.resetLastServerValueSeen();
+  last_client_callback_value_seen_ = 0;
+
+  // Invoke the Echo method again.
+  base::RunLoop run_loop3, run_loop4;
+  server_impl.set_closure(run_loop3.QuitClosure());
+  interface_ptr_->EchoInt(
+      13,
+      BindValueSaver(&last_client_callback_value_seen_,
+                     run_loop4.QuitClosure()));
+  run_loop3.Run();
+
+  // Check that server saw the correct value, but the client has not yet.
+  EXPECT_EQ(13, server_impl.last_server_value_seen());
+  EXPECT_EQ(0, last_client_callback_value_seen_);
+
+  // Now run the Callback again.
+  server_impl.RunCallback();
+  run_loop4.Run();
+
+  // Check that the client has now seen the correct value again.
+  EXPECT_EQ(13, last_client_callback_value_seen_);
+}
+
+// Tests that running the Callback after the Binding has been deleted
+// results in a clean failure.
+TEST_F(BindingCallbackTest, DeleteBindingThenRunCallback) {
+  // Create the ServerImpl.
+  InterfaceImpl server_impl;
+  base::RunLoop run_loop;
+  {
+    // Create the binding in an inner scope so it can be deleted first.
+    Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+    interface_ptr_.set_connection_error_handler(run_loop.QuitClosure());
+
+    // Initialize the test values.
+    server_impl.resetLastServerValueSeen();
+    last_client_callback_value_seen_ = 0;
+
+    // Invoke the Echo method.
+    base::RunLoop run_loop2;
+    server_impl.set_closure(run_loop2.QuitClosure());
+    interface_ptr_->EchoInt(
+        7,
+        BindValueSaver(&last_client_callback_value_seen_, base::Closure()));
+    run_loop2.Run();
+  }
+  // The binding has now been destroyed and the pipe is closed.
+
+  // Check that server saw the correct value, but the client has not yet.
+  EXPECT_EQ(7, server_impl.last_server_value_seen());
+  EXPECT_EQ(0, last_client_callback_value_seen_);
+
+  // Now try to run the Callback. This should do nothing since the pipe
+  // is closed.
+  EXPECT_TRUE(server_impl.RunCallback());
+  PumpMessages();
+
+  // Check that the client has still not seen the correct value.
+  EXPECT_EQ(0, last_client_callback_value_seen_);
+
+  // Attempt to invoke the method again and confirm that an error was
+  // encountered.
+  interface_ptr_->EchoInt(
+      13,
+      BindValueSaver(&last_client_callback_value_seen_, base::Closure()));
+  run_loop.Run();
+  EXPECT_TRUE(interface_ptr_.encountered_error());
+}
+
+// Tests that deleting a Callback without running it after the corresponding
+// binding has already been deleted does not result in a crash.
+TEST_F(BindingCallbackTest, DeleteBindingThenDeleteCallback) {
+  // Create the ServerImpl.
+  InterfaceImpl server_impl;
+  {
+    // Create the binding in an inner scope so it can be deleted first.
+    Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+    // Initialize the test values.
+    server_impl.resetLastServerValueSeen();
+    last_client_callback_value_seen_ = 0;
+
+    // Invoke the Echo method.
+    base::RunLoop run_loop;
+    server_impl.set_closure(run_loop.QuitClosure());
+    interface_ptr_->EchoInt(
+        7,
+        BindValueSaver(&last_client_callback_value_seen_, base::Closure()));
+    run_loop.Run();
+  }
+  // The binding has now been destroyed and the pipe is closed.
+
+  // Check that server saw the correct value, but the client has not yet.
+  EXPECT_EQ(7, server_impl.last_server_value_seen());
+  EXPECT_EQ(0, last_client_callback_value_seen_);
+
+  // Delete the callback without running it. This should not
+  // cause a problem because the insfrastructure can detect that the
+  // binding has already been destroyed and the pipe is closed.
+  server_impl.DeleteCallback();
+}
+
+// Tests that closing a Binding allows us to delete a callback
+// without running it without encountering a crash.
+TEST_F(BindingCallbackTest, CloseBindingBeforeDeletingCallback) {
+  // Create the ServerImpl and the Binding.
+  InterfaceImpl server_impl;
+  Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+  // Initialize the test values.
+  server_impl.resetLastServerValueSeen();
+  last_client_callback_value_seen_ = 0;
+
+  // Invoke the Echo method.
+  base::RunLoop run_loop;
+  server_impl.set_closure(run_loop.QuitClosure());
+  interface_ptr_->EchoInt(
+      7,
+      BindValueSaver(&last_client_callback_value_seen_, base::Closure()));
+  run_loop.Run();
+
+  // Check that server saw the correct value, but the client has not yet.
+  EXPECT_EQ(7, server_impl.last_server_value_seen());
+  EXPECT_EQ(0, last_client_callback_value_seen_);
+
+  // Now close the Binding.
+  binding.Close();
+
+  // Delete the callback without running it. This should not
+  // cause a crash because the insfrastructure can detect that the
+  // binding has already been closed.
+  server_impl.DeleteCallback();
+
+  // Check that the client has still not seen the correct value.
+  EXPECT_EQ(0, last_client_callback_value_seen_);
+}
+
+// Tests that deleting a Callback without using it before the
+// Binding has been destroyed or closed results in a DCHECK.
+TEST_F(BindingCallbackTest, DeleteCallbackBeforeBindingDeathTest) {
+  // Create the ServerImpl and the Binding.
+  InterfaceImpl server_impl;
+  Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+  // Initialize the test values.
+  server_impl.resetLastServerValueSeen();
+  last_client_callback_value_seen_ = 0;
+
+  // Invoke the Echo method.
+  base::RunLoop run_loop;
+  server_impl.set_closure(run_loop.QuitClosure());
+  interface_ptr_->EchoInt(
+      7,
+      BindValueSaver(&last_client_callback_value_seen_, base::Closure()));
+  run_loop.Run();
+
+  // Check that server saw the correct value, but the client has not yet.
+  EXPECT_EQ(7, server_impl.last_server_value_seen());
+  EXPECT_EQ(0, last_client_callback_value_seen_);
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && !defined(OS_ANDROID)
+  // Delete the callback without running it. This should cause a crash in debug
+  // builds due to a DCHECK.
+  std::string regex("Check failed: !is_valid");
+#if defined(OS_WIN)
+  // TODO(msw): Fix MOJO_DCHECK logs and EXPECT_DEATH* on Win: crbug.com/535014
+  regex.clear();
+#endif  // OS_WIN
+  EXPECT_DEATH_IF_SUPPORTED(server_impl.DeleteCallback(), regex.c_str());
+#endif  // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) &&
+        // !defined(OS_ANDROID)
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/binding_unittest.cc b/mojo/public/cpp/bindings/tests/binding_unittest.cc
new file mode 100644
index 0000000..4838ee8
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/binding_unittest.cc
@@ -0,0 +1,414 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: This file tests both binding.h (mojo::Binding) and strong_binding.h
+// (mojo::StrongBinding).
+
+#include "mojo/public/cpp/bindings/binding.h"
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class BindingTestBase : public testing::Test {
+ public:
+  BindingTestBase() {}
+  ~BindingTestBase() override {}
+
+  base::MessageLoop& loop() { return loop_; }
+
+ private:
+  base::MessageLoop loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(BindingTestBase);
+};
+
+class ServiceImpl : public sample::Service {
+ public:
+  explicit ServiceImpl(bool* was_deleted = nullptr)
+      : was_deleted_(was_deleted) {}
+  ~ServiceImpl() override {
+    if (was_deleted_)
+      *was_deleted_ = true;
+  }
+
+ private:
+  // sample::Service implementation
+  void Frobinate(sample::FooPtr foo,
+                 BazOptions options,
+                 sample::PortPtr port,
+                 const FrobinateCallback& callback) override {
+    callback.Run(1);
+  }
+  void GetPort(InterfaceRequest<sample::Port> port) override {}
+
+  bool* const was_deleted_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceImpl);
+};
+
+template <typename... Args>
+void DoSetFlagAndRunClosure(bool* flag,
+                            const base::Closure& closure,
+                            Args... args) {
+  *flag = true;
+  closure.Run();
+}
+
+template <typename... Args>
+base::Callback<void(Args...)> SetFlagAndRunClosure(
+    bool* flag,
+    const base::Closure& callback = base::Closure()) {
+  return base::Bind(&DoSetFlagAndRunClosure<Args...>, flag, callback);
+}
+
+// BindingTest -----------------------------------------------------------------
+
+using BindingTest = BindingTestBase;
+
+TEST_F(BindingTest, Close) {
+  bool called = false;
+  sample::ServicePtr ptr;
+  auto request = GetProxy(&ptr);
+  base::RunLoop run_loop;
+  ptr.set_connection_error_handler(
+      SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
+  ServiceImpl impl;
+  Binding<sample::Service> binding(&impl, std::move(request));
+
+  binding.Close();
+  EXPECT_FALSE(called);
+  run_loop.Run();
+  EXPECT_TRUE(called);
+}
+
+// Tests that destroying a mojo::Binding closes the bound message pipe handle.
+TEST_F(BindingTest, DestroyClosesMessagePipe) {
+  bool encountered_error = false;
+  ServiceImpl impl;
+  sample::ServicePtr ptr;
+  auto request = GetProxy(&ptr);
+  base::RunLoop run_loop;
+  ptr.set_connection_error_handler(
+      SetFlagAndRunClosure(&encountered_error, run_loop.QuitClosure()));
+  bool called = false;
+  base::RunLoop run_loop2;
+  {
+    Binding<sample::Service> binding(&impl, std::move(request));
+    ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                   SetFlagAndRunClosure<int32_t>(&called,
+                                                 run_loop2.QuitClosure()));
+    run_loop2.Run();
+    EXPECT_TRUE(called);
+    EXPECT_FALSE(encountered_error);
+  }
+  // Now that the Binding is out of scope we should detect an error on the other
+  // end of the pipe.
+  run_loop.Run();
+  EXPECT_TRUE(encountered_error);
+
+  // And calls should fail.
+  called = false;
+  ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                 SetFlagAndRunClosure<int32_t>(&called,
+                                               run_loop2.QuitClosure()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(called);
+}
+
+// Tests that the binding's connection error handler gets called when the other
+// end is closed.
+TEST_F(BindingTest, ConnectionError) {
+  bool called = false;
+  {
+    ServiceImpl impl;
+    sample::ServicePtr ptr;
+    Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+    base::RunLoop run_loop;
+    binding.set_connection_error_handler(
+        SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
+    ptr.reset();
+    EXPECT_FALSE(called);
+    run_loop.Run();
+    EXPECT_TRUE(called);
+    // We want to make sure that it isn't called again during destruction.
+    called = false;
+  }
+  EXPECT_FALSE(called);
+}
+
+// Tests that calling Close doesn't result in the connection error handler being
+// called.
+TEST_F(BindingTest, CloseDoesntCallConnectionErrorHandler) {
+  ServiceImpl impl;
+  sample::ServicePtr ptr;
+  Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+  bool called = false;
+  binding.set_connection_error_handler(SetFlagAndRunClosure(&called));
+  binding.Close();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(called);
+
+  // We can also close the other end, and the error handler still won't be
+  // called.
+  ptr.reset();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(called);
+}
+
+class ServiceImplWithBinding : public ServiceImpl {
+ public:
+  ServiceImplWithBinding(bool* was_deleted,
+                         const base::Closure& closure,
+                         InterfaceRequest<sample::Service> request)
+      : ServiceImpl(was_deleted),
+        binding_(this, std::move(request)),
+        closure_(closure) {
+    binding_.set_connection_error_handler(
+        base::Bind(&ServiceImplWithBinding::OnConnectionError,
+                   base::Unretained(this)));
+  }
+
+ private:
+  ~ServiceImplWithBinding() override{
+    closure_.Run();
+  }
+
+  void OnConnectionError() { delete this; }
+
+  Binding<sample::Service> binding_;
+  base::Closure closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceImplWithBinding);
+};
+
+// Tests that the binding may be deleted in the connection error handler.
+TEST_F(BindingTest, SelfDeleteOnConnectionError) {
+  bool was_deleted = false;
+  sample::ServicePtr ptr;
+  // This should delete itself on connection error.
+  base::RunLoop run_loop;
+  new ServiceImplWithBinding(&was_deleted, run_loop.QuitClosure(),
+                             GetProxy(&ptr));
+  ptr.reset();
+  EXPECT_FALSE(was_deleted);
+  run_loop.Run();
+  EXPECT_TRUE(was_deleted);
+}
+
+// Tests that explicitly calling Unbind followed by rebinding works.
+TEST_F(BindingTest, Unbind) {
+  ServiceImpl impl;
+  sample::ServicePtr ptr;
+  Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+
+  bool called = false;
+  base::RunLoop run_loop;
+  ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                 SetFlagAndRunClosure<int32_t>(&called,
+                                               run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_TRUE(called);
+
+  called = false;
+  auto request = binding.Unbind();
+  EXPECT_FALSE(binding.is_bound());
+  // All calls should fail when not bound...
+  ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                 SetFlagAndRunClosure<int32_t>(&called,
+                                               run_loop.QuitClosure()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(called);
+
+  called = false;
+  binding.Bind(std::move(request));
+  EXPECT_TRUE(binding.is_bound());
+  // ...and should succeed again when the rebound.
+  base::RunLoop run_loop2;
+  ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                 SetFlagAndRunClosure<int32_t>(&called,
+                                               run_loop2.QuitClosure()));
+  run_loop2.Run();
+  EXPECT_TRUE(called);
+}
+
+class IntegerAccessorImpl : public sample::IntegerAccessor {
+ public:
+  IntegerAccessorImpl() {}
+  ~IntegerAccessorImpl() override {}
+
+ private:
+  // sample::IntegerAccessor implementation.
+  void GetInteger(const GetIntegerCallback& callback) override {
+    callback.Run(1, sample::Enum::VALUE);
+  }
+  void SetInteger(int64_t data, sample::Enum type) override {}
+
+  DISALLOW_COPY_AND_ASSIGN(IntegerAccessorImpl);
+};
+
+TEST_F(BindingTest, SetInterfacePtrVersion) {
+  IntegerAccessorImpl impl;
+  sample::IntegerAccessorPtr ptr;
+  Binding<sample::IntegerAccessor> binding(&impl, &ptr);
+  EXPECT_EQ(3u, ptr.version());
+}
+
+TEST_F(BindingTest, PauseResume) {
+  bool called = false;
+  base::RunLoop run_loop;
+  sample::ServicePtr ptr;
+  auto request = GetProxy(&ptr);
+  ServiceImpl impl;
+  Binding<sample::Service> binding(&impl, std::move(request));
+  binding.PauseIncomingMethodCallProcessing();
+  ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                 SetFlagAndRunClosure<int32_t>(&called,
+                                               run_loop.QuitClosure()));
+  EXPECT_FALSE(called);
+  base::RunLoop().RunUntilIdle();
+  // Frobinate() should not be called as the binding is paused.
+  EXPECT_FALSE(called);
+
+  // Resume the binding, which should trigger processing.
+  binding.ResumeIncomingMethodCallProcessing();
+  run_loop.Run();
+  EXPECT_TRUE(called);
+}
+
+// Verifies the connection error handler is not run while a binding is paused.
+TEST_F(BindingTest, ErrorHandleNotRunWhilePaused) {
+  bool called = false;
+  base::RunLoop run_loop;
+  sample::ServicePtr ptr;
+  auto request = GetProxy(&ptr);
+  ServiceImpl impl;
+  Binding<sample::Service> binding(&impl, std::move(request));
+  binding.set_connection_error_handler(
+      SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
+  binding.PauseIncomingMethodCallProcessing();
+
+  ptr.reset();
+  base::RunLoop().RunUntilIdle();
+  // The connection error handle should not be called as the binding is paused.
+  EXPECT_FALSE(called);
+
+  // Resume the binding, which should trigger the error handler.
+  binding.ResumeIncomingMethodCallProcessing();
+  run_loop.Run();
+  EXPECT_TRUE(called);
+}
+
+// StrongBindingTest -----------------------------------------------------------
+
+using StrongBindingTest = BindingTestBase;
+
+// Tests that destroying a mojo::StrongBinding closes the bound message pipe
+// handle but does *not* destroy the implementation object.
+TEST_F(StrongBindingTest, DestroyClosesMessagePipe) {
+  base::RunLoop run_loop;
+  bool encountered_error = false;
+  bool was_deleted = false;
+  ServiceImpl impl(&was_deleted);
+  sample::ServicePtr ptr;
+  auto request = GetProxy(&ptr);
+  ptr.set_connection_error_handler(
+      SetFlagAndRunClosure(&encountered_error, run_loop.QuitClosure()));
+  bool called = false;
+  base::RunLoop run_loop2;
+  {
+    StrongBinding<sample::Service> binding(&impl, std::move(request));
+    ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                   SetFlagAndRunClosure<int32_t>(&called,
+                                                 run_loop2.QuitClosure()));
+    run_loop2.Run();
+    EXPECT_TRUE(called);
+    EXPECT_FALSE(encountered_error);
+  }
+  // Now that the StrongBinding is out of scope we should detect an error on the
+  // other end of the pipe.
+  run_loop.Run();
+  EXPECT_TRUE(encountered_error);
+  // But destroying the StrongBinding doesn't destroy the object.
+  ASSERT_FALSE(was_deleted);
+}
+
+class ServiceImplWithStrongBinding : public ServiceImpl {
+ public:
+  ServiceImplWithStrongBinding(bool* was_deleted,
+                               InterfaceRequest<sample::Service> request)
+      : ServiceImpl(was_deleted), binding_(this, std::move(request)) {}
+
+  StrongBinding<sample::Service>& binding() { return binding_; }
+
+ private:
+  StrongBinding<sample::Service> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceImplWithStrongBinding);
+};
+
+// Tests the typical case, where the implementation object owns the
+// StrongBinding (and should be destroyed on connection error).
+TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) {
+  sample::ServicePtr ptr;
+  bool was_deleted = false;
+  // Will delete itself.
+  base::RunLoop run_loop;
+  new ServiceImplWithBinding(&was_deleted, run_loop.QuitClosure(),
+                             GetProxy(&ptr));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(was_deleted);
+
+  ptr.reset();
+  EXPECT_FALSE(was_deleted);
+  run_loop.Run();
+  EXPECT_TRUE(was_deleted);
+}
+
+// Tests that even when the implementation object owns the StrongBinding, that
+// the implementation can still be deleted (which should result in the message
+// pipe being closed). Also checks that the connection error handler doesn't get
+// called.
+TEST_F(StrongBindingTest, ExplicitDeleteImpl) {
+  bool ptr_error_handler_called = false;
+  sample::ServicePtr ptr;
+  auto request = GetProxy(&ptr);
+  base::RunLoop run_loop;
+  ptr.set_connection_error_handler(
+      SetFlagAndRunClosure(&ptr_error_handler_called, run_loop.QuitClosure()));
+  bool was_deleted = false;
+  ServiceImplWithStrongBinding* impl =
+      new ServiceImplWithStrongBinding(&was_deleted, std::move(request));
+  bool binding_error_handler_called = false;
+  impl->binding().set_connection_error_handler(
+      SetFlagAndRunClosure(&binding_error_handler_called));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(ptr_error_handler_called);
+  EXPECT_FALSE(was_deleted);
+
+  delete impl;
+  EXPECT_FALSE(ptr_error_handler_called);
+  EXPECT_TRUE(was_deleted);
+  was_deleted = false;  // It shouldn't be double-deleted!
+  run_loop.Run();
+  EXPECT_TRUE(ptr_error_handler_called);
+  EXPECT_FALSE(was_deleted);
+
+  EXPECT_FALSE(binding_error_handler_called);
+}
+
+}  // namespace
+}  // mojo
diff --git a/mojo/public/cpp/bindings/tests/bindings_perftest.cc b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
new file mode 100644
index 0000000..db0dd05
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
@@ -0,0 +1,137 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const double kMojoTicksPerSecond = 1000000.0;
+
+double MojoTicksToSeconds(MojoTimeTicks ticks) {
+  return ticks / kMojoTicksPerSecond;
+}
+
+class PingServiceImpl : public test::PingService {
+ public:
+  PingServiceImpl() {}
+  ~PingServiceImpl() override {}
+
+  // |PingService| methods:
+  void Ping(const PingCallback& callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PingServiceImpl);
+};
+
+void PingServiceImpl::Ping(const PingCallback& callback) {
+  callback.Run();
+}
+
+class PingPongTest {
+ public:
+  explicit PingPongTest(test::PingServicePtr service);
+
+  void Run(unsigned int iterations);
+
+ private:
+  void OnPingDone();
+
+  test::PingServicePtr service_;
+  unsigned int iterations_to_run_;
+  unsigned int current_iterations_;
+
+  base::Closure quit_closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(PingPongTest);
+};
+
+PingPongTest::PingPongTest(test::PingServicePtr service)
+    : service_(std::move(service)) {}
+
+void PingPongTest::Run(unsigned int iterations) {
+  iterations_to_run_ = iterations;
+  current_iterations_ = 0;
+
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitClosure();
+  service_->Ping(base::Bind(&PingPongTest::OnPingDone, base::Unretained(this)));
+  run_loop.Run();
+}
+
+void PingPongTest::OnPingDone() {
+  current_iterations_++;
+  if (current_iterations_ >= iterations_to_run_) {
+    quit_closure_.Run();
+    return;
+  }
+
+  service_->Ping(base::Bind(&PingPongTest::OnPingDone, base::Unretained(this)));
+}
+
+struct BoundPingService {
+  BoundPingService() : binding(&impl) {
+    binding.Bind(GetProxy(&service));
+  }
+
+  PingServiceImpl impl;
+  test::PingServicePtr service;
+  Binding<test::PingService> binding;
+};
+
+class MojoBindingsPerftest : public testing::Test {
+ public:
+  MojoBindingsPerftest() {}
+
+ protected:
+  base::MessageLoop loop_;
+};
+
+TEST_F(MojoBindingsPerftest, InProcessPingPong) {
+  test::PingServicePtr service;
+  PingServiceImpl impl;
+  Binding<test::PingService> binding(&impl, GetProxy(&service));
+  PingPongTest test(std::move(service));
+
+  {
+    const unsigned int kIterations = 100000;
+    const MojoTimeTicks start_time = MojoGetTimeTicksNow();
+    test.Run(kIterations);
+    const MojoTimeTicks end_time = MojoGetTimeTicksNow();
+    test::LogPerfResult(
+        "InProcessPingPong", "0_Inactive",
+        kIterations / MojoTicksToSeconds(end_time - start_time),
+        "pings/second");
+  }
+
+  {
+    const size_t kNumInactiveServices = 1000;
+    BoundPingService* inactive_services =
+        new BoundPingService[kNumInactiveServices];
+
+    const unsigned int kIterations = 10000;
+    const MojoTimeTicks start_time = MojoGetTimeTicksNow();
+    test.Run(kIterations);
+    const MojoTimeTicks end_time = MojoGetTimeTicksNow();
+    test::LogPerfResult(
+        "InProcessPingPong", "1000_Inactive",
+        kIterations / MojoTicksToSeconds(end_time - start_time),
+        "pings/second");
+
+    delete[] inactive_services;
+  }
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/blink_typemaps.gni b/mojo/public/cpp/bindings/tests/blink_typemaps.gni
new file mode 100644
index 0000000..b71dcf8
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/blink_typemaps.gni
@@ -0,0 +1,8 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+typemaps = [
+  "//mojo/public/cpp/bindings/tests/rect_blink.typemap",
+  "//mojo/public/cpp/bindings/tests/test_native_types_blink.typemap",
+]
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..d75bdd0
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/buffer_unittest.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 <stddef.h>
+
+#include <limits>
+
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.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::FixedBufferForTesting 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 FixedBufferForTesting::Leak passes ownership to the caller.
+TEST(FixedBufferTest, Leak) {
+  void* ptr = nullptr;
+  void* buf_ptr = nullptr;
+  {
+    internal::FixedBufferForTesting 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 FixedBufferForTesting should be empty now.
+    EXPECT_EQ(0u, buf.size());
+    EXPECT_FALSE(buf.Leak());
+  }
+
+  // Since we called Leak, ptr is still writable after FixedBufferForTesting
+  // went out of scope.
+  memset(ptr, 1, 8);
+  free(buf_ptr);
+}
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+TEST(FixedBufferTest, TooBig) {
+  internal::FixedBufferForTesting 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/chromium_typemaps.gni b/mojo/public/cpp/bindings/tests/chromium_typemaps.gni
new file mode 100644
index 0000000..1da7cbf
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/chromium_typemaps.gni
@@ -0,0 +1,9 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+typemaps = [
+  "//mojo/public/cpp/bindings/tests/rect_chromium.typemap",
+  "//mojo/public/cpp/bindings/tests/struct_with_traits.typemap",
+  "//mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap",
+]
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..89cc51d
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -0,0 +1,577 @@
+// 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/connector.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/tests/message_queue.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class MessageAccumulator : public MessageReceiver {
+ public:
+  MessageAccumulator() {}
+  explicit MessageAccumulator(const base::Closure& closure)
+      : closure_(closure) {}
+
+  bool Accept(Message* message) override {
+    queue_.Push(message);
+    if (!closure_.is_null())
+      base::ResetAndReturn(&closure_).Run();
+    return true;
+  }
+
+  bool IsEmpty() const { return queue_.IsEmpty(); }
+
+  void Pop(Message* message) { queue_.Pop(message); }
+
+  void set_closure(const base::Closure& closure) { closure_ = closure; }
+
+  size_t size() const { return queue_.size(); }
+
+ private:
+  MessageQueue queue_;
+  base::Closure closure_;
+};
+
+class ConnectorDeletingMessageAccumulator : public MessageAccumulator {
+ public:
+  ConnectorDeletingMessageAccumulator(Connector** connector)
+      : connector_(connector) {}
+
+  bool Accept(Message* message) override {
+    delete *connector_;
+    *connector_ = nullptr;
+    return MessageAccumulator::Accept(message);
+  }
+
+ private:
+  Connector** connector_;
+};
+
+class ReentrantMessageAccumulator : public MessageAccumulator {
+ public:
+  ReentrantMessageAccumulator(Connector* connector)
+      : connector_(connector), number_of_calls_(0) {}
+
+  bool Accept(Message* message) override {
+    if (!MessageAccumulator::Accept(message))
+      return false;
+    number_of_calls_++;
+    if (number_of_calls_ == 1) {
+      return connector_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+    }
+    return true;
+  }
+
+  int number_of_calls() { return number_of_calls_; }
+
+ private:
+  Connector* connector_;
+  int number_of_calls_;
+};
+
+class ConnectorTest : public testing::Test {
+ public:
+  ConnectorTest() {}
+
+  void SetUp() override {
+    CreateMessagePipe(nullptr, &handle0_, &handle1_);
+  }
+
+  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.message()->MoveTo(message);
+  }
+
+ protected:
+  ScopedMessagePipeHandle handle0_;
+  ScopedMessagePipeHandle handle1_;
+
+ private:
+  base::MessageLoop loop_;
+};
+
+TEST_F(ConnectorTest, Basic) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+
+  base::RunLoop run_loop;
+  MessageAccumulator accumulator(run_loop.QuitClosure());
+  connector1.set_incoming_receiver(&accumulator);
+
+  run_loop.Run();
+
+  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) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  connector1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+  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) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  base::RunLoop run_loop;
+  MessageAccumulator accumulator(run_loop.QuitClosure());
+  connector1.set_incoming_receiver(&accumulator);
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+
+  run_loop.Run();
+
+  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) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  const char* kText[] = {"hello", "world"};
+
+  for (size_t i = 0; i < arraysize(kText); ++i) {
+    Message message;
+    AllocMessage(kText[i], &message);
+
+    connector0.Accept(&message);
+  }
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  for (size_t i = 0; i < arraysize(kText); ++i) {
+    if (accumulator.IsEmpty()) {
+      base::RunLoop run_loop;
+      accumulator.set_closure(run_loop.QuitClosure());
+      run_loop.Run();
+    }
+    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) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  const char* kText[] = {"hello", "world"};
+
+  for (size_t i = 0; i < arraysize(kText); ++i) {
+    Message message;
+    AllocMessage(kText[i], &message);
+
+    connector0.Accept(&message);
+  }
+
+  MessageAccumulator accumulator;
+  connector1.set_incoming_receiver(&accumulator);
+
+  connector1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+  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) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  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 message loop 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 message loop, and then we should start observing the closed pipe.
+  base::RunLoop run_loop;
+  connector0.set_connection_error_handler(run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_TRUE(connector0.encountered_error());
+}
+
+TEST_F(ConnectorTest, MessageWithHandles) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  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());
+
+  base::RunLoop run_loop;
+  MessageAccumulator accumulator(run_loop.QuitClosure());
+  connector1.set_incoming_receiver(&accumulator);
+
+  run_loop.Run();
+
+  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.
+
+  Connector connector_received(std::move(smph), Connector::SINGLE_THREADED_SEND,
+                               base::ThreadTaskRunnerHandle::Get());
+  Connector connector_original(std::move(pipe.handle1),
+                               Connector::SINGLE_THREADED_SEND,
+                               base::ThreadTaskRunnerHandle::Get());
+
+  Message message2;
+  AllocMessage(kText, &message2);
+
+  connector_received.Accept(&message2);
+  base::RunLoop run_loop2;
+  MessageAccumulator accumulator2(run_loop2.QuitClosure());
+  connector_original.set_incoming_receiver(&accumulator2);
+  run_loop2.Run();
+
+  ASSERT_FALSE(accumulator2.IsEmpty());
+
+  accumulator2.Pop(&message_received);
+
+  EXPECT_EQ(
+      std::string(kText),
+      std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, WaitForIncomingMessageWithError) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  // Close the other end of the pipe.
+  handle1_.reset();
+  ASSERT_FALSE(connector0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE));
+}
+
+TEST_F(ConnectorTest, WaitForIncomingMessageWithDeletion) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector* connector1 =
+      new Connector(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                    base::ThreadTaskRunnerHandle::Get());
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+
+  ConnectorDeletingMessageAccumulator accumulator(&connector1);
+  connector1->set_incoming_receiver(&accumulator);
+
+  connector1->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+  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) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  const char* kText[] = {"hello", "world"};
+
+  for (size_t i = 0; i < arraysize(kText); ++i) {
+    Message message;
+    AllocMessage(kText[i], &message);
+
+    connector0.Accept(&message);
+  }
+
+  ReentrantMessageAccumulator accumulator(&connector1);
+  connector1.set_incoming_receiver(&accumulator);
+
+  for (size_t i = 0; i < arraysize(kText); ++i) {
+    if (accumulator.IsEmpty()) {
+      base::RunLoop run_loop;
+      accumulator.set_closure(run_loop.QuitClosure());
+      run_loop.Run();
+    }
+    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());
+}
+
+void ForwardErrorHandler(bool* called, const base::Closure& callback) {
+  *called = true;
+  callback.Run();
+}
+
+TEST_F(ConnectorTest, RaiseError) {
+  base::RunLoop run_loop, run_loop2;
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  bool error_handler_called0 = false;
+  connector0.set_connection_error_handler(
+      base::Bind(&ForwardErrorHandler, &error_handler_called0,
+                 run_loop.QuitClosure()));
+
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  bool error_handler_called1 = false;
+  connector1.set_connection_error_handler(
+      base::Bind(&ForwardErrorHandler, &error_handler_called1,
+                 run_loop2.QuitClosure()));
+
+  const char kText[] = "hello world";
+
+  Message message;
+  AllocMessage(kText, &message);
+
+  connector0.Accept(&message);
+  connector0.RaiseError();
+
+  base::RunLoop run_loop3;
+  MessageAccumulator accumulator(run_loop3.QuitClosure());
+  connector1.set_incoming_receiver(&accumulator);
+
+  run_loop3.Run();
+
+  // Messages sent prior to RaiseError() still arrive at the other end.
+  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())));
+
+  run_loop.Run();
+  run_loop2.Run();
+
+  // Connection error handler is called at both sides.
+  EXPECT_TRUE(error_handler_called0);
+  EXPECT_TRUE(error_handler_called1);
+
+  // The error flag is set at both sides.
+  EXPECT_TRUE(connector0.encountered_error());
+  EXPECT_TRUE(connector1.encountered_error());
+
+  // The message pipe handle is valid at both sides.
+  EXPECT_TRUE(connector0.is_valid());
+  EXPECT_TRUE(connector1.is_valid());
+}
+
+void PauseConnectorAndRunClosure(Connector* connector,
+                                 const base::Closure& closure) {
+  connector->PauseIncomingMethodCallProcessing();
+  closure.Run();
+}
+
+TEST_F(ConnectorTest, PauseWithQueuedMessages) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  const char kText[] = "hello world";
+
+  // Queue up two messages.
+  Message message;
+  AllocMessage(kText, &message);
+  connector0.Accept(&message);
+  AllocMessage(kText, &message);
+  connector0.Accept(&message);
+
+  base::RunLoop run_loop;
+  // Configure the accumulator such that it pauses after the first message is
+  // received.
+  MessageAccumulator accumulator(
+      base::Bind(&PauseConnectorAndRunClosure, &connector1,
+                 run_loop.QuitClosure()));
+  connector1.set_incoming_receiver(&accumulator);
+
+  run_loop.Run();
+
+  // As we paused after the first message we should only have gotten one
+  // message.
+  ASSERT_EQ(1u, accumulator.size());
+}
+
+void AccumulateWithNestedLoop(MessageAccumulator* accumulator,
+                              const base::Closure& closure) {
+  base::RunLoop nested_run_loop;
+  base::MessageLoop::ScopedNestableTaskAllower allow(
+      base::MessageLoop::current());
+  accumulator->set_closure(nested_run_loop.QuitClosure());
+  nested_run_loop.Run();
+  closure.Run();
+}
+
+TEST_F(ConnectorTest, ProcessWhenNested) {
+  Connector connector0(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+  Connector connector1(std::move(handle1_), Connector::SINGLE_THREADED_SEND,
+                       base::ThreadTaskRunnerHandle::Get());
+
+  const char kText[] = "hello world";
+
+  // Queue up two messages.
+  Message message;
+  AllocMessage(kText, &message);
+  connector0.Accept(&message);
+  AllocMessage(kText, &message);
+  connector0.Accept(&message);
+
+  base::RunLoop run_loop;
+  MessageAccumulator accumulator;
+  // When the accumulator gets the first message it spins a nested message
+  // loop. The loop is quit when another message is received.
+  accumulator.set_closure(base::Bind(&AccumulateWithNestedLoop, &accumulator,
+                                     run_loop.QuitClosure()));
+  connector1.set_incoming_receiver(&accumulator);
+
+  run_loop.Run();
+
+  ASSERT_EQ(2u, accumulator.size());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/constant_unittest.cc b/mojo/public/cpp/bindings/tests/constant_unittest.cc
new file mode 100644
index 0000000..f6394f3
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/constant_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/interfaces/bindings/tests/test_constants.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+TEST(ConstantTest, GlobalConstants) {
+  // Compile-time constants.
+  static_assert(kBoolValue == true, "");
+  static_assert(kInt8Value == -2, "");
+  static_assert(kUint8Value == 128U, "");
+  static_assert(kInt16Value == -233, "");
+  static_assert(kUint16Value == 44204U, "");
+  static_assert(kInt32Value == -44204, "");
+  static_assert(kUint32Value == 4294967295U, "");
+  static_assert(kInt64Value == -9223372036854775807, "");
+  static_assert(kUint64Value == 9999999999999999999ULL, "");
+
+  EXPECT_DOUBLE_EQ(kDoubleValue, 3.14159);
+  EXPECT_FLOAT_EQ(kFloatValue, 2.71828f);
+}
+
+TEST(ConstantTest, StructConstants) {
+  // Compile-time constants.
+  static_assert(StructWithConstants::kInt8Value == 5U, "");
+
+  EXPECT_FLOAT_EQ(StructWithConstants::kFloatValue, 765.432f);
+}
+
+TEST(ConstantTest, InterfaceConstants) {
+  // Compile-time constants.
+  static_assert(InterfaceWithConstants::kUint32Value == 20100722, "");
+
+  EXPECT_DOUBLE_EQ(InterfaceWithConstants::kDoubleValue, 12.34567);
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/container_test_util.cc b/mojo/public/cpp/bindings/tests/container_test_util.cc
new file mode 100644
index 0000000..a53d351
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/container_test_util.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 <stddef.h>
+
+#include "mojo/public/cpp/bindings/tests/container_test_util.h"
+
+namespace mojo {
+
+size_t CopyableType::num_instances_ = 0;
+size_t MoveOnlyType::num_instances_ = 0;
+
+CopyableType::CopyableType() : copied_(false), ptr_(this) {
+  num_instances_++;
+}
+
+CopyableType::CopyableType(const CopyableType& other)
+    : copied_(true), ptr_(other.ptr()) {
+  num_instances_++;
+}
+
+CopyableType& CopyableType::operator=(const CopyableType& other) {
+  copied_ = true;
+  ptr_ = other.ptr();
+  return *this;
+}
+
+CopyableType::~CopyableType() {
+  num_instances_--;
+}
+
+MoveOnlyType::MoveOnlyType() : moved_(false), ptr_(this) {
+  num_instances_++;
+}
+
+MoveOnlyType::MoveOnlyType(MoveOnlyType&& other)
+    : moved_(true), ptr_(other.ptr()) {
+  num_instances_++;
+}
+
+MoveOnlyType& MoveOnlyType::operator=(MoveOnlyType&& other) {
+  moved_ = true;
+  ptr_ = other.ptr();
+  return *this;
+}
+
+MoveOnlyType::~MoveOnlyType() {
+  num_instances_--;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/container_test_util.h b/mojo/public/cpp/bindings/tests/container_test_util.h
new file mode 100644
index 0000000..f709c15
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/container_test_util.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_PUBLIC_CPP_BINDINGS_TESTS_CONTAINER_TEST_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_CONTAINER_TEST_UTIL_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+
+namespace mojo {
+
+class CopyableType {
+ public:
+  CopyableType();
+  CopyableType(const CopyableType& other);
+  CopyableType& operator=(const CopyableType& other);
+  ~CopyableType();
+
+  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_;
+};
+
+class MoveOnlyType {
+ public:
+  typedef MoveOnlyType Data_;
+  MoveOnlyType();
+  MoveOnlyType(MoveOnlyType&& other);
+  MoveOnlyType& operator=(MoveOnlyType&& other);
+  ~MoveOnlyType();
+
+  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_;
+
+  DISALLOW_COPY_AND_ASSIGN(MoveOnlyType);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_CONTAINER_TEST_UTIL_H_
diff --git a/mojo/public/cpp/bindings/tests/e2e_perftest.cc b/mojo/public/cpp/bindings/tests/e2e_perftest.cc
new file mode 100644
index 0000000..66ac6dc
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/e2e_perftest.cc
@@ -0,0 +1,206 @@
+// Copyright 2016 The Chromium Authors. 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 <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/perf_time_logger.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/edk/test/scoped_ipc_support.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class EchoServiceImpl : public test::EchoService {
+ public:
+  EchoServiceImpl(InterfaceRequest<EchoService> request,
+                  const base::Closure& quit_closure);
+  ~EchoServiceImpl() override;
+
+  // |EchoService| methods:
+  void Echo(const std::string& test_data,
+            const EchoCallback& callback) override;
+
+ private:
+  const StrongBinding<EchoService> binding_;
+  const base::Closure quit_closure_;
+};
+
+EchoServiceImpl::EchoServiceImpl(InterfaceRequest<EchoService> request,
+                                 const base::Closure& quit_closure)
+    : binding_(this, std::move(request)), quit_closure_(quit_closure) {}
+
+EchoServiceImpl::~EchoServiceImpl() {
+  quit_closure_.Run();
+}
+
+void EchoServiceImpl::Echo(const std::string& test_data,
+                           const EchoCallback& callback) {
+  callback.Run(test_data);
+}
+
+class PingPongTest {
+ public:
+  explicit PingPongTest(test::EchoServicePtr service);
+
+  void RunTest(int iterations, int batch_size, int message_size);
+
+ private:
+  void DoPing();
+  void OnPingDone(const std::string& reply);
+
+  test::EchoServicePtr service_;
+  const base::Callback<void(const std::string&)> ping_done_callback_;
+
+  int iterations_;
+  int batch_size_;
+  std::string message_;
+
+  int current_iterations_;
+  int calls_outstanding_;
+
+  base::Closure quit_closure_;
+};
+
+PingPongTest::PingPongTest(test::EchoServicePtr service)
+    : service_(std::move(service)),
+      ping_done_callback_(
+          base::Bind(&PingPongTest::OnPingDone, base::Unretained(this))) {}
+
+void PingPongTest::RunTest(int iterations, int batch_size, int message_size) {
+  iterations_ = iterations;
+  batch_size_ = batch_size;
+  message_ = std::string(message_size, 'a');
+  current_iterations_ = 0;
+  calls_outstanding_ = 0;
+
+  base::MessageLoop::current()->SetNestableTasksAllowed(true);
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitClosure();
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&PingPongTest::DoPing, base::Unretained(this)));
+  run_loop.Run();
+}
+
+void PingPongTest::DoPing() {
+  DCHECK_EQ(0, calls_outstanding_);
+  current_iterations_++;
+  if (current_iterations_ > iterations_) {
+    quit_closure_.Run();
+    return;
+  }
+
+  calls_outstanding_ = batch_size_;
+  for (int i = 0; i < batch_size_; i++) {
+    service_->Echo(message_, ping_done_callback_);
+  }
+}
+
+void PingPongTest::OnPingDone(const std::string& reply) {
+  DCHECK_GT(calls_outstanding_, 0);
+  calls_outstanding_--;
+
+  if (!calls_outstanding_)
+    DoPing();
+}
+
+class MojoE2EPerftest : public edk::test::MojoTestBase {
+ public:
+  void RunTestOnTaskRunner(base::TaskRunner* runner,
+                           MojoHandle client_mp,
+                           const std::string& test_name) {
+    if (runner == base::ThreadTaskRunnerHandle::Get().get()) {
+      RunTests(client_mp, test_name);
+    } else {
+      base::RunLoop run_loop;
+      runner->PostTaskAndReply(
+          FROM_HERE, base::Bind(&MojoE2EPerftest::RunTests,
+                                base::Unretained(this), client_mp, test_name),
+          run_loop.QuitClosure());
+      run_loop.Run();
+    }
+  }
+
+ protected:
+  base::MessageLoop message_loop_;
+
+ private:
+  void RunTests(MojoHandle client_mp, const std::string& test_name) {
+    const int kMessages = 10000;
+    const int kBatchSizes[] = {1, 10, 100};
+    const int kMessageSizes[] = {8, 64, 512, 4096, 65536};
+
+    test::EchoServicePtr service;
+    service.Bind(InterfacePtrInfo<test::EchoService>(
+        ScopedMessagePipeHandle(MessagePipeHandle(client_mp)),
+        service.version()));
+    PingPongTest test(std::move(service));
+
+    for (int batch_size : kBatchSizes) {
+      for (int message_size : kMessageSizes) {
+        int num_messages = kMessages;
+        if (message_size == 65536)
+          num_messages /= 10;
+        std::string sub_test_name = base::StringPrintf(
+            "%s/%dx%d/%dbytes", test_name.c_str(), num_messages / batch_size,
+            batch_size, message_size);
+        base::PerfTimeLogger timer(sub_test_name.c_str());
+        test.RunTest(num_messages / batch_size, batch_size, message_size);
+      }
+    }
+  }
+};
+
+void CreateAndRunService(InterfaceRequest<test::EchoService> request,
+                         const base::Closure& cb) {
+  new EchoServiceImpl(std::move(request), cb);
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingService, MojoE2EPerftest, mp) {
+  MojoHandle service_mp;
+  EXPECT_EQ("hello", ReadMessageWithHandles(mp, &service_mp, 1));
+
+  InterfaceRequest<test::EchoService> request;
+  request.Bind(ScopedMessagePipeHandle(MessagePipeHandle(service_mp)));
+  base::RunLoop run_loop;
+  edk::test::GetIoTaskRunner()->PostTask(
+      FROM_HERE,
+      base::Bind(&CreateAndRunService, base::Passed(&request),
+                 base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
+                            message_loop_.task_runner(), FROM_HERE,
+                            run_loop.QuitClosure())));
+  run_loop.Run();
+}
+
+TEST_F(MojoE2EPerftest, MultiProcessEchoMainThread) {
+  RUN_CHILD_ON_PIPE(PingService, mp)
+    MojoHandle client_mp, service_mp;
+    CreateMessagePipe(&client_mp, &service_mp);
+    WriteMessageWithHandles(mp, "hello", &service_mp, 1);
+    RunTestOnTaskRunner(message_loop_.task_runner().get(), client_mp,
+                        "MultiProcessEchoMainThread");
+  END_CHILD()
+}
+
+TEST_F(MojoE2EPerftest, MultiProcessEchoIoThread) {
+  RUN_CHILD_ON_PIPE(PingService, mp)
+    MojoHandle client_mp, service_mp;
+    CreateMessagePipe(&client_mp, &service_mp);
+    WriteMessageWithHandles(mp, "hello", &service_mp, 1);
+    RunTestOnTaskRunner(edk::test::GetIoTaskRunner(), client_mp,
+                        "MultiProcessEchoIoThread");
+  END_CHILD()
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/equals_unittest.cc b/mojo/public/cpp/bindings/tests/equals_unittest.cc
new file mode 100644
index 0000000..376c2bd
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/equals_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+namespace {
+
+RectPtr CreateRect() {
+  RectPtr r = Rect::New();
+  r->x = 1;
+  r->y = 2;
+  r->width = 3;
+  r->height = 4;
+  return r;
+}
+
+using EqualsTest = testing::Test;
+
+}  // namespace
+
+TEST_F(EqualsTest, NullStruct) {
+  RectPtr r1;
+  RectPtr r2;
+  EXPECT_TRUE(r1.Equals(r2));
+  EXPECT_TRUE(r2.Equals(r1));
+
+  r1 = CreateRect();
+  EXPECT_FALSE(r1.Equals(r2));
+  EXPECT_FALSE(r2.Equals(r1));
+}
+
+TEST_F(EqualsTest, Struct) {
+  RectPtr r1(CreateRect());
+  RectPtr r2(r1.Clone());
+  EXPECT_TRUE(r1.Equals(r2));
+  r2->y = 1;
+  EXPECT_FALSE(r1.Equals(r2));
+  r2.reset();
+  EXPECT_FALSE(r1.Equals(r2));
+}
+
+TEST_F(EqualsTest, StructNested) {
+  RectPairPtr p1(RectPair::New());
+  p1->first = CreateRect();
+  p1->second = CreateRect();
+  RectPairPtr p2(p1.Clone());
+  EXPECT_TRUE(p1.Equals(p2));
+  p2->second->width = 0;
+  EXPECT_FALSE(p1.Equals(p2));
+  p2->second.reset();
+  EXPECT_FALSE(p1.Equals(p2));
+}
+
+TEST_F(EqualsTest, Array) {
+  NamedRegionPtr n1(NamedRegion::New());
+  n1->name.emplace("n1");
+  n1->rects.emplace();
+  n1->rects->push_back(CreateRect());
+  NamedRegionPtr n2(n1.Clone());
+  EXPECT_TRUE(n1.Equals(n2));
+
+  n2->rects = base::nullopt;
+  EXPECT_FALSE(n1.Equals(n2));
+  n2->rects.emplace();
+  EXPECT_FALSE(n1.Equals(n2));
+
+  n2->rects->push_back(CreateRect());
+  n2->rects->push_back(CreateRect());
+  EXPECT_FALSE(n1.Equals(n2));
+
+  n2->rects->resize(1);
+  (*n2->rects)[0]->width = 0;
+  EXPECT_FALSE(n1.Equals(n2));
+
+  (*n2->rects)[0] = CreateRect();
+  EXPECT_TRUE(n1.Equals(n2));
+}
+
+TEST_F(EqualsTest, Map) {
+  auto n1(NamedRegion::New());
+  n1->name.emplace("foo");
+  n1->rects.emplace();
+  n1->rects->push_back(CreateRect());
+
+  Map<std::string, NamedRegionPtr> m1;
+  m1.insert("foo", std::move(n1));
+
+  decltype(m1) m2;
+  EXPECT_FALSE(m1.Equals(m2));
+
+  m2.insert("bar", m1.at("foo").Clone());
+  EXPECT_FALSE(m1.Equals(m2));
+
+  m2 = m1.Clone();
+  m2.at("foo")->name.emplace("monkey");
+  EXPECT_FALSE(m1.Equals(m2));
+
+  m2 = m1.Clone();
+  m2.at("foo")->rects->push_back(Rect::New());
+  EXPECT_FALSE(m1.Equals(m2));
+
+  m2.at("foo")->rects->resize(1);
+  (*m2.at("foo")->rects)[0]->width = 1;
+  EXPECT_FALSE(m1.Equals(m2));
+
+  m2 = m1.Clone();
+  EXPECT_TRUE(m1.Equals(m2));
+}
+
+TEST_F(EqualsTest, InterfacePtr) {
+  base::MessageLoop message_loop;
+
+  SomeInterfacePtr inf1;
+  SomeInterfacePtr inf2;
+
+  EXPECT_TRUE(inf1.Equals(inf1));
+  EXPECT_TRUE(inf1.Equals(inf2));
+
+  auto inf1_request = GetProxy(&inf1);
+  ALLOW_UNUSED_LOCAL(inf1_request);
+
+  EXPECT_TRUE(inf1.Equals(inf1));
+  EXPECT_FALSE(inf1.Equals(inf2));
+
+  auto inf2_request = GetProxy(&inf2);
+  ALLOW_UNUSED_LOCAL(inf2_request);
+
+  EXPECT_FALSE(inf1.Equals(inf2));
+}
+
+TEST_F(EqualsTest, InterfaceRequest) {
+  base::MessageLoop message_loop;
+
+  InterfaceRequest<SomeInterface> req1;
+  InterfaceRequest<SomeInterface> req2;
+
+  EXPECT_TRUE(req1.Equals(req1));
+  EXPECT_TRUE(req1.Equals(req2));
+
+  SomeInterfacePtr inf1;
+  req1 = GetProxy(&inf1);
+
+  EXPECT_TRUE(req1.Equals(req1));
+  EXPECT_FALSE(req1.Equals(req2));
+
+  SomeInterfacePtr inf2;
+  req2 = GetProxy(&inf2);
+
+  EXPECT_FALSE(req1.Equals(req2));
+}
+
+}  // test
+}  // 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..0bd9b28
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
@@ -0,0 +1,358 @@
+// 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 <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/test_support/test_utils.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";
+
+void RecordString(std::string* storage,
+                  const base::Closure& closure,
+                  const std::string& str) {
+  *storage = str;
+  closure.Run();
+}
+
+base::Callback<void(const std::string&)> MakeStringRecorder(
+    std::string* storage,
+    const base::Closure& closure) {
+  return base::Bind(&RecordString, storage, closure);
+}
+
+class ImportedInterfaceImpl : public imported::ImportedInterface {
+ public:
+  ImportedInterfaceImpl(
+      InterfaceRequest<imported::ImportedInterface> request,
+      const base::Closure& closure)
+      : binding_(this, std::move(request)), closure_(closure) {}
+
+  void DoSomething() override {
+    do_something_count_++;
+    closure_.Run();
+  }
+
+  static int do_something_count() { return do_something_count_; }
+
+ private:
+  static int do_something_count_;
+  Binding<ImportedInterface> binding_;
+  base::Closure closure_;
+};
+int ImportedInterfaceImpl::do_something_count_ = 0;
+
+class SampleNamedObjectImpl : public sample::NamedObject {
+ public:
+  explicit SampleNamedObjectImpl(InterfaceRequest<sample::NamedObject> request)
+      : binding_(this, std::move(request)) {}
+  void SetName(const std::string& name) override { name_ = name; }
+
+  void GetName(const GetNameCallback& callback) override {
+    callback.Run(name_);
+  }
+
+ private:
+  std::string name_;
+  StrongBinding<sample::NamedObject> binding_;
+};
+
+class SampleFactoryImpl : public sample::Factory {
+ public:
+  explicit SampleFactoryImpl(InterfaceRequest<sample::Factory> request)
+      : binding_(this, std::move(request)) {}
+
+  void DoStuff(sample::RequestPtr request,
+               ScopedMessagePipeHandle pipe,
+               const DoStuffCallback& callback) 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 = std::move(pipe0);
+    callback.Run(std::move(response), text1);
+
+    if (request->obj)
+      request->obj->DoSomething();
+  }
+
+  void DoStuff2(ScopedDataPipeConsumerHandle pipe,
+                const DoStuff2Callback& callback) 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;
+
+    MojoHandleSignalsState state;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              MojoWait(pipe.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
+                       MOJO_DEADLINE_INDEFINITE, &state));
+    ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, state.satisfied_signals);
+    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));
+
+    callback.Run(data);
+  }
+
+  void CreateNamedObject(
+      InterfaceRequest<sample::NamedObject> object_request) override {
+    EXPECT_TRUE(object_request.is_pending());
+    new SampleNamedObjectImpl(std::move(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.
+  void RequestImportedInterface(
+      InterfaceRequest<imported::ImportedInterface> imported,
+      const RequestImportedInterfaceCallback& callback) override {}
+  void TakeImportedInterface(
+      imported::ImportedInterfacePtr imported,
+      const TakeImportedInterfaceCallback& callback) override {}
+
+ private:
+  ScopedMessagePipeHandle pipe1_;
+  Binding<sample::Factory> binding_;
+};
+
+class HandlePassingTest : public testing::Test {
+ public:
+  HandlePassingTest() {}
+
+  void TearDown() override { PumpMessages(); }
+
+  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
+
+ private:
+  base::MessageLoop loop_;
+};
+
+void DoStuff(bool* got_response,
+             std::string* got_text_reply,
+             const base::Closure& closure,
+             sample::ResponsePtr response,
+             const std::string& text_reply) {
+  *got_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 = std::move(response->pipe);
+    EXPECT_TRUE(p.is_valid());
+    EXPECT_FALSE(response->pipe.is_valid());
+  }
+
+  *got_response = true;
+  closure.Run();
+}
+
+void DoStuff2(bool* got_response,
+              std::string* got_text_reply,
+              const base::Closure& closure,
+              const std::string& text_reply) {
+  *got_response = true;
+  *got_text_reply = text_reply;
+  closure.Run();
+}
+
+TEST_F(HandlePassingTest, Basic) {
+  sample::FactoryPtr factory;
+  SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+  MessagePipe pipe0;
+  EXPECT_TRUE(WriteTextMessage(pipe0.handle1.get(), kText1));
+
+  MessagePipe pipe1;
+  EXPECT_TRUE(WriteTextMessage(pipe1.handle1.get(), kText2));
+
+  imported::ImportedInterfacePtr imported;
+  base::RunLoop run_loop;
+  ImportedInterfaceImpl imported_impl(GetProxy(&imported),
+                                      run_loop.QuitClosure());
+
+  sample::RequestPtr request(sample::Request::New());
+  request->x = 1;
+  request->pipe = std::move(pipe1.handle0);
+  request->obj = std::move(imported);
+  bool got_response = false;
+  std::string got_text_reply;
+  base::RunLoop run_loop2;
+  factory->DoStuff(std::move(request), std::move(pipe0.handle0),
+                   base::Bind(&DoStuff, &got_response, &got_text_reply,
+                              run_loop2.QuitClosure()));
+
+  EXPECT_FALSE(got_response);
+  int count_before = ImportedInterfaceImpl::do_something_count();
+
+  run_loop.Run();
+  run_loop2.Run();
+
+  EXPECT_TRUE(got_response);
+  EXPECT_EQ(kText1, got_text_reply);
+  EXPECT_EQ(1, ImportedInterfaceImpl::do_something_count() - count_before);
+}
+
+TEST_F(HandlePassingTest, PassInvalid) {
+  sample::FactoryPtr factory;
+  SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+  sample::RequestPtr request(sample::Request::New());
+  request->x = 1;
+  bool got_response = false;
+  std::string got_text_reply;
+  base::RunLoop run_loop;
+  factory->DoStuff(std::move(request), ScopedMessagePipeHandle(),
+                   base::Bind(&DoStuff, &got_response, &got_text_reply,
+                              run_loop.QuitClosure()));
+
+  EXPECT_FALSE(got_response);
+
+  run_loop.Run();
+
+  EXPECT_TRUE(got_response);
+}
+
+// Verifies DataPipeConsumer can be passed and read from.
+TEST_F(HandlePassingTest, DataPipe) {
+  sample::FactoryPtr factory;
+  SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+  // 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";
+  // +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));
+
+  bool got_response = false;
+  std::string got_text_reply;
+  base::RunLoop run_loop;
+  factory->DoStuff2(std::move(consumer_handle),
+                    base::Bind(&DoStuff2, &got_response, &got_text_reply,
+                               run_loop.QuitClosure()));
+
+  EXPECT_FALSE(got_response);
+
+  run_loop.Run();
+
+  EXPECT_TRUE(got_response);
+  EXPECT_EQ(expected_text_reply, got_text_reply);
+}
+
+TEST_F(HandlePassingTest, PipesAreClosed) {
+  sample::FactoryPtr factory;
+  SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+  MessagePipe extra_pipe;
+
+  MojoHandle handle0_value = extra_pipe.handle0.get().value();
+  MojoHandle handle1_value = extra_pipe.handle1.get().value();
+
+  {
+    std::vector<ScopedMessagePipeHandle> pipes(2);
+    pipes[0] = std::move(extra_pipe.handle0);
+    pipes[1] = std::move(extra_pipe.handle1);
+
+    sample::RequestPtr request(sample::Request::New());
+    request->more_pipes = std::move(pipes);
+
+    factory->DoStuff(std::move(request), ScopedMessagePipeHandle(),
+                     sample::Factory::DoStuffCallback());
+  }
+
+  // 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, CreateNamedObject) {
+  sample::FactoryPtr factory;
+  SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+  sample::NamedObjectPtr object1;
+  EXPECT_FALSE(object1);
+
+  InterfaceRequest<sample::NamedObject> object1_request = GetProxy(&object1);
+  EXPECT_TRUE(object1_request.is_pending());
+  factory->CreateNamedObject(std::move(object1_request));
+  EXPECT_FALSE(object1_request.is_pending());  // We've passed the request.
+
+  ASSERT_TRUE(object1);
+  object1->SetName("object1");
+
+  sample::NamedObjectPtr object2;
+  factory->CreateNamedObject(GetProxy(&object2));
+  object2->SetName("object2");
+
+  base::RunLoop run_loop, run_loop2;
+  std::string name1;
+  object1->GetName(MakeStringRecorder(&name1, run_loop.QuitClosure()));
+
+  std::string name2;
+  object2->GetName(MakeStringRecorder(&name2, run_loop2.QuitClosure()));
+
+  run_loop.Run();
+  run_loop2.Run();
+
+  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..aff6251
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
@@ -0,0 +1,740 @@
+// 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 <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/scoping.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+typedef base::Callback<void(double)> CalcCallback;
+
+class MathCalculatorImpl : public math::Calculator {
+ public:
+  explicit MathCalculatorImpl(InterfaceRequest<math::Calculator> request)
+      : total_(0.0), binding_(this, std::move(request)) {}
+  ~MathCalculatorImpl() override {}
+
+  void CloseMessagePipe() { binding_.Close(); }
+
+  void WaitForIncomingMethodCall() { binding_.WaitForIncomingMethodCall(); }
+
+  void Clear(const CalcCallback& callback) override {
+    total_ = 0.0;
+    callback.Run(total_);
+  }
+
+  void Add(double value, const CalcCallback& callback) override {
+    total_ += value;
+    callback.Run(total_);
+  }
+
+  void Multiply(double value, const CalcCallback& callback) override {
+    total_ *= value;
+    callback.Run(total_);
+  }
+
+ private:
+  double total_;
+  Binding<math::Calculator> binding_;
+};
+
+class MathCalculatorUI {
+ public:
+  explicit MathCalculatorUI(math::CalculatorPtr calculator)
+      : calculator_(std::move(calculator)),
+        output_(0.0) {}
+
+  bool encountered_error() const { return calculator_.encountered_error(); }
+  void set_connection_error_handler(const base::Closure& closure) {
+    calculator_.set_connection_error_handler(closure);
+  }
+
+  void Add(double value, const base::Closure& closure) {
+    calculator_->Add(
+        value,
+        base::Bind(&MathCalculatorUI::Output, base::Unretained(this), closure));
+  }
+
+  void Multiply(double value, const base::Closure& closure) {
+    calculator_->Multiply(
+        value,
+        base::Bind(&MathCalculatorUI::Output, base::Unretained(this), closure));
+  }
+
+  double GetOutput() const { return output_; }
+
+ private:
+  void Output(const base::Closure& closure, double output) {
+    output_ = output;
+    if (!closure.is_null())
+      closure.Run();
+  }
+
+  math::CalculatorPtr calculator_;
+  double output_;
+  base::Closure closure_;
+};
+
+class SelfDestructingMathCalculatorUI {
+ public:
+  explicit SelfDestructingMathCalculatorUI(math::CalculatorPtr calculator)
+      : calculator_(std::move(calculator)), nesting_level_(0) {
+    ++num_instances_;
+  }
+
+  void BeginTest(bool nested, const base::Closure& closure) {
+    nesting_level_ = nested ? 2 : 1;
+    calculator_->Add(
+        1.0,
+        base::Bind(&SelfDestructingMathCalculatorUI::Output,
+                   base::Unretained(this), closure));
+  }
+
+  static int num_instances() { return num_instances_; }
+
+  void Output(const base::Closure& closure, double value) {
+    if (--nesting_level_ > 0) {
+      // Add some more and wait for re-entrant call to Output!
+      calculator_->Add(
+          1.0,
+          base::Bind(&SelfDestructingMathCalculatorUI::Output,
+                     base::Unretained(this), closure));
+    } else {
+      closure.Run();
+      delete this;
+    }
+  }
+
+ private:
+  ~SelfDestructingMathCalculatorUI() { --num_instances_; }
+
+  math::CalculatorPtr calculator_;
+  int nesting_level_;
+  static int num_instances_;
+};
+
+// static
+int SelfDestructingMathCalculatorUI::num_instances_ = 0;
+
+class ReentrantServiceImpl : public sample::Service {
+ public:
+  ~ReentrantServiceImpl() override {}
+
+  explicit ReentrantServiceImpl(InterfaceRequest<sample::Service> request)
+      : call_depth_(0),
+        max_call_depth_(0),
+        binding_(this, std::move(request)) {}
+
+  int max_call_depth() { return max_call_depth_; }
+
+  void Frobinate(sample::FooPtr foo,
+                 sample::Service::BazOptions baz,
+                 sample::PortPtr port,
+                 const sample::Service::FrobinateCallback& callback) override {
+    max_call_depth_ = std::max(++call_depth_, max_call_depth_);
+    if (call_depth_ == 1) {
+      EXPECT_TRUE(binding_.WaitForIncomingMethodCall());
+    }
+    call_depth_--;
+    callback.Run(5);
+  }
+
+  void GetPort(mojo::InterfaceRequest<sample::Port> port) override {}
+
+ private:
+  int call_depth_;
+  int max_call_depth_;
+  Binding<sample::Service> binding_;
+};
+
+class IntegerAccessorImpl : public sample::IntegerAccessor {
+ public:
+  IntegerAccessorImpl() : integer_(0) {}
+  ~IntegerAccessorImpl() override {}
+
+  int64_t integer() const { return integer_; }
+
+  void set_closure(const base::Closure& closure) { closure_ = closure; }
+
+ private:
+  // sample::IntegerAccessor implementation.
+  void GetInteger(const GetIntegerCallback& callback) override {
+    callback.Run(integer_, sample::Enum::VALUE);
+  }
+  void SetInteger(int64_t data, sample::Enum type) override {
+    integer_ = data;
+    if (!closure_.is_null()) {
+      closure_.Run();
+      closure_.Reset();
+    }
+  }
+
+  int64_t integer_;
+  base::Closure closure_;
+};
+
+class InterfacePtrTest : public testing::Test {
+ public:
+  InterfacePtrTest() {}
+  ~InterfacePtrTest() override { base::RunLoop().RunUntilIdle(); }
+
+  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
+
+ private:
+  base::MessageLoop loop_;
+};
+
+void SetFlagAndRunClosure(bool* flag, const base::Closure& closure) {
+  *flag = true;
+  closure.Run();
+}
+
+void IgnoreValueAndRunClosure(const base::Closure& closure, int32_t value) {
+  closure.Run();
+}
+
+void ExpectValueAndRunClosure(uint32_t expected_value,
+                              const base::Closure& closure,
+                              uint32_t value) {
+  EXPECT_EQ(expected_value, value);
+  closure.Run();
+}
+
+TEST_F(InterfacePtrTest, IsBound) {
+  math::CalculatorPtr calc;
+  EXPECT_FALSE(calc.is_bound());
+  MathCalculatorImpl calc_impl(GetProxy(&calc));
+  EXPECT_TRUE(calc.is_bound());
+}
+
+TEST_F(InterfacePtrTest, EndToEnd) {
+  math::CalculatorPtr calc;
+  MathCalculatorImpl calc_impl(GetProxy(&calc));
+
+  // Suppose this is instantiated in a process that has pipe1_.
+  MathCalculatorUI calculator_ui(std::move(calc));
+
+  base::RunLoop run_loop, run_loop2;
+  calculator_ui.Add(2.0, run_loop.QuitClosure());
+  calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
+  run_loop.Run();
+  run_loop2.Run();
+
+  EXPECT_EQ(10.0, calculator_ui.GetOutput());
+}
+
+TEST_F(InterfacePtrTest, EndToEnd_Synchronous) {
+  math::CalculatorPtr calc;
+  MathCalculatorImpl calc_impl(GetProxy(&calc));
+
+  // Suppose this is instantiated in a process that has pipe1_.
+  MathCalculatorUI calculator_ui(std::move(calc));
+
+  EXPECT_EQ(0.0, calculator_ui.GetOutput());
+
+  base::RunLoop run_loop;
+  calculator_ui.Add(2.0, run_loop.QuitClosure());
+  EXPECT_EQ(0.0, calculator_ui.GetOutput());
+  calc_impl.WaitForIncomingMethodCall();
+  run_loop.Run();
+  EXPECT_EQ(2.0, calculator_ui.GetOutput());
+
+  base::RunLoop run_loop2;
+  calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
+  EXPECT_EQ(2.0, calculator_ui.GetOutput());
+  calc_impl.WaitForIncomingMethodCall();
+  run_loop2.Run();
+  EXPECT_EQ(10.0, calculator_ui.GetOutput());
+}
+
+TEST_F(InterfacePtrTest, Movable) {
+  math::CalculatorPtr a;
+  math::CalculatorPtr b;
+  MathCalculatorImpl calc_impl(GetProxy(&b));
+
+  EXPECT_TRUE(!a);
+  EXPECT_FALSE(!b);
+
+  a = std::move(b);
+
+  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(
+      InterfacePtrInfo<math::Calculator>(std::move(pipe.handle0), 0u));
+
+  EXPECT_FALSE(!a);
+
+  a.reset();
+
+  EXPECT_TRUE(!a);
+  EXPECT_FALSE(a.internal_state()->is_bound());
+
+  // Test that handle was closed.
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle));
+}
+
+TEST_F(InterfacePtrTest, BindInvalidHandle) {
+  math::CalculatorPtr ptr;
+  EXPECT_FALSE(ptr.get());
+  EXPECT_FALSE(ptr);
+
+  ptr.Bind(InterfacePtrInfo<math::Calculator>());
+  EXPECT_FALSE(ptr.get());
+  EXPECT_FALSE(ptr);
+}
+
+TEST_F(InterfacePtrTest, EncounteredError) {
+  math::CalculatorPtr proxy;
+  MathCalculatorImpl calc_impl(GetProxy(&proxy));
+
+  MathCalculatorUI calculator_ui(std::move(proxy));
+
+  base::RunLoop run_loop;
+  calculator_ui.Add(2.0, run_loop.QuitClosure());
+  run_loop.Run();
+  EXPECT_EQ(2.0, calculator_ui.GetOutput());
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  calculator_ui.Multiply(5.0, base::Closure());
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  // Close the server.
+  calc_impl.CloseMessagePipe();
+
+  // The state change isn't picked up locally yet.
+  base::RunLoop run_loop2;
+  calculator_ui.set_connection_error_handler(run_loop2.QuitClosure());
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  run_loop2.Run();
+
+  // OK, now we see the error.
+  EXPECT_TRUE(calculator_ui.encountered_error());
+}
+
+TEST_F(InterfacePtrTest, EncounteredErrorCallback) {
+  math::CalculatorPtr proxy;
+  MathCalculatorImpl calc_impl(GetProxy(&proxy));
+
+  bool encountered_error = false;
+  base::RunLoop run_loop;
+  proxy.set_connection_error_handler(
+      base::Bind(&SetFlagAndRunClosure, &encountered_error,
+                 run_loop.QuitClosure()));
+
+  MathCalculatorUI calculator_ui(std::move(proxy));
+
+  base::RunLoop run_loop2;
+  calculator_ui.Add(2.0, run_loop2.QuitClosure());
+  run_loop2.Run();
+  EXPECT_EQ(2.0, calculator_ui.GetOutput());
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  calculator_ui.Multiply(5.0, base::Closure());
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  // Close the server.
+  calc_impl.CloseMessagePipe();
+
+  // The state change isn't picked up locally yet.
+  EXPECT_FALSE(calculator_ui.encountered_error());
+
+  run_loop.Run();
+
+  // OK, now we see the error.
+  EXPECT_TRUE(calculator_ui.encountered_error());
+
+  // We should have also been able to observe the error through the error
+  // handler.
+  EXPECT_TRUE(encountered_error);
+}
+
+TEST_F(InterfacePtrTest, DestroyInterfacePtrOnMethodResponse) {
+  math::CalculatorPtr proxy;
+  MathCalculatorImpl calc_impl(GetProxy(&proxy));
+
+  EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
+
+  SelfDestructingMathCalculatorUI* impl =
+      new SelfDestructingMathCalculatorUI(std::move(proxy));
+  base::RunLoop run_loop;
+  impl->BeginTest(false, run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
+}
+
+TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnMethodResponse) {
+  math::CalculatorPtr proxy;
+  MathCalculatorImpl calc_impl(GetProxy(&proxy));
+
+  EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
+
+  SelfDestructingMathCalculatorUI* impl =
+      new SelfDestructingMathCalculatorUI(std::move(proxy));
+  base::RunLoop run_loop;
+  impl->BeginTest(true, run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
+}
+
+TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) {
+  sample::ServicePtr proxy;
+  ReentrantServiceImpl impl(GetProxy(&proxy));
+
+  base::RunLoop run_loop, run_loop2;
+  proxy->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                   base::Bind(&IgnoreValueAndRunClosure,
+                              run_loop.QuitClosure()));
+  proxy->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+                   base::Bind(&IgnoreValueAndRunClosure,
+                              run_loop2.QuitClosure()));
+
+  run_loop.Run();
+  run_loop2.Run();
+
+  EXPECT_EQ(2, impl.max_call_depth());
+}
+
+TEST_F(InterfacePtrTest, QueryVersion) {
+  IntegerAccessorImpl impl;
+  sample::IntegerAccessorPtr ptr;
+  Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr));
+
+  EXPECT_EQ(0u, ptr.version());
+
+  base::RunLoop run_loop;
+  ptr.QueryVersion(base::Bind(&ExpectValueAndRunClosure, 3u,
+                              run_loop.QuitClosure()));
+  run_loop.Run();
+
+  EXPECT_EQ(3u, ptr.version());
+}
+
+TEST_F(InterfacePtrTest, RequireVersion) {
+  IntegerAccessorImpl impl;
+  sample::IntegerAccessorPtr ptr;
+  Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr));
+
+  EXPECT_EQ(0u, ptr.version());
+
+  ptr.RequireVersion(1u);
+  EXPECT_EQ(1u, ptr.version());
+  base::RunLoop run_loop;
+  impl.set_closure(run_loop.QuitClosure());
+  ptr->SetInteger(123, sample::Enum::VALUE);
+  run_loop.Run();
+  EXPECT_FALSE(ptr.encountered_error());
+  EXPECT_EQ(123, impl.integer());
+
+  ptr.RequireVersion(3u);
+  EXPECT_EQ(3u, ptr.version());
+  base::RunLoop run_loop2;
+  impl.set_closure(run_loop2.QuitClosure());
+  ptr->SetInteger(456, sample::Enum::VALUE);
+  run_loop2.Run();
+  EXPECT_FALSE(ptr.encountered_error());
+  EXPECT_EQ(456, impl.integer());
+
+  // Require a version that is not supported by the impl side.
+  ptr.RequireVersion(4u);
+  // This value is set to the input of RequireVersion() synchronously.
+  EXPECT_EQ(4u, ptr.version());
+  base::RunLoop run_loop3;
+  ptr.set_connection_error_handler(run_loop3.QuitClosure());
+  ptr->SetInteger(789, sample::Enum::VALUE);
+  run_loop3.Run();
+  EXPECT_TRUE(ptr.encountered_error());
+  // The call to SetInteger() after RequireVersion(4u) is ignored.
+  EXPECT_EQ(456, impl.integer());
+}
+
+class StrongMathCalculatorImpl : public math::Calculator {
+ public:
+  StrongMathCalculatorImpl(ScopedMessagePipeHandle handle,
+                           bool* error_received,
+                           bool* destroyed,
+                           const base::Closure& closure)
+      : error_received_(error_received),
+        destroyed_(destroyed),
+        closure_(closure),
+        binding_(this, std::move(handle)) {
+    binding_.set_connection_error_handler(
+        base::Bind(&SetFlagAndRunClosure, error_received_, closure_));
+  }
+  ~StrongMathCalculatorImpl() override { *destroyed_ = true; }
+
+  // math::Calculator implementation.
+  void Clear(const CalcCallback& callback) override { callback.Run(total_); }
+
+  void Add(double value, const CalcCallback& callback) override {
+    total_ += value;
+    callback.Run(total_);
+  }
+
+  void Multiply(double value, const CalcCallback& callback) override {
+    total_ *= value;
+    callback.Run(total_);
+  }
+
+ private:
+  double total_ = 0.0;
+  bool* error_received_;
+  bool* destroyed_;
+  base::Closure closure_;
+
+  StrongBinding<math::Calculator> binding_;
+};
+
+TEST(StrongConnectorTest, Math) {
+  base::MessageLoop loop;
+
+  bool error_received = false;
+  bool destroyed = false;
+  MessagePipe pipe;
+  base::RunLoop run_loop;
+  new StrongMathCalculatorImpl(std::move(pipe.handle0), &error_received,
+                               &destroyed, run_loop.QuitClosure());
+
+  math::CalculatorPtr calc;
+  calc.Bind(InterfacePtrInfo<math::Calculator>(std::move(pipe.handle1), 0u));
+
+  {
+    // Suppose this is instantiated in a process that has the other end of the
+    // message pipe.
+    MathCalculatorUI calculator_ui(std::move(calc));
+
+    base::RunLoop run_loop, run_loop2;
+    calculator_ui.Add(2.0, run_loop.QuitClosure());
+    calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
+    run_loop.Run();
+    run_loop2.Run();
+
+    EXPECT_EQ(10.0, calculator_ui.GetOutput());
+    EXPECT_FALSE(error_received);
+    EXPECT_FALSE(destroyed);
+  }
+  // Destroying calculator_ui should close the pipe and generate an error on the
+  // other
+  // end which will destroy the instance since it is strongly bound.
+
+  run_loop.Run();
+  EXPECT_TRUE(error_received);
+  EXPECT_TRUE(destroyed);
+}
+
+class WeakMathCalculatorImpl : public math::Calculator {
+ public:
+  WeakMathCalculatorImpl(ScopedMessagePipeHandle handle,
+                         bool* error_received,
+                         bool* destroyed,
+                         const base::Closure& closure)
+      : error_received_(error_received),
+        destroyed_(destroyed),
+        closure_(closure),
+        binding_(this, std::move(handle)) {
+    binding_.set_connection_error_handler(
+        base::Bind(&SetFlagAndRunClosure, error_received_, closure_));
+  }
+  ~WeakMathCalculatorImpl() override { *destroyed_ = true; }
+
+  void Clear(const CalcCallback& callback) override { callback.Run(total_); }
+
+  void Add(double value, const CalcCallback& callback) override {
+    total_ += value;
+    callback.Run(total_);
+  }
+
+  void Multiply(double value, const CalcCallback& callback) override {
+    total_ *= value;
+    callback.Run(total_);
+  }
+
+ private:
+  double total_ = 0.0;
+  bool* error_received_;
+  bool* destroyed_;
+  base::Closure closure_;
+
+  Binding<math::Calculator> binding_;
+};
+
+TEST(WeakConnectorTest, Math) {
+  base::MessageLoop loop;
+
+  bool error_received = false;
+  bool destroyed = false;
+  MessagePipe pipe;
+  base::RunLoop run_loop;
+  WeakMathCalculatorImpl impl(std::move(pipe.handle0), &error_received,
+                              &destroyed, run_loop.QuitClosure());
+
+  math::CalculatorPtr calc;
+  calc.Bind(InterfacePtrInfo<math::Calculator>(std::move(pipe.handle1), 0u));
+
+  {
+    // Suppose this is instantiated in a process that has the other end of the
+    // message pipe.
+    MathCalculatorUI calculator_ui(std::move(calc));
+
+    base::RunLoop run_loop, run_loop2;
+    calculator_ui.Add(2.0, run_loop.QuitClosure());
+    calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
+    run_loop.Run();
+    run_loop2.Run();
+
+    EXPECT_EQ(10.0, calculator_ui.GetOutput());
+    EXPECT_FALSE(error_received);
+    EXPECT_FALSE(destroyed);
+    // Destroying calculator_ui should close the pipe and generate an error on
+    // the other
+    // end which will destroy the instance since it is strongly bound.
+  }
+
+  run_loop.Run();
+  EXPECT_TRUE(error_received);
+  EXPECT_FALSE(destroyed);
+}
+
+class CImpl : public C {
+ public:
+  CImpl(bool* d_called, InterfaceRequest<C> request,
+        const base::Closure& closure)
+      : d_called_(d_called), binding_(this, std::move(request)),
+        closure_(closure) {}
+  ~CImpl() override {}
+
+ private:
+  void D() override {
+    *d_called_ = true;
+    closure_.Run();
+  }
+
+  bool* d_called_;
+  StrongBinding<C> binding_;
+  base::Closure closure_;
+};
+
+class BImpl : public B {
+ public:
+  BImpl(bool* d_called, InterfaceRequest<B> request,
+        const base::Closure& closure)
+      : d_called_(d_called), binding_(this, std::move(request)),
+        closure_(closure) {}
+  ~BImpl() override {}
+
+ private:
+  void GetC(InterfaceRequest<C> c) override {
+    new CImpl(d_called_, std::move(c), closure_);
+  }
+
+  bool* d_called_;
+  StrongBinding<B> binding_;
+  base::Closure closure_;
+};
+
+class AImpl : public A {
+ public:
+  AImpl(InterfaceRequest<A> request, const base::Closure& closure)
+      : d_called_(false), binding_(this, std::move(request)),
+        closure_(closure) {}
+  ~AImpl() override {}
+
+  bool d_called() const { return d_called_; }
+
+ private:
+  void GetB(InterfaceRequest<B> b) override {
+    new BImpl(&d_called_, std::move(b), closure_);
+  }
+
+  bool d_called_;
+  Binding<A> binding_;
+  base::Closure closure_;
+};
+
+TEST_F(InterfacePtrTest, Scoping) {
+  APtr a;
+  base::RunLoop run_loop;
+  AImpl a_impl(GetProxy(&a), run_loop.QuitClosure());
+
+  EXPECT_FALSE(a_impl.d_called());
+
+  {
+    BPtr b;
+    a->GetB(GetProxy(&b));
+    CPtr c;
+    b->GetC(GetProxy(&c));
+    c->D();
+  }
+
+  // While B & C have fallen out of scope, the pipes will remain until they are
+  // flushed.
+  EXPECT_FALSE(a_impl.d_called());
+  run_loop.Run();
+  EXPECT_TRUE(a_impl.d_called());
+}
+
+class PingTestImpl : public sample::PingTest {
+ public:
+  explicit PingTestImpl(InterfaceRequest<sample::PingTest> request)
+      : binding_(this, std::move(request)) {}
+  ~PingTestImpl() override {}
+
+ private:
+  // sample::PingTest:
+  void Ping(const PingCallback& callback) override { callback.Run(); }
+
+  Binding<sample::PingTest> binding_;
+};
+
+// Tests that FuseProxy does what it's supposed to do.
+TEST_F(InterfacePtrTest, Fusion) {
+  sample::PingTestPtr proxy;
+  PingTestImpl impl(GetProxy(&proxy));
+
+  // Create another PingTest pipe.
+  sample::PingTestPtr ptr;
+  sample::PingTestRequest request = GetProxy(&ptr);
+
+  // Fuse the new pipe to the one hanging off |impl|.
+  EXPECT_TRUE(FuseInterface(std::move(request), proxy.PassInterface()));
+
+  // Ping!
+  bool called = false;
+  base::RunLoop loop;
+  ptr->Ping(base::Bind(&SetFlagAndRunClosure, &called, loop.QuitClosure()));
+  loop.Run();
+  EXPECT_TRUE(called);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/map_common_test.h b/mojo/public/cpp/bindings/tests/map_common_test.h
new file mode 100644
index 0000000..c8654ce
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/map_common_test.h
@@ -0,0 +1,230 @@
+// Copyright 2016 The Chromium Authors. All 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/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/tests/container_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+class String;
+}
+
+namespace mojo {
+
+template <typename T>
+class WTFArray;
+
+template <typename K, typename V>
+class WTFMap;
+
+namespace test {
+namespace {
+
+struct StringIntData {
+  const char* string_data;
+  int int_data;
+} kStringIntData[] = {
+    {"one", 1},
+    {"two", 2},
+    {"three", 3},
+    {"four", 4},
+};
+
+const size_t kStringIntDataSize = 4;
+
+}  // namespace
+
+template <template <typename...> class MapType>
+struct TypeTraits;
+
+template <>
+struct TypeTraits<Map> {
+  using StringType = mojo::String;
+  template <typename T>
+  using ArrayType = Array<T>;
+};
+
+template <>
+struct TypeTraits<WTFMap> {
+  using StringType = WTF::String;
+  template <typename T>
+  using ArrayType = WTFArray<T>;
+};
+
+// Common tests for both mojo::Map and mojo::WTFMap.
+template <template <typename...> class MapType>
+class MapCommonTest {
+ public:
+  using StringType = typename TypeTraits<MapType>::StringType;
+  template <typename T>
+  using ArrayType = typename TypeTraits<MapType>::template ArrayType<T>;
+
+  // Tests null and empty maps.
+  static void NullAndEmpty() {
+    MapType<char, char> map0;
+    EXPECT_TRUE(map0.empty());
+    EXPECT_FALSE(map0.is_null());
+    map0 = nullptr;
+    EXPECT_TRUE(map0.is_null());
+    EXPECT_FALSE(map0.empty());
+
+    MapType<char, char> map1(nullptr);
+    EXPECT_TRUE(map1.is_null());
+    EXPECT_FALSE(map1.empty());
+    map1.SetToEmpty();
+    EXPECT_TRUE(map1.empty());
+    EXPECT_FALSE(map1.is_null());
+  }
+
+  // Tests that basic Map operations work.
+  static void InsertWorks() {
+    MapType<StringType, int> map;
+    for (size_t i = 0; i < kStringIntDataSize; ++i)
+      map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
+
+    for (size_t i = 0; i < kStringIntDataSize; ++i) {
+      EXPECT_EQ(kStringIntData[i].int_data,
+                map.at(kStringIntData[i].string_data));
+    }
+  }
+
+  static void TestIndexOperator() {
+    MapType<StringType, int> map;
+    for (size_t i = 0; i < kStringIntDataSize; ++i)
+      map[kStringIntData[i].string_data] = kStringIntData[i].int_data;
+
+    for (size_t i = 0; i < kStringIntDataSize; ++i) {
+      EXPECT_EQ(kStringIntData[i].int_data,
+                map.at(kStringIntData[i].string_data));
+    }
+  }
+
+  static void TestIndexOperatorAsRValue() {
+    MapType<StringType, int> map;
+    for (size_t i = 0; i < kStringIntDataSize; ++i)
+      map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
+
+    for (size_t i = 0; i < kStringIntDataSize; ++i) {
+      EXPECT_EQ(kStringIntData[i].int_data, map[kStringIntData[i].string_data]);
+    }
+  }
+
+  static void TestIndexOperatorMoveOnly() {
+    ASSERT_EQ(0u, MoveOnlyType::num_instances());
+    MapType<StringType, ArrayType<int32_t>> map;
+
+    for (size_t i = 0; i < kStringIntDataSize; ++i) {
+      const char* key = kStringIntData[i].string_data;
+      ArrayType<int32_t> array(1);
+      array[0] = kStringIntData[i].int_data;
+      map[key] = std::move(array);
+      EXPECT_TRUE(map);
+    }
+
+    // We now read back that data, to test the behavior of operator[].
+    for (size_t i = 0; i < kStringIntDataSize; ++i) {
+      auto& value = map[kStringIntData[i].string_data];
+      ASSERT_EQ(1u, value.size());
+      EXPECT_EQ(kStringIntData[i].int_data, value[0]);
+    }
+  }
+
+  static void MapArrayClone() {
+    MapType<StringType, ArrayType<StringType>> m;
+    for (size_t i = 0; i < kStringIntDataSize; ++i) {
+      ArrayType<StringType> s(1);
+      s[0] = StringType(kStringIntData[i].string_data);
+      m.insert(kStringIntData[i].string_data, std::move(s));
+    }
+
+    MapType<StringType, ArrayType<StringType>> m2 = m.Clone();
+
+    for (size_t i = 0; i < kStringIntDataSize; ++i) {
+      ASSERT_NE(m2.end(), m2.find(kStringIntData[i].string_data));
+      ASSERT_EQ(1u, m2[kStringIntData[i].string_data].size());
+      EXPECT_EQ(StringType(kStringIntData[i].string_data),
+                m2[kStringIntData[i].string_data][0]);
+    }
+  }
+
+  static void ArrayOfMap() {
+    {
+      using MojomType = Array<Map<int32_t, int8_t>>;
+      using UserType = ArrayType<MapType<int32_t, int8_t>>;
+
+      UserType array(1);
+      array[0].insert(1, 42);
+
+      mojo::internal::SerializationContext context;
+      size_t size =
+          mojo::internal::PrepareToSerialize<MojomType>(array, &context);
+      mojo::internal::FixedBufferForTesting buf(size);
+      typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
+      mojo::internal::ContainerValidateParams validate_params(
+          0, false,
+          new mojo::internal::ContainerValidateParams(
+              new mojo::internal::ContainerValidateParams(0, false, nullptr),
+              new mojo::internal::ContainerValidateParams(0, false, nullptr)));
+
+      mojo::internal::Serialize<MojomType>(array, &buf, &data, &validate_params,
+                                           &context);
+
+      UserType deserialized_array;
+      mojo::internal::Deserialize<MojomType>(data, &deserialized_array,
+                                             &context);
+
+      ASSERT_EQ(1u, deserialized_array.size());
+      ASSERT_EQ(1u, deserialized_array[0].size());
+      ASSERT_EQ(42, deserialized_array[0].at(1));
+    }
+
+    {
+      using MojomType = Array<Map<String, Array<bool>>>;
+      using UserType = ArrayType<MapType<StringType, ArrayType<bool>>>;
+
+      UserType array(1);
+      ArrayType<bool> map_value(2);
+      map_value[0] = false;
+      map_value[1] = true;
+      array[0].insert("hello world", std::move(map_value));
+
+      mojo::internal::SerializationContext context;
+      size_t size =
+          mojo::internal::PrepareToSerialize<MojomType>(array, &context);
+      mojo::internal::FixedBufferForTesting buf(size);
+      typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
+      mojo::internal::ContainerValidateParams validate_params(
+          0, false,
+          new mojo::internal::ContainerValidateParams(
+              new mojo::internal::ContainerValidateParams(
+                  0, false, new mojo::internal::ContainerValidateParams(
+                                0, false, nullptr)),
+              new mojo::internal::ContainerValidateParams(
+                  0, false, new mojo::internal::ContainerValidateParams(
+                                0, false, nullptr))));
+      mojo::internal::Serialize<MojomType>(array, &buf, &data, &validate_params,
+                                           &context);
+
+      UserType deserialized_array;
+      mojo::internal::Deserialize<MojomType>(data, &deserialized_array,
+                                             &context);
+
+      ASSERT_EQ(1u, deserialized_array.size());
+      ASSERT_EQ(1u, deserialized_array[0].size());
+      ASSERT_FALSE(deserialized_array[0].at("hello world")[0]);
+      ASSERT_TRUE(deserialized_array[0].at("hello world")[1]);
+    }
+  }
+};
+
+#define MAP_COMMON_TEST(MapType, test_name) \
+  TEST_F(MapType##Test, test_name) { MapCommonTest<MapType>::test_name(); }
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/map_unittest.cc b/mojo/public/cpp/bindings/tests/map_unittest.cc
new file mode 100644
index 0000000..c74a15d
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/map_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 "mojo/public/cpp/bindings/map.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <utility>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/tests/container_test_util.h"
+#include "mojo/public/cpp/bindings/tests/map_common_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+namespace {
+
+using MapTest = testing::Test;
+
+MAP_COMMON_TEST(Map, NullAndEmpty)
+MAP_COMMON_TEST(Map, InsertWorks)
+MAP_COMMON_TEST(Map, TestIndexOperator)
+MAP_COMMON_TEST(Map, TestIndexOperatorAsRValue)
+MAP_COMMON_TEST(Map, TestIndexOperatorMoveOnly)
+MAP_COMMON_TEST(Map, MapArrayClone)
+MAP_COMMON_TEST(Map, ArrayOfMap)
+
+TEST_F(MapTest, ConstructedFromArray) {
+  Array<String> keys(kStringIntDataSize);
+  Array<int> values(kStringIntDataSize);
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    keys[i] = kStringIntData[i].string_data;
+    values[i] = kStringIntData[i].int_data;
+  }
+
+  Map<String, int> map(std::move(keys), std::move(values));
+
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    EXPECT_EQ(kStringIntData[i].int_data,
+              map.at(mojo::String(kStringIntData[i].string_data)));
+  }
+}
+
+TEST_F(MapTest, DecomposeMapTo) {
+  Array<String> keys(kStringIntDataSize);
+  Array<int> values(kStringIntDataSize);
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    keys[i] = kStringIntData[i].string_data;
+    values[i] = kStringIntData[i].int_data;
+  }
+
+  Map<String, int> map(std::move(keys), std::move(values));
+  EXPECT_EQ(kStringIntDataSize, map.size());
+
+  Array<String> keys2;
+  Array<int> values2;
+  map.DecomposeMapTo(&keys2, &values2);
+  EXPECT_EQ(0u, map.size());
+
+  EXPECT_EQ(kStringIntDataSize, keys2.size());
+  EXPECT_EQ(kStringIntDataSize, values2.size());
+
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    // We are not guaranteed that the copies have the same sorting as the
+    // originals.
+    String key = kStringIntData[i].string_data;
+    int value = kStringIntData[i].int_data;
+
+    bool found = false;
+    for (size_t j = 0; j < keys2.size(); ++j) {
+      if (keys2[j] == key) {
+        EXPECT_EQ(value, values2[j]);
+        found = true;
+        break;
+      }
+    }
+
+    EXPECT_TRUE(found);
+  }
+}
+
+TEST_F(MapTest, Insert_Copyable) {
+  ASSERT_EQ(0u, CopyableType::num_instances());
+  mojo::Map<mojo::String, CopyableType> map;
+  std::vector<CopyableType*> value_ptrs;
+
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    const char* key = kStringIntData[i].string_data;
+    CopyableType value;
+    value_ptrs.push_back(value.ptr());
+    map.insert(key, value);
+    ASSERT_EQ(i + 1, map.size());
+    ASSERT_EQ(i + 1, value_ptrs.size());
+    EXPECT_EQ(map.size() + 1, CopyableType::num_instances());
+    EXPECT_TRUE(map.at(key).copied());
+    EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
+    map.at(key).ResetCopied();
+    EXPECT_TRUE(map);
+  }
+
+  // std::map doesn't have a capacity() method like std::vector so this test is
+  // a lot more boring.
+
+  map = nullptr;
+  EXPECT_EQ(0u, CopyableType::num_instances());
+}
+
+TEST_F(MapTest, Insert_MoveOnly) {
+  ASSERT_EQ(0u, MoveOnlyType::num_instances());
+  mojo::Map<mojo::String, MoveOnlyType> map;
+  std::vector<MoveOnlyType*> value_ptrs;
+
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    const char* key = kStringIntData[i].string_data;
+    MoveOnlyType value;
+    value_ptrs.push_back(value.ptr());
+    map.insert(key, std::move(value));
+    ASSERT_EQ(i + 1, map.size());
+    ASSERT_EQ(i + 1, value_ptrs.size());
+    EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances());
+    EXPECT_TRUE(map.at(key).moved());
+    EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
+    map.at(key).ResetMoved();
+    EXPECT_TRUE(map);
+  }
+
+  // std::map doesn't have a capacity() method like std::vector so this test is
+  // a lot more boring.
+
+  map = nullptr;
+  EXPECT_EQ(0u, MoveOnlyType::num_instances());
+}
+
+TEST_F(MapTest, IndexOperator_MoveOnly) {
+  ASSERT_EQ(0u, MoveOnlyType::num_instances());
+  mojo::Map<mojo::String, MoveOnlyType> map;
+  std::vector<MoveOnlyType*> value_ptrs;
+
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    const char* key = kStringIntData[i].string_data;
+    MoveOnlyType value;
+    value_ptrs.push_back(value.ptr());
+    map[key] = std::move(value);
+    ASSERT_EQ(i + 1, map.size());
+    ASSERT_EQ(i + 1, value_ptrs.size());
+    EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances());
+    EXPECT_TRUE(map.at(key).moved());
+    EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
+    map.at(key).ResetMoved();
+    EXPECT_TRUE(map);
+  }
+
+  // std::map doesn't have a capacity() method like std::vector so this test is
+  // a lot more boring.
+
+  map = nullptr;
+  EXPECT_EQ(0u, MoveOnlyType::num_instances());
+}
+
+TEST_F(MapTest, STLToMojo) {
+  std::map<std::string, int> stl_data;
+  for (size_t i = 0; i < kStringIntDataSize; ++i)
+    stl_data[kStringIntData[i].string_data] = kStringIntData[i].int_data;
+
+  Map<String, int32_t> mojo_data = Map<String, int32_t>::From(stl_data);
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    EXPECT_EQ(kStringIntData[i].int_data,
+              mojo_data.at(kStringIntData[i].string_data));
+  }
+}
+
+TEST_F(MapTest, MojoToSTL) {
+  Map<String, int32_t> mojo_map;
+  for (size_t i = 0; i < kStringIntDataSize; ++i)
+    mojo_map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
+
+  std::map<std::string, int> stl_map =
+      mojo_map.To<std::map<std::string, int>>();
+  for (size_t i = 0; i < kStringIntDataSize; ++i) {
+    auto it = stl_map.find(kStringIntData[i].string_data);
+    ASSERT_TRUE(it != stl_map.end());
+    EXPECT_EQ(kStringIntData[i].int_data, it->second);
+  }
+}
+
+TEST_F(MapTest, MoveFromAndToSTLMap_Copyable) {
+  std::map<int32_t, CopyableType> map1;
+  map1.insert(std::make_pair(123, CopyableType()));
+  map1[123].ResetCopied();
+
+  Map<int32_t, CopyableType> mojo_map(std::move(map1));
+  ASSERT_EQ(1u, mojo_map.size());
+  ASSERT_NE(mojo_map.end(), mojo_map.find(123));
+  ASSERT_FALSE(mojo_map[123].copied());
+
+  std::map<int32_t, CopyableType> map2(mojo_map.PassStorage());
+  ASSERT_EQ(1u, map2.size());
+  ASSERT_NE(map2.end(), map2.find(123));
+  ASSERT_FALSE(map2[123].copied());
+
+  ASSERT_EQ(0u, mojo_map.size());
+  ASSERT_TRUE(mojo_map.is_null());
+}
+
+TEST_F(MapTest, MoveFromAndToSTLMap_MoveOnly) {
+  std::map<int32_t, MoveOnlyType> map1;
+  map1.insert(std::make_pair(123, MoveOnlyType()));
+
+  Map<int32_t, MoveOnlyType> mojo_map(std::move(map1));
+  ASSERT_EQ(1u, mojo_map.size());
+  ASSERT_NE(mojo_map.end(), mojo_map.find(123));
+
+  std::map<int32_t, MoveOnlyType> map2(mojo_map.PassStorage());
+  ASSERT_EQ(1u, map2.size());
+  ASSERT_NE(map2.end(), map2.find(123));
+
+  ASSERT_EQ(0u, mojo_map.size());
+  ASSERT_TRUE(mojo_map.is_null());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/message_queue.cc b/mojo/public/cpp/bindings/tests/message_queue.cc
new file mode 100644
index 0000000..96a4829
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/message_queue.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/public/cpp/bindings/tests/message_queue.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace test {
+
+MessageQueue::MessageQueue() {
+}
+
+MessageQueue::~MessageQueue() {
+  while (!queue_.empty())
+    Pop();
+}
+
+bool MessageQueue::IsEmpty() const {
+  return queue_.empty();
+}
+
+void MessageQueue::Push(Message* message) {
+  queue_.push(new Message());
+  message->MoveTo(queue_.back());
+}
+
+void MessageQueue::Pop(Message* message) {
+  DCHECK(!queue_.empty());
+  queue_.front()->MoveTo(message);
+  Pop();
+}
+
+void MessageQueue::Pop() {
+  DCHECK(!queue_.empty());
+  delete queue_.front();
+  queue_.pop();
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/message_queue.h b/mojo/public/cpp/bindings/tests/message_queue.h
new file mode 100644
index 0000000..8859869
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/message_queue.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_CPP_BINDINGS_TESTS_MESSAGE_QUEUE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_MESSAGE_QUEUE_H_
+
+#include <queue>
+
+#include "base/macros.h"
+
+namespace mojo {
+class Message;
+
+namespace test {
+
+// A queue for Message objects.
+class MessageQueue {
+ public:
+  MessageQueue();
+  ~MessageQueue();
+
+  bool IsEmpty() const;
+
+  // This method copies the message data and steals ownership of its handles.
+  void Push(Message* message);
+
+  // Removes the next message from the queue, copying its data and transferring
+  // ownership of its handles to the given |message|.
+  void Pop(Message* message);
+
+  size_t size() const { return queue_.size(); }
+
+ private:
+  void Pop();
+
+  std::queue<Message*> queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageQueue);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_MESSAGE_QUEUE_H_
diff --git a/mojo/public/cpp/bindings/tests/multiplex_router_unittest.cc b/mojo/public/cpp/bindings/tests/multiplex_router_unittest.cc
new file mode 100644
index 0000000..9bfcf2c
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/multiplex_router_unittest.cc
@@ -0,0 +1,344 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+#include "mojo/public/cpp/bindings/tests/message_queue.h"
+#include "mojo/public/cpp/bindings/tests/router_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using mojo::internal::MultiplexRouter;
+
+class MultiplexRouterTest : public testing::Test {
+ public:
+  MultiplexRouterTest() {}
+
+  void SetUp() override {
+    MessagePipe pipe;
+    router0_ = new MultiplexRouter(true, std::move(pipe.handle0),
+                                   base::ThreadTaskRunnerHandle::Get());
+    router1_ = new MultiplexRouter(true, std::move(pipe.handle1),
+                                   base::ThreadTaskRunnerHandle::Get());
+    router0_->CreateEndpointHandlePair(&endpoint0_, &endpoint1_);
+    endpoint1_ =
+        EmulatePassingEndpointHandle(std::move(endpoint1_), router1_.get());
+  }
+
+  void TearDown() override {}
+
+  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
+
+  ScopedInterfaceEndpointHandle EmulatePassingEndpointHandle(
+      ScopedInterfaceEndpointHandle handle,
+      MultiplexRouter* target) {
+    CHECK(!handle.is_local());
+
+    return target->CreateLocalEndpointHandle(handle.release());
+  }
+
+ protected:
+  scoped_refptr<MultiplexRouter> router0_;
+  scoped_refptr<MultiplexRouter> router1_;
+  ScopedInterfaceEndpointHandle endpoint0_;
+  ScopedInterfaceEndpointHandle endpoint1_;
+
+ private:
+  base::MessageLoop loop_;
+};
+
+TEST_F(MultiplexRouterTest, BasicRequestResponse) {
+  InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+  ResponseGenerator generator;
+  InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  base::RunLoop run_loop;
+  client0.AcceptWithResponder(
+      &request,
+      new MessageAccumulator(&message_queue, run_loop.QuitClosure()));
+
+  run_loop.Run();
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  Message response;
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("hello world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+
+  // Send a second message on the pipe.
+  Message request2;
+  AllocRequestMessage(1, "hello again", &request2);
+
+  base::RunLoop run_loop2;
+  client0.AcceptWithResponder(
+      &request2,
+      new MessageAccumulator(&message_queue, run_loop2.QuitClosure()));
+
+  run_loop2.Run();
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("hello again world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+TEST_F(MultiplexRouterTest, BasicRequestResponse_Synchronous) {
+  InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+  ResponseGenerator generator;
+  InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  client0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+  router1_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+  router0_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  Message response;
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("hello world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+
+  // Send a second message on the pipe.
+  Message request2;
+  AllocRequestMessage(1, "hello again", &request2);
+
+  client0.AcceptWithResponder(&request2,
+                              new MessageAccumulator(&message_queue));
+
+  router1_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+  router0_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("hello again world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+TEST_F(MultiplexRouterTest, RequestWithNoReceiver) {
+  InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+  InterfaceEndpointClient client1(std::move(endpoint1_), nullptr,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+
+  // Without an incoming receiver set on client1, we expect client0 to observe
+  // an error as a result of sending a message.
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  base::RunLoop run_loop, run_loop2;
+  client0.set_connection_error_handler(run_loop.QuitClosure());
+  client1.set_connection_error_handler(run_loop2.QuitClosure());
+  client0.AcceptWithResponder(
+      &request, new MessageAccumulator(&message_queue, run_loop.QuitClosure()));
+
+  run_loop.Run();
+  run_loop2.Run();
+
+  EXPECT_TRUE(client0.encountered_error());
+  EXPECT_TRUE(client1.encountered_error());
+  EXPECT_TRUE(message_queue.IsEmpty());
+}
+
+// Tests MultiplexRouter using the LazyResponseGenerator. The responses will not
+// be sent until after the requests have been accepted.
+TEST_F(MultiplexRouterTest, LazyResponses) {
+  InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+  base::RunLoop run_loop;
+  LazyResponseGenerator generator(run_loop.QuitClosure());
+  InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  base::RunLoop run_loop2;
+  client0.AcceptWithResponder(
+      &request,
+      new MessageAccumulator(&message_queue, run_loop2.QuitClosure()));
+  run_loop.Run();
+
+  // The request has been received but the response has not been sent yet.
+  EXPECT_TRUE(message_queue.IsEmpty());
+
+  // Send the response.
+  EXPECT_TRUE(generator.responder_is_valid());
+  generator.CompleteWithResponse();
+  run_loop2.Run();
+
+  // Check the response.
+  EXPECT_FALSE(message_queue.IsEmpty());
+  Message response;
+  message_queue.Pop(&response);
+  EXPECT_EQ(std::string("hello world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+
+  // Send a second message on the pipe.
+  base::RunLoop run_loop3;
+  generator.set_closure(run_loop3.QuitClosure());
+  Message request2;
+  AllocRequestMessage(1, "hello again", &request2);
+
+  base::RunLoop run_loop4;
+  client0.AcceptWithResponder(
+      &request2,
+      new MessageAccumulator(&message_queue, run_loop4.QuitClosure()));
+  run_loop3.Run();
+
+  // The request has been received but the response has not been sent yet.
+  EXPECT_TRUE(message_queue.IsEmpty());
+
+  // Send the second response.
+  EXPECT_TRUE(generator.responder_is_valid());
+  generator.CompleteWithResponse();
+  run_loop4.Run();
+
+  // Check the second response.
+  EXPECT_FALSE(message_queue.IsEmpty());
+  message_queue.Pop(&response);
+  EXPECT_EQ(std::string("hello again world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+void ForwardErrorHandler(bool* called, const base::Closure& callback) {
+  *called = true;
+  callback.Run();
+}
+
+// Tests that if the receiving application destroys the responder_ without
+// sending a response, then we trigger connection error at both sides. Moreover,
+// both sides still appear to have a valid message pipe handle bound.
+TEST_F(MultiplexRouterTest, MissingResponses) {
+  base::RunLoop run_loop0, run_loop1;
+  InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+  bool error_handler_called0 = false;
+  client0.set_connection_error_handler(
+      base::Bind(&ForwardErrorHandler, &error_handler_called0,
+                 run_loop0.QuitClosure()));
+
+  base::RunLoop run_loop3;
+  LazyResponseGenerator generator(run_loop3.QuitClosure());
+  InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
+                                  base::WrapUnique(new PassThroughFilter()),
+                                  false, base::ThreadTaskRunnerHandle::Get());
+  bool error_handler_called1 = false;
+  client1.set_connection_error_handler(
+      base::Bind(&ForwardErrorHandler, &error_handler_called1,
+                 run_loop1.QuitClosure()));
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  client0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+  run_loop3.Run();
+
+  // The request has been received but no response has been sent.
+  EXPECT_TRUE(message_queue.IsEmpty());
+
+  // Destroy the responder MessagerReceiver but don't send any response.
+  generator.CompleteWithoutResponse();
+  run_loop0.Run();
+  run_loop1.Run();
+
+  // Check that no response was received.
+  EXPECT_TRUE(message_queue.IsEmpty());
+
+  // Connection error handler is called at both sides.
+  EXPECT_TRUE(error_handler_called0);
+  EXPECT_TRUE(error_handler_called1);
+
+  // The error flag is set at both sides.
+  EXPECT_TRUE(client0.encountered_error());
+  EXPECT_TRUE(client1.encountered_error());
+
+  // The message pipe handle is valid at both sides.
+  EXPECT_TRUE(router0_->is_valid());
+  EXPECT_TRUE(router1_->is_valid());
+}
+
+TEST_F(MultiplexRouterTest, 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.
+
+  base::RunLoop run_loop;
+  LazyResponseGenerator generator(run_loop.QuitClosure());
+  {
+    InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
+                                    base::WrapUnique(new PassThroughFilter()),
+                                    false, base::ThreadTaskRunnerHandle::Get());
+    InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
+                                    base::WrapUnique(new PassThroughFilter()),
+                                    false, base::ThreadTaskRunnerHandle::Get());
+
+    Message request;
+    AllocRequestMessage(1, "hello", &request);
+
+    MessageQueue message_queue;
+    client0.AcceptWithResponder(&request,
+                                new MessageAccumulator(&message_queue));
+
+    run_loop.Run();
+
+    EXPECT_TRUE(generator.has_responder());
+  }
+
+  EXPECT_FALSE(generator.responder_is_valid());
+  generator.CompleteWithResponse();  // This should end up doing nothing.
+}
+
+// TODO(yzshen): add more tests.
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/pickle_unittest.cc b/mojo/public/cpp/bindings/tests/pickle_unittest.cc
new file mode 100644
index 0000000..bd716cc
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/pickle_unittest.cc
@@ -0,0 +1,408 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/tests/pickled_types_blink.h"
+#include "mojo/public/cpp/bindings/tests/pickled_types_chromium.h"
+#include "mojo/public/cpp/bindings/tests/variant_test_util.h"
+#include "mojo/public/interfaces/bindings/tests/test_native_types.mojom-blink.h"
+#include "mojo/public/interfaces/bindings/tests/test_native_types.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+template <typename T>
+void DoExpectResult(int foo,
+                    int bar,
+                    const base::Closure& callback,
+                    const T& actual) {
+  EXPECT_EQ(foo, actual.foo());
+  EXPECT_EQ(bar, actual.bar());
+  callback.Run();
+}
+
+template <typename T>
+base::Callback<void(const T&)> ExpectResult(const T& t,
+                                            const base::Closure& callback) {
+  return base::Bind(&DoExpectResult<T>, t.foo(), t.bar(), callback);
+}
+
+template <typename T>
+void DoFail(const std::string& reason, const T&) {
+  EXPECT_TRUE(false) << reason;
+}
+
+template <typename T>
+base::Callback<void(const T&)> Fail(const std::string& reason) {
+  return base::Bind(&DoFail<T>, reason);
+}
+
+template <typename T>
+void DoExpectEnumResult(T expected, const base::Closure& callback, T actual) {
+  EXPECT_EQ(expected, actual);
+  callback.Run();
+}
+
+template <typename T>
+base::Callback<void(T)> ExpectEnumResult(T t, const base::Closure& callback) {
+  return base::Bind(&DoExpectEnumResult<T>, t, callback);
+}
+
+template <typename T>
+void DoEnumFail(const std::string& reason, T) {
+  EXPECT_TRUE(false) << reason;
+}
+
+template <typename T>
+base::Callback<void(T)> EnumFail(const std::string& reason) {
+  return base::Bind(&DoEnumFail<T>, reason);
+}
+
+template <typename T>
+void ExpectError(InterfacePtr<T>* proxy, const base::Closure& callback) {
+  proxy->set_connection_error_handler(callback);
+}
+
+template <typename Func, typename Arg>
+void RunSimpleLambda(Func func, Arg arg) { func(std::move(arg)); }
+
+template <typename Arg, typename Func>
+base::Callback<void(Arg)> BindSimpleLambda(Func func) {
+  return base::Bind(&RunSimpleLambda<Func, Arg>, func);
+}
+
+// This implements the generated Chromium variant of PicklePasser.
+class ChromiumPicklePasserImpl : public PicklePasser {
+ public:
+  ChromiumPicklePasserImpl() {}
+
+  // mojo::test::PicklePasser:
+  void PassPickledStruct(const PickledStructChromium& pickle,
+                         const PassPickledStructCallback& callback) override {
+    callback.Run(pickle);
+  }
+
+  void PassPickledEnum(PickledEnumChromium pickle,
+                       const PassPickledEnumCallback& callback) override {
+    callback.Run(pickle);
+  }
+
+  void PassPickleContainer(
+      PickleContainerPtr container,
+      const PassPickleContainerCallback& callback) override {
+    callback.Run(std::move(container));
+  }
+
+  void PassPickles(const std::vector<PickledStructChromium>& pickles,
+                   const PassPicklesCallback& callback) override {
+    callback.Run(pickles);
+  }
+
+  void PassPickleArrays(
+      const std::vector<std::vector<PickledStructChromium>>& pickle_arrays,
+      const PassPickleArraysCallback& callback) override {
+    callback.Run(pickle_arrays);
+  }
+};
+
+// This implements the generated Blink variant of PicklePasser.
+class BlinkPicklePasserImpl : public blink::PicklePasser {
+ public:
+  BlinkPicklePasserImpl() {}
+
+  // mojo::test::blink::PicklePasser:
+  void PassPickledStruct(const PickledStructBlink& pickle,
+                         const PassPickledStructCallback& callback) override {
+    callback.Run(pickle);
+  }
+
+  void PassPickledEnum(PickledEnumBlink pickle,
+                       const PassPickledEnumCallback& callback) override {
+    callback.Run(pickle);
+  }
+
+  void PassPickleContainer(
+      blink::PickleContainerPtr container,
+      const PassPickleContainerCallback& callback) override {
+    callback.Run(std::move(container));
+  }
+
+  void PassPickles(const WTF::Vector<PickledStructBlink>& pickles,
+                   const PassPicklesCallback& callback) override {
+    callback.Run(pickles);
+  }
+
+  void PassPickleArrays(
+      const WTF::Vector<WTF::Vector<PickledStructBlink>>& pickle_arrays,
+      const PassPickleArraysCallback& callback) override {
+    callback.Run(pickle_arrays);
+  }
+};
+
+// A test which runs both Chromium and Blink implementations of the
+// PicklePasser service.
+class PickleTest : public testing::Test {
+ public:
+  PickleTest() {}
+
+  template <typename ProxyType = PicklePasser>
+  InterfacePtr<ProxyType> ConnectToChromiumService() {
+    InterfacePtr<ProxyType> proxy;
+    InterfaceRequest<ProxyType> request = GetProxy(&proxy);
+    chromium_bindings_.AddBinding(
+        &chromium_service_,
+        ConvertInterfaceRequest<PicklePasser>(std::move(request)));
+    return proxy;
+  }
+
+  template <typename ProxyType = blink::PicklePasser>
+  InterfacePtr<ProxyType> ConnectToBlinkService() {
+    InterfacePtr<ProxyType> proxy;
+    InterfaceRequest<ProxyType> request = GetProxy(&proxy);
+    blink_bindings_.AddBinding(
+        &blink_service_,
+        ConvertInterfaceRequest<blink::PicklePasser>(std::move(request)));
+    return proxy;
+  }
+
+ private:
+  base::MessageLoop loop_;
+  ChromiumPicklePasserImpl chromium_service_;
+  BindingSet<PicklePasser> chromium_bindings_;
+  BlinkPicklePasserImpl blink_service_;
+  BindingSet<blink::PicklePasser> blink_bindings_;
+};
+
+}  // namespace
+
+TEST_F(PickleTest, ChromiumProxyToChromiumService) {
+  auto chromium_proxy = ConnectToChromiumService();
+  {
+    base::RunLoop loop;
+    chromium_proxy->PassPickledStruct(
+        PickledStructChromium(1, 2),
+        ExpectResult(PickledStructChromium(1, 2), loop.QuitClosure()));
+    loop.Run();
+  }
+  {
+    base::RunLoop loop;
+    chromium_proxy->PassPickledStruct(
+        PickledStructChromium(4, 5),
+        ExpectResult(PickledStructChromium(4, 5), loop.QuitClosure()));
+    loop.Run();
+  }
+
+  {
+    base::RunLoop loop;
+    chromium_proxy->PassPickledEnum(
+        PickledEnumChromium::VALUE_1,
+        ExpectEnumResult(PickledEnumChromium::VALUE_1, loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+TEST_F(PickleTest, ChromiumProxyToBlinkService) {
+  auto chromium_proxy = ConnectToBlinkService<PicklePasser>();
+  {
+    base::RunLoop loop;
+    chromium_proxy->PassPickledStruct(
+        PickledStructChromium(1, 2),
+        ExpectResult(PickledStructChromium(1, 2), loop.QuitClosure()));
+    loop.Run();
+  }
+  {
+    base::RunLoop loop;
+    chromium_proxy->PassPickledStruct(
+        PickledStructChromium(4, 5),
+        ExpectResult(PickledStructChromium(4, 5), loop.QuitClosure()));
+    loop.Run();
+  }
+  // The Blink service should drop our connection because the
+  // PickledStructBlink ParamTraits deserializer rejects negative values.
+  {
+    base::RunLoop loop;
+    chromium_proxy->PassPickledStruct(
+        PickledStructChromium(-1, -1),
+        Fail<PickledStructChromium>("Blink service should reject this."));
+    ExpectError(&chromium_proxy, loop.QuitClosure());
+    loop.Run();
+  }
+
+  chromium_proxy = ConnectToBlinkService<PicklePasser>();
+  {
+    base::RunLoop loop;
+    chromium_proxy->PassPickledEnum(
+        PickledEnumChromium::VALUE_0,
+        ExpectEnumResult(PickledEnumChromium::VALUE_0, loop.QuitClosure()));
+    loop.Run();
+  }
+
+  // The Blink service should drop our connection because the
+  // PickledEnumBlink ParamTraits deserializer rejects this value.
+  {
+    base::RunLoop loop;
+    chromium_proxy->PassPickledEnum(
+        PickledEnumChromium::VALUE_2,
+        EnumFail<PickledEnumChromium>("Blink service should reject this."));
+    ExpectError(&chromium_proxy, loop.QuitClosure());
+    loop.Run();
+  }
+}
+
+TEST_F(PickleTest, BlinkProxyToBlinkService) {
+  auto blink_proxy = ConnectToBlinkService();
+  {
+    base::RunLoop loop;
+    blink_proxy->PassPickledStruct(
+        PickledStructBlink(1, 1),
+        ExpectResult(PickledStructBlink(1, 1), loop.QuitClosure()));
+    loop.Run();
+  }
+
+  {
+    base::RunLoop loop;
+    blink_proxy->PassPickledEnum(
+        PickledEnumBlink::VALUE_0,
+        ExpectEnumResult(PickledEnumBlink::VALUE_0, loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+TEST_F(PickleTest, BlinkProxyToChromiumService) {
+  auto blink_proxy = ConnectToChromiumService<blink::PicklePasser>();
+  {
+    base::RunLoop loop;
+    blink_proxy->PassPickledStruct(
+        PickledStructBlink(1, 1),
+        ExpectResult(PickledStructBlink(1, 1), loop.QuitClosure()));
+    loop.Run();
+  }
+
+  {
+    base::RunLoop loop;
+    blink_proxy->PassPickledEnum(
+        PickledEnumBlink::VALUE_1,
+        ExpectEnumResult(PickledEnumBlink::VALUE_1, loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+TEST_F(PickleTest, PickleArray) {
+  auto proxy = ConnectToChromiumService();
+  auto pickles = Array<PickledStructChromium>::New(2);
+  pickles[0].set_foo(1);
+  pickles[0].set_bar(2);
+  pickles[0].set_baz(100);
+  pickles[1].set_foo(3);
+  pickles[1].set_bar(4);
+  pickles[1].set_baz(100);
+  {
+    base::RunLoop run_loop;
+    // Verify that the array of pickled structs can be serialized and
+    // deserialized intact. This ensures that the ParamTraits are actually used
+    // rather than doing a byte-for-byte copy of the element data, beacuse the
+    // |baz| field should never be serialized.
+    proxy->PassPickles(
+        std::move(pickles),
+        BindSimpleLambda<const std::vector<PickledStructChromium>&>(
+            [&](const std::vector<PickledStructChromium>& passed) {
+              ASSERT_EQ(2u, passed.size());
+              EXPECT_EQ(1, passed[0].foo());
+              EXPECT_EQ(2, passed[0].bar());
+              EXPECT_EQ(0, passed[0].baz());
+              EXPECT_EQ(3, passed[1].foo());
+              EXPECT_EQ(4, passed[1].bar());
+              EXPECT_EQ(0, passed[1].baz());
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+  }
+}
+
+TEST_F(PickleTest, PickleArrayArray) {
+  auto proxy = ConnectToChromiumService();
+  auto pickle_arrays = std::vector<std::vector<PickledStructChromium>>(2);
+  for (size_t i = 0; i < 2; ++i)
+    pickle_arrays[i] = std::vector<PickledStructChromium>(2);
+
+  pickle_arrays[0][0].set_foo(1);
+  pickle_arrays[0][0].set_bar(2);
+  pickle_arrays[0][0].set_baz(100);
+  pickle_arrays[0][1].set_foo(3);
+  pickle_arrays[0][1].set_bar(4);
+  pickle_arrays[0][1].set_baz(100);
+  pickle_arrays[1][0].set_foo(5);
+  pickle_arrays[1][0].set_bar(6);
+  pickle_arrays[1][0].set_baz(100);
+  pickle_arrays[1][1].set_foo(7);
+  pickle_arrays[1][1].set_bar(8);
+  pickle_arrays[1][1].set_baz(100);
+  {
+    base::RunLoop run_loop;
+    // Verify that the array-of-arrays serializes and deserializes properly.
+    proxy->PassPickleArrays(
+        pickle_arrays,
+        BindSimpleLambda<
+            const std::vector<std::vector<PickledStructChromium>>&>(
+            [&](const std::vector<std::vector<PickledStructChromium>>& passed) {
+              ASSERT_EQ(2u, passed.size());
+              ASSERT_EQ(2u, passed[0].size());
+              ASSERT_EQ(2u, passed[1].size());
+              EXPECT_EQ(1, passed[0][0].foo());
+              EXPECT_EQ(2, passed[0][0].bar());
+              EXPECT_EQ(0, passed[0][0].baz());
+              EXPECT_EQ(3, passed[0][1].foo());
+              EXPECT_EQ(4, passed[0][1].bar());
+              EXPECT_EQ(0, passed[0][1].baz());
+              EXPECT_EQ(5, passed[1][0].foo());
+              EXPECT_EQ(6, passed[1][0].bar());
+              EXPECT_EQ(0, passed[1][0].baz());
+              EXPECT_EQ(7, passed[1][1].foo());
+              EXPECT_EQ(8, passed[1][1].bar());
+              EXPECT_EQ(0, passed[1][1].baz());
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+  }
+}
+
+TEST_F(PickleTest, PickleContainer) {
+  auto proxy = ConnectToChromiumService();
+  PickleContainerPtr pickle_container = PickleContainer::New();
+  pickle_container->f_struct.set_foo(42);
+  pickle_container->f_struct.set_bar(43);
+  pickle_container->f_struct.set_baz(44);
+  pickle_container->f_enum = PickledEnumChromium::VALUE_1;
+  EXPECT_TRUE(pickle_container.Equals(pickle_container));
+  EXPECT_FALSE(pickle_container.Equals(PickleContainer::New()));
+  {
+    base::RunLoop run_loop;
+    proxy->PassPickleContainer(std::move(pickle_container),
+                               BindSimpleLambda<PickleContainerPtr>(
+                                   [&](PickleContainerPtr passed) {
+                                     ASSERT_FALSE(passed.is_null());
+                                     EXPECT_EQ(42, passed->f_struct.foo());
+                                     EXPECT_EQ(43, passed->f_struct.bar());
+                                     EXPECT_EQ(0, passed->f_struct.baz());
+                                     EXPECT_EQ(PickledEnumChromium::VALUE_1,
+                                               passed->f_enum);
+                                     run_loop.Quit();
+                                   }));
+    run_loop.Run();
+  }
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/pickled_types_blink.cc b/mojo/public/cpp/bindings/tests/pickled_types_blink.cc
new file mode 100644
index 0000000..7e55650
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/pickled_types_blink.cc
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/tests/pickled_types_blink.h"
+
+#include "base/logging.h"
+#include "base/pickle.h"
+
+namespace mojo {
+namespace test {
+
+PickledStructBlink::PickledStructBlink() {}
+
+PickledStructBlink::PickledStructBlink(int foo, int bar)
+    : foo_(foo), bar_(bar) {
+  DCHECK_GE(foo_, 0);
+  DCHECK_GE(bar_, 0);
+}
+
+PickledStructBlink::~PickledStructBlink() {}
+
+}  // namespace test
+}  // namespace mojo
+
+namespace IPC {
+
+void ParamTraits<mojo::test::PickledStructBlink>::GetSize(
+    base::PickleSizer* sizer,
+    const param_type& p) {
+  sizer->AddInt();
+  sizer->AddInt();
+}
+
+void ParamTraits<mojo::test::PickledStructBlink>::Write(base::Pickle* m,
+                                                        const param_type& p) {
+  m->WriteInt(p.foo());
+  m->WriteInt(p.bar());
+}
+
+bool ParamTraits<mojo::test::PickledStructBlink>::Read(
+    const base::Pickle* m,
+    base::PickleIterator* iter,
+    param_type* p) {
+  int foo, bar;
+  if (!iter->ReadInt(&foo) || !iter->ReadInt(&bar) || foo < 0 || bar < 0)
+    return false;
+
+  p->set_foo(foo);
+  p->set_bar(bar);
+  return true;
+}
+
+#include "ipc/param_traits_size_macros.h"
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumBlink,
+                          mojo::test::PickledEnumBlink::VALUE_1)
+#include "ipc/param_traits_write_macros.h"
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumBlink,
+                          mojo::test::PickledEnumBlink::VALUE_1)
+#include "ipc/param_traits_read_macros.h"
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumBlink,
+                          mojo::test::PickledEnumBlink::VALUE_1)
+#include "ipc/param_traits_log_macros.h"
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumBlink,
+                          mojo::test::PickledEnumBlink::VALUE_1)
+
+}  // namespace IPC
diff --git a/mojo/public/cpp/bindings/tests/pickled_types_blink.h b/mojo/public/cpp/bindings/tests/pickled_types_blink.h
new file mode 100644
index 0000000..0b7bd20
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/pickled_types_blink.h
@@ -0,0 +1,83 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_PICKLED_TYPES_BLINK_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_PICKLED_TYPES_BLINK_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_param_traits.h"
+
+namespace base {
+class Pickle;
+class PickleIterator;
+class PickleSizer;
+}
+
+namespace mojo {
+namespace test {
+
+// Implementation of types with IPC::ParamTraits for consumers in Blink.
+
+enum class PickledEnumBlink { VALUE_0, VALUE_1 };
+
+// To make things slightly more interesting, this variation of the type doesn't
+// support negative values. It'll DCHECK if you try to construct it with any,
+// and it will fail deserialization if negative values are decoded.
+class PickledStructBlink {
+ public:
+  PickledStructBlink();
+  PickledStructBlink(int foo, int bar);
+  PickledStructBlink(PickledStructBlink&& other) = default;
+  ~PickledStructBlink();
+
+  PickledStructBlink& operator=(PickledStructBlink&& other) = default;
+
+  int foo() const { return foo_; }
+  void set_foo(int foo) {
+    DCHECK_GE(foo, 0);
+    foo_ = foo;
+  }
+
+  int bar() const { return bar_; }
+  void set_bar(int bar) {
+    DCHECK_GE(bar, 0);
+    bar_ = bar;
+  }
+
+ private:
+  int foo_ = 0;
+  int bar_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(PickledStructBlink);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mojo::test::PickledStructBlink> {
+  using param_type = mojo::test::PickledStructBlink;
+
+  static void GetSize(base::PickleSizer* sizer, const param_type& p);
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r);
+  static void Log(const param_type& p, std::string* l) {}
+};
+
+}  // namespace IPC
+
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumBlink,
+                          mojo::test::PickledEnumBlink::VALUE_1)
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_PICKLED_TYPES_BLINK_H_
diff --git a/mojo/public/cpp/bindings/tests/pickled_types_chromium.cc b/mojo/public/cpp/bindings/tests/pickled_types_chromium.cc
new file mode 100644
index 0000000..9957c9a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/pickled_types_chromium.cc
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/tests/pickled_types_chromium.h"
+
+#include "base/pickle.h"
+
+namespace mojo {
+namespace test {
+
+PickledStructChromium::PickledStructChromium() {}
+
+PickledStructChromium::PickledStructChromium(int foo, int bar)
+    : foo_(foo), bar_(bar) {}
+
+PickledStructChromium::~PickledStructChromium() {}
+
+bool operator==(const PickledStructChromium& a,
+                const PickledStructChromium& b) {
+  return a.foo() == b.foo() && a.bar() == b.bar() && a.baz() == b.baz();
+}
+
+}  // namespace test
+}  // namespace mojo
+
+namespace IPC {
+
+void ParamTraits<mojo::test::PickledStructChromium>::GetSize(
+    base::PickleSizer* sizer,
+    const param_type& p) {
+  sizer->AddInt();
+  sizer->AddInt();
+}
+
+void ParamTraits<mojo::test::PickledStructChromium>::Write(
+    base::Pickle* m,
+    const param_type& p) {
+  m->WriteInt(p.foo());
+  m->WriteInt(p.bar());
+}
+
+bool ParamTraits<mojo::test::PickledStructChromium>::Read(
+    const base::Pickle* m,
+    base::PickleIterator* iter,
+    param_type* p) {
+  int foo, bar;
+  if (!iter->ReadInt(&foo) || !iter->ReadInt(&bar))
+    return false;
+
+  p->set_foo(foo);
+  p->set_bar(bar);
+  return true;
+}
+
+#include "ipc/param_traits_size_macros.h"
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumChromium,
+                          mojo::test::PickledEnumChromium::VALUE_2)
+#include "ipc/param_traits_write_macros.h"
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumChromium,
+                          mojo::test::PickledEnumChromium::VALUE_2)
+#include "ipc/param_traits_read_macros.h"
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumChromium,
+                          mojo::test::PickledEnumChromium::VALUE_2)
+#include "ipc/param_traits_log_macros.h"
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumChromium,
+                          mojo::test::PickledEnumChromium::VALUE_2)
+
+}  // namespace IPC
diff --git a/mojo/public/cpp/bindings/tests/pickled_types_chromium.h b/mojo/public/cpp/bindings/tests/pickled_types_chromium.h
new file mode 100644
index 0000000..d9287b6
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/pickled_types_chromium.h
@@ -0,0 +1,81 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_PICKLED_TYPES_CHROMIUM_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_PICKLED_TYPES_CHROMIUM_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_param_traits.h"
+
+namespace base {
+class Pickle;
+class PickleIterator;
+class PickleSizer;
+}
+
+namespace mojo {
+namespace test {
+
+// Implementation of types with IPC::ParamTraits for consumers in the greater
+// Chromium tree.
+
+enum class PickledEnumChromium { VALUE_0, VALUE_1, VALUE_2 };
+
+class PickledStructChromium {
+ public:
+  PickledStructChromium();
+  PickledStructChromium(int foo, int bar);
+  PickledStructChromium(PickledStructChromium&& other) = default;
+  ~PickledStructChromium();
+
+  PickledStructChromium& operator=(PickledStructChromium&& other) = default;
+
+  int foo() const { return foo_; }
+  void set_foo(int foo) { foo_ = foo; }
+
+  int bar() const { return bar_; }
+  void set_bar(int bar) { bar_ = bar; }
+
+  // The |baz| field should never be serialized.
+  int baz() const { return baz_; }
+  void set_baz(int baz) { baz_ = baz; }
+
+ private:
+  int foo_ = 0;
+  int bar_ = 0;
+  int baz_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(PickledStructChromium);
+};
+
+bool operator==(const PickledStructChromium& a, const PickledStructChromium& b);
+
+}  // namespace test
+}  // namespace mojo
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mojo::test::PickledStructChromium> {
+  using param_type = mojo::test::PickledStructChromium;
+
+  static void GetSize(base::PickleSizer* sizer, const param_type& p);
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r);
+  static void Log(const param_type& p, std::string* l) {}
+};
+
+}  // namespace IPC
+
+IPC_ENUM_TRAITS_MAX_VALUE(mojo::test::PickledEnumChromium,
+                          mojo::test::PickledEnumChromium::VALUE_2)
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_PICKLED_TYPES_CHROMIUM_H_
diff --git a/mojo/public/cpp/bindings/tests/rect_blink.h b/mojo/public/cpp/bindings/tests/rect_blink.h
new file mode 100644
index 0000000..de7a792
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/rect_blink.h
@@ -0,0 +1,64 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_BLINK_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_BLINK_H_
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace test {
+
+// An implementation of a hypothetical Rect type specifically for consumers in
+// in Blink. Unlike the Chromium variant (see rect_chromium.h) this does not
+// support negative origin coordinates and is not copyable.
+class RectBlink {
+ public:
+  RectBlink() {}
+  RectBlink(int x, int y, int width, int height) :
+      x_(x), y_(y), width_(width), height_(height) {
+    DCHECK_GE(x_, 0);
+    DCHECK_GE(y_, 0);
+    DCHECK_GE(width_, 0);
+    DCHECK_GE(height_, 0);
+  }
+  ~RectBlink() {}
+
+  int x() const { return x_; }
+  void setX(int x) {
+    DCHECK_GE(x, 0);
+    x_ = x;
+  }
+
+  int y() const { return y_; }
+  void setY(int y) {
+    DCHECK_GE(y, 0);
+    y_ = y;
+  }
+
+  int width() const { return width_; }
+  void setWidth(int width) {
+    DCHECK_GE(width, 0);
+    width_ = width;
+  }
+
+  int height() const { return height_; }
+  void setHeight(int height) {
+    DCHECK_GE(height, 0);
+    height_ = height;
+  }
+
+  int computeArea() const { return width_ * height_; }
+
+ private:
+  int x_ = 0;
+  int y_ = 0;
+  int width_ = 0;
+  int height_ = 0;
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_BLINK_H_
diff --git a/mojo/public/cpp/bindings/tests/rect_blink.typemap b/mojo/public/cpp/bindings/tests/rect_blink.typemap
new file mode 100644
index 0000000..37ee409
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/rect_blink.typemap
@@ -0,0 +1,9 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/interfaces/bindings/tests/rect.mojom"
+public_headers = [ "//mojo/public/cpp/bindings/tests/rect_blink.h" ]
+traits_headers = [ "//mojo/public/cpp/bindings/tests/rect_blink_traits.h" ]
+
+type_mappings = [ "mojo.test.TypemappedRect=mojo::test::RectBlink" ]
diff --git a/mojo/public/cpp/bindings/tests/rect_blink_traits.h b/mojo/public/cpp/bindings/tests/rect_blink_traits.h
new file mode 100644
index 0000000..c5f6068
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/rect_blink_traits.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All 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_RECT_BLINK_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_BLINK_TRAITS_H_
+
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "mojo/public/cpp/bindings/tests/rect_blink.h"
+#include "mojo/public/interfaces/bindings/tests/rect.mojom-blink.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<test::blink::TypemappedRect, test::RectBlink> {
+  static int x(const test::RectBlink& r) { return r.x(); }
+  static int y(const test::RectBlink& r) { return r.y(); }
+  static int width(const test::RectBlink& r) { return r.width(); }
+  static int height(const test::RectBlink& r) { return r.height(); }
+
+  static bool Read(test::blink::TypemappedRectDataView r,
+                   test::RectBlink* out) {
+    if (r.x() < 0 || r.y() < 0 || r.width() < 0 || r.height() < 0) {
+      return false;
+    }
+    out->setX(r.x());
+    out->setY(r.y());
+    out->setWidth(r.width());
+    out->setHeight(r.height());
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_BLINK_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/tests/rect_chromium.h b/mojo/public/cpp/bindings/tests/rect_chromium.h
new file mode 100644
index 0000000..20c4362
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/rect_chromium.h
@@ -0,0 +1,68 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_CHROMIUM_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_CHROMIUM_H_
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace test {
+
+// An implementation of a hypothetical Rect type specifically for consumers in
+// in Chromium.
+class RectChromium {
+ public:
+  RectChromium() {}
+  RectChromium(const RectChromium& other)
+      : x_(other.x_),
+        y_(other.y_),
+        width_(other.width_),
+        height_(other.height_) {}
+  RectChromium(int x, int y, int width, int height) :
+      x_(x), y_(y), width_(width), height_(height) {
+    DCHECK_GE(width_, 0);
+    DCHECK_GE(height_, 0);
+  }
+  ~RectChromium() {}
+
+  RectChromium& operator=(const RectChromium& other) {
+    x_ = other.x_;
+    y_ = other.y_;
+    width_ = other.width_;
+    height_ = other.height_;
+    return *this;
+  }
+
+  int x() const { return x_; }
+  void set_x(int x) { x_ = x; }
+
+  int y() const { return y_; }
+  void set_y(int y) { y_ = y; }
+
+  int width() const { return width_; }
+  void set_width(int width) {
+    DCHECK_GE(width, 0);
+    width_ = width;
+  }
+
+  int height() const { return height_; }
+  void set_height(int height) {
+    DCHECK_GE(height, 0);
+    height_ = height;
+  }
+
+  int GetArea() const { return width_ * height_; }
+
+ private:
+  int x_ = 0;
+  int y_ = 0;
+  int width_ = 0;
+  int height_ = 0;
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_CHROMIUM_H_
diff --git a/mojo/public/cpp/bindings/tests/rect_chromium.typemap b/mojo/public/cpp/bindings/tests/rect_chromium.typemap
new file mode 100644
index 0000000..0da4021
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/rect_chromium.typemap
@@ -0,0 +1,9 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/interfaces/bindings/tests/rect.mojom"
+public_headers = [ "//mojo/public/cpp/bindings/tests/rect_chromium.h" ]
+traits_headers = [ "//mojo/public/cpp/bindings/tests/rect_chromium_traits.h" ]
+
+type_mappings = [ "mojo.test.TypemappedRect=mojo::test::RectChromium" ]
diff --git a/mojo/public/cpp/bindings/tests/rect_chromium_traits.h b/mojo/public/cpp/bindings/tests/rect_chromium_traits.h
new file mode 100644
index 0000000..ab65371
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/rect_chromium_traits.h
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_CHROMIUM_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_CHROMIUM_TRAITS_H_
+
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "mojo/public/cpp/bindings/tests/rect_chromium.h"
+#include "mojo/public/interfaces/bindings/tests/rect.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<test::TypemappedRect, test::RectChromium> {
+  static int x(const test::RectChromium& r) { return r.x(); }
+  static int y(const test::RectChromium& r) { return r.y(); }
+  static int width(const test::RectChromium& r) { return r.width(); }
+  static int height(const test::RectChromium& r) { return r.height(); }
+
+  static bool Read(test::TypemappedRectDataView r, test::RectChromium* out) {
+    if (r.width() < 0 || r.height() < 0)
+      return false;
+    out->set_x(r.x());
+    out->set_y(r.y());
+    out->set_width(r.width());
+    out->set_height(r.height());
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_CHROMIUM_TRAITS_H_
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..5e203a2
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/request_response_unittest.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 <stdint.h>
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/test_support/test_utils.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 sample::Provider {
+ public:
+  explicit ProviderImpl(InterfaceRequest<sample::Provider> request)
+      : binding_(this, std::move(request)) {}
+
+  void EchoString(const std::string& a,
+                  const EchoStringCallback& callback) override {
+    EchoStringCallback callback_copy;
+    // Make sure operator= is used.
+    callback_copy = callback;
+    callback_copy.Run(a);
+  }
+
+  void EchoStrings(const std::string& a,
+                   const std::string& b,
+                   const EchoStringsCallback& callback) override {
+    callback.Run(a, b);
+  }
+
+  void EchoMessagePipeHandle(
+      ScopedMessagePipeHandle a,
+      const EchoMessagePipeHandleCallback& callback) override {
+    callback.Run(std::move(a));
+  }
+
+  void EchoEnum(sample::Enum a, const EchoEnumCallback& callback) override {
+    callback.Run(a);
+  }
+
+  void EchoInt(int32_t a, const EchoIntCallback& callback) override {
+    callback.Run(a);
+  }
+
+  Binding<sample::Provider> binding_;
+};
+
+void RecordString(std::string* storage,
+                  const base::Closure& closure,
+                  const std::string& str) {
+  *storage = str;
+  closure.Run();
+}
+
+void RecordStrings(std::string* storage,
+                   const base::Closure& closure,
+                   const std::string& a,
+                   const std::string& b) {
+  *storage = a + b;
+  closure.Run();
+}
+
+void WriteToMessagePipe(const char* text,
+                        const base::Closure& closure,
+                        ScopedMessagePipeHandle handle) {
+  WriteTextMessage(handle.get(), text);
+  closure.Run();
+}
+
+void RecordEnum(sample::Enum* storage,
+                const base::Closure& closure,
+                sample::Enum value) {
+  *storage = value;
+  closure.Run();
+}
+
+class RequestResponseTest : public testing::Test {
+ public:
+  RequestResponseTest() {}
+  ~RequestResponseTest() override { base::RunLoop().RunUntilIdle(); }
+
+  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
+
+ private:
+  base::MessageLoop loop_;
+};
+
+TEST_F(RequestResponseTest, EchoString) {
+  sample::ProviderPtr provider;
+  ProviderImpl provider_impl(GetProxy(&provider));
+
+  std::string buf;
+  base::RunLoop run_loop;
+  provider->EchoString("hello",
+                       base::Bind(&RecordString, &buf, run_loop.QuitClosure()));
+
+  run_loop.Run();
+
+  EXPECT_EQ(std::string("hello"), buf);
+}
+
+TEST_F(RequestResponseTest, EchoStrings) {
+  sample::ProviderPtr provider;
+  ProviderImpl provider_impl(GetProxy(&provider));
+
+  std::string buf;
+  base::RunLoop run_loop;
+  provider->EchoStrings("hello", " world", base::Bind(&RecordStrings, &buf,
+                                                      run_loop.QuitClosure()));
+
+  run_loop.Run();
+
+  EXPECT_EQ(std::string("hello world"), buf);
+}
+
+TEST_F(RequestResponseTest, EchoMessagePipeHandle) {
+  sample::ProviderPtr provider;
+  ProviderImpl provider_impl(GetProxy(&provider));
+
+  MessagePipe pipe2;
+  base::RunLoop run_loop;
+  provider->EchoMessagePipeHandle(
+      std::move(pipe2.handle1),
+      base::Bind(&WriteToMessagePipe, "hello", run_loop.QuitClosure()));
+
+  run_loop.Run();
+
+  std::string value;
+  ReadTextMessage(pipe2.handle0.get(), &value);
+
+  EXPECT_EQ(std::string("hello"), value);
+}
+
+TEST_F(RequestResponseTest, EchoEnum) {
+  sample::ProviderPtr provider;
+  ProviderImpl provider_impl(GetProxy(&provider));
+
+  sample::Enum value;
+  base::RunLoop run_loop;
+  provider->EchoEnum(sample::Enum::VALUE,
+                     base::Bind(&RecordEnum, &value, run_loop.QuitClosure()));
+  run_loop.Run();
+
+  EXPECT_EQ(sample::Enum::VALUE, value);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/router_test_util.cc b/mojo/public/cpp/bindings/tests/router_test_util.cc
new file mode 100644
index 0000000..3e32ec7
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/router_test_util.cc
@@ -0,0 +1,114 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/tests/router_test_util.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/tests/message_queue.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+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.message()->MoveTo(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.message()->MoveTo(message);
+}
+
+MessageAccumulator::MessageAccumulator(MessageQueue* queue,
+                                       const base::Closure& closure)
+    : queue_(queue), closure_(closure) {}
+
+MessageAccumulator::~MessageAccumulator() {}
+
+bool MessageAccumulator::Accept(Message* message) {
+  queue_->Push(message);
+  if (!closure_.is_null()) {
+    closure_.Run();
+    closure_.Reset();
+  }
+  return true;
+}
+
+ResponseGenerator::ResponseGenerator() {}
+
+bool ResponseGenerator::Accept(Message* message) {
+  return false;
+}
+
+bool ResponseGenerator::AcceptWithResponder(
+    Message* message,
+    MessageReceiverWithStatus* responder) {
+  EXPECT_TRUE(message->has_flag(Message::kFlagExpectsResponse));
+
+  bool result = SendResponse(message->name(), message->request_id(),
+                             reinterpret_cast<const char*>(message->payload()),
+                             responder);
+  EXPECT_TRUE(responder->IsValid());
+  delete responder;
+  return result;
+}
+
+bool ResponseGenerator::SendResponse(uint32_t name,
+                                     uint64_t request_id,
+                                     const char* request_string,
+                                     MessageReceiver* responder) {
+  Message response;
+  std::string response_string(request_string);
+  response_string += " world!";
+  AllocResponseMessage(name, response_string.c_str(), request_id, &response);
+
+  return responder->Accept(&response);
+}
+
+LazyResponseGenerator::LazyResponseGenerator(const base::Closure& closure)
+    : responder_(nullptr), name_(0), request_id_(0), closure_(closure) {}
+
+LazyResponseGenerator::~LazyResponseGenerator() {
+  delete responder_;
+}
+
+bool LazyResponseGenerator::AcceptWithResponder(
+    Message* message,
+    MessageReceiverWithStatus* responder) {
+  name_ = message->name();
+  request_id_ = message->request_id();
+  request_string_ =
+      std::string(reinterpret_cast<const char*>(message->payload()));
+  responder_ = responder;
+  if (!closure_.is_null()) {
+    closure_.Run();
+    closure_.Reset();
+  }
+  return true;
+}
+
+void LazyResponseGenerator::Complete(bool send_response) {
+  if (send_response) {
+    SendResponse(name_, request_id_, request_string_.c_str(), responder_);
+  }
+  delete responder_;
+  responder_ = nullptr;
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/router_test_util.h b/mojo/public/cpp/bindings/tests/router_test_util.h
new file mode 100644
index 0000000..c6fb372
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/router_test_util.h
@@ -0,0 +1,91 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_ROUTER_TEST_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_ROUTER_TEST_UTIL_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/callback.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace test {
+
+class MessageQueue;
+
+void AllocRequestMessage(uint32_t name, const char* text, Message* message);
+void AllocResponseMessage(uint32_t name,
+                          const char* text,
+                          uint64_t request_id,
+                          Message* message);
+
+class MessageAccumulator : public MessageReceiver {
+ public:
+  MessageAccumulator(MessageQueue* queue,
+                     const base::Closure& closure = base::Closure());
+  ~MessageAccumulator() override;
+
+  bool Accept(Message* message) override;
+
+ private:
+  MessageQueue* queue_;
+  base::Closure closure_;
+};
+
+class ResponseGenerator : public MessageReceiverWithResponderStatus {
+ public:
+  ResponseGenerator();
+
+  bool Accept(Message* message) override;
+
+  bool AcceptWithResponder(Message* message,
+                           MessageReceiverWithStatus* responder) override;
+
+  bool SendResponse(uint32_t name,
+                    uint64_t request_id,
+                    const char* request_string,
+                    MessageReceiver* responder);
+};
+
+class LazyResponseGenerator : public ResponseGenerator {
+ public:
+  explicit LazyResponseGenerator(
+      const base::Closure& closure = base::Closure());
+
+  ~LazyResponseGenerator() override;
+
+  bool AcceptWithResponder(Message* message,
+                           MessageReceiverWithStatus* responder) override;
+
+  bool has_responder() const { return !!responder_; }
+
+  bool responder_is_valid() const { return responder_->IsValid(); }
+
+  void set_closure(const base::Closure& closure) { closure_ = closure; }
+
+  // Sends the response and delete the responder.
+  void CompleteWithResponse() { Complete(true); }
+
+  // Deletes the responder without sending a response.
+  void CompleteWithoutResponse() { Complete(false); }
+
+ private:
+  // Completes the request handling by deleting responder_. Optionally
+  // also sends a response.
+  void Complete(bool send_response);
+
+  MessageReceiverWithStatus* responder_;
+  uint32_t name_;
+  uint64_t request_id_;
+  std::string request_string_;
+  base::Closure closure_;
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_ROUTER_TEST_UTIL_H_
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..39a8363
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/router_unittest.cc
@@ -0,0 +1,316 @@
+// Copyright 2014 The Chromium Authors. All 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 <utility>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/tests/message_queue.h"
+#include "mojo/public/cpp/bindings/tests/router_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class RouterTest : public testing::Test {
+ public:
+  RouterTest() {}
+
+  void SetUp() override {
+    CreateMessagePipe(nullptr, &handle0_, &handle1_);
+  }
+
+  void TearDown() override {}
+
+  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
+
+ protected:
+  ScopedMessagePipeHandle handle0_;
+  ScopedMessagePipeHandle handle1_;
+
+ private:
+  base::MessageLoop loop_;
+};
+
+TEST_F(RouterTest, BasicRequestResponse) {
+  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+
+  ResponseGenerator generator;
+  router1.set_incoming_receiver(&generator);
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  base::RunLoop run_loop;
+  router0.AcceptWithResponder(
+      &request, new MessageAccumulator(&message_queue, run_loop.QuitClosure()));
+
+  run_loop.Run();
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  Message response;
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("hello world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+
+  // Send a second message on the pipe.
+  Message request2;
+  AllocRequestMessage(1, "hello again", &request2);
+
+  base::RunLoop run_loop2;
+  router0.AcceptWithResponder(
+      &request2,
+      new MessageAccumulator(&message_queue, run_loop2.QuitClosure()));
+
+  run_loop2.Run();
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("hello again world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+TEST_F(RouterTest, BasicRequestResponse_Synchronous) {
+  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+
+  ResponseGenerator generator;
+  router1.set_incoming_receiver(&generator);
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+  router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+  router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  Message response;
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("hello world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+
+  // Send a second message on the pipe.
+  Message request2;
+  AllocRequestMessage(1, "hello again", &request2);
+
+  router0.AcceptWithResponder(&request2,
+                              new MessageAccumulator(&message_queue));
+
+  router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+  router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+  EXPECT_FALSE(message_queue.IsEmpty());
+
+  message_queue.Pop(&response);
+
+  EXPECT_EQ(std::string("hello again world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+TEST_F(RouterTest, RequestWithNoReceiver) {
+  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+
+  // 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);
+
+  MessageQueue message_queue;
+  base::RunLoop run_loop, run_loop2;
+  router0.set_connection_error_handler(run_loop.QuitClosure());
+  router1.set_connection_error_handler(run_loop2.QuitClosure());
+  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+  run_loop.Run();
+  run_loop2.Run();
+
+  EXPECT_TRUE(router0.encountered_error());
+  EXPECT_TRUE(router1.encountered_error());
+  EXPECT_TRUE(message_queue.IsEmpty());
+}
+
+// Tests Router using the LazyResponseGenerator. The responses will not be
+// sent until after the requests have been accepted.
+TEST_F(RouterTest, LazyResponses) {
+  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+
+  base::RunLoop run_loop;
+  LazyResponseGenerator generator(run_loop.QuitClosure());
+  router1.set_incoming_receiver(&generator);
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  base::RunLoop run_loop2;
+  router0.AcceptWithResponder(
+      &request,
+      new MessageAccumulator(&message_queue, run_loop2.QuitClosure()));
+  run_loop.Run();
+
+  // The request has been received but the response has not been sent yet.
+  EXPECT_TRUE(message_queue.IsEmpty());
+
+  // Send the response.
+  EXPECT_TRUE(generator.responder_is_valid());
+  generator.CompleteWithResponse();
+  run_loop2.Run();
+
+  // Check the response.
+  EXPECT_FALSE(message_queue.IsEmpty());
+  Message response;
+  message_queue.Pop(&response);
+  EXPECT_EQ(std::string("hello world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+
+  // Send a second message on the pipe.
+  base::RunLoop run_loop3;
+  LazyResponseGenerator generator2(run_loop3.QuitClosure());
+
+  router1.set_incoming_receiver(&generator2);
+  Message request2;
+  AllocRequestMessage(1, "hello again", &request2);
+
+  base::RunLoop run_loop4;
+  router0.AcceptWithResponder(
+      &request2,
+      new MessageAccumulator(&message_queue, run_loop4.QuitClosure()));
+  run_loop3.Run();
+
+  // The request has been received but the response has not been sent yet.
+  EXPECT_TRUE(message_queue.IsEmpty());
+
+  // Send the second response.
+  EXPECT_TRUE(generator2.responder_is_valid());
+  generator2.CompleteWithResponse();
+  run_loop4.Run();
+
+  // Check the second response.
+  EXPECT_FALSE(message_queue.IsEmpty());
+  message_queue.Pop(&response);
+  EXPECT_EQ(std::string("hello again world!"),
+            std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+void ForwardErrorHandler(bool* called, const base::Closure& callback) {
+  *called = true;
+  callback.Run();
+}
+
+// Tests that if the receiving application destroys the responder_ without
+// sending a response, then we trigger connection error at both sides. Moreover,
+// both sides still appear to have a valid message pipe handle bound.
+TEST_F(RouterTest, MissingResponses) {
+  base::RunLoop run_loop0, run_loop1;
+  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+  bool error_handler_called0 = false;
+  router0.set_connection_error_handler(
+      base::Bind(&ForwardErrorHandler, &error_handler_called0,
+                 run_loop0.QuitClosure()));
+
+  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
+                           base::ThreadTaskRunnerHandle::Get());
+  bool error_handler_called1 = false;
+  router1.set_connection_error_handler(
+      base::Bind(&ForwardErrorHandler, &error_handler_called1,
+                 run_loop1.QuitClosure()));
+
+  base::RunLoop run_loop3;
+  LazyResponseGenerator generator(run_loop3.QuitClosure());
+  router1.set_incoming_receiver(&generator);
+  router1.set_incoming_receiver(&generator);
+
+  Message request;
+  AllocRequestMessage(1, "hello", &request);
+
+  MessageQueue message_queue;
+  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+  run_loop3.Run();
+
+  // The request has been received but no response has been sent.
+  EXPECT_TRUE(message_queue.IsEmpty());
+
+  // Destroy the responder MessagerReceiver but don't send any response.
+  generator.CompleteWithoutResponse();
+  run_loop0.Run();
+  run_loop1.Run();
+
+  // Check that no response was received.
+  EXPECT_TRUE(message_queue.IsEmpty());
+
+  // Connection error handler is called at both sides.
+  EXPECT_TRUE(error_handler_called0);
+  EXPECT_TRUE(error_handler_called1);
+
+  // The error flag is set at both sides.
+  EXPECT_TRUE(router0.encountered_error());
+  EXPECT_TRUE(router1.encountered_error());
+
+  // The message pipe handle is valid at both sides.
+  EXPECT_TRUE(router0.is_valid());
+  EXPECT_TRUE(router1.is_valid());
+}
+
+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.
+
+  base::RunLoop run_loop;
+  LazyResponseGenerator generator(run_loop.QuitClosure());
+  {
+    internal::Router router0(std::move(handle0_), internal::FilterChain(),
+                             false, base::ThreadTaskRunnerHandle::Get());
+    internal::Router router1(std::move(handle1_), internal::FilterChain(),
+                             false, base::ThreadTaskRunnerHandle::Get());
+
+    router1.set_incoming_receiver(&generator);
+
+    Message request;
+    AllocRequestMessage(1, "hello", &request);
+
+    MessageQueue message_queue;
+    router0.AcceptWithResponder(&request,
+                                new MessageAccumulator(&message_queue));
+
+    run_loop.Run();
+
+    EXPECT_TRUE(generator.has_responder());
+  }
+
+  EXPECT_FALSE(generator.responder_is_valid());
+  generator.CompleteWithResponse();  // 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..00a8dbf
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
@@ -0,0 +1,381 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <algorithm>
+#include <ostream>
+#include <string>
+#include <utility>
+
+#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() {
+  std::string name("foopy");
+
+  BarPtr bar(Bar::New());
+  bar->alpha = 20;
+  bar->beta = 40;
+  bar->gamma = 60;
+  bar->type = Bar::Type::VERTICAL;
+
+  std::vector<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] = std::move(bar);
+  }
+
+  std::vector<uint8_t> data(10);
+  for (size_t i = 0; i < data.size(); ++i)
+    data[i] = static_cast<uint8_t>(data.size() - i);
+
+  std::vector<mojo::ScopedDataPipeConsumerHandle> input_streams(2);
+  std::vector<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] = std::move(consumer);
+    output_streams[i] = std::move(producer);
+  }
+
+  std::vector<std::vector<bool>> array_of_array_of_bools(2);
+  for (size_t i = 0; i < 2; ++i) {
+    std::vector<bool> array_of_bools(2);
+    for (size_t j = 0; j < 2; ++j)
+      array_of_bools[j] = j;
+    array_of_array_of_bools[i] = std::move(array_of_bools);
+  }
+
+  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 = std::move(bar);
+  foo->extra_bars = std::move(extra_bars);
+  foo->data = std::move(data);
+  foo->source = std::move(pipe.handle1);
+  foo->input_streams = std::move(input_streams);
+  foo->output_streams = std::move(output_streams);
+  foo->array_of_array_of_bools = std::move(array_of_array_of_bools);
+
+  return foo;
+}
+
+// Check that the given |Foo| is identical to the one made by |MakeFoo()|.
+void CheckFoo(const Foo& foo) {
+  const std::string kName("foopy");
+  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);
+
+  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_TRUE(foo.input_streams);
+  EXPECT_EQ(2u, foo.input_streams->size());
+
+  EXPECT_TRUE(foo.output_streams);
+  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 std::string& str) {
+  PrintSpacer(depth);
+  std::cout << name << ": \"" << str << "\"" << 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 std::vector<T>& array) {
+  PrintSpacer(depth);
+  std::cout << name << ":" << std::endl;
+  ++depth;
+  for (size_t i = 0; i < array.size(); ++i) {
+    std::stringstream buf;
+    buf << i;
+    Print(depth, buf.str().data(), array.at(i));
+  }
+  --depth;
+}
+
+template <typename T>
+void Print(int depth,
+           const char* name,
+           const base::Optional<std::vector<T>>& array) {
+  if (array)
+    Print(depth, name, *array);
+  else
+    Print(depth, name, std::vector<T>());
+}
+
+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:
+  void Frobinate(FooPtr foo,
+                 BazOptions baz,
+                 PortPtr port,
+                 const Service::FrobinateCallback& callback) 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(BazOptions::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", static_cast<int32_t>(baz));
+      Print(depth, "port", port.get());
+    }
+    callback.Run(5);
+  }
+
+  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:
+  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);
+  }
+
+  bool AcceptWithResponder(mojo::Message* message,
+                           mojo::MessageReceiver* responder) override {
+    return false;
+  }
+};
+
+using BindingsSampleTest = testing::Test;
+
+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(std::move(foo), Service::BazOptions::EXTRA,
+                     std::move(port), Service::FrobinateCallback());
+
+  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.empty());
+  EXPECT_TRUE(defaults->a19.empty());
+  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..dcf2967
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.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.
+
+// Serialization warnings are only recorded when DLOG is enabled.
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+
+#include <stddef.h>
+#include <utility>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using mojo::internal::ContainerValidateParams;
+
+// 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(std::move(pipe.handle1));
+    }
+    array[i] = std::move(nested_array);
+  }
+
+  return array;
+}
+
+class SerializationWarningTest : public testing::Test {
+ public:
+  ~SerializationWarningTest() override {}
+
+ 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::SerializationContext context;
+    mojo::internal::FixedBufferForTesting buf(
+        mojo::internal::PrepareToSerialize<T>(obj, &context));
+    typename mojo::internal::MojomTypeTraits<T>::Data* data;
+    mojo::internal::Serialize<T>(obj, &buf, &data, &context);
+
+    EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+  }
+
+  template <typename T>
+  void TestArrayWarning(T obj,
+                        mojo::internal::ValidationError expected_warning,
+                        const ContainerValidateParams* validate_params) {
+    warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
+
+    mojo::internal::SerializationContext context;
+    mojo::internal::FixedBufferForTesting buf(
+        mojo::internal::PrepareToSerialize<T>(obj, &context));
+    typename mojo::internal::MojomTypeTraits<T>::Data* data;
+    mojo::internal::Serialize<T>(obj, &buf, &data, validate_params, &context);
+
+    EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+  }
+
+  template <typename T>
+  void TestUnionWarning(T obj,
+                        mojo::internal::ValidationError expected_warning) {
+    warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
+
+    mojo::internal::SerializationContext context;
+    mojo::internal::FixedBufferForTesting buf(
+        mojo::internal::PrepareToSerialize<T>(obj, false, &context));
+    typename mojo::internal::MojomTypeTraits<T>::Data* data;
+    mojo::internal::Serialize<T>(obj, &buf, &data, false, &context);
+
+    EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+  }
+
+  mojo::internal::SerializationWarningObserverForTesting warning_observer_;
+};
+
+TEST_F(SerializationWarningTest, HandleInStruct) {
+  Struct2Ptr test_struct(Struct2::New());
+  EXPECT_FALSE(test_struct->hdl.is_valid());
+
+  TestWarning(std::move(test_struct),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE);
+
+  test_struct = Struct2::New();
+  MessagePipe pipe;
+  test_struct->hdl = ScopedHandle::From(std::move(pipe.handle1));
+
+  TestWarning(std::move(test_struct), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, StructInStruct) {
+  Struct3Ptr test_struct(Struct3::New());
+  EXPECT_TRUE(!test_struct->struct_1);
+
+  TestWarning(std::move(test_struct),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_struct = Struct3::New();
+  test_struct->struct_1 = Struct1::New();
+
+  TestWarning(std::move(test_struct), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfStructsInStruct) {
+  Struct4Ptr test_struct(Struct4::New());
+  test_struct->data.resize(1);
+
+  TestWarning(std::move(test_struct),
+              mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+  test_struct = Struct4::New();
+  test_struct->data.resize(0);
+
+  TestWarning(std::move(test_struct), mojo::internal::VALIDATION_ERROR_NONE);
+
+  test_struct = Struct4::New();
+  test_struct->data.resize(1);
+  test_struct->data[0] = Struct1::New();
+
+  TestWarning(std::move(test_struct), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, FixedArrayOfStructsInStruct) {
+  Struct5Ptr test_struct(Struct5::New());
+  test_struct->pair.resize(1);
+  test_struct->pair[0] = Struct1::New();
+
+  TestWarning(std::move(test_struct),
+              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(std::move(test_struct), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfArraysOfHandles) {
+  Array<Array<ScopedHandle>> test_array = CreateTestNestedHandleArray();
+  test_array[0] = nullptr;
+  test_array[1][0] = ScopedHandle();
+
+  ContainerValidateParams validate_params_0(
+      0, true, new ContainerValidateParams(0, true, nullptr));
+  TestArrayWarning(std::move(test_array), mojo::internal::VALIDATION_ERROR_NONE,
+                   &validate_params_0);
+
+  test_array = CreateTestNestedHandleArray();
+  test_array[0] = nullptr;
+  ContainerValidateParams validate_params_1(
+      0, false, new ContainerValidateParams(0, true, nullptr));
+  TestArrayWarning(std::move(test_array),
+                   mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+                   &validate_params_1);
+
+  test_array = CreateTestNestedHandleArray();
+  test_array[1][0] = ScopedHandle();
+  ContainerValidateParams validate_params_2(
+      0, true, new ContainerValidateParams(0, false, nullptr));
+  TestArrayWarning(std::move(test_array),
+                   mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+                   &validate_params_2);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfStrings) {
+  Array<String> test_array(3);
+  for (size_t i = 0; i < test_array.size(); ++i)
+    test_array[i] = "hello";
+
+  ContainerValidateParams validate_params_0(
+      0, true, new ContainerValidateParams(0, false, nullptr));
+  TestArrayWarning(std::move(test_array), mojo::internal::VALIDATION_ERROR_NONE,
+                   &validate_params_0);
+
+  test_array = Array<String>(3);
+  for (size_t i = 0; i < test_array.size(); ++i)
+    test_array[i] = nullptr;
+  ContainerValidateParams validate_params_1(
+      0, false, new ContainerValidateParams(0, false, nullptr));
+  TestArrayWarning(std::move(test_array),
+                   mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+                   &validate_params_1);
+
+  test_array = Array<String>(2);
+  ContainerValidateParams validate_params_2(
+      3, true, new ContainerValidateParams(0, false, nullptr));
+  TestArrayWarning(std::move(test_array),
+                   mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+                   &validate_params_2);
+}
+
+TEST_F(SerializationWarningTest, StructInUnion) {
+  DummyStructPtr dummy(nullptr);
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_dummy(std::move(dummy));
+
+  TestUnionWarning(std::move(obj),
+                   mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+}
+
+TEST_F(SerializationWarningTest, UnionInUnion) {
+  PodUnionPtr pod(nullptr);
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_pod_union(std::move(pod));
+
+  TestUnionWarning(std::move(obj),
+                   mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+}
+
+TEST_F(SerializationWarningTest, HandleInUnion) {
+  ScopedMessagePipeHandle pipe;
+  HandleUnionPtr handle(HandleUnion::New());
+  handle->set_f_message_pipe(std::move(pipe));
+
+  TestUnionWarning(std::move(handle),
+                   mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
+
+#endif
diff --git a/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc b/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc
new file mode 100644
index 0000000..92a31b3
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright 2016 The Chromium Authors. All 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/stl_converters.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/tests/container_test_util.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+using STLConvertersTest = testing::Test;
+
+TEST_F(STLConvertersTest, AvoidUnnecessaryCopies) {
+  Array<CopyableType> mojo_array(1);
+  std::vector<CopyableType> stl_vector = UnwrapToSTLType(std::move(mojo_array));
+  ASSERT_EQ(1u, stl_vector.size());
+  ASSERT_FALSE(stl_vector[0].copied());
+  Array<CopyableType> mojo_array2 = WrapSTLType(std::move(stl_vector));
+  ASSERT_EQ(1u, mojo_array2.size());
+  ASSERT_FALSE(mojo_array2[0].copied());
+
+  Map<int32_t, CopyableType> mojo_map;
+  mojo_map.insert(42, CopyableType());
+  mojo_map[42].ResetCopied();
+  std::map<int32_t, CopyableType> stl_map =
+      UnwrapToSTLType(std::move(mojo_map));
+  ASSERT_EQ(1u, stl_map.size());
+  ASSERT_FALSE(stl_map[42].copied());
+  Map<int32_t, CopyableType> mojo_map2 = WrapSTLType(std::move(stl_map));
+  ASSERT_EQ(1u, mojo_map2.size());
+  ASSERT_FALSE(mojo_map2[42].copied());
+}
+
+TEST_F(STLConvertersTest, RecursiveConversion) {
+  Array<Map<String, Array<int32_t>>> mojo_obj(2);
+  mojo_obj[0] = nullptr;
+  mojo_obj[1]["hello"].push_back(123);
+  mojo_obj[1]["hello"].push_back(456);
+
+  std::vector<std::map<std::string, std::vector<int32_t>>> stl_obj =
+      UnwrapToSTLType(std::move(mojo_obj));
+
+  ASSERT_EQ(2u, stl_obj.size());
+  ASSERT_TRUE(stl_obj[0].empty());
+  ASSERT_EQ(1u, stl_obj[1].size());
+  ASSERT_EQ(2u, stl_obj[1]["hello"].size());
+  ASSERT_EQ(123, stl_obj[1]["hello"][0]);
+  ASSERT_EQ(456, stl_obj[1]["hello"][1]);
+
+  Array<Map<String, Array<int32_t>>> mojo_obj2 =
+      WrapSTLType(std::move(stl_obj));
+
+  ASSERT_EQ(2u, mojo_obj2.size());
+  ASSERT_EQ(0u, mojo_obj2[0].size());
+  // The null flag has been lost when converted to std::map.
+  ASSERT_FALSE(mojo_obj2[0].is_null());
+  ASSERT_EQ(1u, mojo_obj2[1].size());
+  ASSERT_EQ(2u, mojo_obj2[1]["hello"].size());
+  ASSERT_EQ(123, mojo_obj2[1]["hello"][0]);
+  ASSERT_EQ(456, mojo_obj2[1]["hello"][1]);
+}
+
+TEST_F(STLConvertersTest, StopsAtMojoStruct) {
+  Array<NamedRegionPtr> mojo_obj(1);
+  mojo_obj[0] = NamedRegion::New();
+  mojo_obj[0]->name.emplace("hello");
+  mojo_obj[0]->rects.emplace(3);
+
+  std::vector<NamedRegionPtr> stl_obj = UnwrapToSTLType(std::move(mojo_obj));
+
+  ASSERT_EQ(1u, stl_obj.size());
+  ASSERT_EQ("hello", stl_obj[0]->name.value());
+  ASSERT_EQ(3u, stl_obj[0]->rects->size());
+
+  Array<NamedRegionPtr> mojo_obj2 = WrapSTLType(std::move(stl_obj));
+
+  ASSERT_EQ(1u, mojo_obj2.size());
+  ASSERT_EQ("hello", mojo_obj2[0]->name.value());
+  ASSERT_EQ(3u, mojo_obj2[0]->rects->size());
+}
+
+}  // namespace test
+}  // namespace mojo
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..13486ae
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/string_unittest.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/public/cpp/bindings/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+namespace {
+const char* kHelloWorld = "hello world";
+}  // namespace
+
+TEST(StringTest, DefaultIsNotNull) {
+  String s;
+  EXPECT_FALSE(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(kHelloWorld);
+  EXPECT_EQ(std::string(kHelloWorld), s.get());
+}
+
+TEST(StringTest, Assignment) {
+  String s(kHelloWorld);
+  String t = s;  // Makes a copy.
+  EXPECT_FALSE(t.is_null());
+  EXPECT_EQ(std::string(kHelloWorld), t.get());
+  EXPECT_FALSE(s.is_null());
+}
+
+TEST(StringTest, Equality) {
+  String s(kHelloWorld);
+  String t(kHelloWorld);
+  EXPECT_EQ(s, t);
+  EXPECT_TRUE(s == s);
+  EXPECT_FALSE(s != s);
+  EXPECT_TRUE(s == t);
+  EXPECT_FALSE(s != t);
+  EXPECT_TRUE(kHelloWorld == s);
+  EXPECT_TRUE(s == kHelloWorld);
+  EXPECT_TRUE("not" != s);
+  EXPECT_FALSE("not" == s);
+  EXPECT_TRUE(s != "not");
+  EXPECT_FALSE(s == "not");
+
+  // Test null strings.
+  String n1;
+  String n2;
+  EXPECT_TRUE(n1 == n1);
+  EXPECT_FALSE(n1 != n2);
+  EXPECT_TRUE(n1 == n2);
+  EXPECT_FALSE(n1 != n2);
+  EXPECT_TRUE(n1 != s);
+  EXPECT_FALSE(n1 == s);
+  EXPECT_TRUE(s != n1);
+  EXPECT_FALSE(s == n1);
+}
+
+TEST(StringTest, LessThanNullness) {
+  String null;
+  String null2;
+  EXPECT_FALSE(null < null2);
+  EXPECT_FALSE(null2 < null);
+
+  String real("real");
+  EXPECT_TRUE(null < real);
+  EXPECT_FALSE(real < null);
+}
+
+TEST(StringTest, MoveConstructors) {
+  std::string std_str(kHelloWorld);
+
+  String str1(std::move(std_str));
+  EXPECT_TRUE(kHelloWorld == str1);
+
+  String str2(std::move(str1));
+  EXPECT_TRUE(kHelloWorld == str2);
+  EXPECT_TRUE(str1.is_null());
+}
+
+TEST(StringTest, MoveAssignments) {
+  std::string std_str(kHelloWorld);
+
+  String str1;
+  str1 = std::move(std_str);
+  EXPECT_TRUE(kHelloWorld == str1);
+
+  String str2;
+  str2 = std::move(str1);
+  EXPECT_TRUE(kHelloWorld == str2);
+  EXPECT_TRUE(str1.is_null());
+}
+
+TEST(StringTest, Storage) {
+  String str(kHelloWorld);
+
+  EXPECT_TRUE(kHelloWorld == str.storage());
+
+  std::string storage = str.PassStorage();
+  EXPECT_TRUE(str.is_null());
+  EXPECT_TRUE(kHelloWorld == storage);
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc b/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc
new file mode 100644
index 0000000..583dc46
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc
@@ -0,0 +1,386 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/tests/rect_blink.h"
+#include "mojo/public/cpp/bindings/tests/rect_chromium.h"
+#include "mojo/public/cpp/bindings/tests/struct_with_traits_impl.h"
+#include "mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h"
+#include "mojo/public/cpp/bindings/tests/variant_test_util.h"
+#include "mojo/public/interfaces/bindings/tests/struct_with_traits.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/test_native_types.mojom-blink.h"
+#include "mojo/public/interfaces/bindings/tests/test_native_types.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+template <typename T>
+void DoExpectResult(const T& expected,
+                    const base::Closure& callback,
+                    const T& actual) {
+  EXPECT_EQ(expected.x(), actual.x());
+  EXPECT_EQ(expected.y(), actual.y());
+  EXPECT_EQ(expected.width(), actual.width());
+  EXPECT_EQ(expected.height(), actual.height());
+  callback.Run();
+}
+
+template <typename T>
+base::Callback<void(const T&)> ExpectResult(const T& r,
+                                            const base::Closure& callback) {
+  return base::Bind(&DoExpectResult<T>, r, callback);
+}
+
+template <typename T>
+void DoFail(const std::string& reason, const T&) {
+  EXPECT_TRUE(false) << reason;
+}
+
+template <typename T>
+base::Callback<void(const T&)> Fail(const std::string& reason) {
+  return base::Bind(&DoFail<T>, reason);
+}
+
+template <typename T>
+void ExpectError(InterfacePtr<T> *proxy, const base::Closure& callback) {
+  proxy->set_connection_error_handler(callback);
+}
+
+// This implements the generated Chromium variant of RectService.
+class ChromiumRectServiceImpl : public RectService {
+ public:
+  ChromiumRectServiceImpl() {}
+
+  // mojo::test::RectService:
+  void AddRect(const RectChromium& r) override {
+    if (r.GetArea() > largest_rect_.GetArea())
+      largest_rect_ = r;
+  }
+
+  void GetLargestRect(const GetLargestRectCallback& callback) override {
+    callback.Run(largest_rect_);
+  }
+
+ private:
+  RectChromium largest_rect_;
+};
+
+// This implements the generated Blink variant of RectService.
+class BlinkRectServiceImpl : public blink::RectService {
+ public:
+  BlinkRectServiceImpl() {}
+
+  // mojo::test::blink::RectService:
+  void AddRect(const RectBlink& r) override {
+    if (r.computeArea() > largest_rect_.computeArea()) {
+      largest_rect_.setX(r.x());
+      largest_rect_.setY(r.y());
+      largest_rect_.setWidth(r.width());
+      largest_rect_.setHeight(r.height());
+    }
+  }
+
+  void GetLargestRect(const GetLargestRectCallback& callback) override {
+    callback.Run(largest_rect_);
+  }
+
+ private:
+  RectBlink largest_rect_;
+};
+
+// A test which runs both Chromium and Blink implementations of a RectService.
+class StructTraitsTest : public testing::Test,
+                         public TraitsTestService {
+ public:
+  StructTraitsTest() {}
+
+ protected:
+  void BindToChromiumService(RectServiceRequest request) {
+    chromium_bindings_.AddBinding(&chromium_service_, std::move(request));
+  }
+  void BindToChromiumService(blink::RectServiceRequest request) {
+    chromium_bindings_.AddBinding(
+        &chromium_service_,
+        ConvertInterfaceRequest<RectService>(std::move(request)));
+  }
+
+  void BindToBlinkService(blink::RectServiceRequest request) {
+    blink_bindings_.AddBinding(&blink_service_, std::move(request));
+  }
+  void BindToBlinkService(RectServiceRequest request) {
+    blink_bindings_.AddBinding(
+        &blink_service_,
+        ConvertInterfaceRequest<blink::RectService>(std::move(request)));
+  }
+
+  TraitsTestServicePtr GetTraitsTestProxy() {
+    return traits_test_bindings_.CreateInterfacePtrAndBind(this);
+  }
+
+ private:
+  // TraitsTestService:
+  void EchoStructWithTraits(
+      const StructWithTraitsImpl& s,
+      const EchoStructWithTraitsCallback& callback) override {
+    callback.Run(s);
+  }
+
+  void EchoPassByValueStructWithTraits(
+      PassByValueStructWithTraitsImpl s,
+      const EchoPassByValueStructWithTraitsCallback& callback) override {
+    callback.Run(std::move(s));
+  }
+
+  void EchoEnumWithTraits(EnumWithTraitsImpl e,
+                          const EchoEnumWithTraitsCallback& callback) override {
+    callback.Run(e);
+  }
+
+  void EchoStructWithTraitsForUniquePtrTest(
+      std::unique_ptr<int> e,
+      const EchoStructWithTraitsForUniquePtrTestCallback& callback) override {
+    callback.Run(std::move(e));
+  }
+
+  base::MessageLoop loop_;
+
+  ChromiumRectServiceImpl chromium_service_;
+  BindingSet<RectService> chromium_bindings_;
+
+  BlinkRectServiceImpl blink_service_;
+  BindingSet<blink::RectService> blink_bindings_;
+
+  BindingSet<TraitsTestService> traits_test_bindings_;
+};
+
+}  // namespace
+
+TEST_F(StructTraitsTest, ChromiumProxyToChromiumService) {
+  RectServicePtr chromium_proxy;
+  BindToChromiumService(GetProxy(&chromium_proxy));
+  {
+    base::RunLoop loop;
+    chromium_proxy->AddRect(RectChromium(1, 1, 4, 5));
+    chromium_proxy->AddRect(RectChromium(-1, -1, 2, 2));
+    chromium_proxy->GetLargestRect(
+        ExpectResult(RectChromium(1, 1, 4, 5), loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+TEST_F(StructTraitsTest, ChromiumToBlinkService) {
+  RectServicePtr chromium_proxy;
+  BindToBlinkService(GetProxy(&chromium_proxy));
+  {
+    base::RunLoop loop;
+    chromium_proxy->AddRect(RectChromium(1, 1, 4, 5));
+    chromium_proxy->AddRect(RectChromium(2, 2, 5, 5));
+    chromium_proxy->GetLargestRect(
+        ExpectResult(RectChromium(2, 2, 5, 5), loop.QuitClosure()));
+    loop.Run();
+  }
+  // The Blink service should drop our connection because RectBlink's
+  // deserializer rejects negative origins.
+  {
+    base::RunLoop loop;
+    ExpectError(&chromium_proxy, loop.QuitClosure());
+    chromium_proxy->AddRect(RectChromium(-1, -1, 2, 2));
+    chromium_proxy->GetLargestRect(
+        Fail<RectChromium>("The pipe should have been closed."));
+    loop.Run();
+  }
+}
+
+TEST_F(StructTraitsTest, BlinkProxyToBlinkService) {
+  blink::RectServicePtr blink_proxy;
+  BindToBlinkService(GetProxy(&blink_proxy));
+  {
+    base::RunLoop loop;
+    blink_proxy->AddRect(RectBlink(1, 1, 4, 5));
+    blink_proxy->AddRect(RectBlink(10, 10, 20, 20));
+    blink_proxy->GetLargestRect(
+        ExpectResult(RectBlink(10, 10, 20, 20), loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+TEST_F(StructTraitsTest, BlinkProxyToChromiumService) {
+  blink::RectServicePtr blink_proxy;
+  BindToChromiumService(GetProxy(&blink_proxy));
+  {
+    base::RunLoop loop;
+    blink_proxy->AddRect(RectBlink(1, 1, 4, 5));
+    blink_proxy->AddRect(RectBlink(10, 10, 2, 2));
+    blink_proxy->GetLargestRect(
+        ExpectResult(RectBlink(1, 1, 4, 5), loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+void ExpectStructWithTraits(const StructWithTraitsImpl& expected,
+                            const base::Closure& closure,
+                            const StructWithTraitsImpl& passed) {
+  EXPECT_EQ(expected.get_enum(), passed.get_enum());
+  EXPECT_EQ(expected.get_bool(), passed.get_bool());
+  EXPECT_EQ(expected.get_uint32(), passed.get_uint32());
+  EXPECT_EQ(expected.get_uint64(), passed.get_uint64());
+  EXPECT_EQ(expected.get_string(), passed.get_string());
+  EXPECT_EQ(expected.get_string_array(), passed.get_string_array());
+  EXPECT_EQ(expected.get_struct(), passed.get_struct());
+  EXPECT_EQ(expected.get_struct_array(), passed.get_struct_array());
+  EXPECT_EQ(expected.get_struct_map(), passed.get_struct_map());
+  closure.Run();
+}
+
+TEST_F(StructTraitsTest, EchoStructWithTraits) {
+  StructWithTraitsImpl input;
+  input.set_enum(EnumWithTraitsImpl::CUSTOM_VALUE_1);
+  input.set_bool(true);
+  input.set_uint32(7);
+  input.set_uint64(42);
+  input.set_string("hello world!");
+  input.get_mutable_string_array().assign({"hello", "world!"});
+  input.get_mutable_struct().value = 42;
+  input.get_mutable_struct_array().resize(2);
+  input.get_mutable_struct_array()[0].value = 1;
+  input.get_mutable_struct_array()[1].value = 2;
+  input.get_mutable_struct_map()["hello"] = NestedStructWithTraitsImpl(1024);
+  input.get_mutable_struct_map()["world"] = NestedStructWithTraitsImpl(2048);
+
+  base::RunLoop loop;
+  TraitsTestServicePtr proxy = GetTraitsTestProxy();
+
+  proxy->EchoStructWithTraits(
+      input,
+      base::Bind(&ExpectStructWithTraits, input, loop.QuitClosure()));
+  loop.Run();
+}
+
+TEST_F(StructTraitsTest, CloneStructWithTraitsContainer) {
+  StructWithTraitsContainerPtr container = StructWithTraitsContainer::New();
+  container->f_struct.set_uint32(7);
+  container->f_struct.set_uint64(42);
+  StructWithTraitsContainerPtr cloned_container = container.Clone();
+  EXPECT_EQ(7u, cloned_container->f_struct.get_uint32());
+  EXPECT_EQ(42u, cloned_container->f_struct.get_uint64());
+}
+
+void CaptureMessagePipe(ScopedMessagePipeHandle* storage,
+                        const base::Closure& closure,
+                        PassByValueStructWithTraitsImpl passed) {
+  storage->reset(MessagePipeHandle(
+      passed.get_mutable_handle().release().value()));
+  closure.Run();
+}
+
+TEST_F(StructTraitsTest, EchoPassByValueStructWithTraits) {
+  MessagePipe mp;
+  PassByValueStructWithTraitsImpl input;
+  input.get_mutable_handle().reset(mp.handle0.release());
+
+  base::RunLoop loop;
+  TraitsTestServicePtr proxy = GetTraitsTestProxy();
+
+  ScopedMessagePipeHandle received;
+  proxy->EchoPassByValueStructWithTraits(
+      std::move(input),
+      base::Bind(&CaptureMessagePipe, &received, loop.QuitClosure()));
+  loop.Run();
+
+  ASSERT_TRUE(received.is_valid());
+
+  // Verify that the message pipe handle is correctly passed.
+  const char kHello[] = "hello";
+  const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WriteMessageRaw(mp.handle1.get(), kHello, kHelloSize, nullptr, 0,
+                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  EXPECT_EQ(MOJO_RESULT_OK, Wait(received.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                                 MOJO_DEADLINE_INDEFINITE, nullptr));
+
+  char buffer[10] = {0};
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            ReadMessageRaw(received.get(), buffer, &buffer_size, nullptr,
+                           nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kHelloSize, buffer_size);
+  EXPECT_STREQ(kHello, buffer);
+}
+
+void ExpectEnumWithTraits(EnumWithTraitsImpl expected_value,
+                          const base::Closure& closure,
+                          EnumWithTraitsImpl value) {
+  EXPECT_EQ(expected_value, value);
+  closure.Run();
+}
+
+TEST_F(StructTraitsTest, EchoEnumWithTraits) {
+  base::RunLoop loop;
+  TraitsTestServicePtr proxy = GetTraitsTestProxy();
+
+  proxy->EchoEnumWithTraits(
+      EnumWithTraitsImpl::CUSTOM_VALUE_1,
+      base::Bind(&ExpectEnumWithTraits, EnumWithTraitsImpl::CUSTOM_VALUE_1,
+                 loop.QuitClosure()));
+  loop.Run();
+}
+
+TEST_F(StructTraitsTest, SerializeStructWithTraits) {
+  StructWithTraitsImpl input;
+  input.set_enum(EnumWithTraitsImpl::CUSTOM_VALUE_1);
+  input.set_bool(true);
+  input.set_uint32(7);
+  input.set_uint64(42);
+  input.set_string("hello world!");
+  input.get_mutable_string_array().assign({"hello", "world!"});
+  input.get_mutable_struct().value = 42;
+  input.get_mutable_struct_array().resize(2);
+  input.get_mutable_struct_array()[0].value = 1;
+  input.get_mutable_struct_array()[1].value = 2;
+  input.get_mutable_struct_map()["hello"] = NestedStructWithTraitsImpl(1024);
+  input.get_mutable_struct_map()["world"] = NestedStructWithTraitsImpl(2048);
+
+  mojo::Array<uint8_t> data = StructWithTraits::Serialize(&input);
+  StructWithTraitsImpl output;
+  ASSERT_TRUE(StructWithTraits::Deserialize(std::move(data), &output));
+
+  EXPECT_EQ(input.get_enum(), output.get_enum());
+  EXPECT_EQ(input.get_bool(), output.get_bool());
+  EXPECT_EQ(input.get_uint32(), output.get_uint32());
+  EXPECT_EQ(input.get_uint64(), output.get_uint64());
+  EXPECT_EQ(input.get_string(), output.get_string());
+  EXPECT_EQ(input.get_string_array(), output.get_string_array());
+  EXPECT_EQ(input.get_struct(), output.get_struct());
+  EXPECT_EQ(input.get_struct_array(), output.get_struct_array());
+  EXPECT_EQ(input.get_struct_map(), output.get_struct_map());
+}
+
+void ExpectUniquePtr(int expected,
+                     const base::Closure& closure,
+                     std::unique_ptr<int> value) {
+  EXPECT_EQ(expected, *value);
+  closure.Run();
+}
+
+TEST_F(StructTraitsTest, TypemapUniquePtr) {
+  base::RunLoop loop;
+  TraitsTestServicePtr proxy = GetTraitsTestProxy();
+
+  proxy->EchoStructWithTraitsForUniquePtrTest(
+      base::MakeUnique<int>(12345),
+      base::Bind(&ExpectUniquePtr, 12345, loop.QuitClosure()));
+  loop.Run();
+}
+
+}  // 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..76b4cce
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc
@@ -0,0 +1,549 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <utility>
+
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/system/message_pipe.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;
+}
+
+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);
+}
+
+MultiVersionStructPtr MakeMultiVersionStruct() {
+  MultiVersionStructPtr output(MultiVersionStruct::New());
+  output->f_int32 = 123;
+  output->f_rect = MakeRect(5);
+  output->f_string.emplace("hello");
+  output->f_array.emplace(3);
+  (*output->f_array)[0] = 10;
+  (*output->f_array)[1] = 9;
+  (*output->f_array)[2] = 8;
+  MessagePipe pipe;
+  output->f_message_pipe = std::move(pipe.handle0);
+  output->f_int16 = 42;
+
+  return output;
+}
+
+template <typename U, typename T>
+U SerializeAndDeserialize(T input) {
+  using InputDataType = typename mojo::internal::MojomTypeTraits<T>::Data*;
+  using OutputDataType = typename mojo::internal::MojomTypeTraits<U>::Data*;
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<T>(input, &context);
+  mojo::internal::FixedBufferForTesting buf(size + 32);
+  InputDataType data;
+  mojo::internal::Serialize<T>(input, &buf, &data, &context);
+
+  // Set the subsequent area to a special value, so that we can find out if we
+  // mistakenly access the area.
+  void* subsequent_area = buf.Allocate(32);
+  memset(subsequent_area, 0xAA, 32);
+
+  OutputDataType output_data = reinterpret_cast<OutputDataType>(data);
+
+  U output;
+  mojo::internal::Deserialize<U>(output_data, &output, &context);
+  return std::move(output);
+}
+
+using StructTest = testing::Test;
+
+}  // namespace
+
+TEST_F(StructTest, Rect) {
+  RectPtr rect;
+  EXPECT_TRUE(rect.is_null());
+  EXPECT_TRUE(!rect);
+  EXPECT_FALSE(rect);
+
+  rect = nullptr;
+  EXPECT_TRUE(rect.is_null());
+  EXPECT_TRUE(!rect);
+  EXPECT_FALSE(rect);
+
+  rect = MakeRect();
+  EXPECT_FALSE(rect.is_null());
+  EXPECT_FALSE(!rect);
+  EXPECT_TRUE(rect);
+
+  RectPtr null_rect = nullptr;
+  EXPECT_TRUE(null_rect.is_null());
+  EXPECT_TRUE(!null_rect);
+  EXPECT_FALSE(null_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_FALSE(clone_region->name);
+  EXPECT_FALSE(clone_region->rects);
+
+  region->name.emplace("hello world");
+  clone_region = region.Clone();
+  EXPECT_EQ(region->name, clone_region->name);
+
+  region->rects.emplace(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 = mojo::internal::PrepareToSerialize<RectPtr>(rect, nullptr);
+  EXPECT_EQ(8U + 16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::Rect_Data* data;
+  mojo::internal::Serialize<RectPtr>(rect, &buf, &data, nullptr);
+
+  RectPtr rect2;
+  mojo::internal::Deserialize<RectPtr>(data, &rect2, nullptr);
+
+  CheckRect(*rect2);
+}
+
+// Construction of a struct with struct pointers from null.
+TEST_F(StructTest, Construction_StructPointers) {
+  RectPairPtr pair;
+  EXPECT_TRUE(pair.is_null());
+
+  pair = RectPair::New();
+  EXPECT_FALSE(pair.is_null());
+  EXPECT_TRUE(pair->first.is_null());
+  EXPECT_TRUE(pair->first.is_null());
+
+  pair = nullptr;
+  EXPECT_TRUE(pair.is_null());
+}
+
+// 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 = mojo::internal::PrepareToSerialize<RectPairPtr>(pair, nullptr);
+  EXPECT_EQ(8U + 16U + 2 * (8U + 16U), size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::RectPair_Data* data;
+  mojo::internal::Serialize<RectPairPtr>(pair, &buf, &data, nullptr);
+
+  RectPairPtr pair2;
+  mojo::internal::Deserialize<RectPairPtr>(data, &pair2, nullptr);
+
+  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.emplace("region");
+  region->rects.emplace(4);
+  for (size_t i = 0; i < region->rects->size(); ++i)
+    (*region->rects)[i] = MakeRect(static_cast<int32_t>(i) + 1);
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<NamedRegionPtr>(region, nullptr);
+  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::FixedBufferForTesting buf(size);
+  internal::NamedRegion_Data* data;
+  mojo::internal::Serialize<NamedRegionPtr>(region, &buf, &data, nullptr);
+
+  NamedRegionPtr region2;
+  mojo::internal::Deserialize<NamedRegionPtr>(data, &region2, nullptr);
+
+  EXPECT_EQ("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_FALSE(region->name);
+  EXPECT_FALSE(region->rects);
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<NamedRegionPtr>(region, nullptr);
+  EXPECT_EQ(8U +      // header
+                8U +  // name pointer
+                8U,   // rects pointer
+            size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::NamedRegion_Data* data;
+  mojo::internal::Serialize<NamedRegionPtr>(region, &buf, &data, nullptr);
+
+  NamedRegionPtr region2;
+  mojo::internal::Deserialize<NamedRegionPtr>(data, &region2, nullptr);
+
+  EXPECT_FALSE(region2->name);
+  EXPECT_FALSE(region2->rects);
+}
+
+// Tests deserializing structs as a newer version.
+TEST_F(StructTest, Versioning_OldToNew) {
+  {
+    MultiVersionStructV0Ptr input(MultiVersionStructV0::New());
+    input->f_int32 = 123;
+    MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+    expected_output->f_int32 = 123;
+
+    MultiVersionStructPtr output =
+        SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+
+  {
+    MultiVersionStructV1Ptr input(MultiVersionStructV1::New());
+    input->f_int32 = 123;
+    input->f_rect = MakeRect(5);
+    MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+    expected_output->f_int32 = 123;
+    expected_output->f_rect = MakeRect(5);
+
+    MultiVersionStructPtr output =
+        SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+
+  {
+    MultiVersionStructV3Ptr input(MultiVersionStructV3::New());
+    input->f_int32 = 123;
+    input->f_rect = MakeRect(5);
+    input->f_string.emplace("hello");
+    MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+    expected_output->f_int32 = 123;
+    expected_output->f_rect = MakeRect(5);
+    expected_output->f_string.emplace("hello");
+
+    MultiVersionStructPtr output =
+        SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+
+  {
+    MultiVersionStructV5Ptr input(MultiVersionStructV5::New());
+    input->f_int32 = 123;
+    input->f_rect = MakeRect(5);
+    input->f_string.emplace("hello");
+    input->f_array.emplace(3);
+    (*input->f_array)[0] = 10;
+    (*input->f_array)[1] = 9;
+    (*input->f_array)[2] = 8;
+    MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+    expected_output->f_int32 = 123;
+    expected_output->f_rect = MakeRect(5);
+    expected_output->f_string.emplace("hello");
+    expected_output->f_array.emplace(3);
+    (*expected_output->f_array)[0] = 10;
+    (*expected_output->f_array)[1] = 9;
+    (*expected_output->f_array)[2] = 8;
+
+    MultiVersionStructPtr output =
+        SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+
+  {
+    MultiVersionStructV7Ptr input(MultiVersionStructV7::New());
+    input->f_int32 = 123;
+    input->f_rect = MakeRect(5);
+    input->f_string.emplace("hello");
+    input->f_array.emplace(3);
+    (*input->f_array)[0] = 10;
+    (*input->f_array)[1] = 9;
+    (*input->f_array)[2] = 8;
+    MessagePipe pipe;
+    input->f_message_pipe = std::move(pipe.handle0);
+
+    MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+    expected_output->f_int32 = 123;
+    expected_output->f_rect = MakeRect(5);
+    expected_output->f_string.emplace("hello");
+    expected_output->f_array.emplace(3);
+    (*expected_output->f_array)[0] = 10;
+    (*expected_output->f_array)[1] = 9;
+    (*expected_output->f_array)[2] = 8;
+    // Save the raw handle value separately so that we can compare later.
+    MojoHandle expected_handle = input->f_message_pipe.get().value();
+
+    MultiVersionStructPtr output =
+        SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_EQ(expected_handle, output->f_message_pipe.get().value());
+    output->f_message_pipe.reset();
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+}
+
+// Tests deserializing structs as an older version.
+TEST_F(StructTest, Versioning_NewToOld) {
+  {
+    MultiVersionStructPtr input = MakeMultiVersionStruct();
+    MultiVersionStructV7Ptr expected_output(MultiVersionStructV7::New());
+    expected_output->f_int32 = 123;
+    expected_output->f_rect = MakeRect(5);
+    expected_output->f_string.emplace("hello");
+    expected_output->f_array.emplace(3);
+    (*expected_output->f_array)[0] = 10;
+    (*expected_output->f_array)[1] = 9;
+    (*expected_output->f_array)[2] = 8;
+    // Save the raw handle value separately so that we can compare later.
+    MojoHandle expected_handle = input->f_message_pipe.get().value();
+
+    MultiVersionStructV7Ptr output =
+        SerializeAndDeserialize<MultiVersionStructV7Ptr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_EQ(expected_handle, output->f_message_pipe.get().value());
+    output->f_message_pipe.reset();
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+
+  {
+    MultiVersionStructPtr input = MakeMultiVersionStruct();
+    MultiVersionStructV5Ptr expected_output(MultiVersionStructV5::New());
+    expected_output->f_int32 = 123;
+    expected_output->f_rect = MakeRect(5);
+    expected_output->f_string.emplace("hello");
+    expected_output->f_array.emplace(3);
+    (*expected_output->f_array)[0] = 10;
+    (*expected_output->f_array)[1] = 9;
+    (*expected_output->f_array)[2] = 8;
+
+    MultiVersionStructV5Ptr output =
+        SerializeAndDeserialize<MultiVersionStructV5Ptr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+
+  {
+    MultiVersionStructPtr input = MakeMultiVersionStruct();
+    MultiVersionStructV3Ptr expected_output(MultiVersionStructV3::New());
+    expected_output->f_int32 = 123;
+    expected_output->f_rect = MakeRect(5);
+    expected_output->f_string.emplace("hello");
+
+    MultiVersionStructV3Ptr output =
+        SerializeAndDeserialize<MultiVersionStructV3Ptr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+
+  {
+    MultiVersionStructPtr input = MakeMultiVersionStruct();
+    MultiVersionStructV1Ptr expected_output(MultiVersionStructV1::New());
+    expected_output->f_int32 = 123;
+    expected_output->f_rect = MakeRect(5);
+
+    MultiVersionStructV1Ptr output =
+        SerializeAndDeserialize<MultiVersionStructV1Ptr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+
+  {
+    MultiVersionStructPtr input = MakeMultiVersionStruct();
+    MultiVersionStructV0Ptr expected_output(MultiVersionStructV0::New());
+    expected_output->f_int32 = 123;
+
+    MultiVersionStructV0Ptr output =
+        SerializeAndDeserialize<MultiVersionStructV0Ptr>(std::move(input));
+    EXPECT_TRUE(output);
+    EXPECT_TRUE(output->Equals(*expected_output));
+  }
+}
+
+// Serialization test for native struct.
+TEST_F(StructTest, Serialization_NativeStruct) {
+  using Data = mojo::internal::NativeStruct_Data;
+  {
+    // Serialization of a null native struct.
+    NativeStructPtr native;
+    size_t size =
+        mojo::internal::PrepareToSerialize<NativeStructPtr>(native, nullptr);
+    EXPECT_EQ(0u, size);
+    mojo::internal::FixedBufferForTesting buf(size);
+
+    Data* data = nullptr;
+    mojo::internal::Serialize<NativeStructPtr>(std::move(native), &buf, &data,
+                                               nullptr);
+
+    EXPECT_EQ(nullptr, data);
+
+    NativeStructPtr output_native;
+    mojo::internal::Deserialize<NativeStructPtr>(data, &output_native, nullptr);
+    EXPECT_TRUE(output_native.is_null());
+  }
+
+  {
+    // Serialization of a native struct with null data.
+    NativeStructPtr native(NativeStruct::New());
+    size_t size =
+        mojo::internal::PrepareToSerialize<NativeStructPtr>(native, nullptr);
+    EXPECT_EQ(0u, size);
+    mojo::internal::FixedBufferForTesting buf(size);
+
+    Data* data = nullptr;
+    mojo::internal::Serialize<NativeStructPtr>(std::move(native), &buf, &data,
+                                               nullptr);
+
+    EXPECT_EQ(nullptr, data);
+
+    NativeStructPtr output_native;
+    mojo::internal::Deserialize<NativeStructPtr>(data, &output_native, nullptr);
+    EXPECT_TRUE(output_native.is_null());
+  }
+
+  {
+    NativeStructPtr native(NativeStruct::New());
+    native->data = Array<uint8_t>(2);
+    native->data[0] = 'X';
+    native->data[1] = 'Y';
+
+    size_t size =
+        mojo::internal::PrepareToSerialize<NativeStructPtr>(native, nullptr);
+    EXPECT_EQ(16u, size);
+    mojo::internal::FixedBufferForTesting buf(size);
+
+    Data* data = nullptr;
+    mojo::internal::Serialize<NativeStructPtr>(std::move(native), &buf, &data,
+                                               nullptr);
+
+    EXPECT_NE(nullptr, data);
+
+    NativeStructPtr output_native;
+    mojo::internal::Deserialize<NativeStructPtr>(data, &output_native, nullptr);
+    EXPECT_FALSE(output_native.is_null());
+    EXPECT_FALSE(output_native->data.is_null());
+    EXPECT_EQ(2u, output_native->data.size());
+    EXPECT_EQ('X', output_native->data[0]);
+    EXPECT_EQ('Y', output_native->data[1]);
+  }
+}
+
+TEST_F(StructTest, Serialization_PublicAPI) {
+  {
+    // A null struct pointer.
+    RectPtr null_struct;
+    mojo::Array<uint8_t> data = Rect::Serialize(&null_struct);
+    EXPECT_TRUE(data.empty());
+
+    // Initialize it to non-null.
+    RectPtr output(Rect::New());
+    ASSERT_TRUE(Rect::Deserialize(std::move(data), &output));
+    EXPECT_TRUE(output.is_null());
+  }
+
+  {
+    // A struct with no fields.
+    EmptyStructPtr empty_struct(EmptyStruct::New());
+    mojo::Array<uint8_t> data = EmptyStruct::Serialize(&empty_struct);
+    EXPECT_FALSE(data.empty());
+
+    EmptyStructPtr output;
+    ASSERT_TRUE(EmptyStruct::Deserialize(std::move(data), &output));
+    EXPECT_FALSE(output.is_null());
+  }
+
+  {
+    // A simple struct.
+    RectPtr rect = MakeRect();
+    RectPtr cloned_rect = rect.Clone();
+    mojo::Array<uint8_t> data = Rect::Serialize(&rect);
+
+    RectPtr output;
+    ASSERT_TRUE(Rect::Deserialize(std::move(data), &output));
+    EXPECT_TRUE(output.Equals(cloned_rect));
+  }
+
+  {
+    // A struct containing other objects.
+    NamedRegionPtr region(NamedRegion::New());
+    region->name.emplace("region");
+    region->rects.emplace(3);
+    for (size_t i = 0; i < region->rects->size(); ++i)
+      (*region->rects)[i] = MakeRect(static_cast<int32_t>(i) + 1);
+
+    NamedRegionPtr cloned_region = region.Clone();
+    mojo::Array<uint8_t> data = NamedRegion::Serialize(&region);
+
+    // Make sure that the serialized result gets pointers encoded properly.
+    mojo::Array<uint8_t> cloned_data = data.Clone();
+    NamedRegionPtr output;
+    ASSERT_TRUE(NamedRegion::Deserialize(std::move(cloned_data), &output));
+    EXPECT_TRUE(output.Equals(cloned_region));
+  }
+
+  {
+    // Deserialization failure.
+    RectPtr rect = MakeRect();
+    mojo::Array<uint8_t> data = Rect::Serialize(&rect);
+
+    NamedRegionPtr output;
+    EXPECT_FALSE(NamedRegion::Deserialize(std::move(data), &output));
+  }
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits.typemap b/mojo/public/cpp/bindings/tests/struct_with_traits.typemap
new file mode 100644
index 0000000..da061de
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits.typemap
@@ -0,0 +1,24 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/interfaces/bindings/tests/struct_with_traits.mojom"
+public_headers =
+    [ "//mojo/public/cpp/bindings/tests/struct_with_traits_impl.h" ]
+traits_headers =
+    [ "//mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h" ]
+sources = [
+  "//mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc",
+]
+deps = [
+  "//mojo/public/cpp/bindings/tests:struct_with_traits_impl",
+  "//mojo/public/cpp/system:system",
+]
+
+type_mappings = [
+  "mojo.test.EnumWithTraits=mojo::test::EnumWithTraitsImpl",
+  "mojo.test.StructWithTraits=mojo::test::StructWithTraitsImpl",
+  "mojo.test.NestedStructWithTraits=mojo::test::NestedStructWithTraitsImpl",
+  "mojo.test.PassByValueStructWithTraits=mojo::test::PassByValueStructWithTraitsImpl[move_only]",
+  "mojo.test.StructWithTraitsForUniquePtrTest=std::unique_ptr<int>[move_only]",
+]
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl.cc b/mojo/public/cpp/bindings/tests/struct_with_traits_impl.cc
new file mode 100644
index 0000000..e99ec2a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All 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/struct_with_traits_impl.h"
+
+namespace mojo {
+namespace test {
+
+NestedStructWithTraitsImpl::NestedStructWithTraitsImpl() {}
+NestedStructWithTraitsImpl::NestedStructWithTraitsImpl(int32_t in_value)
+    : value(in_value) {}
+
+StructWithTraitsImpl::StructWithTraitsImpl() {}
+
+StructWithTraitsImpl::~StructWithTraitsImpl() {}
+
+StructWithTraitsImpl::StructWithTraitsImpl(const StructWithTraitsImpl& other) =
+    default;
+
+PassByValueStructWithTraitsImpl::PassByValueStructWithTraitsImpl() {}
+
+PassByValueStructWithTraitsImpl::PassByValueStructWithTraitsImpl(
+    PassByValueStructWithTraitsImpl&& other) = default;
+
+PassByValueStructWithTraitsImpl::~PassByValueStructWithTraitsImpl() {}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl.h b/mojo/public/cpp/bindings/tests/struct_with_traits_impl.h
new file mode 100644
index 0000000..b62ba56
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl.h
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All 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_STRUCT_WITH_TRAITS_IMPL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_STRUCT_WITH_TRAITS_IMPL_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+namespace test {
+
+struct NestedStructWithTraitsImpl {
+ public:
+  NestedStructWithTraitsImpl();
+  explicit NestedStructWithTraitsImpl(int32_t in_value);
+
+  bool operator==(const NestedStructWithTraitsImpl& other) const {
+    return value == other.value;
+  }
+
+  int32_t value = 0;
+};
+
+enum class EnumWithTraitsImpl { CUSTOM_VALUE_0 = 10, CUSTOM_VALUE_1 = 11 };
+
+// A type which knows how to look like a mojo::test::StructWithTraits mojom type
+// by way of mojo::StructTraits.
+class StructWithTraitsImpl {
+ public:
+  StructWithTraitsImpl();
+  ~StructWithTraitsImpl();
+
+  StructWithTraitsImpl(const StructWithTraitsImpl& other);
+
+  void set_enum(EnumWithTraitsImpl value) { enum_ = value; }
+  EnumWithTraitsImpl get_enum() const { return enum_; }
+
+  void set_bool(bool value) { bool_ = value; }
+  bool get_bool() const { return bool_; }
+
+  void set_uint32(uint32_t value) { uint32_ = value; }
+  uint32_t get_uint32() const { return uint32_; }
+
+  void set_uint64(uint64_t value) { uint64_ = value; }
+  uint64_t get_uint64() const { return uint64_; }
+
+  void set_string(std::string value) { string_ = value; }
+  base::StringPiece get_string_as_string_piece() const { return string_; }
+  const std::string& get_string() const { return string_; }
+
+  const std::vector<std::string>& get_string_array() const {
+    return string_array_;
+  }
+  std::vector<std::string>& get_mutable_string_array() { return string_array_; }
+
+  const NestedStructWithTraitsImpl& get_struct() const { return struct_; }
+  NestedStructWithTraitsImpl& get_mutable_struct() { return struct_; }
+
+  const std::vector<NestedStructWithTraitsImpl>& get_struct_array() const {
+    return struct_array_;
+  }
+  std::vector<NestedStructWithTraitsImpl>& get_mutable_struct_array() {
+    return struct_array_;
+  }
+
+  const std::map<std::string, NestedStructWithTraitsImpl>& get_struct_map()
+      const {
+    return struct_map_;
+  }
+  std::map<std::string, NestedStructWithTraitsImpl>& get_mutable_struct_map() {
+    return struct_map_;
+  }
+
+ private:
+  EnumWithTraitsImpl enum_ = EnumWithTraitsImpl::CUSTOM_VALUE_0;
+  bool bool_ = false;
+  uint32_t uint32_ = 0;
+  uint64_t uint64_ = 0;
+  std::string string_;
+  std::vector<std::string> string_array_;
+  NestedStructWithTraitsImpl struct_;
+  std::vector<NestedStructWithTraitsImpl> struct_array_;
+  std::map<std::string, NestedStructWithTraitsImpl> struct_map_;
+};
+
+// A type which knows how to look like a mojo::test::PassByValueStructWithTraits
+// mojom type by way of mojo::StructTraits.
+class PassByValueStructWithTraitsImpl {
+ public:
+  PassByValueStructWithTraitsImpl();
+  PassByValueStructWithTraitsImpl(PassByValueStructWithTraitsImpl&& other);
+  ~PassByValueStructWithTraitsImpl();
+
+  ScopedHandle& get_mutable_handle() { return handle_; }
+
+ private:
+  ScopedHandle handle_;
+  DISALLOW_COPY_AND_ASSIGN(PassByValueStructWithTraitsImpl);
+};
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_STRUCT_WITH_TRAITS_IMPL_H_
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc
new file mode 100644
index 0000000..c11b37a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium Authors. All 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/struct_with_traits_impl_traits.h"
+
+namespace mojo {
+namespace {
+
+struct Context {
+  int32_t value;
+};
+
+}  // namespace
+
+// static
+void* StructTraits<test::NestedStructWithTraits,
+                   test::NestedStructWithTraitsImpl>::
+    SetUpContext(const test::NestedStructWithTraitsImpl& input) {
+  Context* context = new Context;
+  context->value = input.value;
+  return context;
+}
+
+// static
+void StructTraits<test::NestedStructWithTraits,
+                  test::NestedStructWithTraitsImpl>::
+    TearDownContext(const test::NestedStructWithTraitsImpl& input,
+                    void* context) {
+  Context* context_obj = static_cast<Context*>(context);
+  CHECK_EQ(context_obj->value, input.value);
+  delete context_obj;
+}
+
+// static
+int32_t StructTraits<test::NestedStructWithTraits,
+                     test::NestedStructWithTraitsImpl>::
+    value(const test::NestedStructWithTraitsImpl& input, void* context) {
+  Context* context_obj = static_cast<Context*>(context);
+  CHECK_EQ(context_obj->value, input.value);
+  return input.value;
+}
+
+// static
+bool StructTraits<test::NestedStructWithTraits,
+                  test::NestedStructWithTraitsImpl>::
+    Read(test::NestedStructWithTraits::DataView data,
+         test::NestedStructWithTraitsImpl* output) {
+  output->value = data.value();
+  return true;
+}
+
+test::EnumWithTraits
+EnumTraits<test::EnumWithTraits, test::EnumWithTraitsImpl>::ToMojom(
+    test::EnumWithTraitsImpl input) {
+  switch (input) {
+    case test::EnumWithTraitsImpl::CUSTOM_VALUE_0:
+      return test::EnumWithTraits::VALUE_0;
+    case test::EnumWithTraitsImpl::CUSTOM_VALUE_1:
+      return test::EnumWithTraits::VALUE_1;
+  };
+
+  NOTREACHED();
+  return test::EnumWithTraits::VALUE_0;
+}
+
+bool EnumTraits<test::EnumWithTraits, test::EnumWithTraitsImpl>::FromMojom(
+    test::EnumWithTraits input,
+    test::EnumWithTraitsImpl* output) {
+  switch (input) {
+    case test::EnumWithTraits::VALUE_0:
+      *output = test::EnumWithTraitsImpl::CUSTOM_VALUE_0;
+      return true;
+    case test::EnumWithTraits::VALUE_1:
+      *output = test::EnumWithTraitsImpl::CUSTOM_VALUE_1;
+      return true;
+  };
+
+  return false;
+}
+
+// static
+bool StructTraits<test::StructWithTraits, test::StructWithTraitsImpl>::Read(
+    test::StructWithTraits::DataView data,
+    test::StructWithTraitsImpl* out) {
+  test::EnumWithTraitsImpl f_enum;
+  if (!data.ReadFEnum(&f_enum))
+    return false;
+  out->set_enum(f_enum);
+
+  out->set_bool(data.f_bool());
+  out->set_uint32(data.f_uint32());
+  out->set_uint64(data.f_uint64());
+
+  base::StringPiece f_string;
+  std::string f_string2;
+  if (!data.ReadFString(&f_string) || !data.ReadFString2(&f_string2) ||
+      f_string != f_string2) {
+    return false;
+  }
+  out->set_string(f_string2);
+
+  if (!data.ReadFStringArray(&out->get_mutable_string_array()))
+    return false;
+
+  if (!data.ReadFStruct(&out->get_mutable_struct()))
+    return false;
+
+  if (!data.ReadFStructArray(&out->get_mutable_struct_array()))
+    return false;
+
+  if (!data.ReadFStructMap(&out->get_mutable_struct_map()))
+    return false;
+
+  return true;
+}
+
+// static
+bool StructTraits<test::PassByValueStructWithTraits,
+                  test::PassByValueStructWithTraitsImpl>::
+    Read(test::PassByValueStructWithTraits::DataView data,
+         test::PassByValueStructWithTraitsImpl* out) {
+  out->get_mutable_handle() = data.TakeFHandle();
+  return true;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h
new file mode 100644
index 0000000..4550526
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h
@@ -0,0 +1,123 @@
+// Copyright 2016 The Chromium Authors. All 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_STRUCT_WITH_TRAITS_IMPL_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_STRUCT_WITH_TRAITS_IMPL_TRAITS_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "mojo/public/cpp/bindings/tests/struct_with_traits_impl.h"
+#include "mojo/public/interfaces/bindings/tests/struct_with_traits.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<test::NestedStructWithTraits,
+                    test::NestedStructWithTraitsImpl> {
+  static void* SetUpContext(const test::NestedStructWithTraitsImpl& input);
+  static void TearDownContext(const test::NestedStructWithTraitsImpl& input,
+                              void* context);
+
+  static int32_t value(const test::NestedStructWithTraitsImpl& input,
+                       void* context);
+
+  static bool Read(test::NestedStructWithTraits::DataView data,
+                   test::NestedStructWithTraitsImpl* output);
+};
+
+template <>
+struct EnumTraits<test::EnumWithTraits, test::EnumWithTraitsImpl> {
+  static test::EnumWithTraits ToMojom(test::EnumWithTraitsImpl input);
+  static bool FromMojom(test::EnumWithTraits input,
+                        test::EnumWithTraitsImpl* output);
+};
+
+template <>
+struct StructTraits<test::StructWithTraits, test::StructWithTraitsImpl> {
+  // Deserialization to test::StructTraitsImpl.
+  static bool Read(test::StructWithTraits::DataView data,
+                   test::StructWithTraitsImpl* out);
+
+  // Fields in test::StructWithTraits.
+  // See src/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom.
+  static test::EnumWithTraitsImpl f_enum(
+      const test::StructWithTraitsImpl& value) {
+    return value.get_enum();
+  }
+
+  static bool f_bool(const test::StructWithTraitsImpl& value) {
+    return value.get_bool();
+  }
+
+  static uint32_t f_uint32(const test::StructWithTraitsImpl& value) {
+    return value.get_uint32();
+  }
+
+  static uint64_t f_uint64(const test::StructWithTraitsImpl& value) {
+    return value.get_uint64();
+  }
+
+  static base::StringPiece f_string(const test::StructWithTraitsImpl& value) {
+    return value.get_string_as_string_piece();
+  }
+
+  static const std::string& f_string2(const test::StructWithTraitsImpl& value) {
+    return value.get_string();
+  }
+
+  static const std::vector<std::string>& f_string_array(
+      const test::StructWithTraitsImpl& value) {
+    return value.get_string_array();
+  }
+
+  static const test::NestedStructWithTraitsImpl& f_struct(
+      const test::StructWithTraitsImpl& value) {
+    return value.get_struct();
+  }
+
+  static const std::vector<test::NestedStructWithTraitsImpl>& f_struct_array(
+      const test::StructWithTraitsImpl& value) {
+    return value.get_struct_array();
+  }
+
+  static const std::map<std::string, test::NestedStructWithTraitsImpl>&
+  f_struct_map(const test::StructWithTraitsImpl& value) {
+    return value.get_struct_map();
+  }
+};
+
+template <>
+struct StructTraits<test::PassByValueStructWithTraits,
+                    test::PassByValueStructWithTraitsImpl> {
+  // Deserialization to test::PassByValueStructTraitsImpl.
+  static bool Read(test::PassByValueStructWithTraits::DataView data,
+                   test::PassByValueStructWithTraitsImpl* out);
+
+  // Fields in test::PassByValueStructWithTraits.
+  // See src/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom.
+  static ScopedHandle f_handle(test::PassByValueStructWithTraitsImpl& value) {
+    return std::move(value.get_mutable_handle());
+  }
+};
+
+template <>
+struct StructTraits<test::StructWithTraitsForUniquePtrTest,
+                    std::unique_ptr<int>> {
+  static int f_int32(const std::unique_ptr<int>& data) { return *data; }
+
+  static bool Read(test::StructWithTraitsForUniquePtrTest::DataView data,
+                   std::unique_ptr<int>* out) {
+    out->reset(new int(data.f_int32()));
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_STRUCT_WITH_TRAITS_IMPL_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/tests/sync_method_unittest.cc b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
new file mode 100644
index 0000000..1253930
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
@@ -0,0 +1,742 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/interfaces/bindings/tests/test_sync_methods.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+template <typename... Args>
+struct LambdaBinder {
+  using CallbackType = base::Callback<void(Args...)>;
+
+  template <typename Func>
+  static void RunLambda(Func func, Args... args) {
+    func(std::move(args)...);
+  }
+
+  template <typename Func>
+  static CallbackType BindLambda(Func func) {
+    return base::Bind(&LambdaBinder::RunLambda<Func>, func);
+  }
+};
+
+class TestSyncCommonImpl {
+ public:
+  TestSyncCommonImpl() {}
+
+  using PingHandler = base::Callback<void(const base::Callback<void()>&)>;
+  using PingBinder = LambdaBinder<const base::Callback<void()>&>;
+  template <typename Func>
+  void set_ping_handler(Func handler) {
+    ping_handler_ = PingBinder::BindLambda(handler);
+  }
+
+  using EchoHandler =
+      base::Callback<void(int32_t, const base::Callback<void(int32_t)>&)>;
+  using EchoBinder =
+      LambdaBinder<int32_t, const base::Callback<void(int32_t)>&>;
+  template <typename Func>
+  void set_echo_handler(Func handler) {
+    echo_handler_ = EchoBinder::BindLambda(handler);
+  }
+
+  using AsyncEchoHandler =
+      base::Callback<void(int32_t, const base::Callback<void(int32_t)>&)>;
+  using AsyncEchoBinder =
+      LambdaBinder<int32_t, const base::Callback<void(int32_t)>&>;
+  template <typename Func>
+  void set_async_echo_handler(Func handler) {
+    async_echo_handler_ = AsyncEchoBinder::BindLambda(handler);
+  }
+
+  using SendInterfaceHandler = base::Callback<void(TestSyncAssociatedPtrInfo)>;
+  using SendInterfaceBinder = LambdaBinder<TestSyncAssociatedPtrInfo>;
+  template <typename Func>
+  void set_send_interface_handler(Func handler) {
+    send_interface_handler_ = SendInterfaceBinder::BindLambda(handler);
+  }
+
+  using SendRequestHandler = base::Callback<void(TestSyncAssociatedRequest)>;
+  using SendRequestBinder = LambdaBinder<TestSyncAssociatedRequest>;
+  template <typename Func>
+  void set_send_request_handler(Func handler) {
+    send_request_handler_ = SendRequestBinder::BindLambda(handler);
+  }
+
+  void PingImpl(const base::Callback<void()>& callback) {
+    if (ping_handler_.is_null()) {
+      callback.Run();
+      return;
+    }
+    ping_handler_.Run(callback);
+  }
+  void EchoImpl(int32_t value, const base::Callback<void(int32_t)>& callback) {
+    if (echo_handler_.is_null()) {
+      callback.Run(value);
+      return;
+    }
+    echo_handler_.Run(value, callback);
+  }
+  void AsyncEchoImpl(int32_t value,
+                     const base::Callback<void(int32_t)>& callback) {
+    if (async_echo_handler_.is_null()) {
+      callback.Run(value);
+      return;
+    }
+    async_echo_handler_.Run(value, callback);
+  }
+  void SendInterfaceImpl(TestSyncAssociatedPtrInfo ptr) {
+    send_interface_handler_.Run(std::move(ptr));
+  }
+  void SendRequestImpl(TestSyncAssociatedRequest request) {
+    send_request_handler_.Run(std::move(request));
+  }
+
+ private:
+  PingHandler ping_handler_;
+  EchoHandler echo_handler_;
+  AsyncEchoHandler async_echo_handler_;
+  SendInterfaceHandler send_interface_handler_;
+  SendRequestHandler send_request_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSyncCommonImpl);
+};
+
+class TestSyncImpl : public TestSync, public TestSyncCommonImpl {
+ public:
+  explicit TestSyncImpl(TestSyncRequest request)
+      : binding_(this, std::move(request)) {}
+
+  // TestSync implementation:
+  void Ping(const PingCallback& callback) override { PingImpl(callback); }
+  void Echo(int32_t value, const EchoCallback& callback) override {
+    EchoImpl(value, callback);
+  }
+  void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override {
+    AsyncEchoImpl(value, callback);
+  }
+
+  Binding<TestSync>* binding() { return &binding_; }
+
+ private:
+  Binding<TestSync> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSyncImpl);
+};
+
+class TestSyncMasterImpl : public TestSyncMaster, public TestSyncCommonImpl {
+ public:
+  explicit TestSyncMasterImpl(TestSyncMasterRequest request)
+      : binding_(this, std::move(request)) {}
+
+  // TestSyncMaster implementation:
+  void Ping(const PingCallback& callback) override { PingImpl(callback); }
+  void Echo(int32_t value, const EchoCallback& callback) override {
+    EchoImpl(value, callback);
+  }
+  void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override {
+    AsyncEchoImpl(value, callback);
+  }
+  void SendInterface(TestSyncAssociatedPtrInfo ptr) override {
+    SendInterfaceImpl(std::move(ptr));
+  }
+  void SendRequest(TestSyncAssociatedRequest request) override {
+    SendRequestImpl(std::move(request));
+  }
+
+  Binding<TestSyncMaster>* binding() { return &binding_; }
+
+ private:
+  Binding<TestSyncMaster> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSyncMasterImpl);
+};
+
+class TestSyncAssociatedImpl : public TestSync, public TestSyncCommonImpl {
+ public:
+  explicit TestSyncAssociatedImpl(TestSyncAssociatedRequest request)
+      : binding_(this, std::move(request)) {}
+
+  // TestSync implementation:
+  void Ping(const PingCallback& callback) override { PingImpl(callback); }
+  void Echo(int32_t value, const EchoCallback& callback) override {
+    EchoImpl(value, callback);
+  }
+  void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override {
+    AsyncEchoImpl(value, callback);
+  }
+
+  AssociatedBinding<TestSync>* binding() { return &binding_; }
+
+ private:
+  AssociatedBinding<TestSync> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSyncAssociatedImpl);
+};
+
+template <typename Interface>
+struct ImplTraits;
+
+template <>
+struct ImplTraits<TestSync> {
+  using Type = TestSyncImpl;
+};
+
+template <>
+struct ImplTraits<TestSyncMaster> {
+  using Type = TestSyncMasterImpl;
+};
+
+template <typename Interface>
+class TestSyncServiceThread {
+ public:
+  TestSyncServiceThread()
+      : thread_("TestSyncServiceThread"), ping_called_(false) {
+    thread_.Start();
+  }
+
+  void SetUp(InterfaceRequest<Interface> request) {
+    CHECK(thread_.task_runner()->BelongsToCurrentThread());
+    impl_.reset(new typename ImplTraits<Interface>::Type(std::move(request)));
+    impl_->set_ping_handler(
+        [this](const typename Interface::PingCallback& callback) {
+          {
+            base::AutoLock locker(lock_);
+            ping_called_ = true;
+          }
+          callback.Run();
+        });
+  }
+
+  void TearDown() {
+    CHECK(thread_.task_runner()->BelongsToCurrentThread());
+    impl_.reset();
+  }
+
+  base::Thread* thread() { return &thread_; }
+  bool ping_called() const {
+    base::AutoLock locker(lock_);
+    return ping_called_;
+  }
+
+ private:
+  base::Thread thread_;
+
+  std::unique_ptr<typename ImplTraits<Interface>::Type> impl_;
+
+  mutable base::Lock lock_;
+  bool ping_called_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSyncServiceThread);
+};
+
+class SyncMethodTest : public testing::Test {
+ public:
+  SyncMethodTest() {}
+  ~SyncMethodTest() override { base::RunLoop().RunUntilIdle(); }
+
+ protected:
+  base::MessageLoop loop_;
+};
+
+template <typename T>
+class SyncMethodCommonTest : public SyncMethodTest {
+ public:
+  SyncMethodCommonTest() {}
+  ~SyncMethodCommonTest() override {}
+};
+
+class SyncMethodAssociatedTest : public SyncMethodTest {
+ public:
+  SyncMethodAssociatedTest() {}
+  ~SyncMethodAssociatedTest() override {}
+
+ protected:
+  void SetUp() override {
+    master_impl_.reset(new TestSyncMasterImpl(GetProxy(&master_ptr_)));
+
+    master_ptr_.associated_group()->CreateAssociatedInterface(
+        AssociatedGroup::WILL_PASS_REQUEST, &asso_ptr_info_, &asso_request_);
+    master_ptr_.associated_group()->CreateAssociatedInterface(
+        AssociatedGroup::WILL_PASS_PTR, &opposite_asso_ptr_info_,
+        &opposite_asso_request_);
+
+    master_impl_->set_send_interface_handler(
+        [this](TestSyncAssociatedPtrInfo ptr) {
+          opposite_asso_ptr_info_ = std::move(ptr);
+        });
+    base::RunLoop run_loop;
+    master_impl_->set_send_request_handler(
+        [this, &run_loop](TestSyncAssociatedRequest request) {
+          asso_request_ = std::move(request);
+          run_loop.Quit();
+        });
+
+    master_ptr_->SendInterface(std::move(opposite_asso_ptr_info_));
+    master_ptr_->SendRequest(std::move(asso_request_));
+    run_loop.Run();
+  }
+
+  void TearDown() override {
+    asso_ptr_info_ = TestSyncAssociatedPtrInfo();
+    asso_request_ = TestSyncAssociatedRequest();
+    opposite_asso_ptr_info_ = TestSyncAssociatedPtrInfo();
+    opposite_asso_request_ = TestSyncAssociatedRequest();
+
+    master_ptr_ = nullptr;
+    master_impl_.reset();
+  }
+
+  InterfacePtr<TestSyncMaster> master_ptr_;
+  std::unique_ptr<TestSyncMasterImpl> master_impl_;
+
+  // An associated interface whose binding lives at the |master_impl_| side.
+  TestSyncAssociatedPtrInfo asso_ptr_info_;
+  TestSyncAssociatedRequest asso_request_;
+
+  // An associated interface whose binding lives at the |master_ptr_| side.
+  TestSyncAssociatedPtrInfo opposite_asso_ptr_info_;
+  TestSyncAssociatedRequest opposite_asso_request_;
+};
+
+void SetFlagAndRunClosure(bool* flag, const base::Closure& closure) {
+  *flag = true;
+  closure.Run();
+}
+
+void ExpectValueAndRunClosure(int32_t expected_value,
+                              const base::Closure& closure,
+                              int32_t value) {
+  EXPECT_EQ(expected_value, value);
+  closure.Run();
+}
+
+template <typename Func>
+void CallAsyncEchoCallback(Func func, int32_t value) {
+  func(value);
+}
+
+template <typename Func>
+TestSync::AsyncEchoCallback BindAsyncEchoCallback(Func func) {
+  return base::Bind(&CallAsyncEchoCallback<Func>, func);
+}
+
+// TestSync and TestSyncMaster exercise Router and MultiplexRouter,
+// respectively.
+using InterfaceTypes = testing::Types<TestSync, TestSyncMaster>;
+TYPED_TEST_CASE(SyncMethodCommonTest, InterfaceTypes);
+
+TYPED_TEST(SyncMethodCommonTest, CallSyncMethodAsynchronously) {
+  InterfacePtr<TypeParam> ptr;
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+
+  base::RunLoop run_loop;
+  ptr->Echo(123, base::Bind(&ExpectValueAndRunClosure, 123,
+                            run_loop.QuitClosure()));
+  run_loop.Run();
+}
+
+TYPED_TEST(SyncMethodCommonTest, BasicSyncCalls) {
+  InterfacePtr<TypeParam> ptr;
+
+  TestSyncServiceThread<TypeParam> service_thread;
+  service_thread.thread()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TestSyncServiceThread<TypeParam>::SetUp,
+                            base::Unretained(&service_thread),
+                            base::Passed(GetProxy(&ptr))));
+  ASSERT_TRUE(ptr->Ping());
+  ASSERT_TRUE(service_thread.ping_called());
+
+  int32_t output_value = -1;
+  ASSERT_TRUE(ptr->Echo(42, &output_value));
+  ASSERT_EQ(42, output_value);
+
+  base::RunLoop run_loop;
+  service_thread.thread()->task_runner()->PostTaskAndReply(
+      FROM_HERE, base::Bind(&TestSyncServiceThread<TypeParam>::TearDown,
+                            base::Unretained(&service_thread)),
+      run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+TYPED_TEST(SyncMethodCommonTest, ReenteredBySyncMethodBinding) {
+  // Test that an interface pointer waiting for a sync call response can be
+  // reentered by a binding serving sync methods on the same thread.
+
+  InterfacePtr<TypeParam> ptr;
+  // The binding lives on the same thread as the interface pointer.
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+  int32_t output_value = -1;
+  ASSERT_TRUE(ptr->Echo(42, &output_value));
+  EXPECT_EQ(42, output_value);
+}
+
+TYPED_TEST(SyncMethodCommonTest, InterfacePtrDestroyedDuringSyncCall) {
+  // Test that it won't result in crash or hang if an interface pointer is
+  // destroyed while it is waiting for a sync call response.
+
+  InterfacePtr<TypeParam> ptr;
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+  impl.set_ping_handler([&ptr](const TestSync::PingCallback& callback) {
+    ptr.reset();
+    callback.Run();
+  });
+  ASSERT_FALSE(ptr->Ping());
+}
+
+TYPED_TEST(SyncMethodCommonTest, BindingDestroyedDuringSyncCall) {
+  // Test that it won't result in crash or hang if a binding is
+  // closed (and therefore the message pipe handle is closed) while the
+  // corresponding interface pointer is waiting for a sync call response.
+
+  InterfacePtr<TypeParam> ptr;
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+  impl.set_ping_handler([&impl](const TestSync::PingCallback& callback) {
+    impl.binding()->Close();
+    callback.Run();
+  });
+  ASSERT_FALSE(ptr->Ping());
+}
+
+TYPED_TEST(SyncMethodCommonTest, NestedSyncCallsWithInOrderResponses) {
+  // Test that we can call a sync method on an interface ptr, while there is
+  // already a sync call ongoing. The responses arrive in order.
+
+  InterfacePtr<TypeParam> ptr;
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+
+  // The same variable is used to store the output of the two sync calls, in
+  // order to test that responses are handled in the correct order.
+  int32_t result_value = -1;
+
+  bool first_call = true;
+  impl.set_echo_handler([&first_call, &ptr, &result_value](
+      int32_t value, const TestSync::EchoCallback& callback) {
+    if (first_call) {
+      first_call = false;
+      ASSERT_TRUE(ptr->Echo(456, &result_value));
+      EXPECT_EQ(456, result_value);
+    }
+    callback.Run(value);
+  });
+
+  ASSERT_TRUE(ptr->Echo(123, &result_value));
+  EXPECT_EQ(123, result_value);
+}
+
+TYPED_TEST(SyncMethodCommonTest, NestedSyncCallsWithOutOfOrderResponses) {
+  // Test that we can call a sync method on an interface ptr, while there is
+  // already a sync call ongoing. The responses arrive out of order.
+
+  InterfacePtr<TypeParam> ptr;
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+
+  // The same variable is used to store the output of the two sync calls, in
+  // order to test that responses are handled in the correct order.
+  int32_t result_value = -1;
+
+  bool first_call = true;
+  impl.set_echo_handler([&first_call, &ptr, &result_value](
+      int32_t value, const TestSync::EchoCallback& callback) {
+    callback.Run(value);
+    if (first_call) {
+      first_call = false;
+      ASSERT_TRUE(ptr->Echo(456, &result_value));
+      EXPECT_EQ(456, result_value);
+    }
+  });
+
+  ASSERT_TRUE(ptr->Echo(123, &result_value));
+  EXPECT_EQ(123, result_value);
+}
+
+TYPED_TEST(SyncMethodCommonTest, AsyncResponseQueuedDuringSyncCall) {
+  // Test that while an interface pointer is waiting for the response to a sync
+  // call, async responses are queued until the sync call completes.
+
+  InterfacePtr<TypeParam> ptr;
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+
+  int32_t async_echo_request_value = -1;
+  TestSync::AsyncEchoCallback async_echo_request_callback;
+  base::RunLoop run_loop1;
+  impl.set_async_echo_handler(
+      [&async_echo_request_value, &async_echo_request_callback, &run_loop1](
+          int32_t value, const TestSync::AsyncEchoCallback& callback) {
+        async_echo_request_value = value;
+        async_echo_request_callback = callback;
+        run_loop1.Quit();
+      });
+
+  bool async_echo_response_dispatched = false;
+  base::RunLoop run_loop2;
+  ptr->AsyncEcho(
+      123,
+      BindAsyncEchoCallback(
+         [&async_echo_response_dispatched, &run_loop2](int32_t result) {
+           async_echo_response_dispatched = true;
+           EXPECT_EQ(123, result);
+           run_loop2.Quit();
+         }));
+  // Run until the AsyncEcho request reaches the service side.
+  run_loop1.Run();
+
+  impl.set_echo_handler(
+      [&async_echo_request_value, &async_echo_request_callback](
+          int32_t value, const TestSync::EchoCallback& callback) {
+        // Send back the async response first.
+        EXPECT_FALSE(async_echo_request_callback.is_null());
+        async_echo_request_callback.Run(async_echo_request_value);
+
+        callback.Run(value);
+      });
+
+  int32_t result_value = -1;
+  ASSERT_TRUE(ptr->Echo(456, &result_value));
+  EXPECT_EQ(456, result_value);
+
+  // Although the AsyncEcho response arrives before the Echo response, it should
+  // be queued and not yet dispatched.
+  EXPECT_FALSE(async_echo_response_dispatched);
+
+  // Run until the AsyncEcho response is dispatched.
+  run_loop2.Run();
+
+  EXPECT_TRUE(async_echo_response_dispatched);
+}
+
+TYPED_TEST(SyncMethodCommonTest, AsyncRequestQueuedDuringSyncCall) {
+  // Test that while an interface pointer is waiting for the response to a sync
+  // call, async requests for a binding running on the same thread are queued
+  // until the sync call completes.
+
+  InterfacePtr<TypeParam> ptr;
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+
+  bool async_echo_request_dispatched = false;
+  impl.set_async_echo_handler([&async_echo_request_dispatched](
+      int32_t value, const TestSync::AsyncEchoCallback& callback) {
+    async_echo_request_dispatched = true;
+    callback.Run(value);
+  });
+
+  bool async_echo_response_dispatched = false;
+  base::RunLoop run_loop;
+  ptr->AsyncEcho(
+      123,
+      BindAsyncEchoCallback(
+         [&async_echo_response_dispatched, &run_loop](int32_t result) {
+           async_echo_response_dispatched = true;
+           EXPECT_EQ(123, result);
+           run_loop.Quit();
+         }));
+
+  impl.set_echo_handler([&async_echo_request_dispatched](
+      int32_t value, const TestSync::EchoCallback& callback) {
+    // Although the AsyncEcho request is sent before the Echo request, it
+    // shouldn't be dispatched yet at this point, because there is an ongoing
+    // sync call on the same thread.
+    EXPECT_FALSE(async_echo_request_dispatched);
+    callback.Run(value);
+  });
+
+  int32_t result_value = -1;
+  ASSERT_TRUE(ptr->Echo(456, &result_value));
+  EXPECT_EQ(456, result_value);
+
+  // Although the AsyncEcho request is sent before the Echo request, it
+  // shouldn't be dispatched yet.
+  EXPECT_FALSE(async_echo_request_dispatched);
+
+  // Run until the AsyncEcho response is dispatched.
+  run_loop.Run();
+
+  EXPECT_TRUE(async_echo_response_dispatched);
+}
+
+TYPED_TEST(SyncMethodCommonTest,
+           QueuedMessagesProcessedBeforeErrorNotification) {
+  // Test that while an interface pointer is waiting for the response to a sync
+  // call, async responses are queued. If the message pipe is disconnected
+  // before the queued messages are processed, the connection error
+  // notification is delayed until all the queued messages are processed.
+
+  InterfacePtr<TypeParam> ptr;
+  typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+
+  int32_t async_echo_request_value = -1;
+  TestSync::AsyncEchoCallback async_echo_request_callback;
+  base::RunLoop run_loop1;
+  impl.set_async_echo_handler(
+      [&async_echo_request_value, &async_echo_request_callback, &run_loop1](
+          int32_t value, const TestSync::AsyncEchoCallback& callback) {
+        async_echo_request_value = value;
+        async_echo_request_callback = callback;
+        run_loop1.Quit();
+      });
+
+  bool async_echo_response_dispatched = false;
+  bool connection_error_dispatched = false;
+  base::RunLoop run_loop2;
+  ptr->AsyncEcho(
+      123,
+      BindAsyncEchoCallback(
+          [&async_echo_response_dispatched, &connection_error_dispatched, &ptr,
+              &run_loop2](int32_t result) {
+            async_echo_response_dispatched = true;
+            // At this point, error notification should not be dispatched
+            // yet.
+            EXPECT_FALSE(connection_error_dispatched);
+            EXPECT_FALSE(ptr.encountered_error());
+            EXPECT_EQ(123, result);
+            run_loop2.Quit();
+          }));
+  // Run until the AsyncEcho request reaches the service side.
+  run_loop1.Run();
+
+  impl.set_echo_handler(
+      [&impl, &async_echo_request_value, &async_echo_request_callback](
+          int32_t value, const TestSync::EchoCallback& callback) {
+        // Send back the async response first.
+        EXPECT_FALSE(async_echo_request_callback.is_null());
+        async_echo_request_callback.Run(async_echo_request_value);
+
+        impl.binding()->Close();
+      });
+
+  base::RunLoop run_loop3;
+  ptr.set_connection_error_handler(
+      base::Bind(&SetFlagAndRunClosure, &connection_error_dispatched,
+                 run_loop3.QuitClosure()));
+
+  int32_t result_value = -1;
+  ASSERT_FALSE(ptr->Echo(456, &result_value));
+  EXPECT_EQ(-1, result_value);
+  ASSERT_FALSE(connection_error_dispatched);
+  EXPECT_FALSE(ptr.encountered_error());
+
+  // Although the AsyncEcho response arrives before the Echo response, it should
+  // be queued and not yet dispatched.
+  EXPECT_FALSE(async_echo_response_dispatched);
+
+  // Run until the AsyncEcho response is dispatched.
+  run_loop2.Run();
+
+  EXPECT_TRUE(async_echo_response_dispatched);
+
+  // Run until the error notification is dispatched.
+  run_loop3.Run();
+
+  ASSERT_TRUE(connection_error_dispatched);
+  EXPECT_TRUE(ptr.encountered_error());
+}
+
+TYPED_TEST(SyncMethodCommonTest, InvalidMessageDuringSyncCall) {
+  // Test that while an interface pointer is waiting for the response to a sync
+  // call, an invalid incoming message will disconnect the message pipe, cause
+  // the sync call to return false, and run the connection error handler
+  // asynchronously.
+
+  MessagePipe pipe;
+
+  InterfacePtr<TypeParam> ptr;
+  ptr.Bind(InterfacePtrInfo<TypeParam>(std::move(pipe.handle0), 0u));
+
+  MessagePipeHandle raw_binding_handle = pipe.handle1.get();
+  typename ImplTraits<TypeParam>::Type impl(
+      MakeRequest<TypeParam>(std::move(pipe.handle1)));
+
+  impl.set_echo_handler([&raw_binding_handle](
+      int32_t value, const TestSync::EchoCallback& callback) {
+    // Write a 1-byte message, which is considered invalid.
+    char invalid_message = 0;
+    MojoResult result =
+        WriteMessageRaw(raw_binding_handle, &invalid_message, 1u, nullptr, 0u,
+                        MOJO_WRITE_MESSAGE_FLAG_NONE);
+    ASSERT_EQ(MOJO_RESULT_OK, result);
+    callback.Run(value);
+  });
+
+  bool connection_error_dispatched = false;
+  base::RunLoop run_loop;
+  ptr.set_connection_error_handler(
+      base::Bind(&SetFlagAndRunClosure, &connection_error_dispatched,
+                 run_loop.QuitClosure()));
+
+  int32_t result_value = -1;
+  ASSERT_FALSE(ptr->Echo(456, &result_value));
+  EXPECT_EQ(-1, result_value);
+  ASSERT_FALSE(connection_error_dispatched);
+
+  run_loop.Run();
+  ASSERT_TRUE(connection_error_dispatched);
+}
+
+TEST_F(SyncMethodAssociatedTest, ReenteredBySyncMethodAssoBindingOfSameRouter) {
+  // Test that an interface pointer waiting for a sync call response can be
+  // reentered by an associated binding serving sync methods on the same thread.
+  // The associated binding belongs to the same MultiplexRouter as the waiting
+  // interface pointer.
+
+  TestSyncAssociatedImpl opposite_asso_impl(std::move(opposite_asso_request_));
+  TestSyncAssociatedPtr opposite_asso_ptr;
+  opposite_asso_ptr.Bind(std::move(opposite_asso_ptr_info_));
+
+  master_impl_->set_echo_handler([&opposite_asso_ptr](
+      int32_t value, const TestSyncMaster::EchoCallback& callback) {
+    int32_t result_value = -1;
+
+    ASSERT_TRUE(opposite_asso_ptr->Echo(123, &result_value));
+    EXPECT_EQ(123, result_value);
+    callback.Run(value);
+  });
+
+  int32_t result_value = -1;
+  ASSERT_TRUE(master_ptr_->Echo(456, &result_value));
+  EXPECT_EQ(456, result_value);
+}
+
+TEST_F(SyncMethodAssociatedTest,
+       ReenteredBySyncMethodAssoBindingOfDifferentRouter) {
+  // Test that an interface pointer waiting for a sync call response can be
+  // reentered by an associated binding serving sync methods on the same thread.
+  // The associated binding belongs to a different MultiplexRouter as the
+  // waiting interface pointer.
+
+  TestSyncAssociatedImpl asso_impl(std::move(asso_request_));
+  TestSyncAssociatedPtr asso_ptr;
+  asso_ptr.Bind(std::move(asso_ptr_info_));
+
+  master_impl_->set_echo_handler(
+      [&asso_ptr](int32_t value, const TestSyncMaster::EchoCallback& callback) {
+        int32_t result_value = -1;
+
+        ASSERT_TRUE(asso_ptr->Echo(123, &result_value));
+        EXPECT_EQ(123, result_value);
+        callback.Run(value);
+      });
+
+  int32_t result_value = -1;
+  ASSERT_TRUE(master_ptr_->Echo(456, &result_value));
+  EXPECT_EQ(456, result_value);
+}
+
+// TODO(yzshen): Add more tests related to associated interfaces.
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/test_native_types_blink.typemap b/mojo/public/cpp/bindings/tests/test_native_types_blink.typemap
new file mode 100644
index 0000000..258acd5
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/test_native_types_blink.typemap
@@ -0,0 +1,17 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/interfaces/bindings/tests/test_native_types.mojom"
+public_headers = [ "//mojo/public/cpp/bindings/tests/pickled_types_blink.h" ]
+sources = [
+  "//mojo/public/cpp/bindings/tests/pickled_types_blink.cc",
+]
+deps = [
+  "//ipc",
+]
+
+type_mappings = [
+  "mojo.test.PickledEnum=mojo::test::PickledEnumBlink",
+  "mojo.test.PickledStruct=mojo::test::PickledStructBlink"
+]
diff --git a/mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap b/mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap
new file mode 100644
index 0000000..a4c0f5a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap
@@ -0,0 +1,18 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/interfaces/bindings/tests/test_native_types.mojom"
+public_headers =
+    [ "//mojo/public/cpp/bindings/tests/pickled_types_chromium.h" ]
+sources = [
+  "//mojo/public/cpp/bindings/tests/pickled_types_chromium.cc",
+]
+deps = [
+  "//ipc",
+]
+
+type_mappings = [
+  "mojo.test.PickledEnum=mojo::test::PickledEnumChromium",
+  "mojo.test.PickledStruct=mojo::test::PickledStructChromium"
+]
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..7eb24ea
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
@@ -0,0 +1,212 @@
+// 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 <stddef.h>
+#include <stdint.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;
+  }
+};
+
+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.emplace(input.name);
+    region->rects = Array<test::RectPtr>::From(input.rects).PassStorage();
+    return region;
+  }
+};
+
+template <>
+struct TypeConverter<RedmondNamedRegion, test::NamedRegionPtr> {
+  static RedmondNamedRegion Convert(const test::NamedRegionPtr& input) {
+    RedmondNamedRegion region;
+    if (input->name)
+      region.name = input->name.value();
+    if (input->rects) {
+      region.rects.reserve(input->rects->size());
+      for (const auto& element : *input->rects)
+        region.rects.push_back(element.To<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(nullptr);
+  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/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc
new file mode 100644
index 0000000..d65a348
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -0,0 +1,1226 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <utility>
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+TEST(UnionTest, PlainOldDataGetterSetter) {
+  PodUnionPtr pod(PodUnion::New());
+
+  pod->set_f_int8(10);
+  EXPECT_EQ(10, pod->get_f_int8());
+  EXPECT_TRUE(pod->is_f_int8());
+  EXPECT_FALSE(pod->is_f_int8_other());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT8);
+
+  pod->set_f_uint8(11);
+  EXPECT_EQ(11, pod->get_f_uint8());
+  EXPECT_TRUE(pod->is_f_uint8());
+  EXPECT_FALSE(pod->is_f_int8());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT8);
+
+  pod->set_f_int16(12);
+  EXPECT_EQ(12, pod->get_f_int16());
+  EXPECT_TRUE(pod->is_f_int16());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT16);
+
+  pod->set_f_uint16(13);
+  EXPECT_EQ(13, pod->get_f_uint16());
+  EXPECT_TRUE(pod->is_f_uint16());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT16);
+
+  pod->set_f_int32(14);
+  EXPECT_EQ(14, pod->get_f_int32());
+  EXPECT_TRUE(pod->is_f_int32());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT32);
+
+  pod->set_f_uint32(static_cast<uint32_t>(15));
+  EXPECT_EQ(static_cast<uint32_t>(15), pod->get_f_uint32());
+  EXPECT_TRUE(pod->is_f_uint32());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT32);
+
+  pod->set_f_int64(16);
+  EXPECT_EQ(16, pod->get_f_int64());
+  EXPECT_TRUE(pod->is_f_int64());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT64);
+
+  pod->set_f_uint64(static_cast<uint64_t>(17));
+  EXPECT_EQ(static_cast<uint64_t>(17), pod->get_f_uint64());
+  EXPECT_TRUE(pod->is_f_uint64());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT64);
+
+  pod->set_f_float(1.5);
+  EXPECT_EQ(1.5, pod->get_f_float());
+  EXPECT_TRUE(pod->is_f_float());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_FLOAT);
+
+  pod->set_f_double(1.9);
+  EXPECT_EQ(1.9, pod->get_f_double());
+  EXPECT_TRUE(pod->is_f_double());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_DOUBLE);
+
+  pod->set_f_bool(true);
+  EXPECT_TRUE(pod->get_f_bool());
+  pod->set_f_bool(false);
+  EXPECT_FALSE(pod->get_f_bool());
+  EXPECT_TRUE(pod->is_f_bool());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_BOOL);
+
+  pod->set_f_enum(AnEnum::SECOND);
+  EXPECT_EQ(AnEnum::SECOND, pod->get_f_enum());
+  EXPECT_TRUE(pod->is_f_enum());
+  EXPECT_EQ(pod->which(), PodUnion::Tag::F_ENUM);
+}
+
+TEST(UnionTest, PodEquals) {
+  PodUnionPtr pod1(PodUnion::New());
+  PodUnionPtr pod2(PodUnion::New());
+
+  pod1->set_f_int8(10);
+  pod2->set_f_int8(10);
+  EXPECT_TRUE(pod1.Equals(pod2));
+
+  pod2->set_f_int8(11);
+  EXPECT_FALSE(pod1.Equals(pod2));
+
+  pod2->set_f_int8_other(10);
+  EXPECT_FALSE(pod1.Equals(pod2));
+}
+
+TEST(UnionTest, PodClone) {
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_int8(10);
+
+  PodUnionPtr pod_clone = pod.Clone();
+  EXPECT_EQ(10, pod_clone->get_f_int8());
+  EXPECT_TRUE(pod_clone->is_f_int8());
+  EXPECT_EQ(pod_clone->which(), PodUnion::Tag::F_INT8);
+}
+
+TEST(UnionTest, PodSerialization) {
+  PodUnionPtr pod1(PodUnion::New());
+  pod1->set_f_int8(10);
+
+  mojo::internal::SerializationContext context;
+  size_t size =
+      mojo::internal::PrepareToSerialize<PodUnionPtr>(pod1, false, &context);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = nullptr;
+  mojo::internal::Serialize<PodUnionPtr>(pod1, &buf, &data, false, &context);
+
+  PodUnionPtr pod2;
+  mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, &context);
+
+  EXPECT_EQ(10, pod2->get_f_int8());
+  EXPECT_TRUE(pod2->is_f_int8());
+  EXPECT_EQ(pod2->which(), PodUnion::Tag::F_INT8);
+}
+
+TEST(UnionTest, EnumSerialization) {
+  PodUnionPtr pod1(PodUnion::New());
+  pod1->set_f_enum(AnEnum::SECOND);
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<PodUnionPtr>(pod1, false, nullptr);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = nullptr;
+  mojo::internal::Serialize<PodUnionPtr>(pod1, &buf, &data, false, nullptr);
+
+  PodUnionPtr pod2;
+  mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, nullptr);
+
+  EXPECT_EQ(AnEnum::SECOND, pod2->get_f_enum());
+  EXPECT_TRUE(pod2->is_f_enum());
+  EXPECT_EQ(pod2->which(), PodUnion::Tag::F_ENUM);
+}
+
+TEST(UnionTest, PodValidation) {
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_int8(10);
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = nullptr;
+  mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_TRUE(
+      internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, SerializeNotNull) {
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_int8(0);
+  size_t size =
+      mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = nullptr;
+  mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+  EXPECT_FALSE(data->is_null());
+}
+
+TEST(UnionTest, SerializeIsNullInlined) {
+  PodUnionPtr pod;
+  size_t size =
+      mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+  EXPECT_EQ(16U, size);
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
+
+  // Check that dirty output buffers are handled correctly by serialization.
+  data->size = 16U;
+  data->tag = PodUnion::Tag::F_UINT16;
+  data->data.f_f_int16 = 20;
+
+  mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, true, nullptr);
+  EXPECT_TRUE(data->is_null());
+
+  PodUnionPtr pod2;
+  mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, nullptr);
+  EXPECT_TRUE(pod2.is_null());
+}
+
+TEST(UnionTest, SerializeIsNullNotInlined) {
+  PodUnionPtr pod;
+  size_t size =
+      mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+  EXPECT_EQ(16U, size);
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = nullptr;
+  mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+  EXPECT_EQ(nullptr, data);
+}
+
+TEST(UnionTest, NullValidation) {
+  void* buf = nullptr;
+  mojo::internal::ValidationContext validation_context(buf, 0, 0);
+  EXPECT_TRUE(internal::PodUnion_Data::Validate(
+      buf, &validation_context, false));
+}
+
+TEST(UnionTest, OutOfAlignmentValidation) {
+  size_t size = sizeof(internal::PodUnion_Data);
+  // Get an aligned object and shift the alignment.
+  mojo::internal::FixedBufferForTesting aligned_buf(size + 1);
+  void* raw_buf = aligned_buf.Leak();
+  char* buf = reinterpret_cast<char*>(raw_buf) + 1;
+
+  internal::PodUnion_Data* data =
+      reinterpret_cast<internal::PodUnion_Data*>(buf);
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_FALSE(internal::PodUnion_Data::Validate(
+      buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, OOBValidation) {
+  size_t size = sizeof(internal::PodUnion_Data) - 1;
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  void* raw_buf = buf.Leak();
+  EXPECT_FALSE(
+      internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, UnknownTagValidation) {
+  size_t size = sizeof(internal::PodUnion_Data);
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
+  data->tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(0xFFFFFF);
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  void* raw_buf = buf.Leak();
+  EXPECT_FALSE(
+      internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, UnknownEnumValueValidation) {
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_enum(static_cast<AnEnum>(0xFFFF));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = nullptr;
+  mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_FALSE(
+      internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, UnknownExtensibleEnumValueValidation) {
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_extensible_enum(static_cast<AnExtensibleEnum>(0xFFFF));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::PodUnion_Data* data = nullptr;
+  mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_TRUE(
+      internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, StringGetterSetter) {
+  ObjectUnionPtr pod(ObjectUnion::New());
+
+  String hello("hello world");
+  pod->set_f_string(hello);
+  EXPECT_EQ(hello, pod->get_f_string());
+  EXPECT_TRUE(pod->is_f_string());
+  EXPECT_EQ(pod->which(), ObjectUnion::Tag::F_STRING);
+}
+
+TEST(UnionTest, StringEquals) {
+  ObjectUnionPtr pod1(ObjectUnion::New());
+  ObjectUnionPtr pod2(ObjectUnion::New());
+
+  pod1->set_f_string("hello world");
+  pod2->set_f_string("hello world");
+  EXPECT_TRUE(pod1.Equals(pod2));
+
+  pod2->set_f_string("hello universe");
+  EXPECT_FALSE(pod1.Equals(pod2));
+}
+
+TEST(UnionTest, StringClone) {
+  ObjectUnionPtr pod(ObjectUnion::New());
+
+  String hello("hello world");
+  pod->set_f_string(hello);
+  ObjectUnionPtr pod_clone = pod.Clone();
+  EXPECT_EQ(hello, pod_clone->get_f_string());
+  EXPECT_TRUE(pod_clone->is_f_string());
+  EXPECT_EQ(pod_clone->which(), ObjectUnion::Tag::F_STRING);
+}
+
+TEST(UnionTest, StringSerialization) {
+  ObjectUnionPtr pod1(ObjectUnion::New());
+
+  String hello("hello world");
+  pod1->set_f_string(hello);
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(pod1, false, nullptr);
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(pod1, &buf, &data, false, nullptr);
+
+  ObjectUnionPtr pod2;
+  mojo::internal::Deserialize<ObjectUnionPtr>(data, &pod2, nullptr);
+  EXPECT_EQ(hello, pod2->get_f_string());
+  EXPECT_TRUE(pod2->is_f_string());
+  EXPECT_EQ(pod2->which(), ObjectUnion::Tag::F_STRING);
+}
+
+TEST(UnionTest, NullStringValidation) {
+  size_t size = sizeof(internal::ObjectUnion_Data);
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf);
+  data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
+  data->data.unknown = 0x0;
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  void* raw_buf = buf.Leak();
+  EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, StringPointerOverflowValidation) {
+  size_t size = sizeof(internal::ObjectUnion_Data);
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf);
+  data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
+  data->data.unknown = 0xFFFFFFFFFFFFFFFF;
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  void* raw_buf = buf.Leak();
+  EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, StringValidateOOB) {
+  size_t size = 32;
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf);
+  data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
+
+  data->data.f_f_string.offset = 8;
+  char* ptr = reinterpret_cast<char*>(&data->data.f_f_string);
+  mojo::internal::ArrayHeader* array_header =
+      reinterpret_cast<mojo::internal::ArrayHeader*>(ptr + *ptr);
+  array_header->num_bytes = 20;  // This should go out of bounds.
+  array_header->num_elements = 20;
+  mojo::internal::ValidationContext validation_context(data, 32, 0);
+  void* raw_buf = buf.Leak();
+  EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+// TODO(azani): Move back in array_unittest.cc when possible.
+// Array tests
+TEST(UnionTest, PodUnionInArray) {
+  SmallStructPtr small_struct(SmallStruct::New());
+  small_struct->pod_union_array.emplace(2);
+  small_struct->pod_union_array.value()[0] = PodUnion::New();
+  small_struct->pod_union_array.value()[1] = PodUnion::New();
+
+  small_struct->pod_union_array.value()[0]->set_f_int8(10);
+  small_struct->pod_union_array.value()[1]->set_f_int16(12);
+
+  EXPECT_EQ(10, small_struct->pod_union_array.value()[0]->get_f_int8());
+  EXPECT_EQ(12, small_struct->pod_union_array.value()[1]->get_f_int16());
+}
+
+TEST(UnionTest, PodUnionInArraySerialization) {
+  Array<PodUnionPtr> array(2);
+  array[0] = PodUnion::New();
+  array[1] = PodUnion::New();
+
+  array[0]->set_f_int8(10);
+  array[1]->set_f_int16(12);
+  EXPECT_EQ(2U, array.size());
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<Array<PodUnionPtr>>(array, nullptr);
+  EXPECT_EQ(40U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  mojo::internal::Array_Data<internal::PodUnion_Data>* data;
+  mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
+  mojo::internal::Serialize<Array<PodUnionPtr>>(array, &buf, &data,
+                                                &validate_params, nullptr);
+
+  Array<PodUnionPtr> array2;
+  mojo::internal::Deserialize<Array<PodUnionPtr>>(data, &array2, nullptr);
+
+  EXPECT_EQ(2U, array2.size());
+
+  EXPECT_EQ(10, array2[0]->get_f_int8());
+  EXPECT_EQ(12, array2[1]->get_f_int16());
+}
+
+TEST(UnionTest, PodUnionInArraySerializationWithNull) {
+  Array<PodUnionPtr> array(2);
+  array[0] = PodUnion::New();
+
+  array[0]->set_f_int8(10);
+  EXPECT_EQ(2U, array.size());
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<Array<PodUnionPtr>>(array, nullptr);
+  EXPECT_EQ(40U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  mojo::internal::Array_Data<internal::PodUnion_Data>* data;
+  mojo::internal::ContainerValidateParams validate_params(0, true, nullptr);
+  mojo::internal::Serialize<Array<PodUnionPtr>>(array, &buf, &data,
+                                                &validate_params, nullptr);
+
+  Array<PodUnionPtr> array2;
+  mojo::internal::Deserialize<Array<PodUnionPtr>>(data, &array2, nullptr);
+
+  EXPECT_EQ(2U, array2.size());
+
+  EXPECT_EQ(10, array2[0]->get_f_int8());
+  EXPECT_TRUE(array2[1].is_null());
+}
+
+TEST(UnionTest, ObjectUnionInArraySerialization) {
+  Array<ObjectUnionPtr> array(2);
+  array[0] = ObjectUnion::New();
+  array[1] = ObjectUnion::New();
+
+  array[0]->set_f_string("hello");
+  array[1]->set_f_string("world");
+  EXPECT_EQ(2U, array.size());
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<Array<ObjectUnionPtr>>(array, nullptr);
+  EXPECT_EQ(72U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+
+  mojo::internal::Array_Data<internal::ObjectUnion_Data>* data;
+  mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
+  mojo::internal::Serialize<Array<ObjectUnionPtr>>(array, &buf, &data,
+                                                   &validate_params, nullptr);
+
+  std::vector<char> new_buf;
+  new_buf.resize(size);
+
+  void* raw_buf = buf.Leak();
+  memcpy(new_buf.data(), raw_buf, size);
+  free(raw_buf);
+
+  data =
+      reinterpret_cast<mojo::internal::Array_Data<internal::ObjectUnion_Data>*>(
+          new_buf.data());
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  ASSERT_TRUE(mojo::internal::Array_Data<internal::ObjectUnion_Data>::Validate(
+      data, &validation_context, &validate_params));
+
+  Array<ObjectUnionPtr> array2;
+  mojo::internal::Deserialize<Array<ObjectUnionPtr>>(data, &array2, nullptr);
+
+  EXPECT_EQ(2U, array2.size());
+
+  EXPECT_EQ(String("hello"), array2[0]->get_f_string());
+  EXPECT_EQ(String("world"), array2[1]->get_f_string());
+}
+
+// TODO(azani): Move back in struct_unittest.cc when possible.
+// Struct tests
+TEST(UnionTest, Clone_Union) {
+  SmallStructPtr small_struct(SmallStruct::New());
+  small_struct->pod_union = PodUnion::New();
+  small_struct->pod_union->set_f_int8(10);
+
+  SmallStructPtr clone = small_struct.Clone();
+  EXPECT_EQ(10, clone->pod_union->get_f_int8());
+}
+
+// Serialization test of a struct with a union of plain old data.
+TEST(UnionTest, Serialization_UnionOfPods) {
+  SmallStructPtr small_struct(SmallStruct::New());
+  small_struct->pod_union = PodUnion::New();
+  small_struct->pod_union->set_f_int32(10);
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct,
+                                                                   &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::SmallStruct_Data* data = nullptr;
+  mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data,
+                                            &context);
+
+  SmallStructPtr deserialized;
+  mojo::internal::Deserialize<SmallStructPtr>(data, &deserialized, &context);
+
+  EXPECT_EQ(10, deserialized->pod_union->get_f_int32());
+}
+
+// Serialization test of a struct with a union of structs.
+TEST(UnionTest, Serialization_UnionOfObjects) {
+  SmallObjStructPtr obj_struct(SmallObjStruct::New());
+  obj_struct->obj_union = ObjectUnion::New();
+  String hello("hello world");
+  obj_struct->obj_union->set_f_string(hello);
+
+  size_t size = mojo::internal::PrepareToSerialize<SmallObjStructPtr>(
+      obj_struct, nullptr);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::SmallObjStruct_Data* data = nullptr;
+  mojo::internal::Serialize<SmallObjStructPtr>(obj_struct, &buf, &data,
+                                               nullptr);
+
+  SmallObjStructPtr deserialized;
+  mojo::internal::Deserialize<SmallObjStructPtr>(data, &deserialized, nullptr);
+
+  EXPECT_EQ(hello, deserialized->obj_union->get_f_string());
+}
+
+// Validation test of a struct with a union.
+TEST(UnionTest, Validation_UnionsInStruct) {
+  SmallStructPtr small_struct(SmallStruct::New());
+  small_struct->pod_union = PodUnion::New();
+  small_struct->pod_union->set_f_int32(10);
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct,
+                                                                   &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::SmallStruct_Data* data = nullptr;
+  mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data,
+                                            &context);
+
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_TRUE(internal::SmallStruct_Data::Validate(
+      raw_buf, &validation_context));
+  free(raw_buf);
+}
+
+// Validation test of a struct union fails due to unknown union tag.
+TEST(UnionTest, Validation_PodUnionInStruct_Failure) {
+  SmallStructPtr small_struct(SmallStruct::New());
+  small_struct->pod_union = PodUnion::New();
+  small_struct->pod_union->set_f_int32(10);
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct,
+                                                                   &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::SmallStruct_Data* data = nullptr;
+  mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data,
+                                            &context);
+  data->pod_union.tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(100);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_FALSE(internal::SmallStruct_Data::Validate(
+      raw_buf, &validation_context));
+  free(raw_buf);
+}
+
+// Validation fails due to non-nullable null union in struct.
+TEST(UnionTest, Validation_NullUnion_Failure) {
+  SmallStructNonNullableUnionPtr small_struct(
+      SmallStructNonNullableUnion::New());
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<SmallStructNonNullableUnionPtr>(
+          small_struct, nullptr);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::SmallStructNonNullableUnion_Data* data =
+      internal::SmallStructNonNullableUnion_Data::New(&buf);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_FALSE(internal::SmallStructNonNullableUnion_Data::Validate(
+      raw_buf, &validation_context));
+  free(raw_buf);
+}
+
+// Validation passes with nullable null union.
+TEST(UnionTest, Validation_NullableUnion) {
+  SmallStructPtr small_struct(SmallStruct::New());
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct,
+                                                                   &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::SmallStruct_Data* data = nullptr;
+  mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data,
+                                            &context);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_TRUE(internal::SmallStruct_Data::Validate(
+      raw_buf, &validation_context));
+  free(raw_buf);
+}
+
+// TODO(azani): Move back in map_unittest.cc when possible.
+// Map Tests
+TEST(UnionTest, PodUnionInMap) {
+  SmallStructPtr small_struct(SmallStruct::New());
+  small_struct->pod_union_map.emplace();
+  small_struct->pod_union_map.value()["one"] = PodUnion::New();
+  small_struct->pod_union_map.value()["two"] = PodUnion::New();
+
+  small_struct->pod_union_map.value()["one"]->set_f_int8(8);
+  small_struct->pod_union_map.value()["two"]->set_f_int16(16);
+
+  EXPECT_EQ(8, small_struct->pod_union_map.value()["one"]->get_f_int8());
+  EXPECT_EQ(16, small_struct->pod_union_map.value()["two"]->get_f_int16());
+}
+
+TEST(UnionTest, PodUnionInMapSerialization) {
+  Map<String, PodUnionPtr> map;
+  map.insert("one", PodUnion::New());
+  map.insert("two", PodUnion::New());
+
+  map["one"]->set_f_int8(8);
+  map["two"]->set_f_int16(16);
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<Map<String, PodUnionPtr>>(
+      map, &context);
+  EXPECT_EQ(120U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  typename mojo::internal::MojomTypeTraits<Map<String, PodUnionPtr>>::Data*
+      data;
+  mojo::internal::ContainerValidateParams validate_params(
+      new mojo::internal::ContainerValidateParams(0, false, nullptr),
+      new mojo::internal::ContainerValidateParams(0, false, nullptr));
+  mojo::internal::Serialize<Map<String, PodUnionPtr>>(
+      map, &buf, &data, &validate_params, &context);
+
+  Map<String, PodUnionPtr> map2;
+  mojo::internal::Deserialize<Map<String, PodUnionPtr>>(data, &map2, &context);
+
+  EXPECT_EQ(8, map2["one"]->get_f_int8());
+  EXPECT_EQ(16, map2["two"]->get_f_int16());
+}
+
+TEST(UnionTest, PodUnionInMapSerializationWithNull) {
+  Map<String, PodUnionPtr> map;
+  map.insert("one", PodUnion::New());
+  map.insert("two", nullptr);
+
+  map["one"]->set_f_int8(8);
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<Map<String, PodUnionPtr>>(
+      map, &context);
+  EXPECT_EQ(120U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  typename mojo::internal::MojomTypeTraits<Map<String, PodUnionPtr>>::Data*
+      data;
+  mojo::internal::ContainerValidateParams validate_params(
+      new mojo::internal::ContainerValidateParams(0, false, nullptr),
+      new mojo::internal::ContainerValidateParams(0, true, nullptr));
+  mojo::internal::Serialize<Map<String, PodUnionPtr>>(
+      map, &buf, &data, &validate_params, &context);
+
+  Map<String, PodUnionPtr> map2;
+  mojo::internal::Deserialize<Map<String, PodUnionPtr>>(data, &map2, &context);
+
+  EXPECT_EQ(8, map2["one"]->get_f_int8());
+  EXPECT_TRUE(map2["two"].is_null());
+}
+
+TEST(UnionTest, StructInUnionGetterSetterPasser) {
+  DummyStructPtr dummy(DummyStruct::New());
+  dummy->f_int8 = 8;
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_dummy(std::move(dummy));
+
+  EXPECT_EQ(8, obj->get_f_dummy()->f_int8);
+}
+
+TEST(UnionTest, StructInUnionSerialization) {
+  DummyStructPtr dummy(DummyStruct::New());
+  dummy->f_int8 = 8;
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_dummy(std::move(dummy));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+  EXPECT_EQ(32U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  ObjectUnionPtr obj2;
+  mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr);
+  EXPECT_EQ(8, obj2->get_f_dummy()->f_int8);
+}
+
+TEST(UnionTest, StructInUnionValidation) {
+  DummyStructPtr dummy(DummyStruct::New());
+  dummy->f_int8 = 8;
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_dummy(std::move(dummy));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, StructInUnionValidationNonNullable) {
+  mojo::internal::SerializationWarningObserverForTesting suppress_warning;
+
+  DummyStructPtr dummy(nullptr);
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_dummy(std::move(dummy));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, StructInUnionValidationNullable) {
+  DummyStructPtr dummy(nullptr);
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_nullable(std::move(dummy));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, ArrayInUnionGetterSetter) {
+  Array<int8_t> array(2);
+  array[0] = 8;
+  array[1] = 9;
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_array_int8(std::move(array));
+
+  EXPECT_EQ(8, obj->get_f_array_int8()[0]);
+  EXPECT_EQ(9, obj->get_f_array_int8()[1]);
+}
+
+TEST(UnionTest, ArrayInUnionSerialization) {
+  Array<int8_t> array(2);
+  array[0] = 8;
+  array[1] = 9;
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_array_int8(std::move(array));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+  EXPECT_EQ(32U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  ObjectUnionPtr obj2;
+  mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr);
+
+  EXPECT_EQ(8, obj2->get_f_array_int8()[0]);
+  EXPECT_EQ(9, obj2->get_f_array_int8()[1]);
+}
+
+TEST(UnionTest, ArrayInUnionValidation) {
+  Array<int8_t> array(2);
+  array[0] = 8;
+  array[1] = 9;
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_array_int8(std::move(array));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+
+  EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, MapInUnionGetterSetter) {
+  std::unordered_map<std::string, int8_t> map;
+  map.insert({"one", 1});
+  map.insert({"two", 2});
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_map_int8(std::move(map));
+
+  EXPECT_EQ(1, obj->get_f_map_int8()["one"]);
+  EXPECT_EQ(2, obj->get_f_map_int8()["two"]);
+}
+
+TEST(UnionTest, MapInUnionSerialization) {
+  std::unordered_map<std::string, int8_t> map;
+  map.insert({"one", 1});
+  map.insert({"two", 2});
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_map_int8(std::move(map));
+
+  mojo::internal::SerializationContext context;
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, &context);
+  EXPECT_EQ(112U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, &context);
+
+  ObjectUnionPtr obj2;
+  mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, &context);
+
+  EXPECT_EQ(1, obj2->get_f_map_int8()["one"]);
+  EXPECT_EQ(2, obj2->get_f_map_int8()["two"]);
+}
+
+TEST(UnionTest, MapInUnionValidation) {
+  std::unordered_map<std::string, int8_t> map;
+  map.insert({"one", 1});
+  map.insert({"two", 2});
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_map_int8(std::move(map));
+
+  mojo::internal::SerializationContext context;
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, &context);
+  EXPECT_EQ(112U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, &context);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+
+  EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, UnionInUnionGetterSetter) {
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_int8(10);
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_pod_union(std::move(pod));
+
+  EXPECT_EQ(10, obj->get_f_pod_union()->get_f_int8());
+}
+
+TEST(UnionTest, UnionInUnionSerialization) {
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_int8(10);
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_pod_union(std::move(pod));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+  EXPECT_EQ(32U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  ObjectUnionPtr obj2;
+  mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr);
+  EXPECT_EQ(10, obj2->get_f_pod_union()->get_f_int8());
+}
+
+TEST(UnionTest, UnionInUnionValidation) {
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_int8(10);
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_pod_union(std::move(pod));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+  EXPECT_EQ(32U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, UnionInUnionValidationNonNullable) {
+  mojo::internal::SerializationWarningObserverForTesting suppress_warning;
+
+  PodUnionPtr pod(nullptr);
+
+  ObjectUnionPtr obj(ObjectUnion::New());
+  obj->set_f_pod_union(std::move(pod));
+
+  size_t size =
+      mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::ObjectUnion_Data* data = nullptr;
+  mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 0);
+  EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, HandleInUnionGetterSetter) {
+  ScopedMessagePipeHandle pipe0;
+  ScopedMessagePipeHandle pipe1;
+
+  CreateMessagePipe(nullptr, &pipe0, &pipe1);
+
+  HandleUnionPtr handle(HandleUnion::New());
+  handle->set_f_message_pipe(std::move(pipe1));
+
+  std::string golden("hello world");
+  WriteTextMessage(pipe0.get(), golden);
+
+  std::string actual;
+  ReadTextMessage(handle->get_f_message_pipe().get(), &actual);
+
+  EXPECT_EQ(golden, actual);
+}
+
+TEST(UnionTest, HandleInUnionSerialization) {
+  ScopedMessagePipeHandle pipe0;
+  ScopedMessagePipeHandle pipe1;
+
+  CreateMessagePipe(nullptr, &pipe0, &pipe1);
+
+  HandleUnionPtr handle(HandleUnion::New());
+  handle->set_f_message_pipe(std::move(pipe1));
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>(
+      handle, false, &context);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::HandleUnion_Data* data = nullptr;
+  mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false,
+                                            &context);
+  EXPECT_EQ(1U, context.handles.size());
+
+  HandleUnionPtr handle2(HandleUnion::New());
+  mojo::internal::Deserialize<HandleUnionPtr>(data, &handle2, &context);
+
+  std::string golden("hello world");
+  WriteTextMessage(pipe0.get(), golden);
+
+  std::string actual;
+  ReadTextMessage(handle2->get_f_message_pipe().get(), &actual);
+
+  EXPECT_EQ(golden, actual);
+}
+
+TEST(UnionTest, HandleInUnionValidation) {
+  ScopedMessagePipeHandle pipe0;
+  ScopedMessagePipeHandle pipe1;
+
+  CreateMessagePipe(nullptr, &pipe0, &pipe1);
+
+  HandleUnionPtr handle(HandleUnion::New());
+  handle->set_f_message_pipe(std::move(pipe1));
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>(
+      handle, false, &context);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::HandleUnion_Data* data = nullptr;
+  mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false,
+                                            &context);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 1);
+  EXPECT_TRUE(internal::HandleUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+TEST(UnionTest, HandleInUnionValidationNull) {
+  mojo::internal::SerializationWarningObserverForTesting suppress_warning;
+
+  ScopedMessagePipeHandle pipe;
+  HandleUnionPtr handle(HandleUnion::New());
+  handle->set_f_message_pipe(std::move(pipe));
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>(
+      handle, false, &context);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::HandleUnion_Data* data = nullptr;
+  mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false,
+                                            &context);
+
+  void* raw_buf = buf.Leak();
+  mojo::internal::ValidationContext validation_context(
+      data, static_cast<uint32_t>(size), 1);
+  EXPECT_FALSE(internal::HandleUnion_Data::Validate(
+      raw_buf, &validation_context, false));
+  free(raw_buf);
+}
+
+class SmallCacheImpl : public SmallCache {
+ public:
+  explicit SmallCacheImpl(const base::Closure& closure)
+      : int_value_(0), closure_(closure) {}
+  ~SmallCacheImpl() override {}
+  int64_t int_value() const { return int_value_; }
+
+ private:
+  void SetIntValue(int64_t int_value) override {
+    int_value_ = int_value;
+    closure_.Run();
+  }
+  void GetIntValue(const GetIntValueCallback& callback) override {
+    callback.Run(int_value_);
+  }
+
+  int64_t int_value_;
+  base::Closure closure_;
+};
+
+TEST(UnionTest, InterfaceInUnion) {
+  base::MessageLoop message_loop;
+  base::RunLoop run_loop;
+  SmallCacheImpl impl(run_loop.QuitClosure());
+  SmallCachePtr ptr;
+  Binding<SmallCache> bindings(&impl, GetProxy(&ptr));
+
+  HandleUnionPtr handle(HandleUnion::New());
+  handle->set_f_small_cache(std::move(ptr));
+
+  handle->get_f_small_cache()->SetIntValue(10);
+  run_loop.Run();
+  EXPECT_EQ(10, impl.int_value());
+}
+
+TEST(UnionTest, InterfaceInUnionSerialization) {
+  base::MessageLoop message_loop;
+  base::RunLoop run_loop;
+  SmallCacheImpl impl(run_loop.QuitClosure());
+  SmallCachePtr ptr;
+  Binding<SmallCache> bindings(&impl, GetProxy(&ptr));
+
+  mojo::internal::SerializationContext context;
+  HandleUnionPtr handle(HandleUnion::New());
+  handle->set_f_small_cache(std::move(ptr));
+  size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>(
+      handle, false, &context);
+  EXPECT_EQ(16U, size);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  internal::HandleUnion_Data* data = nullptr;
+  mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false,
+                                            &context);
+  EXPECT_EQ(1U, context.handles.size());
+
+  HandleUnionPtr handle2(HandleUnion::New());
+  mojo::internal::Deserialize<HandleUnionPtr>(data, &handle2, &context);
+
+  handle2->get_f_small_cache()->SetIntValue(10);
+  run_loop.Run();
+  EXPECT_EQ(10, impl.int_value());
+}
+
+class UnionInterfaceImpl : public UnionInterface {
+ public:
+  UnionInterfaceImpl() {}
+  ~UnionInterfaceImpl() override {}
+
+ private:
+  void Echo(PodUnionPtr in, const EchoCallback& callback) override {
+    callback.Run(std::move(in));
+  }
+};
+
+void ExpectInt16(int16_t value, PodUnionPtr out) {
+  EXPECT_EQ(value, out->get_f_int16());
+}
+
+TEST(UnionTest, UnionInInterface) {
+  base::MessageLoop message_loop;
+  UnionInterfaceImpl impl;
+  UnionInterfacePtr ptr;
+  Binding<UnionInterface> bindings(&impl, GetProxy(&ptr));
+
+  PodUnionPtr pod(PodUnion::New());
+  pod->set_f_int16(16);
+
+  ptr->Echo(std::move(pod), base::Bind(&ExpectInt16, 16));
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/validation_context_unittest.cc b/mojo/public/cpp/bindings/tests/validation_context_unittest.cc
new file mode 100644
index 0000000..aceaf1c
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_context_unittest.cc
@@ -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.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using Handle_Data = mojo::internal::Handle_Data;
+
+const void* ToPtr(uintptr_t ptr) {
+  return reinterpret_cast<const void*>(ptr);
+}
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+TEST(ValidationContextTest, ConstructorRangeOverflow) {
+  {
+    // Test memory range overflow.
+    internal::ValidationContext context(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 5000, 0);
+
+    EXPECT_FALSE(context.IsValidRange(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1));
+    EXPECT_FALSE(context.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::ValidationContext context(ToPtr(0), 0, num_handles);
+
+    EXPECT_FALSE(context.ClaimHandle(Handle_Data(0)));
+    EXPECT_FALSE(context.ClaimHandle(
+        Handle_Data(std::numeric_limits<uint32_t>::max() - 1)));
+
+    EXPECT_TRUE(context.ClaimHandle(
+        Handle_Data(internal::kEncodedInvalidHandleValue)));
+  }
+}
+#endif
+
+TEST(ValidationContextTest, IsValidRange) {
+  {
+    internal::ValidationContext context(ToPtr(1234), 100, 0);
+
+    // Basics.
+    EXPECT_FALSE(context.IsValidRange(ToPtr(100), 5));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1230), 50));
+    EXPECT_TRUE(context.IsValidRange(ToPtr(1234), 5));
+    EXPECT_TRUE(context.IsValidRange(ToPtr(1240), 50));
+    EXPECT_TRUE(context.IsValidRange(ToPtr(1234), 100));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 101));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1240), 100));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1333), 5));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(2234), 5));
+
+    // ClaimMemory() updates the valid range.
+    EXPECT_TRUE(context.ClaimMemory(ToPtr(1254), 10));
+
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 1));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1254), 10));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1263), 1));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1263), 10));
+    EXPECT_TRUE(context.IsValidRange(ToPtr(1264), 10));
+    EXPECT_TRUE(context.IsValidRange(ToPtr(1264), 70));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1264), 71));
+  }
+
+  {
+    internal::ValidationContext context(ToPtr(1234), 100, 0);
+    // Should return false for empty ranges.
+    EXPECT_FALSE(context.IsValidRange(ToPtr(0), 0));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1200), 0));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 0));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1240), 0));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(2234), 0));
+  }
+
+  {
+    // The valid memory range is empty.
+    internal::ValidationContext context(ToPtr(1234), 0, 0);
+
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 1));
+    EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 0));
+  }
+
+  {
+    internal::ValidationContext context(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 2000), 1000, 0);
+
+    // Test overflow.
+    EXPECT_FALSE(context.IsValidRange(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), 4000));
+    EXPECT_FALSE(context.IsValidRange(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 1500),
+        std::numeric_limits<uint32_t>::max()));
+
+    // This should be fine.
+    EXPECT_TRUE(context.IsValidRange(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), 200));
+  }
+}
+
+TEST(ValidationContextTest, ClaimHandle) {
+  {
+    internal::ValidationContext context(ToPtr(0), 0, 10);
+
+    // Basics.
+    EXPECT_TRUE(context.ClaimHandle(Handle_Data(0)));
+    EXPECT_FALSE(context.ClaimHandle(Handle_Data(0)));
+
+    EXPECT_TRUE(context.ClaimHandle(Handle_Data(9)));
+    EXPECT_FALSE(context.ClaimHandle(Handle_Data(10)));
+
+    // Should fail because it is smaller than the max index that has been
+    // claimed.
+    EXPECT_FALSE(context.ClaimHandle(Handle_Data(8)));
+
+    // Should return true for invalid handle.
+    EXPECT_TRUE(context.ClaimHandle(
+        Handle_Data(internal::kEncodedInvalidHandleValue)));
+    EXPECT_TRUE(context.ClaimHandle(
+        Handle_Data(internal::kEncodedInvalidHandleValue)));
+  }
+
+  {
+    // No handle to claim.
+    internal::ValidationContext context(ToPtr(0), 0, 0);
+
+    EXPECT_FALSE(context.ClaimHandle(Handle_Data(0)));
+
+    // Should still return true for invalid handle.
+    EXPECT_TRUE(context.ClaimHandle(
+        Handle_Data(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::ValidationContext context(
+        ToPtr(0), 0, std::numeric_limits<uint32_t>::max());
+
+    EXPECT_TRUE(context.ClaimHandle(
+        Handle_Data(std::numeric_limits<uint32_t>::max() - 1)));
+    EXPECT_FALSE(context.ClaimHandle(
+        Handle_Data(std::numeric_limits<uint32_t>::max() - 1)));
+    EXPECT_FALSE(context.ClaimHandle(Handle_Data(0)));
+
+    // Should still return true for invalid handle.
+    EXPECT_TRUE(context.ClaimHandle(
+        Handle_Data(internal::kEncodedInvalidHandleValue)));
+  }
+}
+
+TEST(ValidationContextTest, ClaimMemory) {
+  {
+    internal::ValidationContext context(ToPtr(1000), 2000, 0);
+
+    // Basics.
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(500), 100));
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(800), 300));
+    EXPECT_TRUE(context.ClaimMemory(ToPtr(1000), 100));
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(1099), 100));
+    EXPECT_TRUE(context.ClaimMemory(ToPtr(1100), 200));
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(2000), 1001));
+    EXPECT_TRUE(context.ClaimMemory(ToPtr(2000), 500));
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(2000), 500));
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(1400), 100));
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(3000), 1));
+    EXPECT_TRUE(context.ClaimMemory(ToPtr(2500), 500));
+  }
+
+  {
+    // No memory to claim.
+    internal::ValidationContext context(ToPtr(10000), 0, 0);
+
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(10000), 1));
+    EXPECT_FALSE(context.ClaimMemory(ToPtr(10000), 0));
+  }
+
+  {
+    internal::ValidationContext context(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 1000), 500, 0);
+
+    // Test overflow.
+    EXPECT_FALSE(context.ClaimMemory(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 750), 4000));
+    EXPECT_FALSE(
+        context.ClaimMemory(ToPtr(std::numeric_limits<uintptr_t>::max() - 750),
+                            std::numeric_limits<uint32_t>::max()));
+
+    // This should be fine.
+    EXPECT_TRUE(context.ClaimMemory(
+        ToPtr(std::numeric_limits<uintptr_t>::max() - 750), 200));
+  }
+}
+
+}  // 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..f309737
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
@@ -0,0 +1,412 @@
+// Copyright 2014 The Chromium Authors. All 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 <stddef.h>
+#include <stdint.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) {
+  static_assert(sizeof(float) == 4, "sizeof(float) 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) {
+  static_assert(sizeof(double) == 8, "sizeof(double) 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..d08f359
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_
+
+#include <stddef.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_t a;
+//     bool b;
+//     bool c;
+//   };
+//   struct Foo {
+//     Bar x;
+//     uint32_t y;
+//   };
+//
+// The following describes a valid message whose payload is a Foo struct:
+//   // message header
+//   [dist4]message_header   // num_bytes
+//   [u4]3                   // version
+//   [u4]0                   // type
+//   [u4]1                   // flags
+//   [u8]1234                // request_id
+//   [anchr]message_header
+//
+//   // payload
+//   [dist4]foo      // num_bytes
+//   [u4]2           // version
+//   [dist8]bar_ptr  // x
+//   [u4]0xABCD      // y
+//   [u4]0           // padding
+//   [anchr]foo
+//
+//   [anchr]bar_ptr
+//   [dist4]bar   // num_bytes
+//   [u4]3        // version
+//   [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..d254383
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -0,0 +1,499 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/connector.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.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/message_header_validator.h"
+#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/interfaces/bindings/tests/validation_test_associated_interfaces.mojom.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->Initialize(static_cast<uint32_t>(data.size()),
+                      false /* zero_initialized */);
+  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);
+  ASSERT_FALSE(tests.empty());
+
+  for (size_t i = 0; i < tests.size(); ++i) {
+    Message message;
+    std::string expected;
+    ASSERT_TRUE(ReadTestCase(tests[i], &message, &expected));
+
+    std::string result;
+    base::RunLoop run_loop;
+    mojo::internal::ValidationErrorObserverForTesting observer(
+        run_loop.QuitClosure());
+    ignore_result(test_message_receiver->Accept(&message));
+    if (expected != "PASS")  // Observer only gets called on errors.
+      run_loop.Run();
+    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:
+  bool Accept(Message* message) override {
+    return true;  // Any message is OK.
+  }
+};
+
+class ValidationTest : public testing::Test {
+ public:
+  ValidationTest() {}
+
+ protected:
+  base::MessageLoop loop_;
+};
+
+class ValidationIntegrationTest : public ValidationTest {
+ public:
+  ValidationIntegrationTest() : test_message_receiver_(nullptr) {}
+
+  ~ValidationIntegrationTest() override {}
+
+  void SetUp() override {
+    ScopedMessagePipeHandle tester_endpoint;
+    ASSERT_EQ(MOJO_RESULT_OK,
+              CreateMessagePipe(nullptr, &tester_endpoint, &testee_endpoint_));
+    test_message_receiver_ =
+        new TestMessageReceiver(this, std::move(tester_endpoint));
+  }
+
+  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 std::move(testee_endpoint_);
+  }
+
+ private:
+  class TestMessageReceiver : public MessageReceiver {
+   public:
+    TestMessageReceiver(ValidationIntegrationTest* owner,
+                        ScopedMessagePipeHandle handle)
+        : owner_(owner),
+          connector_(std::move(handle),
+                     mojo::Connector::SINGLE_THREADED_SEND,
+                     base::ThreadTaskRunnerHandle::Get()) {
+      connector_.set_enforce_errors_from_incoming_receiver(false);
+    }
+    ~TestMessageReceiver() override {}
+
+    bool Accept(Message* message) override {
+      return connector_.Accept(message);
+    }
+
+   public:
+    ValidationIntegrationTest* owner_;
+    mojo::Connector connector_;
+  };
+
+  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
+
+  TestMessageReceiver* test_message_receiver_;
+  ScopedMessagePipeHandle testee_endpoint_;
+};
+
+class IntegrationTestInterfaceImpl : public IntegrationTestInterface {
+ public:
+  ~IntegrationTestInterfaceImpl() override {}
+
+  void Method0(BasicStructPtr param0,
+               const Method0Callback& callback) override {
+    callback.Run(Array<uint8_t>::New(0u));
+  }
+};
+
+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::MessageHeaderValidator>();
+  validators.Append<ConformanceTestInterface::RequestValidator_>();
+
+  RunValidationTests("conformance_", validators.GetHead());
+}
+
+TEST_F(ValidationTest, AssociatedConformace) {
+  DummyMessageReceiver dummy_receiver;
+  mojo::internal::FilterChain validators(&dummy_receiver);
+  validators.Append<mojo::MessageHeaderValidator>();
+  validators.Append<AssociatedConformanceTestInterface::RequestValidator_>();
+
+  RunValidationTests("associated_conformance_", validators.GetHead());
+}
+
+// This test is similar to Conformance test but its goal is specifically
+// do bounds-check testing of message validation. For example we test the
+// detection of off-by-one errors in method ordinals.
+TEST_F(ValidationTest, BoundsCheck) {
+  DummyMessageReceiver dummy_receiver;
+  mojo::internal::FilterChain validators(&dummy_receiver);
+  validators.Append<mojo::MessageHeaderValidator>();
+  validators.Append<BoundsCheckTestInterface::RequestValidator_>();
+
+  RunValidationTests("boundscheck_", validators.GetHead());
+}
+
+// This test is similar to the Conformance test but for responses.
+TEST_F(ValidationTest, ResponseConformance) {
+  DummyMessageReceiver dummy_receiver;
+  mojo::internal::FilterChain validators(&dummy_receiver);
+  validators.Append<mojo::MessageHeaderValidator>();
+  validators.Append<ConformanceTestInterface::ResponseValidator_>();
+
+  RunValidationTests("resp_conformance_", validators.GetHead());
+}
+
+// This test is similar to the BoundsCheck test but for responses.
+TEST_F(ValidationTest, ResponseBoundsCheck) {
+  DummyMessageReceiver dummy_receiver;
+  mojo::internal::FilterChain validators(&dummy_receiver);
+  validators.Append<mojo::MessageHeaderValidator>();
+  validators.Append<BoundsCheckTestInterface::ResponseValidator_>();
+
+  RunValidationTests("resp_boundscheck_", validators.GetHead());
+}
+
+// Test that InterfacePtr<X> applies the correct validators and they don't
+// conflict with each other:
+//   - MessageHeaderValidator
+//   - X::ResponseValidator_
+TEST_F(ValidationIntegrationTest, InterfacePtr) {
+  IntegrationTestInterfacePtr interface_ptr = MakeProxy(
+      InterfacePtrInfo<IntegrationTestInterface>(testee_endpoint(), 0u));
+  interface_ptr.internal_state()->EnableTestingMode();
+
+  RunValidationTests("integration_intf_resp", test_message_receiver());
+  RunValidationTests("integration_msghdr", test_message_receiver());
+}
+
+// Test that Binding<X> applies the correct validators and they don't
+// conflict with each other:
+//   - MessageHeaderValidator
+//   - X::RequestValidator_
+TEST_F(ValidationIntegrationTest, Binding) {
+  IntegrationTestInterfaceImpl interface_impl;
+  Binding<IntegrationTestInterface> binding(
+      &interface_impl,
+      MakeRequest<IntegrationTestInterface>(testee_endpoint()));
+  binding.EnableTestingMode();
+
+  RunValidationTests("integration_intf_rqst", test_message_receiver());
+  RunValidationTests("integration_msghdr", test_message_receiver());
+}
+
+// Test pointer validation (specifically, that the encoded offset is 32-bit)
+TEST_F(ValidationTest, ValidateEncodedPointer) {
+  uint64_t offset;
+
+  offset = 0ULL;
+  EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset));
+
+  offset = 1ULL;
+  EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset));
+
+  // offset must be <= 32-bit.
+  offset = std::numeric_limits<uint32_t>::max() + 1ULL;
+  EXPECT_FALSE(mojo::internal::ValidateEncodedPointer(&offset));
+}
+
+// Tests the IsKnownEnumValue() function generated for BasicEnum.
+TEST(EnumValueValidationTest, BasicEnum) {
+  // BasicEnum can have -3,0,1,10 as possible integral values.
+  EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-4)));
+  EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(-3)));
+  EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-2)));
+  EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-1)));
+  EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(0)));
+  EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(1)));
+  EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(2)));
+  EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(9)));
+  // In the mojom, we represent this value as hex (0xa).
+  EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(10)));
+  EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(11)));
+}
+
+// Tests the IsKnownEnumValue() method generated for StructWithEnum.
+TEST(EnumValueValidationTest, EnumWithin) {
+  // StructWithEnum::EnumWithin can have [0,4] as possible integral values.
+  EXPECT_FALSE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(-1)));
+  EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(0)));
+  EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(1)));
+  EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(2)));
+  EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(3)));
+  EXPECT_FALSE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(4)));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/variant_test_util.h b/mojo/public/cpp/bindings/tests/variant_test_util.h
new file mode 100644
index 0000000..3f6b1f1
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/variant_test_util.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All 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_VARIANT_TEST_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_VARIANT_TEST_UTIL_H_
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace mojo {
+namespace test {
+
+// Converts a request of Interface1 to a request of Interface0. Interface0 and
+// Interface1 are expected to be two variants of the same mojom interface.
+// In real-world use cases, users shouldn't need to worry about this. Because it
+// is rare to deal with two variants of the same interface in the same app.
+template <typename Interface0, typename Interface1>
+InterfaceRequest<Interface0> ConvertInterfaceRequest(
+    InterfaceRequest<Interface1> request) {
+  DCHECK_EQ(0, strcmp(Interface0::Name_, Interface1::Name_));
+  InterfaceRequest<Interface0> result;
+  result.Bind(request.PassMessagePipe());
+  return result;
+}
+
+}  // namespace test
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_TESTS_VARIANT_TEST_UTIL_H_
diff --git a/mojo/public/cpp/bindings/tests/versioning_apptest.cc b/mojo/public/cpp/bindings/tests/versioning_apptest.cc
new file mode 100644
index 0000000..3a08cdd
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/versioning_apptest.cc
@@ -0,0 +1,123 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "mojo/public/interfaces/bindings/tests/versioning_test_client.mojom.h"
+#include "services/shell/public/cpp/application_test_base.h"
+#include "services/shell/public/cpp/connector.h"
+
+namespace mojo {
+namespace test {
+namespace versioning {
+
+class VersioningApplicationTest : public ApplicationTestBase {
+ public:
+  VersioningApplicationTest() : ApplicationTestBase() {}
+  ~VersioningApplicationTest() override {}
+
+ protected:
+  // ApplicationTestBase overrides.
+  void SetUp() override {
+    ApplicationTestBase::SetUp();
+
+    connector()->ConnectToInterface("mojo:versioning_test_service", &database_);
+  }
+
+  HumanResourceDatabasePtr database_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(VersioningApplicationTest);
+};
+
+TEST_F(VersioningApplicationTest, Struct) {
+  // The service side uses a newer version of Employee defintion.
+  // The returned struct should be truncated.
+  EmployeePtr employee(Employee::New());
+  employee->employee_id = 1;
+  employee->name = "Homer Simpson";
+  employee->department = DEPARTMENT_DEV;
+
+  database_->QueryEmployee(1, true,
+                           [&employee](EmployeePtr returned_employee,
+                                       Array<uint8_t> returned_finger_print) {
+                             EXPECT_TRUE(employee->Equals(*returned_employee));
+                             EXPECT_FALSE(returned_finger_print.is_null());
+                           });
+  database_.WaitForIncomingResponse();
+
+  // Passing a struct of older version to the service side works.
+  EmployeePtr new_employee(Employee::New());
+  new_employee->employee_id = 2;
+  new_employee->name = "Marge Simpson";
+  new_employee->department = DEPARTMENT_SALES;
+
+  database_->AddEmployee(new_employee.Clone(),
+                         [](bool success) { EXPECT_TRUE(success); });
+  database_.WaitForIncomingResponse();
+
+  database_->QueryEmployee(
+      2, false, [&new_employee](EmployeePtr returned_employee,
+                                Array<uint8_t> returned_finger_print) {
+        EXPECT_TRUE(new_employee->Equals(*returned_employee));
+        EXPECT_TRUE(returned_finger_print.is_null());
+      });
+  database_.WaitForIncomingResponse();
+}
+
+TEST_F(VersioningApplicationTest, QueryVersion) {
+  EXPECT_EQ(0u, database_.version());
+  database_.QueryVersion([](uint32_t version) { EXPECT_EQ(1u, version); });
+  database_.WaitForIncomingResponse();
+  EXPECT_EQ(1u, database_.version());
+}
+
+TEST_F(VersioningApplicationTest, RequireVersion) {
+  EXPECT_EQ(0u, database_.version());
+
+  database_.RequireVersion(1);
+  EXPECT_EQ(1u, database_.version());
+  database_->QueryEmployee(3, false,
+                           [](EmployeePtr returned_employee,
+                              Array<uint8_t> returned_finger_print) {});
+  database_.WaitForIncomingResponse();
+  EXPECT_FALSE(database_.encountered_error());
+
+  // Requiring a version higher than what the service side implements will close
+  // the pipe.
+  database_.RequireVersion(3);
+  EXPECT_EQ(3u, database_.version());
+  database_->QueryEmployee(1, false,
+                           [](EmployeePtr returned_employee,
+                              Array<uint8_t> returned_finger_print) {});
+  database_.WaitForIncomingResponse();
+  EXPECT_TRUE(database_.encountered_error());
+}
+
+TEST_F(VersioningApplicationTest, CallNonexistentMethod) {
+  EXPECT_EQ(0u, database_.version());
+
+  Array<uint8_t> new_finger_print(128);
+  for (size_t i = 0; i < 128; ++i)
+    new_finger_print[i] = i + 13;
+
+  // Although the client side doesn't know whether the service side supports
+  // version 1, calling a version 1 method succeeds as long as the service side
+  // supports version 1.
+  database_->AttachFingerPrint(1, new_finger_print.Clone(),
+                               [](bool success) { EXPECT_TRUE(success); });
+  database_.WaitForIncomingResponse();
+
+  // Calling a version 2 method (which the service side doesn't support) closes
+  // the pipe.
+  database_->ListEmployeeIds([](Array<uint64_t> ids) { EXPECT_TRUE(false); });
+  database_.WaitForIncomingResponse();
+  EXPECT_TRUE(database_.encountered_error());
+}
+
+}  // namespace versioning
+}  // namespace examples
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/versioning_test_service.cc b/mojo/public/cpp/bindings/tests/versioning_test_service.cc
new file mode 100644
index 0000000..2f554d9
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/versioning_test_service.cc
@@ -0,0 +1,127 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <map>
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/interfaces/bindings/tests/versioning_test_service.mojom.h"
+#include "services/shell/public/cpp/application_runner.h"
+#include "services/shell/public/cpp/interface_factory.h"
+#include "services/shell/public/cpp/service.h"
+
+namespace mojo {
+namespace test {
+namespace versioning {
+
+struct EmployeeInfo {
+ public:
+  EmployeeInfo() {}
+
+  EmployeePtr employee;
+  Array<uint8_t> finger_print;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(EmployeeInfo);
+};
+
+class HumanResourceDatabaseImpl : public HumanResourceDatabase {
+ public:
+  explicit HumanResourceDatabaseImpl(
+      InterfaceRequest<HumanResourceDatabase> request)
+      : strong_binding_(this, std::move(request)) {
+    // Pretend that there is already some data in the system.
+    EmployeeInfo* info = new EmployeeInfo();
+    employees_[1] = info;
+    info->employee = Employee::New();
+    info->employee->employee_id = 1;
+    info->employee->name = "Homer Simpson";
+    info->employee->department = DEPARTMENT_DEV;
+    info->employee->birthday = Date::New();
+    info->employee->birthday->year = 1955;
+    info->employee->birthday->month = 5;
+    info->employee->birthday->day = 12;
+    info->finger_print.resize(1024);
+    for (uint32_t i = 0; i < 1024; ++i)
+      info->finger_print[i] = i;
+  }
+
+  ~HumanResourceDatabaseImpl() override {
+    for (auto iter = employees_.begin(); iter != employees_.end(); ++iter)
+      delete iter->second;
+  }
+
+  void AddEmployee(EmployeePtr employee,
+                   const AddEmployeeCallback& callback) override {
+    uint64_t id = employee->employee_id;
+    if (employees_.find(id) == employees_.end())
+      employees_[id] = new EmployeeInfo();
+    employees_[id]->employee = std::move(employee);
+    callback.Run(true);
+  }
+
+  void QueryEmployee(uint64_t id,
+                     bool retrieve_finger_print,
+                     const QueryEmployeeCallback& callback) override {
+    if (employees_.find(id) == employees_.end()) {
+      callback.Run(nullptr, Array<uint8_t>());
+      return;
+    }
+    callback.Run(employees_[id]->employee.Clone(),
+                 retrieve_finger_print ? employees_[id]->finger_print.Clone()
+                                       : Array<uint8_t>());
+  }
+
+  void AttachFingerPrint(uint64_t id,
+                         Array<uint8_t> finger_print,
+                         const AttachFingerPrintCallback& callback) override {
+    if (employees_.find(id) == employees_.end()) {
+      callback.Run(false);
+      return;
+    }
+    employees_[id]->finger_print = std::move(finger_print);
+    callback.Run(true);
+  }
+
+ private:
+  std::map<uint64_t, EmployeeInfo*> employees_;
+
+  StrongBinding<HumanResourceDatabase> strong_binding_;
+};
+
+class HumanResourceSystemServer
+    : public shell::Service,
+      public InterfaceFactory<HumanResourceDatabase> {
+ public:
+  HumanResourceSystemServer() {}
+
+  // shell::Service implementation.
+  bool OnConnect(Connection* connection) override {
+    connection->AddInterface<HumanResourceDatabase>(this);
+    return true;
+  }
+
+  // InterfaceFactory<HumanResourceDatabase> implementation.
+  void Create(Connection* connection,
+              InterfaceRequest<HumanResourceDatabase> request) override {
+    // It will be deleted automatically when the underlying pipe encounters a
+    // connection error.
+    new HumanResourceDatabaseImpl(std::move(request));
+  }
+};
+
+}  // namespace versioning
+}  // namespace test
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle request) {
+  mojo::ApplicationRunner runner(
+      new mojo::test::versioning::HumanResourceSystemServer());
+
+  return runner.Run(request);
+}
diff --git a/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc
new file mode 100644
index 0000000..bb54b9c
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All 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/wtf_array.h"
+
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
+#include "mojo/public/cpp/bindings/tests/array_common_test.h"
+#include "mojo/public/cpp/bindings/tests/container_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using WTFArrayTest = testing::Test;
+
+ARRAY_COMMON_TEST(WTFArray, NullAndEmpty)
+ARRAY_COMMON_TEST(WTFArray, Basic)
+ARRAY_COMMON_TEST(WTFArray, Bool)
+ARRAY_COMMON_TEST(WTFArray, Handle)
+ARRAY_COMMON_TEST(WTFArray, HandlesAreClosed)
+ARRAY_COMMON_TEST(WTFArray, Clone)
+ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfPOD)
+ARRAY_COMMON_TEST(WTFArray, Serialization_EmptyArrayOfPOD)
+ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfArrayOfPOD)
+ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfBool)
+ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfString)
+ARRAY_COMMON_TEST(WTFArray, Resize_Copyable)
+ARRAY_COMMON_TEST(WTFArray, Resize_MoveOnly)
+
+TEST_F(WTFArrayTest, MoveFromAndToWTFVector_Copyable) {
+  WTF::Vector<CopyableType> vec1(1);
+  WTFArray<CopyableType> arr(std::move(vec1));
+  ASSERT_EQ(1u, arr.size());
+  ASSERT_FALSE(arr[0].copied());
+
+  WTF::Vector<CopyableType> vec2(arr.PassStorage());
+  ASSERT_EQ(1u, vec2.size());
+  ASSERT_FALSE(vec2[0].copied());
+
+  ASSERT_EQ(0u, arr.size());
+  ASSERT_TRUE(arr.is_null());
+}
+
+TEST_F(WTFArrayTest, MoveFromAndToWTFVector_MoveOnly) {
+  WTF::Vector<MoveOnlyType> vec1(1);
+  WTFArray<MoveOnlyType> arr(std::move(vec1));
+
+  ASSERT_EQ(1u, arr.size());
+
+  WTF::Vector<MoveOnlyType> vec2(arr.PassStorage());
+  ASSERT_EQ(1u, vec2.size());
+
+  ASSERT_EQ(0u, arr.size());
+  ASSERT_TRUE(arr.is_null());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc
new file mode 100644
index 0000000..a70c134
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All 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/wtf_map.h"
+
+#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
+#include "mojo/public/cpp/bindings/tests/container_test_util.h"
+#include "mojo/public/cpp/bindings/tests/map_common_test.h"
+#include "mojo/public/cpp/bindings/wtf_array.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/wtf/text/WTFString.h"
+
+namespace mojo {
+namespace test {
+
+namespace {
+
+using WTFMapTest = testing::Test;
+
+MAP_COMMON_TEST(WTFMap, NullAndEmpty)
+MAP_COMMON_TEST(WTFMap, InsertWorks)
+MAP_COMMON_TEST(WTFMap, TestIndexOperator)
+MAP_COMMON_TEST(WTFMap, TestIndexOperatorAsRValue)
+MAP_COMMON_TEST(WTFMap, TestIndexOperatorMoveOnly)
+MAP_COMMON_TEST(WTFMap, MapArrayClone)
+MAP_COMMON_TEST(WTFMap, ArrayOfMap)
+
+TEST_F(WTFMapTest, MoveFromAndToWTFHashMap_Copyable) {
+  WTF::HashMap<int32_t, CopyableType> map1;
+  map1.add(123, CopyableType());
+  map1.find(123)->value.ResetCopied();
+  ASSERT_FALSE(map1.find(123)->value.copied());
+
+  WTFMap<int32_t, CopyableType> mojo_map(std::move(map1));
+  ASSERT_EQ(1u, mojo_map.size());
+  ASSERT_NE(mojo_map.end(), mojo_map.find(123));
+  ASSERT_FALSE(mojo_map[123].copied());
+
+  WTF::HashMap<int32_t, CopyableType> map2(mojo_map.PassStorage());
+  ASSERT_EQ(1u, map2.size());
+  ASSERT_NE(map2.end(), map2.find(123));
+  ASSERT_FALSE(map2.find(123)->value.copied());
+
+  ASSERT_EQ(0u, mojo_map.size());
+  ASSERT_TRUE(mojo_map.is_null());
+}
+
+TEST_F(WTFMapTest, MoveFromAndToWTFHashMap_MoveOnly) {
+  WTF::HashMap<int32_t, MoveOnlyType> map1;
+  map1.add(123, MoveOnlyType());
+
+  WTFMap<int32_t, MoveOnlyType> mojo_map(std::move(map1));
+  ASSERT_EQ(1u, mojo_map.size());
+  ASSERT_NE(mojo_map.end(), mojo_map.find(123));
+
+  WTF::HashMap<int32_t, MoveOnlyType> map2(mojo_map.PassStorage());
+  ASSERT_EQ(1u, map2.size());
+  ASSERT_NE(map2.end(), map2.find(123));
+
+  ASSERT_EQ(0u, mojo_map.size());
+  ASSERT_TRUE(mojo_map.is_null());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
new file mode 100644
index 0000000..97392d4
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
@@ -0,0 +1,322 @@
+// Copyright 2016 The Chromium Authors. 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/run_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
+#include "mojo/public/cpp/bindings/tests/variant_test_util.h"
+#include "mojo/public/interfaces/bindings/tests/test_wtf_types.mojom-blink.h"
+#include "mojo/public/interfaces/bindings/tests/test_wtf_types.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+const char kHelloWorld[] = "hello world";
+
+// Replace the "o"s in "hello world" with "o"s with acute.
+const char kUTF8HelloWorld[] = "hell\xC3\xB3 w\xC3\xB3rld";
+
+class TestWTFImpl : public TestWTF {
+ public:
+  explicit TestWTFImpl(TestWTFRequest request)
+      : binding_(this, std::move(request)) {}
+
+  // mojo::test::TestWTF implementation:
+  void EchoString(const base::Optional<std::string>& str,
+                  const EchoStringCallback& callback) override {
+    callback.Run(str);
+  }
+
+  void EchoStringArray(
+      const base::Optional<std::vector<base::Optional<std::string>>>& arr,
+      const EchoStringArrayCallback& callback) override {
+    callback.Run(std::move(arr));
+  }
+
+  void EchoStringMap(
+      const base::Optional<
+          std::unordered_map<std::string, base::Optional<std::string>>>&
+          str_map,
+      const EchoStringMapCallback& callback) override {
+    callback.Run(std::move(str_map));
+  }
+
+ private:
+  Binding<TestWTF> binding_;
+};
+
+class WTFTypesTest : public testing::Test {
+ public:
+  WTFTypesTest() {}
+
+ private:
+  base::MessageLoop loop_;
+};
+
+WTF::Vector<WTF::String> ConstructStringArray() {
+  WTF::Vector<WTF::String> strs(4);
+  // strs[0] is null.
+  // strs[1] is empty.
+  strs[1] = "";
+  strs[2] = kHelloWorld;
+  strs[3] = WTF::String::fromUTF8(kUTF8HelloWorld);
+
+  return strs;
+}
+
+WTF::HashMap<WTF::String, WTF::String> ConstructStringMap() {
+  WTF::HashMap<WTF::String, WTF::String> str_map;
+  // A null string as value.
+  str_map.add("0", WTF::String());
+  str_map.add("1", kHelloWorld);
+  str_map.add("2", WTF::String::fromUTF8(kUTF8HelloWorld));
+
+  return str_map;
+}
+
+void ExpectString(const WTF::String& expected_string,
+                  const base::Closure& closure,
+                  const WTF::String& string) {
+  EXPECT_EQ(expected_string, string);
+  closure.Run();
+}
+
+void ExpectStringArray(WTF::Optional<WTF::Vector<WTF::String>>* expected_arr,
+                       const base::Closure& closure,
+                       const WTF::Optional<WTF::Vector<WTF::String>>& arr) {
+  EXPECT_EQ(*expected_arr, arr);
+  closure.Run();
+}
+
+void ExpectStringMap(
+    WTF::Optional<WTF::HashMap<WTF::String, WTF::String>>* expected_map,
+    const base::Closure& closure,
+    const WTF::Optional<WTF::HashMap<WTF::String, WTF::String>>& map) {
+  EXPECT_EQ(*expected_map, map);
+  closure.Run();
+}
+
+}  // namespace
+
+TEST_F(WTFTypesTest, Serialization_WTFArrayToWTFArray) {
+  WTFArray<WTF::String> strs = ConstructStringArray();
+  auto cloned_strs = strs.Clone();
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<Array<mojo::String>>(
+      cloned_strs, &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  typename mojo::internal::MojomTypeTraits<Array<mojo::String>>::Data* data;
+  mojo::internal::ContainerValidateParams validate_params(
+      0, true, new mojo::internal::ContainerValidateParams(0, false, nullptr));
+  mojo::internal::Serialize<Array<mojo::String>>(cloned_strs, &buf, &data,
+                                                 &validate_params, &context);
+
+  WTFArray<WTF::String> strs2;
+  mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
+
+  EXPECT_TRUE(strs.Equals(strs2));
+}
+
+TEST_F(WTFTypesTest, Serialization_WTFVectorToWTFVector) {
+  WTF::Vector<WTF::String> strs = ConstructStringArray();
+  auto cloned_strs = strs;
+
+  mojo::internal::SerializationContext context;
+  size_t size = mojo::internal::PrepareToSerialize<Array<mojo::String>>(
+      cloned_strs, &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  typename mojo::internal::MojomTypeTraits<Array<mojo::String>>::Data* data;
+  mojo::internal::ContainerValidateParams validate_params(
+      0, true, new mojo::internal::ContainerValidateParams(0, false, nullptr));
+  mojo::internal::Serialize<Array<mojo::String>>(cloned_strs, &buf, &data,
+                                                 &validate_params, &context);
+
+  WTF::Vector<WTF::String> strs2;
+  mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
+
+  EXPECT_EQ(strs, strs2);
+}
+
+TEST_F(WTFTypesTest, Serialization_WTFArrayToMojoArray) {
+  WTFArray<WTF::String> strs = ConstructStringArray();
+
+  mojo::internal::SerializationContext context;
+  size_t size =
+      mojo::internal::PrepareToSerialize<Array<mojo::String>>(strs, &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  typename mojo::internal::MojomTypeTraits<Array<mojo::String>>::Data* data;
+  mojo::internal::ContainerValidateParams validate_params(
+      0, true, new mojo::internal::ContainerValidateParams(0, false, nullptr));
+  mojo::internal::Serialize<Array<mojo::String>>(strs, &buf, &data,
+                                                 &validate_params, &context);
+
+  Array<mojo::String> strs2;
+  mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
+
+  ASSERT_EQ(4u, strs2.size());
+  EXPECT_TRUE(strs2[0].is_null());
+  EXPECT_TRUE("" == strs2[1]);
+  EXPECT_TRUE(kHelloWorld == strs2[2]);
+  EXPECT_TRUE(kUTF8HelloWorld == strs2[3]);
+}
+
+TEST_F(WTFTypesTest, Serialization_WTFMapToWTFMap) {
+  using WTFType = WTFMap<WTF::String, WTF::String>;
+  using MojomType = Map<mojo::String, mojo::String>;
+
+  WTFType str_map = ConstructStringMap();
+  WTFType cloned_str_map = str_map.Clone();
+
+  mojo::internal::SerializationContext context;
+  size_t size =
+      mojo::internal::PrepareToSerialize<MojomType>(cloned_str_map, &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
+  mojo::internal::ContainerValidateParams validate_params(
+      new mojo::internal::ContainerValidateParams(
+          0, false,
+          new mojo::internal::ContainerValidateParams(0, false, nullptr)),
+      new mojo::internal::ContainerValidateParams(
+          0, true,
+          new mojo::internal::ContainerValidateParams(0, false, nullptr)));
+  mojo::internal::Serialize<MojomType>(cloned_str_map, &buf, &data,
+                                       &validate_params, &context);
+
+  WTFType str_map2;
+  mojo::internal::Deserialize<MojomType>(data, &str_map2, &context);
+
+  EXPECT_TRUE(str_map.Equals(str_map2));
+}
+
+TEST_F(WTFTypesTest, Serialization_WTFMapToMojoMap) {
+  using WTFType = WTFMap<WTF::String, WTF::String>;
+  using MojomType = Map<mojo::String, mojo::String>;
+
+  WTFType str_map = ConstructStringMap();
+
+  mojo::internal::SerializationContext context;
+  size_t size =
+      mojo::internal::PrepareToSerialize<MojomType>(str_map, &context);
+
+  mojo::internal::FixedBufferForTesting buf(size);
+  typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
+  mojo::internal::ContainerValidateParams validate_params(
+      new mojo::internal::ContainerValidateParams(
+          0, false,
+          new mojo::internal::ContainerValidateParams(0, false, nullptr)),
+      new mojo::internal::ContainerValidateParams(
+          0, true,
+          new mojo::internal::ContainerValidateParams(0, false, nullptr)));
+  mojo::internal::Serialize<MojomType>(str_map, &buf, &data, &validate_params,
+                                       &context);
+
+  MojomType str_map2;
+  mojo::internal::Deserialize<MojomType>(data, &str_map2, &context);
+
+  ASSERT_EQ(3u, str_map2.size());
+  EXPECT_TRUE(str_map2["0"].is_null());
+  EXPECT_TRUE(kHelloWorld == str_map2["1"]);
+  EXPECT_TRUE(kUTF8HelloWorld == str_map2["2"]);
+}
+
+TEST_F(WTFTypesTest, Serialization_PublicAPI) {
+  blink::TestWTFStructPtr input(blink::TestWTFStruct::New());
+  input->str = kHelloWorld;
+  input->integer = 42;
+
+  blink::TestWTFStructPtr cloned_input = input.Clone();
+
+  WTFArray<uint8_t> data = blink::TestWTFStruct::Serialize(&input);
+
+  blink::TestWTFStructPtr output;
+  ASSERT_TRUE(blink::TestWTFStruct::Deserialize(std::move(data), &output));
+  EXPECT_TRUE(cloned_input.Equals(output));
+}
+
+TEST_F(WTFTypesTest, SendString) {
+  blink::TestWTFPtr ptr;
+  TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr)));
+
+  WTF::Vector<WTF::String> strs = ConstructStringArray();
+
+  for (size_t i = 0; i < strs.size(); ++i) {
+    base::RunLoop loop;
+    // Test that a WTF::String is unchanged after the following conversion:
+    //   - serialized;
+    //   - deserialized as base::Optional<std::string>;
+    //   - serialized;
+    //   - deserialized as WTF::String.
+    ptr->EchoString(strs[i],
+                    base::Bind(&ExpectString, strs[i], loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+TEST_F(WTFTypesTest, SendStringArray) {
+  blink::TestWTFPtr ptr;
+  TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr)));
+
+  WTF::Optional<WTF::Vector<WTF::String>> arrs[3];
+  // arrs[0] is empty.
+  arrs[0].emplace();
+  // arrs[1] is null.
+  arrs[2] = ConstructStringArray();
+
+  for (size_t i = 0; i < arraysize(arrs); ++i) {
+    base::RunLoop loop;
+    // Test that a WTF::Optional<WTF::Vector<WTF::String>> is unchanged after
+    // the following conversion:
+    //   - serialized;
+    //   - deserialized as
+    //     base::Optional<std::vector<base::Optional<std::string>>>;
+    //   - serialized;
+    //   - deserialized as WTF::Optional<WTF::Vector<WTF::String>>.
+    ptr->EchoStringArray(
+        arrs[i], base::Bind(&ExpectStringArray, base::Unretained(&arrs[i]),
+                            loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+TEST_F(WTFTypesTest, SendStringMap) {
+  blink::TestWTFPtr ptr;
+  TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr)));
+
+  WTF::Optional<WTF::HashMap<WTF::String, WTF::String>> maps[3];
+  // maps[0] is empty.
+  maps[0].emplace();
+  // maps[1] is null.
+  maps[2] = ConstructStringMap();
+
+  for (size_t i = 0; i < arraysize(maps); ++i) {
+    base::RunLoop loop;
+    // Test that a WTF::Optional<WTF::HashMap<WTF::String, WTF::String>> is
+    // unchanged after the following conversion:
+    //   - serialized;
+    //   - deserialized as base::Optional<
+    //     std::unordered_map<std::string, base::Optional<std::string>>>;
+    //   - serialized;
+    //   - deserialized as WTF::Optional<WTF::HashMap<WTF::String,
+    //     WTF::String>>.
+    ptr->EchoStringMap(maps[i],
+                       base::Bind(&ExpectStringMap, base::Unretained(&maps[i]),
+                                  loop.QuitClosure()));
+    loop.Run();
+  }
+}
+
+}  // 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..1446ab3
--- /dev/null
+++ b/mojo/public/cpp/bindings/type_converter.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_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
+
+#include <stdint.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_t x;
+//     int32_t 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;
+//     }
+//   };
+//   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/bindings/wtf_array.h b/mojo/public/cpp/bindings/wtf_array.h
new file mode 100644
index 0000000..46d9a69
--- /dev/null
+++ b/mojo/public/cpp/bindings/wtf_array.h
@@ -0,0 +1,197 @@
+// Copyright 2016 The Chromium Authors. All 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_WTF_ARRAY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "third_party/WebKit/Source/wtf/Vector.h"
+
+namespace mojo {
+
+// Represents an array backed by WTF::Vector. Comparing with WTF::Vector,
+// mojo::WTFArray is move-only and can be null.
+// It is easy to convert between WTF::Vector<T> and mojo::WTFArray<T>:
+//   - constructor WTFArray(WTF::Vector<T>&&) takes the contents of a
+//     WTF::Vector<T>;
+//   - method PassStorage() passes the underlying WTF::Vector.
+template <typename T>
+class WTFArray {
+ public:
+  // Constructs an empty array.
+  WTFArray() : is_null_(false) {}
+  // Constructs a null array.
+  WTFArray(std::nullptr_t null_pointer) : is_null_(true) {}
+
+  // Constructs a new non-null array of the specified size. The elements will
+  // be value-initialized (meaning that they will be initialized by their
+  // default constructor, if any, or else zero-initialized).
+  explicit WTFArray(size_t size) : vec_(size), is_null_(false) {}
+  ~WTFArray() {}
+
+  // Moves the contents of |other| into this array.
+  WTFArray(WTF::Vector<T>&& other) : vec_(std::move(other)), is_null_(false) {}
+  WTFArray(WTFArray&& other) : is_null_(true) { Take(&other); }
+
+  WTFArray& operator=(WTF::Vector<T>&& other) {
+    vec_ = std::move(other);
+    is_null_ = false;
+    return *this;
+  }
+  WTFArray& operator=(WTFArray&& other) {
+    Take(&other);
+    return *this;
+  }
+
+  WTFArray& operator=(std::nullptr_t null_pointer) {
+    is_null_ = true;
+    vec_.clear();
+    return *this;
+  }
+
+  // Creates a non-null array of the specified size. The elements will be
+  // value-initialized (meaning that they will be initialized by their default
+  // constructor, if any, or else zero-initialized).
+  static WTFArray New(size_t size) { return WTFArray(size); }
+
+  // Creates a new array with a copy of the contents of |other|.
+  template <typename U>
+  static WTFArray From(const U& other) {
+    return TypeConverter<WTFArray, U>::Convert(other);
+  }
+
+  // Copies the contents of this array to a new object of type |U|.
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, WTFArray>::Convert(*this);
+  }
+
+  // Indicates whether the array is null (which is distinct from empty).
+  bool is_null() const {
+    // When the array is set to null, the underlying storage |vec_| shouldn't
+    // contain any elements.
+    DCHECK(!is_null_ || vec_.isEmpty());
+    return is_null_;
+  }
+
+  // Indicates whether the array is empty (which is distinct from null).
+  bool empty() const { return vec_.isEmpty() && !is_null_; }
+
+  // Returns a reference to the first element of the array. Calling this on a
+  // null or empty array causes undefined behavior.
+  const T& front() const { return vec_.first(); }
+  T& front() { return vec_.first(); }
+
+  // Returns the size of the array, which will be zero if the array is null.
+  size_t size() const { return vec_.size(); }
+
+  // Returns a reference to the element at zero-based |offset|. Calling this on
+  // an array with size less than |offset|+1 causes undefined behavior.
+  const T& at(size_t offset) const { return vec_.at(offset); }
+  const T& operator[](size_t offset) const { return at(offset); }
+  T& at(size_t offset) { return vec_.at(offset); }
+  T& operator[](size_t offset) { return at(offset); }
+
+  // Resizes the array to |size| and makes it non-null. Otherwise, works just
+  // like the resize method of |WTF::Vector|.
+  void resize(size_t size) {
+    is_null_ = false;
+    vec_.resize(size);
+  }
+
+  // Sets the array to empty (even if previously it was null.)
+  void SetToEmpty() { resize(0); }
+
+  // Returns a const reference to the |WTF::Vector| managed by this class. If
+  // the array is null, this will be an empty vector.
+  const WTF::Vector<T>& storage() const { return vec_; }
+
+  // Passes the underlying storage and resets this array to null.
+  //
+  // TODO(yzshen): Consider changing this to a rvalue-ref-qualified conversion
+  // to WTF::Vector<T> after we move to MSVC 2015.
+  WTF::Vector<T> PassStorage() {
+    is_null_ = true;
+    return std::move(vec_);
+  }
+
+  void Swap(WTFArray* other) {
+    std::swap(is_null_, other->is_null_);
+    vec_.swap(other->vec_);
+  }
+
+  // Swaps the contents of this array with the specified vector, making this
+  // array non-null. Since the vector cannot represent null, it will just be
+  // made empty if this array is null.
+  void Swap(WTF::Vector<T>* other) {
+    is_null_ = false;
+    vec_.swap(*other);
+  }
+
+  // Returns a copy of the array where each value of the new array has been
+  // "cloned" from the corresponding value of this array. If the element type
+  // defines a Clone() method, it will be used; otherwise copy
+  // constructor/assignment will be used.
+  //
+  // 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 containing Mojo handles).
+  WTFArray Clone() const {
+    WTFArray result;
+    result.is_null_ = is_null_;
+    result.vec_ = internal::Clone(vec_);
+    return result;
+  }
+
+  // Indicates whether the contents of this array are equal to |other|. A null
+  // array is only equal to another null array. If the element type defines an
+  // Equals() method, it will be used; otherwise == operator will be used.
+  bool Equals(const WTFArray& other) const {
+    if (is_null() != other.is_null())
+      return false;
+    return internal::Equals(vec_, other.vec_);
+  }
+
+ private:
+  // TODO(dcheng): Use an explicit conversion operator.
+  typedef WTF::Vector<T> WTFArray::*Testable;
+
+ public:
+  operator Testable() const {
+    // When the array is set to null, the underlying storage |vec_| shouldn't
+    // contain any elements.
+    DCHECK(!is_null_ || vec_.isEmpty());
+    return is_null_ ? 0 : &WTFArray::vec_;
+  }
+
+ private:
+  // Forbid the == and != operators explicitly, otherwise WTFArray will be
+  // converted to Testable to do == or != comparison.
+  template <typename U>
+  bool operator==(const WTFArray<U>& other) const = delete;
+  template <typename U>
+  bool operator!=(const WTFArray<U>& other) const = delete;
+
+  void Take(WTFArray* other) {
+    operator=(nullptr);
+    Swap(other);
+  }
+
+  WTF::Vector<T> vec_;
+  bool is_null_;
+
+  DISALLOW_COPY_AND_ASSIGN(WTFArray);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_
diff --git a/mojo/public/cpp/bindings/wtf_map.h b/mojo/public/cpp/bindings/wtf_map.h
new file mode 100644
index 0000000..0aba959
--- /dev/null
+++ b/mojo/public/cpp/bindings/wtf_map.h
@@ -0,0 +1,200 @@
+// Copyright 2016 The Chromium Authors. All 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_WTF_MAP_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "third_party/WebKit/Source/wtf/HashMap.h"
+#include "third_party/WebKit/Source/wtf/text/StringHash.h"
+
+namespace mojo {
+
+// Represents a map backed by WTF::HashMap. Comparing with WTF::HashMap,
+// mojo::WTFMap is move-only and can be null.
+//
+// It is easy to convert between WTF::HashMap<K, V> and mojo::WTFMap<K, V>:
+//   - constructor WTFMap(WTF::HashMap<K, V>&&) takes the contents of a
+//     WTF::HashMap<K, V>;
+//   - method PassStorage() passes the underlying WTF::HashMap.
+//
+// NOTE: WTF::HashMap disallows certain key values. For integer types, those are
+// 0 and -1 (max value instead of -1 for unsigned). For string, that is null.
+template <typename Key, typename Value>
+class WTFMap {
+ public:
+  using Iterator = typename WTF::HashMap<Key, Value>::iterator;
+  using ConstIterator = typename WTF::HashMap<Key, Value>::const_iterator;
+
+  // Constructs an empty map.
+  WTFMap() : is_null_(false) {}
+  // Constructs a null map.
+  WTFMap(std::nullptr_t null_pointer) : is_null_(true) {}
+
+  ~WTFMap() {}
+
+  WTFMap(WTF::HashMap<Key, Value>&& other)
+      : map_(std::move(other)), is_null_(false) {}
+  WTFMap(WTFMap&& other) : is_null_(true) { Take(&other); }
+
+  WTFMap& operator=(WTF::HashMap<Key, Value>&& other) {
+    is_null_ = false;
+    map_ = std::move(other);
+    return *this;
+  }
+  WTFMap& operator=(WTFMap&& other) {
+    Take(&other);
+    return *this;
+  }
+
+  WTFMap& operator=(std::nullptr_t null_pointer) {
+    is_null_ = true;
+    map_.clear();
+    return *this;
+  }
+
+  static bool IsValidKey(const Key& key) {
+    return WTF::HashMap<Key, Value>::isValidKey(key);
+  }
+
+  // Copies the contents of some other type of map into a new WTFMap using a
+  // TypeConverter.
+  template <typename U>
+  static WTFMap From(const U& other) {
+    return TypeConverter<WTFMap, U>::Convert(other);
+  }
+
+  // Copies the contents of the WTFMap into some other type of map.
+  template <typename U>
+  U To() const {
+    return TypeConverter<U, WTFMap>::Convert(*this);
+  }
+
+  // Indicates whether the map is null (which is distinct from empty).
+  bool is_null() const { return is_null_; }
+
+  // Indicates whether the map is empty (which is distinct from null).
+  bool empty() const { return map_.isEmpty() && !is_null_; }
+
+  // Indicates the number of keys in the map, which will be zero if the map is
+  // null.
+  size_t size() const { return map_.size(); }
+
+  // Inserts a key-value pair into the map. Like WTF::HashMap::add(), this does
+  // not insert |value| if |key| is already a member of the map.
+  void insert(const Key& key, const Value& value) {
+    is_null_ = false;
+    map_.add(key, value);
+  }
+  void insert(const Key& key, Value&& value) {
+    is_null_ = false;
+    map_.add(key, std::move(value));
+  }
+
+  // Returns a reference to the value associated with the specified key,
+  // crashing the process if the key is not present in the map.
+  Value& at(const Key& key) { return map_.find(key)->value; }
+  const Value& at(const Key& key) const { return map_.find(key)->value; }
+
+  // Returns a reference to the value associated with the specified key,
+  // creating a new entry if the key is not already present in the map. A
+  // newly-created value will be value-initialized (meaning that it will be
+  // initialized by the default constructor of the value type, if any, or else
+  // will be zero-initialized).
+  Value& operator[](const Key& key) {
+    is_null_ = false;
+    if (!map_.contains(key))
+      map_.add(key, Value());
+    return at(key);
+  }
+
+  // Sets the map to empty (even if previously it was null).
+  void SetToEmpty() {
+    is_null_ = false;
+    map_.clear();
+  }
+
+  // Returns a const reference to the WTF::HashMap managed by this class. If
+  // this object is null, the return value will be an empty map.
+  const WTF::HashMap<Key, Value>& storage() const { return map_; }
+
+  // Passes the underlying storage and resets this map to null.
+  WTF::HashMap<Key, Value> PassStorage() {
+    is_null_ = true;
+    return std::move(map_);
+  }
+
+  // Swaps the contents of this WTFMap with another WTFMap of the same type
+  // (including nullness).
+  void Swap(WTFMap<Key, Value>* other) {
+    std::swap(is_null_, other->is_null_);
+    map_.swap(other->map_);
+  }
+
+  // Swaps the contents of this WTFMap with an WTF::HashMap containing keys and
+  // values of the same type. Since WTF::HashMap cannot represent the null
+  // state, the WTF::HashMap will be empty if WTFMap is null. The WTFMap will
+  // always be left in a non-null state.
+  void Swap(WTF::HashMap<Key, Value>* other) {
+    is_null_ = false;
+    map_.swap(*other);
+  }
+
+  // Returns a new WTFMap that contains a copy of the contents of this map. If
+  // the key/value type defines a Clone() method, it will be used; otherwise
+  // copy constructor/assignment will be used.
+  //
+  // Please note that calling this method will fail compilation if the key/value
+  // type cannot be cloned (which usually means that it is a Mojo handle type or
+  // a type containing Mojo handles).
+  WTFMap Clone() const {
+    WTFMap result;
+    result.is_null_ = is_null_;
+    result.map_ = internal::Clone(map_);
+    return result;
+  }
+
+  // Indicates whether the contents of this map are equal to those of another
+  // WTFMap (including nullness). If the key/value type defines an Equals()
+  // method, it will be used; otherwise == operator will be used.
+  bool Equals(const WTFMap& other) const {
+    if (is_null() != other.is_null())
+      return false;
+    return internal::Equals(map_, other.map_);
+  }
+
+  ConstIterator begin() const { return map_.begin(); }
+  Iterator begin() { return map_.begin(); }
+
+  ConstIterator end() const { return map_.end(); }
+  Iterator end() { return map_.end(); }
+
+  // Returns the iterator pointing to the entry for |key|, if present, or else
+  // returns end().
+  ConstIterator find(const Key& key) const { return map_.find(key); }
+  Iterator find(const Key& key) { return map_.find(key); }
+
+  explicit operator bool() const { return !is_null_; }
+
+ private:
+  void Take(WTFMap* other) {
+    operator=(nullptr);
+    Swap(other);
+  }
+
+  WTF::HashMap<Key, Value> map_;
+  bool is_null_;
+
+  DISALLOW_COPY_AND_ASSIGN(WTFMap);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
new file mode 100644
index 0000000..8dcec71
--- /dev/null
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("system") {
+  sources = [
+    "buffer.cc",
+    "buffer.h",
+    "core.h",
+    "data_pipe.h",
+    "functions.h",
+    "handle.h",
+    "message.h",
+    "message_pipe.h",
+    "platform_handle.cc",
+    "platform_handle.h",
+    "watcher.cc",
+    "watcher.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//mojo/public/c/system",
+  ]
+}
diff --git a/mojo/public/cpp/system/buffer.cc b/mojo/public/cpp/system/buffer.cc
new file mode 100644
index 0000000..49f45d8
--- /dev/null
+++ b/mojo/public/cpp/system/buffer.cc
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All 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/system/buffer.h"
+
+namespace mojo {
+
+// static
+ScopedSharedBufferHandle SharedBufferHandle::Create(uint64_t num_bytes) {
+  MojoCreateSharedBufferOptions options = {
+      sizeof(options), MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE};
+  SharedBufferHandle handle;
+  MojoCreateSharedBuffer(&options, num_bytes, handle.mutable_value());
+  return MakeScopedHandle(handle);
+}
+
+ScopedSharedBufferHandle SharedBufferHandle::Clone(
+    SharedBufferHandle::AccessMode access_mode) const {
+  ScopedSharedBufferHandle result;
+  if (!is_valid())
+    return result;
+
+  MojoDuplicateBufferHandleOptions options = {
+      sizeof(options), MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE};
+  if (access_mode == AccessMode::READ_ONLY)
+    options.flags |= MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY;
+  SharedBufferHandle result_handle;
+  MojoDuplicateBufferHandle(value(), &options, result_handle.mutable_value());
+  result.reset(result_handle);
+  return result;
+}
+
+ScopedSharedBufferMapping SharedBufferHandle::Map(uint64_t size) const {
+  return MapAtOffset(size, 0);
+}
+
+ScopedSharedBufferMapping SharedBufferHandle::MapAtOffset(
+    uint64_t size,
+    uint64_t offset) const {
+  void* buffer = nullptr;
+  MojoMapBuffer(value(), offset, size, &buffer, MOJO_MAP_BUFFER_FLAG_NONE);
+  return ScopedSharedBufferMapping(buffer);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h
new file mode 100644
index 0000000..449c6ce
--- /dev/null
+++ b/mojo/public/cpp/system/buffer.h
@@ -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.
+
+// This file provides a C++ wrapping around the Mojo C API for shared buffers,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/buffer.h" for complete documentation of the
+// API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+namespace internal {
+
+struct Unmapper {
+  void operator()(void* buffer) {
+    MojoResult result = MojoUnmapBuffer(buffer);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+  }
+};
+
+}  // namespace internal
+
+using ScopedSharedBufferMapping = std::unique_ptr<void, internal::Unmapper>;
+
+class SharedBufferHandle;
+
+typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle;
+
+// A strongly-typed representation of a |MojoHandle| referring to a shared
+// buffer.
+class SharedBufferHandle : public Handle {
+ public:
+  enum class AccessMode {
+    READ_WRITE,
+    READ_ONLY,
+  };
+
+  SharedBufferHandle() {}
+  explicit SharedBufferHandle(MojoHandle value) : Handle(value) {}
+
+  // Copying and assignment allowed.
+
+  // Creates a new SharedBufferHandle. Returns an invalid handle on failure.
+  static ScopedSharedBufferHandle Create(uint64_t num_bytes);
+
+  // Clones this shared buffer handle. If |access_mode| is READ_ONLY or this is
+  // a read-only handle, the new handle will be read-only. On failure, this will
+  // return an empty result.
+  ScopedSharedBufferHandle Clone(AccessMode = AccessMode::READ_WRITE) const;
+
+  // Maps |size| bytes of this shared buffer. On failure, this will return a
+  // null mapping.
+  ScopedSharedBufferMapping Map(uint64_t size) const;
+
+  // Maps |size| bytes of this shared buffer, starting |offset| bytes into the
+  // buffer. On failure, this will return a null mapping.
+  ScopedSharedBufferMapping MapAtOffset(uint64_t size, uint64_t offset) const;
+};
+
+static_assert(sizeof(SharedBufferHandle) == sizeof(Handle),
+              "Bad size for C++ SharedBufferHandle");
+static_assert(sizeof(ScopedSharedBufferHandle) == sizeof(SharedBufferHandle),
+              "Bad size for C++ ScopedSharedBufferHandle");
+
+}  // 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..f1d18d9
--- /dev/null
+++ b/mojo/public/cpp/system/core.h
@@ -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.
+
+#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/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..0dbc3c7
--- /dev/null
+++ b/mojo/public/cpp/system/data_pipe.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.
+
+// This file provides a C++ wrapping around the Mojo C API for data pipes,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/data_pipe.h" for complete documentation of
+// the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+// A strongly-typed representation of a |MojoHandle| to the producer end of a
+// data pipe.
+class DataPipeProducerHandle : public Handle {
+ public:
+  DataPipeProducerHandle() {}
+  explicit DataPipeProducerHandle(MojoHandle value) : Handle(value) {}
+
+  // Copying and assignment allowed.
+};
+
+static_assert(sizeof(DataPipeProducerHandle) == sizeof(Handle),
+              "Bad size for C++ DataPipeProducerHandle");
+
+typedef ScopedHandleBase<DataPipeProducerHandle> ScopedDataPipeProducerHandle;
+static_assert(sizeof(ScopedDataPipeProducerHandle) ==
+                  sizeof(DataPipeProducerHandle),
+              "Bad size for C++ ScopedDataPipeProducerHandle");
+
+// A strongly-typed representation of a |MojoHandle| to the consumer end of a
+// data pipe.
+class DataPipeConsumerHandle : public Handle {
+ public:
+  DataPipeConsumerHandle() {}
+  explicit DataPipeConsumerHandle(MojoHandle value) : Handle(value) {}
+
+  // Copying and assignment allowed.
+};
+
+static_assert(sizeof(DataPipeConsumerHandle) == sizeof(Handle),
+              "Bad size for C++ DataPipeConsumerHandle");
+
+typedef ScopedHandleBase<DataPipeConsumerHandle> ScopedDataPipeConsumerHandle;
+static_assert(sizeof(ScopedDataPipeConsumerHandle) ==
+                  sizeof(DataPipeConsumerHandle),
+              "Bad size for C++ ScopedDataPipeConsumerHandle");
+
+// Creates a new data pipe. See |MojoCreateDataPipe()| for complete
+// documentation.
+inline MojoResult CreateDataPipe(
+    const MojoCreateDataPipeOptions* options,
+    ScopedDataPipeProducerHandle* data_pipe_producer,
+    ScopedDataPipeConsumerHandle* data_pipe_consumer) {
+  DCHECK(data_pipe_producer);
+  DCHECK(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;
+}
+
+// Writes to a data pipe. See |MojoWriteData| for complete documentation.
+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);
+}
+
+// Begins a two-phase write to a data pipe. See |MojoBeginWriteData()| for
+// complete documentation.
+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);
+}
+
+// Completes a two-phase write to a data pipe. See |MojoEndWriteData()| for
+// complete documentation.
+inline MojoResult EndWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+                                  uint32_t num_bytes_written) {
+  return MojoEndWriteData(data_pipe_producer.value(), num_bytes_written);
+}
+
+// Reads from a data pipe. See |MojoReadData()| for complete documentation.
+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);
+}
+
+// Begins a two-phase read from a data pipe. See |MojoBeginReadData()| for
+// complete documentation.
+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);
+}
+
+// Completes a two-phase read from a data pipe. See |MojoEndReadData()| for
+// complete documentation.
+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 =
+      CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
+  ALLOW_UNUSED_LOCAL(result);
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+}
+
+inline DataPipe::DataPipe(const MojoCreateDataPipeOptions& options) {
+  MojoResult result =
+      CreateDataPipe(&options, &producer_handle, &consumer_handle);
+  ALLOW_UNUSED_LOCAL(result);
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+}
+
+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..9cfe316
--- /dev/null
+++ b/mojo/public/cpp/system/functions.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.
+
+// This file provides a C++ wrapping around the standalone functions of the Mojo
+// C API, replacing the prefix of "Mojo" with a "mojo" namespace.
+//
+// Please see "mojo/public/c/system/functions.h" for complete documentation of
+// the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+
+#include "mojo/public/c/system/functions.h"
+
+namespace mojo {
+
+// Returns the current |MojoTimeTicks| value. See |MojoGetTimeTicksNow()| for
+// complete documentation.
+inline MojoTimeTicks GetTimeTicksNow() {
+  return MojoGetTimeTicksNow();
+}
+
+// The C++ wrappers for |MojoWait()| and |MojoWaitMany()| are defined in
+// "handle.h".
+// TODO(ggowan): Consider making the C and C++ APIs more consistent in the
+// organization of the functions into different header files (since in the C
+// API, those functions are defined in "functions.h").
+
+}  // 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..5b2eb7b
--- /dev/null
+++ b/mojo/public/cpp/system/handle.h
@@ -0,0 +1,308 @@
+// Copyright 2014 The Chromium Authors. All 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 <stdint.h>
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/types.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 wrapper.
+//
+// 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 {
+ public:
+  using RawHandleType = HandleType;
+
+  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(ScopedHandleBase&& other) : handle_(other.release()) {}
+  ScopedHandleBase& operator=(ScopedHandleBase&& other) {
+    if (&other != this) {
+      CloseIfNecessary();
+      handle_ = other.release();
+    }
+    return *this;
+  }
+
+  const HandleType& get() const { return handle_; }
+  const HandleType* operator->() const { return &handle_; }
+
+  template <typename PassedHandleType>
+  static ScopedHandleBase<HandleType> From(
+      ScopedHandleBase<PassedHandleType> other) {
+    static_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() 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(); }
+
+  bool operator==(const ScopedHandleBase& other) const {
+    return handle_.value() == other.get().value();
+  }
+
+ private:
+  void CloseIfNecessary() {
+    if (handle_.is_valid())
+      handle_.Close();
+  }
+
+  HandleType handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedHandleBase);
+};
+
+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; }
+
+  void Close() {
+    DCHECK(is_valid());
+    MojoResult result = MojoClose(value_);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+  }
+
+ private:
+  MojoHandle value_;
+
+  // Copying and assignment allowed.
+};
+
+// Should have zero overhead.
+static_assert(sizeof(Handle) == sizeof(MojoHandle), "Bad size for C++ Handle");
+
+// The scoper should also impose no more overhead.
+typedef ScopedHandleBase<Handle> ScopedHandle;
+static_assert(sizeof(ScopedHandle) == sizeof(Handle),
+              "Bad size for C++ ScopedHandle");
+
+inline MojoResult Wait(Handle handle,
+                       MojoHandleSignals signals,
+                       MojoDeadline deadline,
+                       MojoHandleSignalsState* signals_state) {
+  return MojoWait(handle.value(), signals, deadline, signals_state);
+}
+
+const uint32_t kInvalidWaitManyIndexValue = static_cast<uint32_t>(-1);
+
+// Simplify the interpretation of the output from |MojoWaitMany()|.
+class WaitManyResult {
+ public:
+  explicit WaitManyResult(MojoResult mojo_wait_many_result)
+      : result(mojo_wait_many_result), index(kInvalidWaitManyIndexValue) {}
+
+  WaitManyResult(MojoResult mojo_wait_many_result, uint32_t result_index)
+      : result(mojo_wait_many_result), index(result_index) {}
+
+  // A valid handle index is always returned if |WaitMany()| succeeds, but may
+  // or may not be returned if |WaitMany()| returns an error. Use this helper
+  // function to check if |index| is a valid index into the handle array.
+  bool IsIndexValid() const { return index != kInvalidWaitManyIndexValue; }
+
+  // The |signals_states| array is always returned by |WaitMany()| on success,
+  // but may or may not be returned if |WaitMany()| returns an error. Use this
+  // helper function to check if |signals_states| holds valid data.
+  bool AreSignalsStatesValid() const {
+    return result != MOJO_RESULT_INVALID_ARGUMENT &&
+           result != MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  MojoResult result;
+  uint32_t index;
+};
+
+// |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,
+          class SignalsStateVectorType>
+inline WaitManyResult WaitMany(const HandleVectorType& handles,
+                               const FlagsVectorType& signals,
+                               MojoDeadline deadline,
+                               SignalsStateVectorType* signals_states) {
+  if (signals.size() != handles.size() ||
+      (signals_states && signals_states->size() != signals.size()))
+    return WaitManyResult(MOJO_RESULT_INVALID_ARGUMENT);
+  if (handles.size() >= kInvalidWaitManyIndexValue)
+    return WaitManyResult(MOJO_RESULT_RESOURCE_EXHAUSTED);
+
+  if (handles.size() == 0) {
+    return WaitManyResult(
+        MojoWaitMany(nullptr, nullptr, 0, deadline, nullptr, nullptr));
+  }
+
+  uint32_t result_index = kInvalidWaitManyIndexValue;
+  const Handle& first_handle = handles[0];
+  const MojoHandleSignals& first_signals = signals[0];
+  MojoHandleSignalsState* first_state =
+      signals_states ? &(*signals_states)[0] : nullptr;
+  MojoResult result =
+      MojoWaitMany(reinterpret_cast<const MojoHandle*>(&first_handle),
+                   &first_signals, static_cast<uint32_t>(handles.size()),
+                   deadline, &result_index, first_state);
+  return WaitManyResult(result, result_index);
+}
+
+// C++ 4.10, regarding pointer conversion, says that an integral null pointer
+// constant can be converted to |std::nullptr_t| (which is a typedef for
+// |decltype(nullptr)|). The opposite direction is not allowed.
+template <class HandleVectorType, class FlagsVectorType>
+inline WaitManyResult WaitMany(const HandleVectorType& handles,
+                               const FlagsVectorType& signals,
+                               MojoDeadline deadline,
+                               decltype(nullptr) signals_states) {
+  if (signals.size() != handles.size())
+    return WaitManyResult(MOJO_RESULT_INVALID_ARGUMENT);
+  if (handles.size() >= kInvalidWaitManyIndexValue)
+    return WaitManyResult(MOJO_RESULT_RESOURCE_EXHAUSTED);
+
+  if (handles.size() == 0) {
+    return WaitManyResult(
+        MojoWaitMany(nullptr, nullptr, 0, deadline, nullptr, nullptr));
+  }
+
+  uint32_t result_index = kInvalidWaitManyIndexValue;
+  const Handle& first_handle = handles[0];
+  const MojoHandleSignals& first_signals = signals[0];
+  MojoResult result = MojoWaitMany(
+      reinterpret_cast<const MojoHandle*>(&first_handle), &first_signals,
+      static_cast<uint32_t>(handles.size()), deadline, &result_index, nullptr);
+  return WaitManyResult(result, result_index);
+}
+
+// |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();
+}
+
+// Comparison, so that |Handle|s can be used as keys in hash maps.
+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/message.cc b/mojo/public/cpp/system/message.cc
new file mode 100644
index 0000000..09d8d46
--- /dev/null
+++ b/mojo/public/cpp/system/message.cc
@@ -0,0 +1,13 @@
+// Copyright 2016 The Chromium Authors. All 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/system/message.h"
+
+namespace mojo {
+
+ScopedMessageHandle::~ScopedMessageHandle() {
+
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/message.h b/mojo/public/cpp/system/message.h
new file mode 100644
index 0000000..d4406ee
--- /dev/null
+++ b/mojo/public/cpp/system/message.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All 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_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_H_
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+const MojoMessageHandle kInvalidMessageHandleValue =
+    MOJO_MESSAGE_HANDLE_INVALID;
+
+// Handle wrapper base class for a |MojoMessageHandle|.
+class MessageHandle {
+ public:
+  MessageHandle() : value_(kInvalidMessageHandleValue) {}
+  explicit MessageHandle(MojoMessageHandle value) : value_(value) {}
+  ~MessageHandle() {}
+
+  void swap(MessageHandle& other) {
+    MojoMessageHandle temp = value_;
+    value_ = other.value_;
+    other.value_ = temp;
+  }
+
+  bool is_valid() const { return value_ != kInvalidMessageHandleValue; }
+
+  const MojoMessageHandle& value() const { return value_; }
+  MojoMessageHandle* mutable_value() { return &value_; }
+  void set_value(MojoMessageHandle value) { value_ = value; }
+
+  void Close() {
+    DCHECK(is_valid());
+    MojoResult result = MojoFreeMessage(value_);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+  }
+
+ private:
+  MojoMessageHandle value_;
+};
+
+using ScopedMessageHandle = ScopedHandleBase<MessageHandle>;
+
+inline MojoResult AllocMessage(size_t num_bytes,
+                               const MojoHandle* handles,
+                               size_t num_handles,
+                               MojoAllocMessageFlags flags,
+                               ScopedMessageHandle* handle) {
+  DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max());
+  DCHECK_LE(num_handles, std::numeric_limits<uint32_t>::max());
+  MojoMessageHandle raw_handle;
+  MojoResult rv = MojoAllocMessage(static_cast<uint32_t>(num_bytes), handles,
+                                   static_cast<uint32_t>(num_handles), flags,
+                                   &raw_handle);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  handle->reset(MessageHandle(raw_handle));
+  return MOJO_RESULT_OK;
+}
+
+inline MojoResult GetMessageBuffer(MessageHandle message, void** buffer) {
+  DCHECK(message.is_valid());
+  return MojoGetMessageBuffer(message.value(), buffer);
+}
+
+inline MojoResult NotifyBadMessage(MessageHandle message,
+                                   const base::StringPiece& error) {
+  DCHECK(message.is_valid());
+  return MojoNotifyBadMessage(message.value(), error.data(), error.size());
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_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..7fbe43f
--- /dev/null
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -0,0 +1,158 @@
+// Copyright 2014 The Chromium 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 provides a C++ wrapping around the Mojo C API for message pipes,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/message_pipe.h" for complete documentation
+// of the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/message.h"
+
+namespace mojo {
+
+// A strongly-typed representation of a |MojoHandle| to one end of a message
+// pipe.
+class MessagePipeHandle : public Handle {
+ public:
+  MessagePipeHandle() {}
+  explicit MessagePipeHandle(MojoHandle value) : Handle(value) {}
+
+  // Copying and assignment allowed.
+};
+
+static_assert(sizeof(MessagePipeHandle) == sizeof(Handle),
+              "Bad size for C++ MessagePipeHandle");
+
+typedef ScopedHandleBase<MessagePipeHandle> ScopedMessagePipeHandle;
+static_assert(sizeof(ScopedMessagePipeHandle) == sizeof(MessagePipeHandle),
+              "Bad size for C++ ScopedMessagePipeHandle");
+
+// Creates a message pipe. See |MojoCreateMessagePipe()| for complete
+// documentation.
+inline MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+                                    ScopedMessagePipeHandle* message_pipe0,
+                                    ScopedMessagePipeHandle* message_pipe1) {
+  DCHECK(message_pipe0);
+  DCHECK(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;
+}
+
+// The following "...Raw" versions fully expose the underlying API, and don't
+// help with ownership of handles (especially when writing messages). It is
+// expected that in most cases these methods will be called through generated
+// bindings anyway.
+// TODO(vtl): Write friendlier versions of these functions (using scoped
+// handles and/or vectors) if there is a demonstrated need for them.
+
+// Writes to a message pipe.  If handles are attached, on success the handles
+// will no longer be valid (the receiver will receive equivalent, but logically
+// different, handles). See |MojoWriteMessage()| for complete documentation.
+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);
+}
+
+// Reads from a message pipe. See |MojoReadMessage()| for complete
+// documentation.
+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);
+}
+
+// Writes to a message pipe. Takes ownership of |message| and any attached
+// handles.
+inline MojoResult WriteMessageNew(MessagePipeHandle message_pipe,
+                                  ScopedMessageHandle message,
+                                  MojoWriteMessageFlags flags) {
+  return MojoWriteMessageNew(
+      message_pipe.value(), message.release().value(), flags);
+}
+
+// Reads from a message pipe. See |MojoReadMessageNew()| for complete
+// documentation.
+inline MojoResult ReadMessageNew(MessagePipeHandle message_pipe,
+                                 ScopedMessageHandle* message,
+                                 uint32_t* num_bytes,
+                                 MojoHandle* handles,
+                                 uint32_t* num_handles,
+                                 MojoReadMessageFlags flags) {
+  MojoMessageHandle raw_message;
+  MojoResult rv = MojoReadMessageNew(message_pipe.value(), &raw_message,
+                                     num_bytes, handles, num_handles, flags);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  message->reset(MessageHandle(raw_message));
+  return MOJO_RESULT_OK;
+}
+
+// Fuses two message pipes together at the given handles. See
+// |MojoFuseMessagePipes()| for complete documentation.
+inline MojoResult FuseMessagePipes(ScopedMessagePipeHandle message_pipe0,
+                                   ScopedMessagePipeHandle message_pipe1) {
+  return MojoFuseMessagePipes(message_pipe0.release().value(),
+                              message_pipe1.release().value());
+}
+
+// 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 = CreateMessagePipe(nullptr, &handle0, &handle1);
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+  DCHECK(handle0.is_valid());
+  DCHECK(handle1.is_valid());
+}
+
+inline MessagePipe::MessagePipe(const MojoCreateMessagePipeOptions& options) {
+  MojoResult result = CreateMessagePipe(&options, &handle0, &handle1);
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+  DCHECK(handle0.is_valid());
+  DCHECK(handle1.is_valid());
+}
+
+inline MessagePipe::~MessagePipe() {
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
diff --git a/mojo/public/cpp/system/platform_handle.cc b/mojo/public/cpp/system/platform_handle.cc
new file mode 100644
index 0000000..e4a6088
--- /dev/null
+++ b/mojo/public/cpp/system/platform_handle.cc
@@ -0,0 +1,129 @@
+// Copyright 2016 The Chromium Authors. All 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/system/platform_handle.h"
+
+namespace mojo {
+
+namespace {
+
+uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) {
+#if defined(OS_WIN)
+  return reinterpret_cast<uint64_t>(file);
+#else
+  return static_cast<uint64_t>(file);
+#endif
+}
+
+base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) {
+#if defined(OS_WIN)
+  return reinterpret_cast<base::PlatformFile>(value);
+#else
+  return static_cast<base::PlatformFile>(value);
+#endif
+}
+
+}  // namespace
+
+ScopedHandle WrapPlatformFile(base::PlatformFile platform_file) {
+  MojoPlatformHandle platform_handle;
+  platform_handle.struct_size = sizeof(MojoPlatformHandle);
+  platform_handle.type = kPlatformFileHandleType;
+  platform_handle.value = PlatformHandleValueFromPlatformFile(platform_file);
+
+  MojoHandle mojo_handle;
+  MojoResult result = MojoWrapPlatformHandle(&platform_handle, &mojo_handle);
+  CHECK_EQ(result, MOJO_RESULT_OK);
+
+  return ScopedHandle(Handle(mojo_handle));
+}
+
+MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file) {
+  MojoPlatformHandle platform_handle;
+  platform_handle.struct_size = sizeof(MojoPlatformHandle);
+  MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(),
+                                               &platform_handle);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  if (platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) {
+    *file = base::kInvalidPlatformFile;
+  } else {
+    CHECK_EQ(platform_handle.type, kPlatformFileHandleType);
+    *file = PlatformFileFromPlatformHandleValue(platform_handle.value);
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+ScopedSharedBufferHandle WrapSharedMemoryHandle(
+    const base::SharedMemoryHandle& memory_handle,
+    size_t size,
+    bool read_only) {
+  MojoPlatformHandle platform_handle;
+  platform_handle.struct_size = sizeof(MojoPlatformHandle);
+  platform_handle.type = kPlatformSharedBufferHandleType;
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  platform_handle.value =
+      static_cast<uint64_t>(memory_handle.GetMemoryObject());
+#elif defined(OS_POSIX)
+  platform_handle.value = PlatformHandleValueFromPlatformFile(memory_handle.fd);
+#elif defined(OS_WIN)
+  platform_handle.value =
+      PlatformHandleValueFromPlatformFile(memory_handle.GetHandle());
+#endif
+
+  MojoPlatformSharedBufferHandleFlags flags =
+      MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE;
+  if (read_only)
+    flags |= MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY;
+
+  MojoHandle mojo_handle;
+  MojoResult result = MojoWrapPlatformSharedBufferHandle(
+      &platform_handle, size, flags, &mojo_handle);
+  CHECK_EQ(result, MOJO_RESULT_OK);
+
+  return ScopedSharedBufferHandle(SharedBufferHandle(mojo_handle));
+}
+
+MojoResult UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,
+                                    base::SharedMemoryHandle* memory_handle,
+                                    size_t* size,
+                                    bool* read_only) {
+  MojoPlatformHandle platform_handle;
+  platform_handle.struct_size = sizeof(MojoPlatformHandle);
+
+  MojoPlatformSharedBufferHandleFlags flags;
+  size_t num_bytes;
+  MojoResult result = MojoUnwrapPlatformSharedBufferHandle(
+      handle.release().value(), &platform_handle, &num_bytes, &flags);
+  if (result != MOJO_RESULT_OK)
+    return result;
+
+  if (size)
+    *size = num_bytes;
+
+  if (read_only)
+    *read_only = flags & MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT);
+  *memory_handle = base::SharedMemoryHandle(
+      static_cast<mach_port_t>(platform_handle.value), num_bytes,
+      base::GetCurrentProcId());
+#elif defined(OS_POSIX)
+  CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR);
+  *memory_handle = base::SharedMemoryHandle(
+      static_cast<int>(platform_handle.value), false);
+#elif defined(OS_WIN)
+  CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE);
+  *memory_handle = base::SharedMemoryHandle(
+      reinterpret_cast<HANDLE>(platform_handle.value),
+      base::GetCurrentProcId());
+#endif
+
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/platform_handle.h b/mojo/public/cpp/system/platform_handle.h
new file mode 100644
index 0000000..2a81734
--- /dev/null
+++ b/mojo/public/cpp/system/platform_handle.h
@@ -0,0 +1,76 @@
+// Copyright 2016 The Chromium 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 provides a C++ wrapping around the Mojo C API for platform handles,
+// replacing the prefix of "Mojo" with a "mojo" namespace.
+//
+// Please see "mojo/public/c/system/platform_handle.h" for complete
+// documentation of the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_PLATFORM_HANDLE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_PLATFORM_HANDLE_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/process/process_handle.h"
+#include "mojo/public/c/system/platform_handle.h"
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/handle.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace mojo {
+
+#if defined(OS_POSIX)
+const MojoPlatformHandleType kPlatformFileHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+const MojoPlatformHandleType kPlatformSharedBufferHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
+#else
+const MojoPlatformHandleType kPlatformSharedBufferHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
+#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
+
+#elif defined(OS_WIN)
+const MojoPlatformHandleType kPlatformFileHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
+
+const MojoPlatformHandleType kPlatformSharedBufferHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
+#endif  // defined(OS_POSIX)
+
+// Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object.
+ScopedHandle WrapPlatformFile(base::PlatformFile platform_file);
+
+// Unwraps a PlatformFile from a Mojo handle.
+MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file);
+
+// Wraps a base::SharedMemoryHandle as a Mojo handle. Takes ownership of the
+// SharedMemoryHandle. Note that |read_only| is only an indicator of whether
+// |memory_handle| only supports read-only mapping. It does NOT have any
+// influence on the access control of the shared buffer object.
+ScopedSharedBufferHandle WrapSharedMemoryHandle(
+    const base::SharedMemoryHandle& memory_handle,
+    size_t size,
+    bool read_only);
+
+// Unwraps a base::SharedMemoryHandle from a Mojo handle. The caller assumes
+// responsibility for the lifetime of the SharedMemoryHandle.
+MojoResult UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,
+                                    base::SharedMemoryHandle* memory_handle,
+                                    size_t* size,
+                                    bool* read_only);
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_PLATFORM_HANDLE_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..8f98b92
--- /dev/null
+++ b/mojo/public/cpp/system/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.
+
+source_set("tests") {
+  testonly = true
+
+  sources = [
+    "core_unittest.cc",
+    "watcher_unittest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/public/c/system/tests",
+    "//mojo/public/cpp/system",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//testing/gtest",
+  ]
+}
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..e503db0
--- /dev/null
+++ b/mojo/public/cpp/system/tests/core_unittest.cc
@@ -0,0 +1,524 @@
+// Copyright 2014 The Chromium 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 <stddef.h>
+#include <stdint.h>
+#include <map>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const MojoHandleSignals kSignalReadableWritable =
+    MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE;
+
+const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE |
+                                     MOJO_HANDLE_SIGNAL_WRITABLE |
+                                     MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+
+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(std::move(h));
+
+    // 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, nullptr));
+
+    std::vector<Handle> wh;
+    wh.push_back(h.get());
+    std::vector<MojoHandleSignals> sigs;
+    sigs.push_back(~MOJO_HANDLE_SIGNAL_NONE);
+    WaitManyResult wait_many_result =
+        WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE, nullptr);
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, wait_many_result.result);
+    EXPECT_TRUE(wait_many_result.IsIndexValid());
+    EXPECT_FALSE(wait_many_result.AreSignalsStatesValid());
+
+    // Make sure that our specialized template correctly handles |NULL| as well
+    // as |nullptr|.
+    wait_many_result = WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE, NULL);
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, wait_many_result.result);
+    EXPECT_EQ(0u, wait_many_result.index);
+    EXPECT_TRUE(wait_many_result.IsIndexValid());
+    EXPECT_FALSE(wait_many_result.AreSignalsStatesValid());
+  }
+
+  // |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();
+      MojoHandleSignalsState state;
+
+      EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+                Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
+
+      EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+      EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+      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);
+      std::vector<MojoHandleSignalsState> states(sigs.size());
+      WaitManyResult wait_many_result = WaitMany(wh, sigs, 1000, &states);
+      EXPECT_EQ(MOJO_RESULT_OK, wait_many_result.result);
+      EXPECT_EQ(1u, wait_many_result.index);
+      EXPECT_TRUE(wait_many_result.IsIndexValid());
+      EXPECT_TRUE(wait_many_result.AreSignalsStatesValid());
+      EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[0].satisfied_signals);
+      EXPECT_EQ(kSignalAll, states[0].satisfiable_signals);
+      EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[1].satisfied_signals);
+      EXPECT_EQ(kSignalAll, states[1].satisfiable_signals);
+
+      // Test closing |h1| explicitly.
+      Close(std::move(h1));
+      EXPECT_FALSE(h1.get().is_valid());
+
+      // Make sure |h1| is closed.
+      EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+                Wait(Handle(hv1), ~MOJO_HANDLE_SIGNAL_NONE,
+                     MOJO_DEADLINE_INDEFINITE, nullptr));
+
+      EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+                Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                     MOJO_DEADLINE_INDEFINITE, &state));
+
+      EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+      EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+    }
+    // |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));
+
+      MojoHandleSignalsState state;
+      EXPECT_EQ(MOJO_RESULT_OK, Wait(h1.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                                     MOJO_DEADLINE_INDEFINITE, &state));
+      EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+      EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+      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, &state));
+      EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+      EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+      memset(buffer, 0, sizeof(buffer));
+      buffer_size = static_cast<uint32_t>(sizeof(buffer));
+      for (size_t i = 0; i < arraysize(handles); i++)
+        handles[i] = kInvalidHandleValue;
+      handles_count = static_cast<uint32_t>(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, &state));
+      EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+      EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+      memset(buffer, 0, sizeof(buffer));
+      buffer_size = static_cast<uint32_t>(sizeof(buffer));
+      for (size_t i = 0; i < arraysize(handles); i++)
+        handles[i] = kInvalidHandleValue;
+      handles_count = static_cast<uint32_t>(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;
+    if (CreateMessagePipe(nullptr, &h2, &h3) != MOJO_RESULT_OK)
+      CreateMessagePipe(nullptr, &h2, &h3);  // Must be old EDK.
+
+    // 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;
+    if (CreateMessagePipe(nullptr, &h2, &h3) != MOJO_RESULT_OK)
+      CreateMessagePipe(nullptr, &h2, &h3);  // Must be old EDK.
+
+    // 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 = SharedBufferHandle::Create(1024);
+  EXPECT_TRUE(buffer1.is_valid());
+
+  ScopedSharedBufferHandle buffer2 = SharedBufferHandle::Create(1024);
+  EXPECT_TRUE(buffer2.is_valid());
+
+  // If this fails to close buffer1, ScopedHandleBase::CloseIfNecessary() will
+  // assert.
+  buffer1 = std::move(buffer2);
+
+  EXPECT_TRUE(buffer1.is_valid());
+  EXPECT_FALSE(buffer2.is_valid());
+}
+
+TEST(CoreCppTest, BasicSharedBuffer) {
+  ScopedSharedBufferHandle h0 = SharedBufferHandle::Create(100);
+  ASSERT_TRUE(h0.is_valid());
+
+  // Map everything.
+  ScopedSharedBufferMapping mapping = h0->Map(100);
+  ASSERT_TRUE(mapping);
+  static_cast<char*>(mapping.get())[50] = 'x';
+
+  // Duplicate |h0| to |h1|.
+  ScopedSharedBufferHandle h1 =
+      h0->Clone(SharedBufferHandle::AccessMode::READ_ONLY);
+  ASSERT_TRUE(h1.is_valid());
+
+  // Close |h0|.
+  h0.reset();
+
+  // The mapping should still be good.
+  static_cast<char*>(mapping.get())[51] = 'y';
+
+  // Unmap it.
+  mapping.reset();
+
+  // Map half of |h1|.
+  mapping = h1->MapAtOffset(50, 50);
+  ASSERT_TRUE(mapping);
+
+  // It should have what we wrote.
+  EXPECT_EQ('x', static_cast<char*>(mapping.get())[0]);
+  EXPECT_EQ('y', static_cast<char*>(mapping.get())[1]);
+
+  // Unmap it.
+  mapping.reset();
+  h1.reset();
+
+  // Creating a 1 EB shared buffer should fail without crashing.
+  EXPECT_FALSE(SharedBufferHandle::Create(1ULL << 60).is_valid());
+}
+
+// TODO(vtl): Write data pipe tests.
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/tests/watcher_unittest.cc b/mojo/public/cpp/system/tests/watcher_unittest.cc
new file mode 100644
index 0000000..5cb3ed2
--- /dev/null
+++ b/mojo/public/cpp/system/tests/watcher_unittest.cc
@@ -0,0 +1,208 @@
+// Copyright 2016 The Chromium Authors. All 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/system/watcher.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+template <typename Handler>
+void RunResultHandler(Handler f, MojoResult result) { f(result); }
+
+template <typename Handler>
+Watcher::ReadyCallback OnReady(Handler f) {
+  return base::Bind(&RunResultHandler<Handler>, f);
+}
+
+Watcher::ReadyCallback NotReached() {
+  return OnReady([] (MojoResult) { NOTREACHED(); });
+}
+
+class WatcherTest : public testing::Test {
+ public:
+  WatcherTest() {}
+  ~WatcherTest() override {}
+
+  void SetUp() override {
+    message_loop_.reset(new base::MessageLoop);
+  }
+
+  void TearDown() override {
+    message_loop_.reset();
+  }
+
+ protected:
+  std::unique_ptr<base::MessageLoop> message_loop_;
+};
+
+TEST_F(WatcherTest, WatchBasic) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  bool notified = false;
+  base::RunLoop run_loop;
+  Watcher b_watcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            OnReady([&] (MojoResult result) {
+                              EXPECT_EQ(MOJO_RESULT_OK, result);
+                              notified = true;
+                              run_loop.Quit();
+                            })));
+  EXPECT_TRUE(b_watcher.IsWatching());
+
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+  run_loop.Run();
+  EXPECT_TRUE(notified);
+
+  b_watcher.Cancel();
+}
+
+TEST_F(WatcherTest, WatchUnsatisfiable) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+  a.reset();
+
+  Watcher b_watcher;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            NotReached()));
+  EXPECT_FALSE(b_watcher.IsWatching());
+}
+
+TEST_F(WatcherTest, WatchInvalidHandle) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+  a.reset();
+  b.reset();
+
+  Watcher b_watcher;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            NotReached()));
+  EXPECT_FALSE(b_watcher.IsWatching());
+}
+
+TEST_F(WatcherTest, Cancel) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  base::RunLoop run_loop;
+  Watcher b_watcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            NotReached()));
+  EXPECT_TRUE(b_watcher.IsWatching());
+  b_watcher.Cancel();
+  EXPECT_FALSE(b_watcher.IsWatching());
+
+  // This should never trigger the watcher.
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+TEST_F(WatcherTest, CancelOnClose) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  base::RunLoop run_loop;
+  Watcher b_watcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            OnReady([&] (MojoResult result) {
+                              EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+                              run_loop.Quit();
+                            })));
+  EXPECT_TRUE(b_watcher.IsWatching());
+
+  // This should trigger the watcher above.
+  b.reset();
+
+  run_loop.Run();
+
+  EXPECT_FALSE(b_watcher.IsWatching());
+}
+
+TEST_F(WatcherTest, CancelOnDestruction) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+  base::RunLoop run_loop;
+  {
+    Watcher b_watcher;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                              NotReached()));
+    EXPECT_TRUE(b_watcher.IsWatching());
+
+    // |b_watcher| should be cancelled when it goes out of scope.
+  }
+
+  // This should never trigger the watcher above.
+  EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0,
+                                            MOJO_WRITE_MESSAGE_FLAG_NONE));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+TEST_F(WatcherTest, NotifyOnMessageLoopDestruction) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  bool notified = false;
+  Watcher b_watcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            OnReady([&] (MojoResult result) {
+                              EXPECT_EQ(MOJO_RESULT_ABORTED, result);
+                              notified = true;
+                            })));
+  EXPECT_TRUE(b_watcher.IsWatching());
+
+  message_loop_.reset();
+
+  EXPECT_TRUE(notified);
+
+  EXPECT_TRUE(b_watcher.IsWatching());
+  b_watcher.Cancel();
+}
+
+TEST_F(WatcherTest, CloseAndCancel) {
+  ScopedMessagePipeHandle a, b;
+  CreateMessagePipe(nullptr, &a, &b);
+
+  Watcher b_watcher;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                            OnReady([](MojoResult result) { FAIL(); })));
+  EXPECT_TRUE(b_watcher.IsWatching());
+
+  // This should trigger the watcher above...
+  b.reset();
+  // ...but the watcher is cancelled first.
+  b_watcher.Cancel();
+
+  EXPECT_FALSE(b_watcher.IsWatching());
+
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/watcher.cc b/mojo/public/cpp/system/watcher.cc
new file mode 100644
index 0000000..d9319fb
--- /dev/null
+++ b/mojo/public/cpp/system/watcher.cc
@@ -0,0 +1,150 @@
+// Copyright 2016 The Chromium Authors. All 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/system/watcher.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/c/system/functions.h"
+
+namespace mojo {
+
+class Watcher::MessageLoopObserver
+    : public base::MessageLoop::DestructionObserver {
+ public:
+  explicit MessageLoopObserver(Watcher* watcher) : watcher_(watcher) {
+    base::MessageLoop::current()->AddDestructionObserver(this);
+  }
+
+  ~MessageLoopObserver() override {
+    StopObservingIfNecessary();
+  }
+
+ private:
+  // base::MessageLoop::DestructionObserver:
+  void WillDestroyCurrentMessageLoop() override {
+    StopObservingIfNecessary();
+    if (watcher_->IsWatching()) {
+      // TODO(yzshen): Remove this notification. crbug.com/604762
+      watcher_->OnHandleReady(MOJO_RESULT_ABORTED);
+    }
+  }
+
+  void StopObservingIfNecessary() {
+    if (is_observing_) {
+      is_observing_ = false;
+      base::MessageLoop::current()->RemoveDestructionObserver(this);
+    }
+  }
+
+  bool is_observing_ = true;
+  Watcher* watcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
+};
+
+Watcher::Watcher(scoped_refptr<base::SingleThreadTaskRunner> runner)
+    : task_runner_(std::move(runner)),
+      is_default_task_runner_(task_runner_ ==
+                              base::ThreadTaskRunnerHandle::Get()),
+      weak_factory_(this) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  weak_self_ = weak_factory_.GetWeakPtr();
+}
+
+Watcher::~Watcher() {
+  if(IsWatching())
+    Cancel();
+}
+
+bool Watcher::IsWatching() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return handle_.is_valid();
+}
+
+MojoResult Watcher::Start(Handle handle,
+                          MojoHandleSignals signals,
+                          const ReadyCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!IsWatching());
+  DCHECK(!callback.is_null());
+
+  message_loop_observer_.reset(new MessageLoopObserver(this));
+  callback_ = callback;
+  handle_ = handle;
+  MojoResult result = MojoWatch(handle_.value(), signals,
+                                &Watcher::CallOnHandleReady,
+                                reinterpret_cast<uintptr_t>(this));
+  if (result != MOJO_RESULT_OK) {
+    handle_.set_value(kInvalidHandleValue);
+    callback_.Reset();
+    message_loop_observer_.reset();
+    DCHECK(result == MOJO_RESULT_FAILED_PRECONDITION ||
+           result == MOJO_RESULT_INVALID_ARGUMENT);
+    return result;
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+void Watcher::Cancel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // The watch may have already been cancelled if the handle was closed.
+  if (!handle_.is_valid())
+    return;
+
+  MojoResult result =
+      MojoCancelWatch(handle_.value(), reinterpret_cast<uintptr_t>(this));
+  message_loop_observer_.reset();
+  // |result| may be MOJO_RESULT_INVALID_ARGUMENT if |handle_| has closed, but
+  // OnHandleReady has not yet been called.
+  DCHECK(result == MOJO_RESULT_INVALID_ARGUMENT || result == MOJO_RESULT_OK);
+  handle_.set_value(kInvalidHandleValue);
+  callback_.Reset();
+}
+
+void Watcher::OnHandleReady(MojoResult result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  ReadyCallback callback = callback_;
+  if (result == MOJO_RESULT_CANCELLED) {
+    message_loop_observer_.reset();
+    handle_.set_value(kInvalidHandleValue);
+    callback_.Reset();
+  }
+
+  // NOTE: It's legal for |callback| to delete |this|.
+  if (!callback.is_null())
+    callback.Run(result);
+}
+
+// static
+void Watcher::CallOnHandleReady(uintptr_t context,
+                                MojoResult result,
+                                MojoHandleSignalsState signals_state,
+                                MojoWatchNotificationFlags flags) {
+  // NOTE: It is safe to assume the Watcher still exists because this callback
+  // will never be run after the Watcher's destructor.
+  //
+  // TODO: Maybe we should also expose |signals_state| through the Watcher API.
+  // Current HandleWatcher users have no need for it, so it's omitted here.
+  Watcher* watcher = reinterpret_cast<Watcher*>(context);
+  if ((flags & MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM) &&
+      watcher->task_runner_->RunsTasksOnCurrentThread() &&
+      watcher->is_default_task_runner_) {
+    // System notifications will trigger from the task runner passed to
+    // mojo::edk::InitIPCSupport(). In Chrome this happens to always be the
+    // default task runner for the IO thread.
+    watcher->OnHandleReady(result);
+  } else {
+    watcher->task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&Watcher::OnHandleReady, watcher->weak_self_, result));
+  }
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/watcher.h b/mojo/public/cpp/system/watcher.h
new file mode 100644
index 0000000..82f3e81
--- /dev/null
+++ b/mojo/public/cpp/system/watcher.h
@@ -0,0 +1,125 @@
+// Copyright 2016 The Chromium Authors. All 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_WATCHER_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+// A Watcher watches a single Mojo handle for signal state changes.
+//
+// NOTE: Watchers may only be used on threads which have a running MessageLoop.
+class Watcher {
+ public:
+  // A callback to be called any time a watched handle changes state in some
+  // interesting way. The |result| argument indicates one of the following
+  // conditions depending on its value:
+  //
+  //   |MOJO_RESULT_OK|: One or more of the signals being watched is satisfied.
+  //
+  //   |MOJO_RESULT_FAILED_PRECONDITION|: None of the signals being watched can
+  //       ever be satisfied again.
+  //
+  //   |MOJO_RESULT_CANCELLED|: The handle has been closed and the watch has
+  //       been cancelled implicitly.
+  //
+  //   |MOJO_RESULT_ABORTED|: Notifications can no longer be delivered for this
+  //       watcher for some unspecified reason, e.g., the watching thread may
+  //       be shutting down soon. Note that it is still necessary to explicitly
+  //       Cancel() the watch in this case.
+  using ReadyCallback = base::Callback<void(MojoResult result)>;
+
+  explicit Watcher(scoped_refptr<base::SingleThreadTaskRunner> runner =
+                       base::ThreadTaskRunnerHandle::Get());
+
+  // NOTE: This destructor automatically calls |Cancel()| if the Watcher is
+  // still active.
+  ~Watcher();
+
+  // Indicates if the Watcher is currently watching a handle.
+  bool IsWatching() const;
+
+  // Starts watching |handle|. A Watcher may only watch one handle at a time,
+  // but it is safe to call this more than once as long as the previous watch
+  // has been cancelled (i.e. |is_watching()| returns |false|.)
+  //
+  // If no signals in |signals| can ever be satisfied for |handle|, this returns
+  // |MOJO_RESULT_FAILED_PRECONDITION|.
+  //
+  // If |handle| is not a valid watchable (message or data pipe) handle, this
+  // returns |MOJO_RESULT_INVALID_ARGUMENT|.
+  //
+  // Otherwise |MOJO_RESULT_OK| is returned and the handle will be watched until
+  // closure or cancellation.
+  //
+  // Once the watch is started, |callback| may be called at any time on the
+  // current thread until |Cancel()| is called or the handle is closed.
+  //
+  // Destroying the Watcher implicitly calls |Cancel()|.
+  MojoResult Start(Handle handle,
+                   MojoHandleSignals signals,
+                   const ReadyCallback& callback);
+
+  // Cancels the current watch. Once this returns, the callback previously
+  // passed to |Start()| will never be called again for this Watcher.
+  void Cancel();
+
+  Handle handle() const { return handle_; }
+  ReadyCallback ready_callback() const { return callback_; }
+
+ private:
+  class MessageLoopObserver;
+  friend class MessageLoopObserver;
+
+  void OnHandleReady(MojoResult result);
+
+  static void CallOnHandleReady(uintptr_t context,
+                                MojoResult result,
+                                MojoHandleSignalsState signals_state,
+                                MojoWatchNotificationFlags flags);
+
+  base::ThreadChecker thread_checker_;
+
+  // The TaskRunner of this Watcher's owning thread. This field is safe to
+  // access from any thread.
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  // Whether |task_runner_| is the same as base::ThreadTaskRunnerHandle::Get()
+  // for the thread.
+  const bool is_default_task_runner_;
+
+  std::unique_ptr<MessageLoopObserver> message_loop_observer_;
+
+  // A persistent weak reference to this Watcher which can be passed to the
+  // Dispatcher any time this object should be signalled. Safe to access (but
+  // not to dereference!) from any thread.
+  base::WeakPtr<Watcher> weak_self_;
+
+  // Fields below must only be accessed on the Watcher's owning thread.
+
+  // The handle currently under watch. Not owned.
+  Handle handle_;
+
+  // The callback to call when the handle is signaled.
+  ReadyCallback callback_;
+
+  base::WeakPtrFactory<Watcher> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(Watcher);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
diff --git a/mojo/public/cpp/test_support/BUILD.gn b/mojo/public/cpp/test_support/BUILD.gn
new file mode 100644
index 0000000..3d33b4a
--- /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/public/mojo_public.gyp:mojo_public_test_utils
+static_library("test_utils") {
+  testonly = true
+
+  sources = [
+    "lib/test_support.cc",
+    "lib/test_utils.cc",
+    "test_utils.h",
+  ]
+
+  deps = [
+    "//mojo/public/c/test_support",
+    "//mojo/public/cpp/system",
+    "//testing/gtest",
+  ]
+}
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..92288c4
--- /dev/null
+++ b/mojo/public/cpp/test_support/lib/test_utils.cc
@@ -0,0 +1,100 @@
+// 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 <stddef.h>
+#include <stdint.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,
+                nullptr);
+      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,
+                          const char* sub_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, sub_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..9a536e6
--- /dev/null
+++ b/mojo/public/cpp/test_support/test_support.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_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,
+                          const char* sub_test_name,
+                          double value,
+                          const char* units) {
+  MojoTestSupportLogPerfResult(test_name, sub_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..6fd5a9e
--- /dev/null
+++ b/mojo/public/cpp/test_support/test_utils.h
@@ -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.
+
+#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,
+                          const char* sub_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/interfaces/BUILD.gn b/mojo/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..654145b
--- /dev/null
+++ b/mojo/public/interfaces/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("interfaces") {
+  deps = [
+    "application",
+    "bindings",
+    "network",
+  ]
+}
diff --git a/mojo/public/interfaces/bindings/BUILD.gn b/mojo/public/interfaces/bindings/BUILD.gn
new file mode 100644
index 0000000..c7421b9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../tools/bindings/mojom.gni")
+
+mojom("bindings") {
+  sources = [
+    "interface_control_messages.mojom",
+    "pipe_control_messages.mojom",
+  ]
+}
diff --git a/mojo/public/interfaces/bindings/interface_control_messages.mojom b/mojo/public/interfaces/bindings/interface_control_messages.mojom
new file mode 100644
index 0000000..2143c06
--- /dev/null
+++ b/mojo/public/interfaces/bindings/interface_control_messages.mojom
@@ -0,0 +1,89 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[JavaPackage="org.chromium.mojo.bindings"]
+module mojo;
+
+// For each user-defined interface, some control functions are provided at the
+// same end of the message pipe as the user-defined interface, providing
+// information about the user-defined interface.
+
+////////////////////////////////////////////////////////////////////////////////
+// Run@0xFFFFFFFF(RunInput input) => (RunOutput? output);
+//
+// This control function runs the input command. If the command is not
+// supported, |output| is set to null; otherwise |output| stores the result,
+// whose type depends on the input.
+//
+// TODO(yzshen): Once union support is ready, switch the following definition
+// to:
+// struct RunMessageParams {
+//   RunInput input;
+// };
+// union RunInput {
+//   QueryVersion query_version;
+// };
+//
+// struct RunResponseMessageParams {
+//   RunOutput? output;
+// };
+// union RunOutput {
+//   QueryVersionResult query_version_result;
+// };
+
+const uint32 kRunMessageId = 0xFFFFFFFF;
+
+struct RunMessageParams {
+  // The reserved fields make the layout compatible with the RunInput union
+  // described above.
+  uint32 reserved0;  // Must be set to 16.
+  uint32 reserved1;  // Must be set to 0;
+  QueryVersion query_version;
+};
+
+struct RunResponseMessageParams {
+  // The reserved fields make the layout compatible with the RunOutput union
+  // described above.
+  uint32 reserved0;  // Must be set to 16.
+  uint32 reserved1;  // Must be set to 0.
+  QueryVersionResult query_version_result;
+};
+
+// Queries the max supported version of the user-defined interface.
+struct QueryVersion {
+};
+struct QueryVersionResult {
+  uint32 version;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// RunOrClosePipe@0xFFFFFFFE(RunOrClosePipeInput input);
+//
+// This control function runs the input command. If the operation fails or the
+// command is not supported, the message pipe is closed.
+//
+// TODO(yzshen): Once union support is ready, switch the following definition
+// to:
+// struct RunOrClosePipeMessageParams {
+//   RunOrClosePipeInput input;
+// };
+// union RunOrClosePipeInput {
+//   RequireVersion require_version;
+// };
+
+const uint32 kRunOrClosePipeMessageId = 0xFFFFFFFE;
+
+struct RunOrClosePipeMessageParams {
+  // The reserved fields make the layout compatible with the RunOrClosePipeInput
+  // union described above.
+  uint32 reserved0;  // Must be set to 16.
+  uint32 reserved1;  // Must be set to 0.
+  RequireVersion require_version;
+};
+
+// If the specified version of the user-defined interface is not supported, the
+// function fails and the pipe is closed.
+struct RequireVersion {
+  uint32 version;
+};
diff --git a/mojo/public/interfaces/bindings/pipe_control_messages.mojom b/mojo/public/interfaces/bindings/pipe_control_messages.mojom
new file mode 100644
index 0000000..c743ffe
--- /dev/null
+++ b/mojo/public/interfaces/bindings/pipe_control_messages.mojom
@@ -0,0 +1,50 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[JavaPackage="org.chromium.mojo.bindings.pipecontrol"]
+module mojo.pipe_control;
+
+// For each message pipe running user-defined interfaces, some control
+// functions are provided and used by the routers at both ends of the pipe, so
+// that they can coordinate to manage interface endpoints.
+// All these control messages will have the interface ID field in the message
+// header set to invalid.
+
+////////////////////////////////////////////////////////////////////////////////
+// RunOrClosePipe@0xFFFFFFFE(RunOrClosePipeInput input);
+//
+// This control function runs the input command. If the operation fails or the
+// command is not supported, the message pipe is closed.
+
+const uint32 kRunOrClosePipeMessageId = 0xFFFFFFFE;
+
+struct RunOrClosePipeMessageParams {
+  RunOrClosePipeInput input;
+};
+
+union RunOrClosePipeInput {
+  PeerAssociatedEndpointClosedEvent peer_associated_endpoint_closed_event;
+  AssociatedEndpointClosedBeforeSentEvent
+      associated_endpoint_closed_before_sent_event;
+};
+
+// An event to notify that an interface endpoint set up at the message sender
+// side has been closed.
+// 
+// This event is only used for associated interfaces. When a master interface
+// is closed, the message pipe is shutdown directly.
+struct PeerAssociatedEndpointClosedEvent {
+  // The interface ID.
+  uint32 id;
+};
+
+// An event to notify that an interface endpoint that is meant to be set up at
+// the message receiver side has been closed before sent over the message pipe.
+//
+// This event is only used for associated interfaces.
+struct AssociatedEndpointClosedBeforeSentEvent {
+  // The interface ID.
+  uint32 id;
+};
+
diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
new file mode 100644
index 0000000..0a48076
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -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.
+
+import("../../../tools/bindings/mojom.gni")
+
+mojom("test_interfaces") {
+  testonly = true
+  sources = [
+    "math_calculator.mojom",
+    "no_module.mojom",
+    "ping_service.mojom",
+    "rect.mojom",
+    "regression_tests.mojom",
+    "sample_factory.mojom",
+    "sample_interfaces.mojom",
+    "sample_service.mojom",
+    "scoping.mojom",
+    "serialization_test_structs.mojom",
+    "test_constants.mojom",
+    "test_native_types.mojom",
+    "test_structs.mojom",
+    "test_sync_methods.mojom",
+    "validation_test_interfaces.mojom",
+  ]
+  public_deps = [
+    ":test_mojom_import",
+    ":test_mojom_import2",
+  ]
+
+  use_new_wrapper_types = true
+}
+
+mojom("test_mojom_import") {
+  testonly = true
+  sources = [
+    "sample_import.mojom",
+  ]
+  use_new_wrapper_types = true
+}
+
+mojom("test_mojom_import_wrapper") {
+  testonly = true
+  public_deps = [
+    ":test_mojom_import",
+  ]
+}
+
+mojom("test_mojom_import_wrapper_wrapper") {
+  testonly = true
+  public_deps = [
+    ":test_mojom_import_wrapper",
+  ]
+}
+
+mojom("test_mojom_import2") {
+  testonly = true
+  sources = [
+    "sample_import2.mojom",
+  ]
+  public_deps = [
+    ":test_mojom_import",
+    ":test_mojom_import_wrapper_wrapper",
+  ]
+  use_new_wrapper_types = true
+}
+
+mojom("test_struct_traits_interfaces") {
+  testonly = true
+  sources = [
+    "struct_with_traits.mojom",
+  ]
+  use_new_wrapper_types = true
+}
+
+mojom("test_interfaces_experimental") {
+  testonly = true
+  sources = [
+    "test_unions.mojom",
+  ]
+  use_new_wrapper_types = true
+}
+
+mojom("test_associated_interfaces") {
+  # These files are not included in the test_interfaces target because
+  # associated interfaces are not supported by all bindings languages yet.
+  testonly = true
+  sources = [
+    "test_associated_interfaces.mojom",
+    "validation_test_associated_interfaces.mojom",
+  ]
+  use_new_wrapper_types = true
+}
+
+mojom("versioning_test_service_interfaces") {
+  testonly = true
+  sources = [
+    "versioning_test_service.mojom",
+  ]
+  use_new_wrapper_types = true
+}
+
+mojom("versioning_test_client_interfaces") {
+  testonly = true
+  sources = [
+    "versioning_test_client.mojom",
+  ]
+  use_new_wrapper_types = true
+}
+
+mojom("test_wtf_types") {
+  testonly = true
+
+  sources = [
+    "test_wtf_types.mojom",
+  ]
+  use_new_wrapper_types = true
+}
+
+mojom("test_no_sources") {
+  testonly = true
+
+  public_deps = [
+    ":test_interfaces",
+  ]
+}
diff --git a/mojo/public/interfaces/bindings/tests/OWNERS b/mojo/public/interfaces/bindings/tests/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
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/associated_conformance_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data
new file mode 100644
index 0000000..efac7ed
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data
@@ -0,0 +1,13 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]0                  // version
+[u4]4                  // associated interface pointer: interface ID
+[u4]1                  // associated interface pointer: version
+[anchr]method0_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data
new file mode 100644
index 0000000..dd16401
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data
@@ -0,0 +1,14 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]0                  // version
+[u4]0xFFFFFFFF         // associated interface pointer: unexpected invalid
+                       // interface ID
+[u4]1                  // associated interface pointer: version
+[anchr]method0_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.expected
new file mode 100644
index 0000000..d8eda1f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data
new file mode 100644
index 0000000..7ebbe07
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data
@@ -0,0 +1,13 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]1                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method1_params  // num_bytes
+[u4]0                  // version
+[u4]4                  // associated interface request
+[u4]0                  // padding
+[anchr]method1_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data
new file mode 100644
index 0000000..d74a306
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data
@@ -0,0 +1,14 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]1                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method1_params  // num_bytes
+[u4]0                  // version
+[u4]0xFFFFFFFF         // associated interface request: unexpected invalid
+                       // interface ID
+[u4]0                  // padding
+[anchr]method1_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.expected
new file mode 100644
index 0000000..d8eda1f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data
new file mode 100644
index 0000000..511cd0d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data
@@ -0,0 +1,15 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]2                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]0                  // version
+[u4]0                  // associated interface pointer: 0 is the special master
+                       // interface ID and should never be used as associated
+                       // interface ID
+[u4]1                  // associated interface pointer: version
+[anchr]method2_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected
new file mode 100644
index 0000000..420b421
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data
new file mode 100644
index 0000000..da22db1
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data
@@ -0,0 +1,21 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[dist4]associated_interface_array  // num_bytes
+[u4]2                              // num_elements
+[u4]4                              // interface ID
+[u4]14                             // version
+[u4]5                              // interface ID
+[u4]18                             // version
+[anchr]associated_interface_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data
new file mode 100644
index 0000000..788cefa
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data
@@ -0,0 +1,21 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[dist4]associated_interface_array  // num_bytes
+[u4]2                              // num_elements
+[u4]0xFFFFFFFF                     // unexpected invalid interface ID
+[u4]14                             // version
+[u4]5                              // interface ID
+[u4]18                             // version
+[anchr]associated_interface_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.expected
new file mode 100644
index 0000000..d8eda1f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.data b/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.data
new file mode 100644
index 0000000..30032a1
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.data
@@ -0,0 +1,7 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version number
+[u4]0                  // interface ID
+[u4]2                  // There is no Method2
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.expected b/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.expected
new file mode 100644
index 0000000..a32d895
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/boundscheck_msghdr_no_such_method.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD
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..68899f4
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data
@@ -0,0 +1,2 @@
+[u4]24  // num_bytes: Bigger than the total size of the message.
+[u4]0   // version
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_flag_combo.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.data
new file mode 100644
index 0000000..dfb2dd2
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0x80000000         // name
+[u4]3                  // flags: This combination is illegal.
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.expected
new file mode 100644
index 0000000..c33fde3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS
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..27804a8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0x80000000         // name
+[u4]1                  // flags: This is a response message which expects to
+                       // have a request ID.
+[u4]0                  // padding
+[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_no_such_method.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.data
new file mode 100644
index 0000000..6302bae
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.data
@@ -0,0 +1,7 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version number
+[u4]0                  // interface ID
+[u4]9999               // There is no Method9999.
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected
new file mode 100644
index 0000000..a32d895
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD
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..2fd0fcd
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data
@@ -0,0 +1,6 @@
+[u4]0xFFFFFFFF  // num_bytes: Test whether a huge value will cause overflow.
+[u4]0           // version
+[u4]0           // interface ID
+[u4]0x80000000  // name
+[u4]0           // flags
+[u4]0           // padding
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..f58eca9
--- /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]0                  // version
+[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..e98f66f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data
@@ -0,0 +1,6 @@
+[u4]0           // num_bytes
+[u4]0           // version
+[u4]0           // interface ID
+[u4]0x80000000  // name
+[u4]0           // flags
+[u4]0           // padding
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_version_mismatch_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.data
new file mode 100644
index 0000000..df9e418
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.data
@@ -0,0 +1,9 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0x80000000         // name
+[u4]0                  // flags
+[u4]0                  // padding
+[u8]0                  // Extra bytes that result in mismatched |num_bytes| and
+                       // |version|.
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_1.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_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_version_mismatch_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.data
new file mode 100644
index 0000000..e2c574e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.data
@@ -0,0 +1,10 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0x80000000         // name
+[u4]1                  // flags
+[u4]0                  // padding
+[u8]0                  // request_id
+[u8]0                  // Extra bytes that result in mismatched |num_bytes| and
+                       // |version|.
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_2.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_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_version_mismatch_3.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.data
new file mode 100644
index 0000000..f7a321b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.data
@@ -0,0 +1,7 @@
+[dist4]message_header  // num_bytes
+[u4]8                  // version: |num_bytes| is too small for |version|.
+[u4]0                  // interface ID
+[u4]0x80000000         // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_version_mismatch_3.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..841da5e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data
@@ -0,0 +1,13 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]0                  // version
+[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..cff6a30
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data
@@ -0,0 +1,11 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[u4]16  // num_bytes: Incomplete struct.
+[u4]0   // version
+[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..3f03ab2
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data
@@ -0,0 +1,9 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[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_invalid_request_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.data
new file mode 100644
index 0000000..7aee806
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]2                  // flags: kMessageIsResponse is set in a request.
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected
new file mode 100644
index 0000000..c33fde3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data
new file mode 100644
index 0000000..5448c5f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data
@@ -0,0 +1,9 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]1                  // flags: kMessageExpectsResponse is set in a request
+                       // for a method that does not take a response.
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected
new file mode 100644
index 0000000..c33fde3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS
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..4a3e2fe
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data
@@ -0,0 +1,12 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[u4]0xFFFFFFFF  // num_bytes: Test whether a huge value will cause overflow.
+[u4]0           // version
+[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..fa4c555
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data
@@ -0,0 +1,11 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes: Less than the minimal size that we expect.
+[u4]0                  // version
+[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..d62206e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data
@@ -0,0 +1,12 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[u4]4                  // num_bytes: Less than the size of struct header.
+[u4]0                  // version
+[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_mthd10_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.data
new file mode 100644
index 0000000..5dca2fe
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.data
@@ -0,0 +1,48 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]10                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method10_params  // num_bytes
+[u4]0                   // version
+[dist8]map_data_ptr     // param0
+[anchr]method10_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header  // num_bytes
+[u4]0                          // version
+[dist8]key_array_ptr
+[dist8]value_array_ptr
+[anchr]map_data_struct_header
+
+[anchr]key_array_ptr
+[dist4]key_array_member   // num_bytes
+[u4]2                     // num_elements
+[dist8]key_string_1
+[dist8]key_string_2
+[anchr]key_array_member
+
+[anchr]key_string_1
+[dist4]key_string_1_member  // num_bytes
+[u4]5                       // num_elements
+0 1 2 3 4
+[anchr]key_string_1_member
+
+[u4]0 [u4]0 [u1]0 [u1]0 [u1]0  // manual padding for array alignment
+
+[anchr]key_string_2
+[dist4]key_string_2_member  // num_bytes
+[u4]5                       // num_elements
+5 6 7 8 9
+[anchr]key_string_2_member
+
+[u4]0 [u4]0 [u1]0 [u1]0 [u1]0  // manual padding for array alignment
+
+[anchr]value_array_ptr
+[dist4]value_array_member   // num_bytes
+[u4]2                       // num_elements
+1 2
+[anchr]value_array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.data
new file mode 100644
index 0000000..f64fbc3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.data
@@ -0,0 +1,48 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]10                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method10_params  // num_bytes
+[u4]0                   // version
+[dist8]map_data_ptr     // param0
+[anchr]method10_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header  // num_bytes
+[u4]0                          // version
+[dist8]key_array_ptr
+[dist8]value_array_ptr
+[anchr]map_data_struct_header
+
+[anchr]key_array_ptr
+[dist4]key_array_member   // num_bytes
+[u4]2                     // num_elements
+[dist8]key_string_1
+[dist8]key_string_2
+[anchr]key_array_member
+
+[anchr]key_string_1
+[dist4]key_string_1_member  // num_bytes
+[u4]5                       // num_elements
+0 1 2 3 4
+[anchr]key_string_1_member
+
+[u4]0 [u4]0 [u1]0 [u1]0 [u1]0  // manual padding for array alignment
+
+[anchr]key_string_2
+[dist4]key_string_2_member  // num_bytes
+[u4]5                       // num_elements
+0 1 2 3 4
+[anchr]key_string_2_member
+
+[u4]0 [u4]0 [u1]0 [u1]0 [u1]0  // manual padding for array alignment
+
+[anchr]value_array_ptr
+[dist4]value_array_member   // num_bytes
+[u4]2                       // num_elements
+1 2
+[anchr]value_array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_good_non_unique_keys.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.data
new file mode 100644
index 0000000..3a99dc2
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.data
@@ -0,0 +1,25 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]10                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method10_params  // num_bytes
+[u4]0                   // version
+[dist8]map_data_ptr     // param0
+[anchr]method10_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header  // num_bytes
+[u4]0                          // version
+[u8]0                          // null keys array
+[dist8]value_array_ptr
+[anchr]map_data_struct_header
+
+[anchr]value_array_ptr
+[dist4]value_array_member   // num_bytes
+[u4]2                       // num_elements
+1 2
+[anchr]value_array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_keys.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.data
new file mode 100644
index 0000000..459d806
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.data
@@ -0,0 +1,40 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]10                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method10_params  // num_bytes
+[u4]0                   // version
+[dist8]map_data_ptr     // param0
+[anchr]method10_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header  // num_bytes
+[u4]0                          // version
+[dist8]key_array_ptr
+[u8]0                          // null values array
+[anchr]map_data_struct_header
+
+[anchr]key_array_ptr
+[dist4]key_array_member   // num_bytes
+[u4]2                     // num_elements
+[dist8]key_string_1
+[dist8]key_string_2
+[anchr]key_array_member
+
+[anchr]key_string_1
+[dist4]key_string_1_member  // num_bytes
+[u4]5                       // num_elements
+0 1 2 3 4
+[anchr]key_string_1_member
+
+[u4]0 [u4]0 [u1]0 [u1]0 [u1]0  // manual padding for array alignment
+
+[anchr]key_string_2
+[dist4]key_string_2_member  // num_bytes
+[u4]5                       // num_elements
+5 6 7 8 9
+[anchr]key_string_2_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_null_values.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.data
new file mode 100644
index 0000000..9127a26
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.data
@@ -0,0 +1,40 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]10                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method10_params  // num_bytes
+[u4]0                   // version
+[dist8]map_data_ptr     // param0
+[anchr]method10_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header  // num_bytes
+[u4]0                          // version
+[dist8]key_array_ptr
+[dist8]value_array_ptr
+[anchr]map_data_struct_header
+
+[anchr]key_array_ptr
+[dist4]key_array_member   // num_bytes
+[u4]2                     // num_elements
+[dist8]key_string_1
+[u8]0                     // one null key
+[anchr]key_array_member
+
+[anchr]key_string_1
+[dist4]key_string_1_member  // num_bytes
+[u4]5                       // num_elements
+0 1 2 3 4
+[anchr]key_string_1_member
+
+[u4]0 [u4]0 [u1]0 [u1]0 [u1]0  // manual padding for array alignment
+
+[anchr]value_array_ptr
+[dist4]value_array_member   // num_bytes
+[u4]2                       // num_elements
+1 2
+[anchr]value_array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_one_null_key.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.data
new file mode 100644
index 0000000..a2f9038
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.data
@@ -0,0 +1,48 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]10                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method10_params  // num_bytes
+[u4]0                   // version
+[dist8]map_data_ptr     // param0
+[anchr]method10_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header  // num_bytes
+[u4]0                          // version
+[dist8]key_array_ptr
+[dist8]value_array_ptr
+[anchr]map_data_struct_header
+
+[anchr]key_array_ptr
+[dist4]key_array_member   // num_bytes
+[u4]2                     // num_elements
+[dist8]key_string_1
+[dist8]key_string_2
+[anchr]key_array_member
+
+[anchr]key_string_1
+[dist4]key_string_1_member  // num_bytes
+[u4]5                       // num_elements
+0 1 2 3 4
+[anchr]key_string_1_member
+
+[u4]0 [u4]0 [u1]0 [u1]0 [u1]0  // manual padding for array alignment
+
+[anchr]key_string_2
+[dist4]key_string_2_member  // num_bytes
+[u4]5                       // num_elements
+5 6 7 8 9
+[anchr]key_string_2_member
+
+[u4]0 [u4]0 [u1]0 [u1]0 [u1]0  // manual padding for array alignment
+
+[anchr]value_array_ptr
+[dist4]value_array_member   // num_bytes
+[u4]1                       // num_elements
+1                           // unequal size
+[anchr]value_array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.expected
new file mode 100644
index 0000000..2798d48
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd10_unequal_array_size.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.data
new file mode 100644
index 0000000..781079b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.data
@@ -0,0 +1,19 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]11                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method11_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method11_params
+
+[anchr]param0_ptr
+[dist4]struct_g         // num_bytes
+[u4]0                   // version
+[s4]123                 // i
+[u4]0                   // padding
+[anchr]struct_g
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version0.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.data
new file mode 100644
index 0000000..b9ab5bf
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.data
@@ -0,0 +1,20 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]11                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method11_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method11_params
+
+[anchr]param0_ptr
+[dist4]struct_g         // num_bytes
+[u4]1                   // version
+[s4]123                 // i
+[u4]0                   // padding
+[u8]0                   // struct_a
+[anchr]struct_g
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version1.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.data
new file mode 100644
index 0000000..7d61446
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.data
@@ -0,0 +1,20 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]11                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method11_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method11_params
+
+[anchr]param0_ptr
+[dist4]struct_g         // num_bytes
+[u4]2                   // version
+[s4]123                 // i
+[u4]0                   // padding
+[u8]0                   // struct_a
+[anchr]struct_g
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version2.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.data
new file mode 100644
index 0000000..3c3ee12
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.data
@@ -0,0 +1,28 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]11                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method11_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method11_params
+
+[anchr]param0_ptr
+[dist4]struct_g         // num_bytes
+[u4]3                   // version
+[s4]123                 // i
+[b]00000001             // b
+0 0 0                   // padding
+[u8]0                   // struct_a
+[dist8]str_ptr          // str
+[anchr]struct_g
+
+[anchr]str_ptr
+[dist4]string           // num_bytes
+[u4]2                   // num_elements
+0 1
+[anchr]string
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version3.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.data
new file mode 100644
index 0000000..2e9fde6
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.data
@@ -0,0 +1,30 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]11                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method11_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method11_params
+
+[anchr]param0_ptr
+[dist4]struct_g         // num_bytes
+[u4]5                   // version: Newer than what the validator knows.
+                        // It is okay that the size is the same as the latest
+                        // version that the validator knows.
+[s4]123                 // i
+[b]00000001             // b
+0 0 0                   // padding
+[u8]0                   // struct_a
+[dist8]str_ptr          // str
+[anchr]struct_g
+
+[anchr]str_ptr
+[dist4]string           // num_bytes
+[u4]2                   // num_elements
+0 1
+[anchr]string
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_1.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.data
new file mode 100644
index 0000000..9a95626
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.data
@@ -0,0 +1,30 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]11                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method11_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method11_params
+
+[anchr]param0_ptr
+[dist4]struct_g         // num_bytes
+[u4]5                   // version: Newer than what the validator knows.
+[s4]123                 // i
+[b]00000001             // b
+0 0 0                   // padding
+[u8]0                   // struct_a
+[dist8]str_ptr          // str
+[u8]0                   // unknown contents
+[u8]0                   // unknown contents
+[anchr]struct_g
+
+[anchr]str_ptr
+[dist4]string           // num_bytes
+[u4]2                   // num_elements
+0 1
+[anchr]string
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_good_version_newer_than_known_2.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.data
new file mode 100644
index 0000000..c2e5a8d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.data
@@ -0,0 +1,21 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]11                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method11_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method11_params
+
+[anchr]param0_ptr
+[dist4]struct_g         // num_bytes: The size is too big for the version.
+[u4]1                   // version
+[s4]123                 // i
+[u4]0                   // padding
+[u8]0                   // struct_a
+[u8]0                   // Unexpected contents that cause the mismatch.
+[anchr]struct_g
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_1.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.data
new file mode 100644
index 0000000..edfe5fa
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.data
@@ -0,0 +1,19 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]11                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method11_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method11_params
+
+[anchr]param0_ptr
+[dist4]struct_g         // num_bytes: The size is too small for the version.
+[u4]2                   // version
+[s4]123                 // i
+[u4]0                   // padding
+[anchr]struct_g
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd11_num_bytes_version_mismatch_2.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data
new file mode 100644
index 0000000..13135ae
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data
@@ -0,0 +1,9 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]12                 // name
+[u4]0                  // flags: kMessageExpectsResponse is not set but
+                       // expected.
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected
new file mode 100644
index 0000000..c33fde3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.data
new file mode 100644
index 0000000..51973be
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.data
@@ -0,0 +1,17 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]13                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method13_params // num_bytes
+[u4]0                  // version
+[u4]0xFFFFFFFF         // param0
+[u4]1234
+[u4]65535              // param1
+[u4]0xFFFFFFFF         // param2
+[u4]3242
+[u4]0                  // padding
+[anchr]method13_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_1.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.data
new file mode 100644
index 0000000..b739731
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.data
@@ -0,0 +1,19 @@
+[handles]2
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]13                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method13_params // num_bytes
+[u4]0                  // version
+[u4]0                  // param0
+[u4]1234
+[u4]65535              // param1
+[u4]1                  // param2
+[u4]3242
+[u4]0                  // padding
+[anchr]method13_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd13_good_2.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_known_enum_values.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_known_enum_values.data
new file mode 100644
index 0000000..1444849
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_known_enum_values.data
@@ -0,0 +1,13 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]14                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method14_params // num_bytes
+[u4]0                  // version
+[u4]0                  // param0
+[u4]1                  // param1
+[anchr]method14_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_known_enum_values.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_known_enum_values.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_known_enum_values.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_uknown_extensible_enum_value.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_uknown_extensible_enum_value.data
new file mode 100644
index 0000000..50b9ea3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_uknown_extensible_enum_value.data
@@ -0,0 +1,13 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]14                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method14_params // num_bytes
+[u4]0                  // version
+[u4]0                  // param0
+[u4]0xFFFFFFFF         // param1: Unknown value is okay for extensible enum.
+[anchr]method14_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_uknown_extensible_enum_value.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_uknown_extensible_enum_value.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_good_uknown_extensible_enum_value.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_uknown_non_extensible_enum_value.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_uknown_non_extensible_enum_value.data
new file mode 100644
index 0000000..567f23b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_uknown_non_extensible_enum_value.data
@@ -0,0 +1,14 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]14                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method14_params // num_bytes
+[u4]0                  // version
+[u4]0xFFFFFFFF         // param0: Unknown value is not allowed for
+                       // non-extensible enum.
+[u4]2                  // param1
+[anchr]method14_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_uknown_non_extensible_enum_value.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_uknown_non_extensible_enum_value.expected
new file mode 100644
index 0000000..9ef4ce3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd14_uknown_non_extensible_enum_value.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNKNOWN_ENUM_VALUE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_known_enum_array_values.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_known_enum_array_values.data
new file mode 100644
index 0000000..c418d89
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_known_enum_array_values.data
@@ -0,0 +1,27 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]15                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method15_params // num_bytes
+[u4]0                  // version
+[dist8]enum_array_0    // param0
+[dist8]enum_array_1    // param1
+[anchr]method15_params
+
+[anchr]enum_array_0
+[dist4]enum_array_0_member  // num_bytes
+[u4]2                       // num_elements
+[u4]0
+[u4]1
+[anchr]enum_array_0_member
+
+[anchr]enum_array_1
+[dist4]enum_array_1_member  // num_bytes
+[u4]2                       // num_elements
+[u4]0
+[u4]1
+[anchr]enum_array_1_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_known_enum_array_values.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_known_enum_array_values.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_known_enum_array_values.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_uknown_extensible_enum_array_value.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_uknown_extensible_enum_array_value.data
new file mode 100644
index 0000000..b6be6d9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_uknown_extensible_enum_array_value.data
@@ -0,0 +1,20 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]15                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method15_params // num_bytes
+[u4]0                  // version
+[u8]0                  // param0
+[dist8]enum_array_1    // param1
+[anchr]method15_params
+
+[anchr]enum_array_1
+[dist4]enum_array_1_member  // num_bytes
+[u4]2                       // num_elements
+[u4]0
+[u4]0x1234                  // Unknown value is okay for extensible enum.
+[anchr]enum_array_1_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_uknown_extensible_enum_array_value.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_uknown_extensible_enum_array_value.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_uknown_extensible_enum_array_value.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.data
new file mode 100644
index 0000000..1cd3484
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.data
@@ -0,0 +1,21 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]15                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method15_params // num_bytes
+[u4]0                  // version
+[dist8]enum_array_0    // param0
+[u8]0                  // param1
+[anchr]method15_params
+
+[anchr]enum_array_0
+[dist4]enum_array_0_member  // num_bytes
+[u4]2                       // num_elements
+[u4]0x5678                  // Unknown value is not allowed for non-extensible
+                            // enum.
+[u4]1
+[anchr]enum_array_0_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.expected
new file mode 100644
index 0000000..9ef4ce3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNKNOWN_ENUM_VALUE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_key.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_key.data
new file mode 100644
index 0000000..0425ea7
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_key.data
@@ -0,0 +1,34 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]16                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method16_params  // num_bytes
+[u4]0                   // version
+[dist8]map_data_ptr     // param0
+[anchr]method16_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header  // num_bytes
+[u4]0                          // version
+[dist8]key_array_ptr
+[dist8]value_array_ptr
+[anchr]map_data_struct_header
+
+[anchr]key_array_ptr
+[dist4]key_array_member     // num_bytes
+[u4]2                       // num_elements
+[u4]0x5678                  // Unknown value is not allowed for non-extensible
+                            // enum.
+[u4]1
+[anchr]key_array_member
+
+[anchr]value_array_ptr
+[dist4]value_array_member   // num_bytes
+[u4]2                       // num_elements
+[u4]1
+[u4]2
+[anchr]value_array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_key.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_key.expected
new file mode 100644
index 0000000..9ef4ce3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_key.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNKNOWN_ENUM_VALUE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_value.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_value.data
new file mode 100644
index 0000000..2c2ea26
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_value.data
@@ -0,0 +1,34 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]16                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method16_params  // num_bytes
+[u4]0                   // version
+[dist8]map_data_ptr     // param0
+[anchr]method16_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header  // num_bytes
+[u4]0                          // version
+[dist8]key_array_ptr
+[dist8]value_array_ptr
+[anchr]map_data_struct_header
+
+[anchr]key_array_ptr
+[dist4]key_array_member     // num_bytes
+[u4]2                       // num_elements
+[u4]1
+[u4]2
+[anchr]key_array_member
+
+[anchr]value_array_ptr
+[dist4]value_array_member   // num_bytes
+[u4]2                       // num_elements
+[u4]0x5678                  // Unknown value is not allowed for non-extensible
+                            // enum.
+[u4]1
+[anchr]value_array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_value.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_value.expected
new file mode 100644
index 0000000..9ef4ce3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd16_uknown_non_extensible_enum_map_value.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNKNOWN_ENUM_VALUE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_good.data
new file mode 100644
index 0000000..48807ab
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_good.data
@@ -0,0 +1,23 @@
+[handles]10  // Larger than the number of handles that we know about is okay.
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]17                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method17_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method17_params
+
+[anchr]param0_ptr
+[dist4]interface_array  // num_bytes
+[u4]2                   // num_elements
+[u4]4                   // handle
+[u4]14                  // version
+[u4]5                   // handle
+[u4]18                  // version
+[anchr]interface_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_interface_handle_out_of_range_in_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_interface_handle_out_of_range_in_array.data
new file mode 100644
index 0000000..e549a10
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_interface_handle_out_of_range_in_array.data
@@ -0,0 +1,24 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]17                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method17_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method17_params
+
+[anchr]param0_ptr
+[dist4]interface_array  // num_bytes
+[u4]2                   // num_elements
+[u4]4                   // handle
+[u4]14                  // version
+[u4]10                  // handle: It is outside of the valid encoded handle
+                        // range [0, 10)
+[u4]18                  // version
+[anchr]interface_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_interface_handle_out_of_range_in_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_interface_handle_out_of_range_in_array.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_interface_handle_out_of_range_in_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_unexpected_invalid_interface_in_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_unexpected_invalid_interface_in_array.data
new file mode 100644
index 0000000..1ce1d92
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_unexpected_invalid_interface_in_array.data
@@ -0,0 +1,23 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]17                 // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method17_params  // num_bytes
+[u4]0                   // version
+[dist8]param0_ptr       // param0
+[anchr]method17_params
+
+[anchr]param0_ptr
+[dist4]interface_array  // num_bytes
+[u4]2                   // num_elements
+[u4]4                   // handle
+[u4]14                  // version
+[u4]0xFFFFFFFF          // handle: An unexpected invalid handle.
+[u4]18                  // version
+[anchr]interface_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_unexpected_invalid_interface_in_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_unexpected_invalid_interface_in_array.expected
new file mode 100644
index 0000000..6768236
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd17_unexpected_invalid_interface_in_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE
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..b6c201a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data
@@ -0,0 +1,18 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]1                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method1_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[anchr]method1_params
+
+[anchr]param0_ptr
+[dist4]struct_a  // num_bytes
+[u4]0            // version
+[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..ec39b71
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data
@@ -0,0 +1,20 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]1                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method1_params  // num_bytes
+[u4]0                  // version
+[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]0            // version
+[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..6d92050
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data
@@ -0,0 +1,13 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]1                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method1_params   // num_bytes
+[u4]0                   // version
+[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..569733b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data
@@ -0,0 +1,12 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]1                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method1_params  // num_bytes
+[u4]0                  // version
+[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..40719f5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data
@@ -0,0 +1,34 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]2                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b     // num_bytes
+[u4]0               // version
+[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]0                   // version
+[u8]12345               // i
+[anchr]struct_a_member
+
+[anchr]param1_ptr
+[dist4]struct_a_param  // num_bytes
+[u4]0                  // version
+[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..ef6525b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data
@@ -0,0 +1,27 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]2                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b     // num_bytes
+[u4]0               // version
+[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]0            // version
+[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..58b25a1
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data
@@ -0,0 +1,32 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]2                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b     // num_bytes
+[u4]0               // version
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+[anchr]struct_a_ptr
+[dist4]struct_a_member  // num_bytes
+[u4]0                   // version
+
+[anchr]param1_ptr
+// The following |num_bytes| and |version| fields are also the |i| field of the
+// previous struct.
+[dist4]struct_a_param   // num_bytes
+[u4]0                   // version
+[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..3038ed8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data
@@ -0,0 +1,34 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]2                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method2_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b     // num_bytes
+[u4]0               // version
+[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]0                  // version
+[u8]67890              // i
+[anchr]struct_a_param
+
+[anchr]struct_a_ptr
+[dist4]struct_a_member  // num_bytes
+[u4]0                   // version
+[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..6814636
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data
@@ -0,0 +1,18 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[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..45021c0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data
@@ -0,0 +1,18 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[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..3d38702
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data
@@ -0,0 +1,20 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[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..2f9e091
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data
@@ -0,0 +1,13 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params   // num_bytes
+[u4]0                   // version
+[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..ad26763
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data
@@ -0,0 +1,19 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[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..d773458
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data
@@ -0,0 +1,16 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[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..ca462a5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data
@@ -0,0 +1,15 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[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..5adfbba
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data
@@ -0,0 +1,21 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[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..0f96c4b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data
@@ -0,0 +1,12 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]3                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method3_params  // num_bytes
+[u4]0                  // version
+[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..84943d2
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data
@@ -0,0 +1,34 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]4                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method4_params  // num_bytes: Larger than what we know is okay.
+[u4]3                  // version: 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]0             // version
+[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..2f84185
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data
@@ -0,0 +1,26 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]4                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method4_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c   // num_bytes
+[u4]0             // version
+[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..d863e64
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data
@@ -0,0 +1,32 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]4                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method4_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c   // num_bytes
+[u4]0             // version
+[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..b61423a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data
@@ -0,0 +1,35 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]4                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method4_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c   // num_bytes
+[u4]0             // version
+[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..dcec895
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data
@@ -0,0 +1,37 @@
+[handles]10  // Larger than the number of handles that we know about is okay.
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]5                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[u4]4                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]0                // version
+[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]0                     // version
+[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..d4a82ed
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data
@@ -0,0 +1,38 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]5                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]0                  // version
+[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]0                // version
+[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]0                     // version
+[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..9ee7a48
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data
@@ -0,0 +1,37 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]5                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[u4]4                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]0                // version
+[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]0                     // version
+[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..cb01cae
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data
@@ -0,0 +1,37 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]5                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[u4]4                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]0                // version
+[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]0                     // version
+[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..b06ae0a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data
@@ -0,0 +1,36 @@
+[handles]5
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]5                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[u4]4                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]0                // version
+[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]0                     // version
+[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..f641de0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data
@@ -0,0 +1,38 @@
+[handles]10
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]5                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method5_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[u4]9                  // param1
+[u4]0                  // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e      // num_bytes
+[u4]0                // version
+[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]0                     // version
+[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..fb3f862
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data
@@ -0,0 +1,24 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]6                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method6_params  // num_bytes
+[u4]0                  // version
+[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..c8cacf0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data
@@ -0,0 +1,24 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]6                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method6_params  // num_bytes
+[u4]0                  // version
+[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..c972676
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data
@@ -0,0 +1,40 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]7                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method7_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f        // num_bytes
+[u4]0                  // version
+[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]2                  // num_elements
+[u8]0                  // A null pointer, which is okay.
+[dist8]array_element_ptr
+[anchr]array_param
+
+[anchr]array_element_ptr
+[dist4]array_element   // num_bytes
+[u4]3                  // num_elements
+0 1 2
+[anchr]array_element
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_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..4d25cf2
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data
@@ -0,0 +1,26 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]7                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method7_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f        // num_bytes
+[u4]0                  // version
+[u8]0                  // fixed_size_array: An unexpected null pointer.
+[anchr]struct_f
+
+[anchr]param1_ptr
+[dist4]array_param     // num_bytes
+[u4]2                  // num_elements
+[u8]0                  // A null pointer, which is okay.
+[u8]0                  // A null pointer, which is okay.
+[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_mthd7_unmatched_array_elements.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.data
new file mode 100644
index 0000000..cee6e14
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.data
@@ -0,0 +1,34 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]7                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method7_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f        // num_bytes
+[u4]0                  // version
+[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]2                  // num_elements
+[u8]0                  // A null pointer, which is okay.
+[u8]0                  // A null pointer, which is okay.
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.data
new file mode 100644
index 0000000..3095a73
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.data
@@ -0,0 +1,40 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]7                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method7_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[dist8]param1_ptr      // param1
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f        // num_bytes
+[u4]0                  // version
+[dist8]array_ptr       // fixed_size_array
+[anchr]struct_f
+
+[anchr]array_ptr
+[dist4]array_member    // num_bytes
+[u4]3                  // num_elements
+0 1 3
+[anchr]array_member
+
+[u4]0 [u1]0            // Padding for alignment of next array.
+
+[anchr]param1_ptr
+[dist4]array_param     // num_bytes
+[u4]2                  // num_elements
+[dist8]array_element_ptr
+[u8]0                  // A null pointer, which is okay.
+[anchr]array_param
+
+[anchr]array_element_ptr
+[dist4]array_element   // num_bytes
+[u4]4                  // num_elements: Too many elements.
+0 1 2 3
+[anchr]array_element
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unmatched_array_elements_nested.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
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..b19e141
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data
@@ -0,0 +1,21 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]8                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method8_params  // num_bytes
+[u4]0                  // version
+[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..08c4bc3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data
@@ -0,0 +1,32 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]8                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method8_params  // num_bytes
+[u4]0                  // version
+[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..03f2a10
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data
@@ -0,0 +1,12 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]8                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method8_params  // num_bytes
+[u4]0                  // version
+[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..b1b4462
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data
@@ -0,0 +1,33 @@
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]8                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method8_params  // num_bytes
+[u4]0                  // version
+[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..6ed0009
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data
@@ -0,0 +1,36 @@
+[handles]4
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]9                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method9_params  // num_bytes
+[u4]0                  // version
+[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..90feced
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data
@@ -0,0 +1,14 @@
+[handles]4
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]9                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method9_params  // num_bytes
+[u4]0                  // version
+[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..e87fbcb
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data
@@ -0,0 +1,27 @@
+[handles]4
+
+[dist4]message_header  // num_bytes
+[u4]0                  // version
+[u4]0                  // interface ID
+[u4]9                  // name
+[u4]0                  // flags
+[u4]0                  // padding
+[anchr]message_header
+
+[dist4]method9_params  // num_bytes
+[u4]0                  // version
+[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_intf_resp_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.data
new file mode 100644
index 0000000..7da356f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.data
@@ -0,0 +1,19 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]2                  // flags kMessageIsResponse
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]0                  // version
+[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_intf_resp_mthd0_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.data
new file mode 100644
index 0000000..bdcfc46
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.data
@@ -0,0 +1,19 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]2                  // flags kMessageIsResponse
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]0                  // version
+[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_intf_resp_mthd0_unexpected_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_resp_mthd0_unexpected_array_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.data
new file mode 100644
index 0000000..a1fe69d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.data
@@ -0,0 +1,20 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]1                  // flags kMessageExpectsResponse
+[u4]0                  // padding
+[u8]7                  // request_id
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[dist4]basic_struct  // num_bytes
+[u4]0                // version
+[s4]-1               // a
+[u4]0                // padding
+[anchr]basic_struct
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.data
new file mode 100644
index 0000000..e689adb
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.data
@@ -0,0 +1,17 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0                  // name
+[u4]1                  // flags kMessageExpectsResponse
+[u4]0                  // padding
+[u8]7                  // request_id
+[anchr]message_header
+
+[dist4]method0_params  // num_bytes
+[u4]0                  // version
+[dist8]param0_ptr      // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[u4]0  // num_bytes: The struct size is too small.
+[u4]0  // version
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf_rqst_mthd0_unexpected_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_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..7198afa
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]0xffffffff         // name
+[u4]3                  // flags: This combination is illegal.
+[u4]0                  // padding
+[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..c33fde3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.data b/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.data
new file mode 100644
index 0000000..7283509
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]1                  // name: Method1 does not have a response message.
+[u4]2                  // flags: kMessageIsResponse
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.expected b/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.expected
new file mode 100644
index 0000000..65a48b3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_boundscheck_msghdr_no_such_method.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD
\ No newline at end of file
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data
new file mode 100644
index 0000000..d1d8d3f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]12                 // name
+[u4]0                  // flags: kMessageIsResponse is not set in a response.
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected
new file mode 100644
index 0000000..c33fde3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data
new file mode 100644
index 0000000..091b68c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]12                 // name
+[u4]1                  // flags: kMessageExpectsResponse is set in a response.
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected
new file mode 100644
index 0000000..c33fde3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data
new file mode 100644
index 0000000..0eda287
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data
@@ -0,0 +1,8 @@
+[dist4]message_header  // num_bytes
+[u4]1                  // version
+[u4]0                  // interface ID
+[u4]11                 // name: Method11 does not have a response message.
+[u4]2                  // flags: kMessageIsResponse
+[u4]0                  // padding
+[u8]1                  // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected
new file mode 100644
index 0000000..65a48b3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD
\ No newline at end of file
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..7d1b171
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/math_calculator.mojom
@@ -0,0 +1,12 @@
+// 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;
+
+interface Calculator {
+  Clear@0() => (double value@0);
+  Add@1(double value@0) => (double value@0);
+  Multiply@2(double value@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/ping_service.mojom b/mojo/public/interfaces/bindings/tests/ping_service.mojom
new file mode 100644
index 0000000..ba6ad3d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/ping_service.mojom
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.ping"]
+module mojo.test;
+
+interface PingService {
+  Ping() => ();
+};
+
+interface EchoService {
+  Echo(string test_data) => (string echo_data);
+};
diff --git a/mojo/public/interfaces/bindings/tests/rect.mojom b/mojo/public/interfaces/bindings/tests/rect.mojom
new file mode 100644
index 0000000..833c76b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/rect.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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_structs"]
+module mojo.test;
+
+struct Rect {
+  int32 x;
+  int32 y;
+  int32 width;
+  int32 height;
+};
+
+// A copy of Rect that can be typemapped. Arrays of Rect are currently used,
+// which do not support typemapping.
+struct TypemappedRect {
+  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..d87a3ad
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/regression_tests.mojom
@@ -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.
+
+// 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
+};
+
+struct Edge {
+  Vertex? v;
+};
+
+struct Vertex {
+  EmptyStruct? e;
+};
+
+struct EmptyStruct {
+};
+
+struct A {
+  B? b;
+};
+
+struct B {
+  A? a;
+};
+
+// Previously, a field or parameter called |handles| would be shadowed by a
+// method parameter in generated C++ bindings code.
+struct HandlesNameCollisionStruct {
+  EmptyStruct handles;
+};
+
+struct HandlesHandleNameCollisionStruct {
+  handle handles;
+};
+
+union HandlesNameCollisionUnion {
+  int32 handles;
+};
+
+struct HandlesUnionNameCollisionStruct {
+  HandlesNameCollisionUnion handles;
+};
+
+interface HandlesNameCollisionInterface {
+  Method(EmptyStruct handles) => (handle handles);
+};
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..ade3bf3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_factory.mojom
@@ -0,0 +1,41 @@
+// 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.sample"]
+module sample;
+
+import "sample_import.mojom";
+
+// 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);
+};
+
+interface Factory {
+  DoStuff(Request request, handle<message_pipe>? pipe) =>
+      (Response response, string text);
+  DoStuff2(handle<data_pipe_consumer> pipe) => (string text);
+  CreateNamedObject(NamedObject& obj);
+  RequestImportedInterface(
+      imported.ImportedInterface& obj) => (imported.ImportedInterface& obj);
+  TakeImportedInterface(
+      imported.ImportedInterface obj) => (imported.ImportedInterface obj);
+};
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..d28cb7b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_import.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.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();
+};
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..ca4e81c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_import2.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.imported"]
+module imported;
+
+import "sample_import.mojom";
+
+// 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;
+  imported.Color color = Color.BLACK;
+  Point location;
+  Size size;
+};
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..5960d75
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom
@@ -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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample",
+ JavaConstantsClassName="InterfaceConstants",
+ Foo = "hello world"]
+module sample;
+
+const uint64 kLong = 4405;
+
+enum Enum {
+  VALUE
+};
+
+interface PingTest {
+  Ping() => ();
+};
+
+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);
+  EchoInt(int32 a) => (int32 a);
+};
+
+interface IntegerAccessor {
+  GetInteger() => (int64 data, [MinVersion=2] Enum type);
+  [MinVersion=1]
+  SetInteger(int64 data, [MinVersion=3] Enum type);
+};
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..761cb91
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_service.mojom
@@ -0,0 +1,112 @@
+
+// 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.sample"]
+module sample;
+
+import "sample_import.mojom";
+import "sample_import2.mojom";
+
+const uint8 kTwelve = 12;
+
+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;
+};
+
+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;
+};
+
+interface Service {
+  enum BazOptions {
+    REGULAR = 0,
+    EXTRA
+  };
+  const uint8 kFavoriteBaz = 1;
+  Frobinate@0(Foo? foo@0, BazOptions baz@1, Port? port@2) => (int32 result@0);
+  GetPort@1(Port& port @0);
+};
+
+// This interface is referenced above where it is defined. It also refers to
+// itself from a method.
+interface Port {
+  PostMessageToPort@0(string message_text@0, Port port@1);
+};
diff --git a/mojo/public/interfaces/bindings/tests/scoping.mojom b/mojo/public/interfaces/bindings/tests/scoping.mojom
new file mode 100644
index 0000000..2e9edb1
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/scoping.mojom
@@ -0,0 +1,17 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.test;
+
+interface A {
+  GetB(B& b);
+};
+
+interface B {
+  GetC(C& c);
+};
+
+interface C {
+  D();
+};
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..1239e16
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/serialization_test_structs.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.
+
+[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/struct_with_traits.mojom b/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom
new file mode 100644
index 0000000..b1b7437
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium 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;
+
+// TODO(yzshen): Rename *WithTraits* types to something more readable.
+
+struct NestedStructWithTraits {
+  int32 value;
+};
+
+enum EnumWithTraits {
+  VALUE_0,
+  VALUE_1
+};
+
+struct StructWithTraits {
+  EnumWithTraits f_enum;
+  bool f_bool;
+  uint32 f_uint32;
+  uint64 f_uint64;
+  string f_string;
+  string f_string2;
+  array<string> f_string_array;
+  NestedStructWithTraits f_struct;
+  array<NestedStructWithTraits> f_struct_array;
+  map<string, NestedStructWithTraits> f_struct_map;
+};
+
+// Test that this container can be cloned.
+struct StructWithTraitsContainer {
+  StructWithTraits f_struct;
+};
+
+struct PassByValueStructWithTraits {
+  handle f_handle;
+};
+
+// The custom type for PassByValueStructWithTraits is not clonable. Test that
+// this container can compile as long as Clone() is not used.
+struct PassByValueStructWithTraitsContainer {
+  PassByValueStructWithTraits f_struct;
+};
+
+struct StructWithTraitsForUniquePtrTest {
+  int32 f_int32;
+};
+
+interface TraitsTestService {
+  EchoStructWithTraits(StructWithTraits s) => (StructWithTraits passed);
+
+  EchoPassByValueStructWithTraits(PassByValueStructWithTraits s) =>
+      (PassByValueStructWithTraits passed);
+
+  EchoEnumWithTraits(EnumWithTraits e) => (EnumWithTraits passed);
+
+  EchoStructWithTraitsForUniquePtrTest(StructWithTraitsForUniquePtrTest e) => (
+      StructWithTraitsForUniquePtrTest passed);
+};
diff --git a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom
new file mode 100644
index 0000000..534cfd8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.test;
+
+interface FooInterface {};
+
+struct StructContainsAssociated {
+  associated FooInterface? foo_interface;
+  associated FooInterface& foo_request;
+  array<associated FooInterface> foo_interfaces;
+  array<associated FooInterface&> foo_requests;
+};
+
+union UnionContainsAssociated {
+  associated FooInterface foo_interface;
+  associated FooInterface& foo_request;
+  array<associated FooInterface> foo_interfaces;
+  array<associated FooInterface&> foo_requests;
+};
+
+interface InterfacePassesAssociated {
+  PassFoo(associated FooInterface foo_interface,
+          associated FooInterface& foo_request) =>
+         (associated FooInterface foo_interface,
+          associated FooInterface& foo_request);
+
+  PassStruct(StructContainsAssociated foo_struct) =>
+            (StructContainsAssociated foo_struct);
+
+  PassUnion(UnionContainsAssociated foo_union) =>
+           (UnionContainsAssociated foo_union);
+};
+
+interface IntegerSender {
+  Echo(int32 value) => (int32 value);
+  Send(int32 value);
+};
+
+interface IntegerSenderConnection {
+  GetSender(associated IntegerSender& sender);
+  AsyncGetSender() => (associated IntegerSender sender);
+};
diff --git a/mojo/public/interfaces/bindings/tests/test_constants.mojom b/mojo/public/interfaces/bindings/tests/test_constants.mojom
new file mode 100644
index 0000000..462d512
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_constants.mojom
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_constants"]
+module mojo.test;
+
+// Integral types.
+const bool kBoolValue = true;
+
+const int8 kInt8Value = -2;
+
+// In the range of (MAX_INT8, MAX_UINT8].
+const uint8 kUint8Value = 128;
+
+// In the range of [MIN_INT16, MIN_INT8).
+const int16 kInt16Value = -233;
+
+// In the range of (MAX_INT16, MAX_UINT16].
+const uint16 kUint16Value = 44204;
+
+// In the range of [MIN_INT32, MIN_INT16).
+const int32 kInt32Value = -44204;
+
+// In the range of (MAX_INT32, MAX_UINT32].
+const uint32 kUint32Value = 4294967295;
+
+// In the range of [MIN_INT64, MIN_INT32).
+const int64 kInt64Value = -9223372036854775807;
+
+// In the range of (MAX_INT64, MAX_UINT64].
+const uint64 kUint64Value = 9999999999999999999;
+
+// Floating point types.
+const double kDoubleValue = 3.14159;
+const double kDoubleInfinity = double.INFINITY;
+const double kDoubleNegativeInfinity = double.NEGATIVE_INFINITY;
+const double kDoubleNaN = double.NAN;
+
+const float kFloatValue = 2.71828;
+const float kFloatInfinity = float.INFINITY;
+const float kFloatNegativeInfinity = float.NEGATIVE_INFINITY;
+const float kFloatNaN = float.NAN;
+
+struct StructWithConstants {
+  const int8 kInt8Value = 5;
+  const float kFloatValue = 765.432;
+};
+
+interface InterfaceWithConstants {
+  const uint32 kUint32Value = 20100722;
+  const double kDoubleValue = 12.34567;
+};
diff --git a/mojo/public/interfaces/bindings/tests/test_native_types.mojom b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
new file mode 100644
index 0000000..46c6f69
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.test;
+
+import "mojo/public/interfaces/bindings/tests/rect.mojom";
+
+// Used to verify that structs can be declared with no body in mojom.
+
+[Native]
+struct PickledStruct;
+
+[Native]
+enum PickledEnum;
+
+struct PickleContainer {
+  PickledStruct f_struct;
+  PickledEnum f_enum;
+};
+
+interface PicklePasser {
+  PassPickledStruct(PickledStruct pickle) => (PickledStruct passed);
+  PassPickledEnum(PickledEnum pickle) => (PickledEnum passed);
+  PassPickleContainer(PickleContainer container) => (PickleContainer passed);
+  PassPickles(array<PickledStruct> pickles) => (array<PickledStruct> passed);
+  PassPickleArrays(array<array<PickledStruct>> pickle_arrays)
+      => (array<array<PickledStruct>> passed);
+};
+
+// Used to verify support for native serialization of mojom-defined structs
+// using StrucTraits with different variants of the Rect type from rect.mojom.
+
+interface RectService {
+  AddRect(TypemappedRect r);
+  GetLargestRect() => (TypemappedRect largest);
+};
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..2709d49
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_structs.mojom
@@ -0,0 +1,382 @@
+// 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.test_structs"]
+module mojo.test;
+
+import "mojo/public/interfaces/bindings/tests/rect.mojom";
+
+struct NamedRegion {
+  string? name;
+  array<Rect>? rects;
+};
+
+struct RectPair {
+  Rect? first;
+  Rect? second;
+};
+
+struct EmptyStruct {
+};
+
+[Native]
+struct UnmappedNativeStruct;
+
+// Used to verify that struct fields which don't specify a default 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;
+};
+
+// Used to verify that the code generated for enum and const values defined
+// within a struct is correct. Assuming that a constant's value can be a literal
+// or another constant and that enum values can either be an integer constant or
+// another value from the same enum type.
+
+struct ScopedConstants {
+  const int32 TEN = 10;
+  const int32 ALSO_TEN = TEN;
+  enum EType {
+    E0,
+    E1,
+    E2 = 10,
+    E3 = E2,
+    E4,
+  };
+  EType f0 = E0; // 0
+  EType f1 = E1; // 1
+  EType f2 = E2; // 10
+  EType f3 = E3; // 10
+  EType f4 = E4; // 11
+  int32 f5 = TEN;
+  int32 f6 = ALSO_TEN;
+};
+
+// Used to verify that all possible Map key field types can be encoded and
+// decoded successfully.
+
+struct MapKeyTypes {
+  // TODO(yzshen): WTF::HashMap doesn't support bool as key.
+  // map<bool, bool> f0;
+  map<int8, int8> f1;
+  map<uint8, uint8> f2;
+  map<int16, int16> f3;
+  map<uint16, uint16> f4;
+  map<int32, int32> f5;
+  map<uint32, uint32> f6;
+  map<int64, int64> f7;
+  map<uint64, uint64> f8;
+  map<float, float> f9;
+  map<double, double> f10;
+  map<string, string> f11;
+};
+
+// Used to verify that various map value types can be encoded and decoded
+// successfully.
+
+struct MapValueTypes {
+  map<string, array<string>> f0;
+  map<string, array<string>?> f1;
+  map<string, array<string?>> f2;
+  map<string, array<string, 2>> f3;
+  map<string, array<array<string, 2>?>> f4;
+  map<string, array<array<string, 2>, 1>> f5;
+  map<string, Rect?> f6;
+  map<string, map<string, string>> f7;
+  map<string, array<map<string, string>>> f8;
+  map<string, handle> f9;
+  map<string, array<handle>> f10;
+  map<string, map<string, handle>> f11;
+};
+
+// Used to verify that various array types can be encoded and decoded
+// successfully.
+
+struct ArrayValueTypes {
+  array<int8> f0;
+  array<int16> f1;
+  array<int32> f2;
+  array<int64> f3;
+  array<float> f4;
+  array<double> f5;
+  array<SomeInterface> f6;
+  array<SomeInterface&> f7;
+};
+
+// Used to verify that various float and double values can be encoded and
+// decoded correctly.
+
+struct FloatNumberValues {
+  const double V0 = double.INFINITY;
+  const double V1 = double.NEGATIVE_INFINITY;
+  const double V2 = double.NAN;
+  const float V3 = float.INFINITY;
+  const float V4 = float.NEGATIVE_INFINITY;
+  const float V5 = float.NAN;
+  const float V6 = 0;
+  const double V7 = 1234567890.123;
+  const double V8 = 1.2E+20;
+  const double V9 = -1.2E+20;
+
+  double f0 = V0;
+  double f1 = V1;
+  double f2 = V2;
+  float f3 = V3;
+  float f4 = V4;
+  float f5 = V5;
+  float f6 = V6;
+  double f7 = V7;
+  double f8 = V8;
+  double f9 = V9;
+};
+
+// Used to verify that various signed integer values can be encoded and
+// decoded correctly.
+
+struct IntegerNumberValues {
+  const int8 V0 = -128; // Minimum
+  const int8 V1 = -1;   // -1
+  const int8 V2 = 0;    // 0
+  const int8 V3 = 42;   // An arbitrary valid value.
+  const int8 V4 = 127;  // Maximum
+
+  const int16 V5 = -32768; // ...
+  const int16 V6 = -1;
+  const int16 V7 = 0;
+  const int16 V8 = 12345;
+  const int16 V9 = 32767;
+
+  const int32 V10 = -2147483648;
+  const int32 V11 = -1;
+  const int32 V12 = 0;
+  const int32 V13 = 1234567890;
+  const int32 V14 = 2147483647;
+
+  // The limits for JavaScript integers are +/- (2^53 - 1).
+  const int64 V15 = -9007199254740991; // Number.MIN_SAFE_INTEGER
+  const int64 V16 = -1;
+  const int64 V17 = 0;
+  const int64 V18 = 1234567890123456;
+  const int64 V19 = 9007199254740991; // Number.MAX_SAFE_INTEGER
+
+  int8 f0 = V0;
+  int8 f1 = V1;
+  int8 f2 = V2;
+  int8 f3 = V3;
+  int8 f4 = V4;
+
+  int16 f5 = V5;
+  int16 f6 = V6;
+  int16 f7 = V7;
+  int16 f8 = V8;
+  int16 f9 = V9;
+
+  int32 f10 = V10;
+  int32 f11 = V11;
+  int32 f12 = V12;
+  int32 f13 = V13;
+  int32 f14 = V14;
+
+  int64 f15 = V15;
+  int64 f16 = V16;
+  int64 f17 = V17;
+  int64 f18 = V18;
+  int64 f19 = V19;
+};
+
+// Used to verify that various unsigned integer values can be encoded and
+// decoded correctly.
+
+struct UnsignedNumberValues {
+  const uint8 V0 = 0;    // Minimum = 0.
+  const uint8 V1 = 42;   // An arbitrary valid value.
+  const uint8 V2 = 0xFF; // Maximum
+
+  const uint16 V3 = 0; // ...
+  const uint16 V4 = 12345;
+  const uint16 V5 = 0xFFFF;
+
+  const uint32 V6 = 0;
+  const uint32 V7 = 1234567890;
+  const uint32 V8 = 0xFFFFFFFF;
+
+  // The limits for JavaScript integers are +/- (2^53 - 1).
+  const uint64 V9 = 0;
+  const uint64 V10 = 1234567890123456;
+  const uint64 V11 = 9007199254740991; // Number.MAX_SAFE_INTEGER
+
+  uint8 f0 = V0;
+  uint8 f1 = V1;
+  uint8 f2 = V2;
+
+  uint16 f3 = V3;
+  uint16 f4 = V4;
+  uint16 f5 = V5;
+
+  uint32 f6 = V6;
+  uint32 f7  = V7;
+  uint32 f8 = V8;
+
+  uint64 f9 = V9;
+  uint64 f10 = V10;
+  uint64 f11 = V11;
+};
+
+// Used to verify that various (packed) boolean array values can be encoded
+// and decoded correctly.
+
+struct BitArrayValues {
+  array<bool, 1> f0;
+  array<bool, 7> f1;
+  array<bool, 9> f2;
+  array<bool> f3;
+  array<array<bool>> f4;
+  array<array<bool>?> f5;
+  array<array<bool, 2>?> f6;
+};
+
+// Used to verify that different versions can be decoded correctly.
+
+struct MultiVersionStruct {
+  [MinVersion=0]
+  int32 f_int32;
+  [MinVersion=1]
+  Rect? f_rect;
+  [MinVersion=3]
+  string? f_string;
+  [MinVersion=5]
+  array<int8>? f_array;
+  [MinVersion=7]
+  handle<message_pipe>? f_message_pipe;
+  [MinVersion=7]
+  bool f_bool;
+  [MinVersion=9]
+  int16 f_int16;
+};
+
+struct MultiVersionStructV0 {
+  [MinVersion=0]
+  int32 f_int32;
+};
+
+struct MultiVersionStructV1 {
+  [MinVersion=0]
+  int32 f_int32;
+  [MinVersion=1]
+  Rect? f_rect;
+};
+
+struct MultiVersionStructV3 {
+  [MinVersion=0]
+  int32 f_int32;
+  [MinVersion=1]
+  Rect? f_rect;
+  [MinVersion=3]
+  string? f_string;
+};
+
+struct MultiVersionStructV5 {
+  [MinVersion=0]
+  int32 f_int32;
+  [MinVersion=1]
+  Rect? f_rect;
+  [MinVersion=3]
+  string? f_string;
+  [MinVersion=5]
+  array<int8>? f_array;
+};
+
+struct MultiVersionStructV7 {
+  [MinVersion=0]
+  int32 f_int32;
+  [MinVersion=1]
+  Rect? f_rect;
+  [MinVersion=3]
+  string? f_string;
+  [MinVersion=5]
+  array<int8>? f_array;
+  [MinVersion=7]
+  handle<message_pipe>? f_message_pipe;
+  [MinVersion=7]
+  bool f_bool;
+};
+
+// Used to verify that interfaces that are struct members can be defined in the
+// same file.
+
+interface SomeInterface {
+  SomeMethod(RectPair pair) => (RectPair other_pair);
+};
+
+struct ContainsInterface {
+  SomeInterface some_interface;
+};
+
+// Verify that a field can be called |other|.
+
+struct ContainsOther {
+  int32 other;
+};
+
+// Used to verify that structs can contain interface requests.
+
+struct ContainsInterfaceRequest {
+  SomeInterface& request;
+};
diff --git a/mojo/public/interfaces/bindings/tests/test_sync_methods.mojom b/mojo/public/interfaces/bindings/tests/test_sync_methods.mojom
new file mode 100644
index 0000000..3b8cfe6
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_sync_methods.mojom
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium 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 TestSyncCodeGeneration {
+  [Sync]
+  NoInput() => (int32 result);
+
+  [Sync]
+  NoOutput(int32 value) => ();
+
+  [Sync]
+  NoInOut() => ();
+
+  [Sync]
+  HaveInOut(int32 value1, int32 value2) => (int32 result1, int32 result2);
+};
+
+interface TestSync {
+  [Sync]
+  Ping() => ();
+
+  [Sync]
+  Echo(int32 value) => (int32 result);
+
+  AsyncEcho(int32 value) => (int32 result);
+};
+
+// Test sync method support with associated interfaces.
+interface TestSyncMaster {
+  [Sync]
+  Ping() => ();
+
+  [Sync]
+  Echo(int32 value) => (int32 result);
+
+  AsyncEcho(int32 value) => (int32 result);
+
+  SendInterface(associated TestSync ptr);
+
+  SendRequest(associated TestSync& request);
+};
diff --git a/mojo/public/interfaces/bindings/tests/test_unions.mojom b/mojo/public/interfaces/bindings/tests/test_unions.mojom
new file mode 100644
index 0000000..41e1ed6
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_unions.mojom
@@ -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.
+
+module mojo.test;
+
+enum AnEnum {
+  FIRST, SECOND
+};
+
+[Extensible]
+enum AnExtensibleEnum {
+  FIRST, SECOND, THIRD
+};
+
+union PodUnion {
+  int8   f_int8;
+  int8   f_int8_other;
+  uint8  f_uint8;
+  int16  f_int16;
+  uint16 f_uint16;
+  int32  f_int32;
+  uint32 f_uint32;
+  int64  f_int64;
+  uint64 f_uint64;
+  float  f_float;
+  double f_double;
+  bool   f_bool;
+  AnEnum f_enum;
+  AnExtensibleEnum f_extensible_enum;
+};
+
+union ObjectUnion {
+  int8   f_int8;
+  string f_string;
+  DummyStruct f_dummy;
+  DummyStruct? f_nullable;
+  array<int8> f_array_int8;
+  map<string, int8> f_map_int8;
+  PodUnion f_pod_union;
+  // Test that Clone() is defined after SmallStruct is declared.
+  array<SmallStruct> f_small_structs;
+};
+
+union HandleUnion {
+  handle f_handle;
+  handle<message_pipe> f_message_pipe;
+  handle<data_pipe_consumer> f_data_pipe_consumer;
+  handle<data_pipe_producer> f_data_pipe_producer;
+  handle<shared_buffer> f_shared_buffer;
+  SmallCache f_small_cache;
+  SmallCache& f_small_cache_request;
+};
+
+struct WrapperStruct {
+  ObjectUnion? object_union;
+  PodUnion? pod_union;
+  HandleUnion? handle_union;
+};
+
+struct DummyStruct {
+  int8 f_int8;
+};
+
+struct SmallStruct {
+  DummyStruct? dummy_struct;
+  PodUnion? pod_union;
+  array<PodUnion>? pod_union_array;
+  array<PodUnion?>? nullable_pod_union_array;
+  array<DummyStruct>? s_array;
+  map<string, PodUnion>? pod_union_map;
+  map<string, PodUnion?>? nullable_pod_union_map;
+};
+
+struct SmallStructNonNullableUnion {
+  PodUnion pod_union;
+};
+
+struct SmallObjStruct {
+  ObjectUnion obj_union;
+  int8 f_int8;
+};
+
+interface SmallCache {
+  SetIntValue(int64 int_value);
+  GetIntValue() => (int64 int_value);
+};
+
+interface UnionInterface {
+  Echo(PodUnion in_val) => (PodUnion out_val);
+};
+
+struct TryNonNullStruct {
+  DummyStruct? nullable;
+  DummyStruct non_nullable;
+};
+
+union OldUnion {
+  int8 f_int8;
+};
+
+union NewUnion {
+  int8 f_int8;
+  int16 f_int16;
+};
diff --git a/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom
new file mode 100644
index 0000000..2fdbea8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium 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;
+
+struct TestWTFCodeGeneration {
+  string str;
+  string? nullable_str;
+  array<string> strs;
+  array<string?> nullable_strs;
+  array<array<int32>> arrays;
+  array<bool> bools;
+  array<handle<message_pipe>> handles;
+  map<string, string?> str_map;
+  map<int32, array<int32>> array_map;
+  map<int32, handle<message_pipe>> handle_map;
+  array<map<string, string?>> str_maps;
+};
+
+union TestWTFCodeGeneration2 {
+  string str;
+  array<string> strs;
+  map<string, string?> str_map;
+};
+
+struct TestWTFStruct {
+  string str;
+  int32 integer;
+};
+
+interface TestWTF {
+  EchoString(string? str) => (string? str);
+  EchoStringArray(array<string?>? arr) => (array<string?>? arr);
+  EchoStringMap(map<string, string?>? str_map)
+      => (map<string, string?>? str_map);
+};
diff --git a/mojo/public/interfaces/bindings/tests/validation_test_associated_interfaces.mojom b/mojo/public/interfaces/bindings/tests/validation_test_associated_interfaces.mojom
new file mode 100644
index 0000000..2fa77ff
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/validation_test_associated_interfaces.mojom
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.test;
+
+// Associated interfaces are not supported by all language bindings yet.
+// Eventually these definitions should live in validation_test_interfaces.mojom.
+
+interface InterfaceX {};
+
+interface AssociatedConformanceTestInterface {
+  Method0(associated InterfaceX param0);
+  Method1(associated InterfaceX& param0);
+  Method2(associated InterfaceX? param0);
+  Method3(array<associated InterfaceX> param0);
+};
+
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..c46c0a5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
@@ -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.
+
+
+[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;
+};
+
+struct StructG {
+  int32 i;
+  [MinVersion=1]
+  StructA? struct_a;
+  [MinVersion=3]
+  string? str;
+  [MinVersion=3]
+  bool b;
+};
+
+interface InterfaceA {
+};
+
+enum EnumA {
+  ENUM_A_0,
+  ENUM_A_1
+};
+
+[Extensible]
+enum EnumB {
+  ENUM_B_0,
+  ENUM_B_1,
+  ENUM_B_2
+};
+
+// This interface is used for testing bounds-checking in the mojom
+// binding code. If you add a method please update the files
+// ./data/validation/boundscheck_*. If you add a response please update
+// ./data/validation/resp_boundscheck_*.
+interface BoundsCheckTestInterface {
+  Method0(uint8 param0) => (uint8 param0);
+  Method1(uint8 param0);
+};
+
+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<array<uint8, 3>?, 2> param1);
+  Method8(array<array<string>?> param0);
+  Method9(array<array<handle?>>? param0);
+  Method10(map<string, uint8> param0);
+  Method11(StructG param0);
+  Method12(float param0) => (float param0);
+  Method13(InterfaceA? param0, uint32 param1, InterfaceA? param2);
+  Method14(EnumA param0, EnumB param1);
+  Method15(array<EnumA>? param0, array<EnumB>? param1);
+  Method16(map<EnumA, EnumA>? param0);
+  Method17(array<InterfaceA> param0);
+};
+
+struct BasicStruct {
+  int32 a;
+};
+
+interface IntegrationTestInterface {
+  Method0(BasicStruct param0) => (array<uint8> param0);
+};
+
+// An enum generates a enum-value validation function, so we want to test it.
+// E.g., valid enum values for this enum should be:  -3, 0, 1, 10
+enum BasicEnum {
+  A,
+  B,
+  C = A,
+  D = -3,
+  E = 0xA
+};
+
+// The enum validation function should be generated within the scope of this
+// struct.
+struct StructWithEnum {
+  enum EnumWithin {
+    A, B, C, D
+  };
+};
diff --git a/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom b/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom
new file mode 100644
index 0000000..f0136db
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.test.versioning;
+
+// versioning_test_service.mojom and versioning_test_client.mojom contain
+// different versions of Mojom definitions for a fictitious human resource
+// management system. They are used to test the versioning mechanism.
+
+enum Department {
+  SALES,
+  DEV
+};
+
+struct Employee {
+  uint64 employee_id;
+  string name;
+  Department department;
+};
+
+interface HumanResourceDatabase {
+  AddEmployee(Employee employee) => (bool success);
+
+  QueryEmployee(uint64 id, [MinVersion=1] bool retrieve_finger_print)
+      => (Employee? employee, [MinVersion=1] array<uint8>? finger_print);
+
+  [MinVersion=1]
+  AttachFingerPrint(uint64 id, array<uint8> finger_print)
+      => (bool success);
+
+  [MinVersion=2]
+  ListEmployeeIds() => (array<uint64>? ids);
+};
diff --git a/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom b/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom
new file mode 100644
index 0000000..59b3832
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.test.versioning;
+
+// versioning_test_service.mojom and versioning_test_client.mojom contain
+// different versions of Mojom definitions for a fictitious human resource
+// management system. They are used to test the versioning mechanism.
+
+enum Department {
+  SALES,
+  DEV
+};
+
+struct Date {
+  uint16 year;
+  uint8 month;
+  uint8 day;
+};
+
+struct Employee {
+  uint64 employee_id;
+  string name;
+  Department department;
+  [MinVersion=1] Date? birthday;
+};
+
+interface HumanResourceDatabase {
+  AddEmployee(Employee employee) => (bool success);
+
+  QueryEmployee(uint64 id, [MinVersion=1] bool retrieve_finger_print)
+      => (Employee? employee, [MinVersion=1] array<uint8>? finger_print);
+
+  [MinVersion=1]
+  AttachFingerPrint(uint64 id, array<uint8> finger_print)
+      => (bool success);
+};
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
new file mode 100644
index 0000000..e33faaa
--- /dev/null
+++ b/mojo/public/java/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("//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/ResultAnd.java",
+    "system/src/org/chromium/mojo/system/SharedBufferHandle.java",
+    "system/src/org/chromium/mojo/system/UntypedHandle.java",
+    "system/src/org/chromium/mojo/system/RunLoop.java",
+  ]
+}
+
+android_library("bindings") {
+  java_files = [
+    "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java",
+    "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java",
+    "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/DataHeader.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/InterfaceControlMessagesHelper.java",
+    "bindings/src/org/chromium/mojo/bindings/Interface.java",
+    "bindings/src/org/chromium/mojo/bindings/InterfaceRequest.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",
+    "bindings/src/org/chromium/mojo/bindings/Union.java",
+  ]
+
+  deps = [
+    ":system",
+    "//base:base_java",
+  ]
+
+  srcjar_deps = [ "../interfaces/bindings:bindings_java_sources" ]
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java
new file mode 100644
index 0000000..ee8f631
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java
@@ -0,0 +1,11 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Associated interface is not supported yet.
+ */
+public class AssociatedInterfaceNotSupported {
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java
new file mode 100644
index 0000000..1b07cd1
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java
@@ -0,0 +1,11 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Associated interface is not supported yet.
+ */
+public class AssociatedInterfaceRequestNotSupported {
+}
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..6146316
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
@@ -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.
+
+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 interface, which consists of a serialized handle (4
+     * bytes) and a version number (4 bytes).
+     */
+    public static final int SERIALIZED_INTERFACE_SIZE = 8;
+
+    /**
+     * 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 size, in bytes, of a serialized union.
+     */
+    public static final int UNION_SIZE = 16;
+
+    /**
+     * The header for a serialized map element.
+     */
+    public static final DataHeader MAP_STRUCT_HEADER = new DataHeader(24, 0);
+
+    /**
+     * The value used for the expected length of a non-fixed size array.
+     */
+    public static final int UNSPECIFIED_ARRAY_LENGTH = -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 int align(int size) {
+        return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Returns |true| if and only if the two objects are equals, handling |null|.
+     */
+    public static boolean equals(Object o1, Object o2) {
+        if (o1 == o2) {
+            return true;
+        }
+        if (o1 == null) {
+            return false;
+        }
+        return o1.equals(o2);
+    }
+
+    /**
+     * Returns the hash code of the object, handling |null|.
+     */
+    public static int hashCode(Object o) {
+        if (o == null) {
+            return 0;
+        }
+        return o.hashCode();
+    }
+
+    /**
+     * Returns the hash code of the value.
+     */
+    public static int hashCode(boolean o) {
+        return o ? 1231 : 1237;
+    }
+
+    /**
+     * Returns the hash code of the value.
+     */
+    public static int hashCode(long o) {
+        return (int) (o ^ (o >>> 32));
+    }
+
+    /**
+     * Returns the hash code of the value.
+     */
+    public static int hashCode(float o) {
+        return Float.floatToIntBits(o);
+    }
+
+    /**
+     * Returns the hash code of the value.
+     */
+    public static int hashCode(double o) {
+        return hashCode(Double.doubleToLongBits(o));
+    }
+
+    /**
+     * Returns the hash code of the value.
+     */
+    public static int hashCode(int o) {
+        return o;
+    }
+
+    /**
+     * 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 |null| 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..f601fb8
--- /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.
+ */
+public 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..3686bcf
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
@@ -0,0 +1,250 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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 org.chromium.mojo.system.ResultAnd;
+
+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) {
+            MessageReceiver incomingMessageReceiver = mIncomingMessageReceiver;
+            mIncomingMessageReceiver = null;
+            incomingMessageReceiver.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) {
+            mCancellable = null;
+            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) {
+        close();
+        assert mCancellable == null;
+        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() {
+        ResultAnd<Boolean> result;
+        do {
+            try {
+                result = readAndDispatchMessage(mMessagePipeHandle, mIncomingMessageReceiver);
+            } catch (MojoException e) {
+                onError(e);
+                return;
+            }
+        } while (result.getValue());
+        if (result.getMojoResult() == MojoResult.SHOULD_WAIT) {
+            registerAsyncWaiterForRead();
+        } else {
+            onError(new MojoException(result.getMojoResult()));
+        }
+    }
+
+    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 ResultAnd<Boolean> readAndDispatchMessage(
+            MessagePipeHandle handle, MessageReceiver receiver) {
+        // TODO(qsr) Allow usage of a pool of pre-allocated buffer for performance.
+        ResultAnd<ReadMessageResult> result =
+                handle.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE);
+        if (result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED) {
+            return new ResultAnd<Boolean>(result.getMojoResult(), false);
+        }
+        ReadMessageResult readResult = result.getValue();
+        assert readResult != null;
+        ByteBuffer buffer = ByteBuffer.allocateDirect(readResult.getMessageSize());
+        result = handle.readMessage(
+                buffer, readResult.getHandlesCount(), MessagePipeHandle.ReadFlags.NONE);
+        if (receiver != null && result.getMojoResult() == MojoResult.OK) {
+            boolean accepted = receiver.accept(new Message(buffer, result.getValue().getHandles()));
+            return new ResultAnd<Boolean>(result.getMojoResult(), accepted);
+        }
+        return new ResultAnd<Boolean>(result.getMojoResult(), false);
+    }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java
new file mode 100644
index 0000000..96acec9
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * The header for a mojo complex element.
+ */
+public 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 ELEMENTS_OR_VERSION_OFFSET = 4;
+
+    /**
+     * The size of the object owning this header.
+     */
+    public final int size;
+
+    /**
+     * Number of element (for an array) or version (for a struct) of the object owning this
+     * header.
+     */
+    public final int elementsOrVersion;
+
+    /**
+     * Constructor.
+     */
+    public DataHeader(int size, int elementsOrVersion) {
+        super();
+        this.size = size;
+        this.elementsOrVersion = elementsOrVersion;
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + elementsOrVersion;
+        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 (elementsOrVersion == other.elementsOrVersion && size == other.size);
+    }
+}
\ No newline at end of file
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..755fb01
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
@@ -0,0 +1,747 @@
+// Copyright 2014 The Chromium 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.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.LITTLE_ENDIAN);
+        mBaseOffset = baseOffset;
+        mValidator = validator;
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} at the current position.
+     */
+    public DataHeader readDataHeader() {
+        // Claim the memory for the header.
+        mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE);
+        DataHeader result = readDataHeaderAtOffset(0, false);
+        // Claim the rest of the memory.
+        mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + result.size);
+        return result;
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} for an union at the given offset.
+     */
+    public DataHeader readDataHeaderForUnion(int offset) {
+        DataHeader result = readDataHeaderAtOffset(offset, true);
+        if (result.size == 0) {
+            if (result.elementsOrVersion != 0) {
+                throw new DeserializationException(
+                        "Unexpected version tag for a null union. Expecting 0, found: "
+                        + result.elementsOrVersion);
+            }
+        } else if (result.size != BindingsHelper.UNION_SIZE) {
+            throw new DeserializationException(
+                    "Unexpected size of an union. The size must be 0 for a null union, or 16 for "
+                    + "a non-null union.");
+        }
+        return result;
+    }
+
+    /**
+     * @returns a decoder suitable to decode an union defined as the root object of a message.
+     */
+    public Decoder decoderForSerializedUnion() {
+        mValidator.claimMemory(0, BindingsHelper.UNION_SIZE);
+        return this;
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} at the given offset.
+     */
+    private DataHeader readDataHeaderAtOffset(int offset, boolean isUnion) {
+        int size = readInt(offset + DataHeader.SIZE_OFFSET);
+        int elementsOrVersion = readInt(offset + DataHeader.ELEMENTS_OR_VERSION_OFFSET);
+        if (size < 0) {
+            throw new DeserializationException(
+                    "Negative size. Unsigned integers are not valid for java.");
+        }
+        if (elementsOrVersion < 0 && (!isUnion || elementsOrVersion != -1)) {
+            throw new DeserializationException(
+                    "Negative elements or version. Unsigned integers are not valid for java.");
+        }
+
+        return new DataHeader(size, elementsOrVersion);
+    }
+
+    public DataHeader readAndValidateDataHeader(DataHeader[] versionArray) {
+        DataHeader header = readDataHeader();
+        int maxVersionIndex = versionArray.length - 1;
+        if (header.elementsOrVersion <= versionArray[maxVersionIndex].elementsOrVersion) {
+            DataHeader referenceHeader = null;
+            for (int index = maxVersionIndex; index >= 0; index--) {
+                DataHeader dataHeader = versionArray[index];
+                if (header.elementsOrVersion >= dataHeader.elementsOrVersion) {
+                    referenceHeader = dataHeader;
+                    break;
+                }
+            }
+            if (referenceHeader == null || referenceHeader.size != header.size) {
+                throw new DeserializationException(
+                        "Header doesn't correspond to any known version.");
+            }
+        } else {
+            if (header.size < versionArray[maxVersionIndex].size) {
+                throw new DeserializationException("Message newer than the last known version"
+                        + " cannot be shorter than required by the last known version.");
+            }
+        }
+        return header;
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+     * array where elements are pointers.
+     */
+    public DataHeader readDataHeaderForPointerArray(int expectedLength) {
+        return readDataHeaderForArray(BindingsHelper.POINTER_SIZE, expectedLength);
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+     * array where elements are unions.
+     */
+    public DataHeader readDataHeaderForUnionArray(int expectedLength) {
+        return readDataHeaderForArray(BindingsHelper.UNION_SIZE, expectedLength);
+    }
+
+    /**
+     * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for a map.
+     */
+    public void readDataHeaderForMap() {
+        DataHeader si = readDataHeader();
+        if (si.size != BindingsHelper.MAP_STRUCT_HEADER.size) {
+            throw new DeserializationException(
+                    "Incorrect header for map. The size is incorrect.");
+        }
+        if (si.elementsOrVersion != BindingsHelper.MAP_STRUCT_HEADER.elementsOrVersion) {
+            throw new DeserializationException(
+                    "Incorrect header for map. The version is incorrect.");
+        }
+    }
+
+    /**
+     * 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.elementsOrVersion + 7) / BindingsHelper.ALIGNMENT];
+        d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+        d.mMessage.getData().get(bytes);
+        boolean[] result = new boolean[si.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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;
+        }
+        int version = readInt(offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
+        return manager.attachProxy(handle, version);
+    }
+
+    /**
+     * 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 an associated interface at the given offset. Not yet supported.
+     */
+    public AssociatedInterfaceNotSupported readAssociatedServiceInterfaceNotSupported(int offset,
+            boolean nullable) {
+        return null;
+    }
+
+    /**
+     * Deserializes an associated interface request at the given offset. Not yet supported.
+     */
+    public AssociatedInterfaceRequestNotSupported readAssociatedInterfaceRequestNotSupported(
+            int offset, boolean nullable) {
+        return null;
+    }
+
+    /**
+     * 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.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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.elementsOrVersion];
+        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(BindingsHelper.SERIALIZED_INTERFACE_SIZE, expectedLength);
+        S[] result = manager.buildArray(si.elementsOrVersion);
+        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_INTERFACE_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.elementsOrVersion];
+        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;
+    }
+
+    /**
+     * Deserializes an array of associated interfaces at the given offset. Not yet supported.
+     */
+    public AssociatedInterfaceNotSupported[] readAssociatedServiceInterfaceNotSupporteds(
+            int offset, int arrayNullability, int expectedLength) {
+        return null;
+    }
+
+    /**
+     * Deserializes an array of associated interface requests at the given offset. Not yet
+     * supported.
+     */
+    public AssociatedInterfaceRequestNotSupported[] readAssociatedInterfaceRequestNotSupporteds(
+            int offset, int arrayNullability, int expectedLength) {
+        return null;
+    }
+
+    /**
+     * 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.elementsOrVersion + 7) / 8) {
+            throw new DeserializationException("Array header is incorrect.");
+        }
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+                && dataHeader.elementsOrVersion != expectedLength) {
+            throw new DeserializationException("Incorrect array length. Expected: " + expectedLength
+                    + ", but got: " + dataHeader.elementsOrVersion + ".");
+        }
+        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.elementsOrVersion)) {
+            throw new DeserializationException("Array header is incorrect.");
+        }
+        if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+                && dataHeader.elementsOrVersion != expectedLength) {
+            throw new DeserializationException("Incorrect array length. Expected: " + expectedLength
+                    + ", but got: " + dataHeader.elementsOrVersion + ".");
+        }
+        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..3c86a8d
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
@@ -0,0 +1,587 @@
+// Copyright 2014 The Chromium 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.Handler;
+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 data
+         *            structure 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.LITTLE_ENDIAN);
+            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 data
+     *            structure 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.elementsOrVersion, DataHeader.ELEMENTS_OR_VERSION_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 |= (byte) (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);
+    }
+
+    /**
+     * Encode a {@link Union} at the given offset.
+     */
+    public void encode(Union v, int offset, boolean nullable) {
+        if (v == null && !nullable) {
+            throw new SerializationException(
+                    "Trying to encode a null pointer for a non-nullable type.");
+        }
+        if (v == null) {
+            encode(0L, offset);
+            encode(0L, offset + DataHeader.HEADER_SIZE);
+            return;
+        }
+        v.encode(this, offset);
+    }
+
+    /**
+     * 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);
+            encode(0, offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
+            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.Proxy) {
+            Handler handler = ((Interface.Proxy) v).getProxyHandler();
+            encode(handler.passHandle(), offset, nullable);
+            encode(handler.getVersion(), offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
+            return;
+        }
+        Pair<MessagePipeHandle, MessagePipeHandle> handles =
+                mEncoderState.core.createMessagePipe(null);
+        manager.bind(v, handles.first);
+        encode(handles.second, offset, nullable);
+        encode(manager.getVersion(), offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Encode an associated interface. Not yet supported.
+     */
+    public void encode(AssociatedInterfaceNotSupported v, int offset, boolean nullable) {
+    }
+
+    /**
+     * Encode an associated interface request. Not yet supported.
+     */
+    public void encode(AssociatedInterfaceRequestNotSupported v, int offset, boolean 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);
+    }
+
+    /**
+     * Returns an {@link Encoder} suitable for encoding an array of union of the given length.
+     */
+    public Encoder encodeUnionArray(int length, int offset, int expectedLength) {
+        return encoderForArray(BindingsHelper.UNION_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] |= (byte) (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_INTERFACE_SIZE, v.length, offset, expectedLength);
+        for (int i = 0; i < v.length; ++i) {
+            e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_INTERFACE_SIZE * i,
+                    BindingsHelper.isElementNullable(arrayNullability), manager);
+        }
+    }
+
+    public Encoder encoderForMap(int offset) {
+        encodePointerToNextUnclaimedData(offset);
+        return getEncoderAtDataOffset(BindingsHelper.MAP_STRUCT_HEADER);
+    }
+
+    /**
+     * Encodes a pointer to the next unclaimed memory and returns an encoder suitable to encode an
+     * union at this location.
+     */
+    public Encoder encoderForUnionPointer(int offset) {
+        encodePointerToNextUnclaimedData(offset);
+        Encoder result = new Encoder(mEncoderState);
+        result.mEncoderState.claimMemory(16);
+        return result;
+    }
+
+    /**
+     * 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 an array of associated interfaces. Not yet supported.
+     */
+    public void encode(AssociatedInterfaceNotSupported[] v, int offset, int arrayNullability,
+            int expectedLength) {}
+
+    /**
+     * Encodes an array of associated interface requests. Not yet supported.
+     */
+    public void encode(AssociatedInterfaceRequestNotSupported[] v, int offset, int arrayNullability,
+            int expectedLength) {}
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Claim the given amount of memory at the end of the buffer, resizing it if needed.
+     */
+    void claimMemory(int size) {
+        mEncoderState.claimMemory(BindingsHelper.align(size));
+    }
+
+    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..c621d9b
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
@@ -0,0 +1,185 @@
+// Copyright 2014 The Chromium 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 org.chromium.mojo.system.ResultAnd;
+
+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 {
+                ResultAnd<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 (mLock) {
+                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..c2bbc8e
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
@@ -0,0 +1,409 @@
+// Copyright 2014 The Chromium 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.Callbacks.Callback1;
+import org.chromium.mojo.bindings.Interface.AbstractProxy.HandlerImpl;
+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 {
+        /**
+         * Class allowing to interact with the proxy itself.
+         */
+        public interface Handler extends Closeable {
+            /**
+             * Sets the {@link ConnectionErrorHandler} that will be notified of errors.
+             */
+            public void setErrorHandler(ConnectionErrorHandler errorHandler);
+
+            /**
+             * Unbinds the proxy and passes the handle. Can return null if the proxy is not bound or
+             * if the proxy is not over a message pipe.
+             */
+            public MessagePipeHandle passHandle();
+
+            /**
+             * Returns the version number of the interface that the remote side supports.
+             */
+            public int getVersion();
+
+            /**
+             * Queries the max version that the remote side supports. On completion, the result will
+             * be returned as the input of |callback|. The version number of this interface pointer
+             * will also be updated.
+             */
+            public void queryVersion(Callback1<Integer> callback);
+
+            /**
+             * If the remote side doesn't support the specified version, it will close its end of
+             * the message pipe asynchronously. The call does nothing if |version| is no greater
+             * than getVersion().
+             * <p>
+             * If you make a call to requireVersion() with a version number X which is not supported
+             * by the remote side, it is guaranteed that all calls to the interface methods after
+             * requireVersion(X) will be ignored.
+             */
+            public void requireVersion(int version);
+        }
+
+        /**
+         * Returns the {@link Handler} object allowing to interact with the proxy itself.
+         */
+        public Handler getProxyHandler();
+    }
+
+    /**
+     * Base implementation of {@link Proxy}.
+     */
+    abstract class AbstractProxy implements Proxy {
+        /**
+         * Implementation of {@link Handler}.
+         */
+        protected static class HandlerImpl implements Proxy.Handler, ConnectionErrorHandler {
+            /**
+             * 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;
+
+            /**
+             * The currently known version of the interface.
+             */
+            private int mVersion = 0;
+
+            /**
+             * 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 HandlerImpl(Core core, MessageReceiverWithResponder messageReceiver) {
+                this.mCore = core;
+                this.mMessageReceiver = messageReceiver;
+            }
+
+            void setVersion(int version) {
+                mVersion = version;
+            }
+
+            /**
+             * Returns the message receiver to send message to.
+             */
+            public MessageReceiverWithResponder getMessageReceiver() {
+                return mMessageReceiver;
+            }
+
+            /**
+             * Returns the Core implementation.
+             */
+            public Core getCore() {
+                return mCore;
+            }
+
+            /**
+             * Sets the {@link ConnectionErrorHandler} that will be notified of errors.
+             */
+            @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();
+            }
+
+            /**
+             * @see Interface.Proxy.Handler#passHandle()
+             */
+            @Override
+            public MessagePipeHandle passHandle() {
+                @SuppressWarnings("unchecked")
+                HandleOwner<MessagePipeHandle> handleOwner =
+                        (HandleOwner<MessagePipeHandle>) mMessageReceiver;
+                return handleOwner.passHandle();
+            }
+
+            /**
+             * @see Handler#getVersion()
+             */
+            @Override
+            public int getVersion() {
+                return mVersion;
+            }
+
+            /**
+             * @see Handler#queryVersion(org.chromium.mojo.bindings.Callbacks.Callback1)
+             */
+            @Override
+            public void queryVersion(final Callback1<Integer> callback) {
+                RunMessageParams message = new RunMessageParams();
+                message.reserved0 = 16;
+                message.reserved1 = 0;
+                message.queryVersion = new QueryVersion();
+
+                InterfaceControlMessagesHelper.sendRunMessage(getCore(), mMessageReceiver, message,
+                        new Callback1<RunResponseMessageParams>() {
+                            @Override
+                            public void call(RunResponseMessageParams response) {
+                                mVersion = response.queryVersionResult.version;
+                                callback.call(mVersion);
+                            }
+                        });
+            }
+
+            /**
+             * @see Handler#requireVersion(int)
+             */
+            @Override
+            public void requireVersion(int version) {
+                if (mVersion >= version) {
+                    return;
+                }
+                mVersion = version;
+                RunOrClosePipeMessageParams message = new RunOrClosePipeMessageParams();
+                message.reserved0 = 16;
+                message.reserved1 = 0;
+                message.requireVersion = new RequireVersion();
+                message.requireVersion.version = version;
+                InterfaceControlMessagesHelper.sendRunOrClosePipeMessage(
+                        getCore(), mMessageReceiver, message);
+            }
+        }
+
+        /**
+         * The handler associated with this proxy.
+         */
+        private final HandlerImpl mHandler;
+
+        protected AbstractProxy(Core core, MessageReceiverWithResponder messageReceiver) {
+            mHandler = new HandlerImpl(core, messageReceiver);
+        }
+
+        /**
+         * @see Interface#close()
+         */
+        @Override
+        public void close() {
+            mHandler.close();
+        }
+
+        /**
+         * @see Proxy#getProxyHandler()
+         */
+        @Override
+        public HandlerImpl getProxyHandler() {
+            return mHandler;
+        }
+
+        /**
+         * @see ConnectionErrorHandler#onConnectionError(org.chromium.mojo.system.MojoException)
+         */
+        @Override
+        public void onConnectionError(MojoException e) {
+            mHandler.onConnectionError(e);
+        }
+    }
+
+    /**
+     * 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();
+
+        /**
+         * Returns the version of the managed interface.
+         */
+        public abstract int getVersion();
+
+        /**
+         * 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 bound to an implementation of the interface.
+         */
+        public final P attachProxy(MessagePipeHandle handle, int version) {
+            RouterImpl router = new RouterImpl(handle);
+            P proxy = attachProxy(handle.getCore(), router);
+            DelegatingConnectionErrorHandler handlers = new DelegatingConnectionErrorHandler();
+            handlers.addConnectionErrorHandler(proxy);
+            router.setErrorHandler(handlers);
+            router.start();
+            ((HandlerImpl) proxy.getProxyHandler()).setVersion(version);
+            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, 0);
+            return Pair.create(proxy, new InterfaceRequest<I>(handles.second));
+        }
+
+        public final InterfaceRequest<I> asInterfaceRequest(MessagePipeHandle handle) {
+            return new InterfaceRequest<I>(handle);
+        }
+
+        /**
+         * 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/InterfaceControlMessagesHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
new file mode 100644
index 0000000..939fb93
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
@@ -0,0 +1,89 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.bindings.Callbacks.Callback1;
+import org.chromium.mojo.bindings.Interface.Manager;
+import org.chromium.mojo.bindings.Interface.Proxy;
+import org.chromium.mojo.system.Core;
+
+/**
+ * Helper class to handle interface control messages. See
+ * mojo/public/interfaces/bindings/interface_control_messages.mojom.
+ */
+public class InterfaceControlMessagesHelper {
+    /**
+     * MessageReceiver that forwards a message containing a {@link RunResponseMessageParams} to a
+     * callback.
+     */
+    private static class RunResponseForwardToCallback
+            extends SideEffectFreeCloseable implements MessageReceiver {
+        private final Callback1<RunResponseMessageParams> mCallback;
+
+        RunResponseForwardToCallback(Callback1<RunResponseMessageParams> callback) {
+            mCallback = callback;
+        }
+
+        /**
+         * @see MessageReceiver#accept(Message)
+         */
+        @Override
+        public boolean accept(Message message) {
+            RunResponseMessageParams response =
+                    RunResponseMessageParams.deserialize(message.asServiceMessage().getPayload());
+            mCallback.call(response);
+            return true;
+        }
+    }
+
+    /**
+     * Sends the given run message through the receiver, registering the callback.
+     */
+    public static void sendRunMessage(Core core, MessageReceiverWithResponder receiver,
+            RunMessageParams params, Callback1<RunResponseMessageParams> callback) {
+        Message message = params.serializeWithHeader(
+                core, new MessageHeader(InterfaceControlMessagesConstants.RUN_MESSAGE_ID,
+                        MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0));
+        receiver.acceptWithResponder(message, new RunResponseForwardToCallback(callback));
+    }
+
+    /**
+     * Sends the given run or close pipe message through the receiver.
+     */
+    public static void sendRunOrClosePipeMessage(
+            Core core, MessageReceiverWithResponder receiver, RunOrClosePipeMessageParams params) {
+        Message message = params.serializeWithHeader(core,
+                new MessageHeader(InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID));
+        receiver.accept(message);
+    }
+
+    /**
+     * Handles a received run message.
+     */
+    public static <I extends Interface, P extends Proxy> boolean handleRun(
+            Core core, Manager<I, P> manager, ServiceMessage message, MessageReceiver responder) {
+        RunResponseMessageParams response = new RunResponseMessageParams();
+        response.reserved0 = 16;
+        response.reserved1 = 0;
+        response.queryVersionResult = new QueryVersionResult();
+        response.queryVersionResult.version = manager.getVersion();
+
+        return responder.accept(response.serializeWithHeader(
+                core, new MessageHeader(InterfaceControlMessagesConstants.RUN_MESSAGE_ID,
+                        MessageHeader.MESSAGE_IS_RESPONSE_FLAG,
+                        message.getHeader().getRequestId())));
+    }
+
+    /**
+     * Handles a received run or close pipe message. Closing the pipe is handled by returning
+     * |false|.
+     */
+    public static <I extends Interface, P extends Proxy> boolean handleRunOrClosePipe(
+            Manager<I, P> manager, ServiceMessage message) {
+        Message payload = message.getPayload();
+        RunOrClosePipeMessageParams query = RunOrClosePipeMessageParams.deserialize(payload);
+        return query.requireVersion.version <= manager.getVersion();
+    }
+}
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..61899b4
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.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.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();
+    }
+
+    /**
+     * Returns an {@link InterfaceRequest} that wraps the given handle. This method is not type safe
+     * and should be avoided unless absolutely necessary.
+     */
+    @SuppressWarnings("rawtypes")
+    public static InterfaceRequest asInterfaceRequestUnsafe(MessagePipeHandle handle) {
+        return new InterfaceRequest(handle);
+    }
+}
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..771d22b
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Header information for a message.
+ */
+public class MessageHeader {
+
+    private static final int SIMPLE_MESSAGE_SIZE = 24;
+    private static final int SIMPLE_MESSAGE_VERSION = 0;
+    private static final DataHeader SIMPLE_MESSAGE_STRUCT_INFO =
+            new DataHeader(SIMPLE_MESSAGE_SIZE, SIMPLE_MESSAGE_VERSION);
+
+    private static final int MESSAGE_WITH_REQUEST_ID_SIZE = 32;
+    private static final int MESSAGE_WITH_REQUEST_ID_VERSION = 1;
+    private static final DataHeader MESSAGE_WITH_REQUEST_ID_STRUCT_INFO =
+            new DataHeader(MESSAGE_WITH_REQUEST_ID_SIZE, MESSAGE_WITH_REQUEST_ID_VERSION);
+
+    private static final int INTERFACE_ID_OFFSET = 8;
+    private static final int TYPE_OFFSET = 12;
+    private static final int FLAGS_OFFSET = 16;
+    private static final int REQUEST_ID_OFFSET = 24;
+
+    /**
+     * 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);
+
+        // Currently associated interfaces are not supported.
+        int interfaceId = decoder.readInt(INTERFACE_ID_OFFSET);
+        if (interfaceId != 0) {
+          throw new DeserializationException("Non-zero interface ID, expecting zero since "
+                  + "associated interfaces are not yet supported.");
+        }
+
+        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);
+        // Set the interface ID field to 0.
+        encoder.encode(0, INTERFACE_ID_OFFSET);
+        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;
+        return (BindingsHelper.equals(mDataHeader, other.mDataHeader)
+                && 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.elementsOrVersion < SIMPLE_MESSAGE_VERSION) {
+            throw new DeserializationException("Incorrect number of fields, expecting at least "
+                    + SIMPLE_MESSAGE_VERSION + ", but got: " + dataHeader.elementsOrVersion);
+        }
+        if (dataHeader.size < SIMPLE_MESSAGE_SIZE) {
+            throw new DeserializationException(
+                    "Incorrect message size, expecting at least " + SIMPLE_MESSAGE_SIZE
+                    + ", but got: " + dataHeader.size);
+        }
+        if (dataHeader.elementsOrVersion == SIMPLE_MESSAGE_VERSION
+                && dataHeader.size != SIMPLE_MESSAGE_SIZE) {
+            throw new DeserializationException("Incorrect message size for a message with "
+                    + SIMPLE_MESSAGE_VERSION + " fields, expecting " + SIMPLE_MESSAGE_SIZE
+                    + ", but got: " + dataHeader.size);
+        }
+        if (dataHeader.elementsOrVersion == MESSAGE_WITH_REQUEST_ID_VERSION
+                && dataHeader.size != MESSAGE_WITH_REQUEST_ID_SIZE) {
+            throw new DeserializationException("Incorrect message size for a message with "
+                    + MESSAGE_WITH_REQUEST_ID_VERSION + " 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..16b29b4
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
@@ -0,0 +1,274 @@
+// Copyright 2014 The Chromium 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.annotation.SuppressLint;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of {@link Router}.
+ */
+@SuppressLint("UseSparseArrays")  // https://crbug.com/600699
+public class RouterImpl implements Router {
+
+    /**
+     * {@link MessageReceiver} used as the {@link Connector} callback.
+     */
+    private class HandleIncomingMessageThunk implements MessageReceiver {
+
+        /**
+         * @see MessageReceiver#accept(Message)
+         */
+        @Override
+        public boolean accept(Message message) {
+            return handleIncomingMessage(message);
+        }
+
+        /**
+         * @see MessageReceiver#close()
+         */
+        @Override
+        public void close() {
+            handleConnectorClose();
+        }
+
+    }
+
+    /**
+     *
+     * {@link MessageReceiver} used to return responses to the caller.
+     */
+    class ResponderThunk implements MessageReceiver {
+        private boolean mAcceptWasInvoked = false;
+
+        /**
+         * @see
+         * MessageReceiver#accept(Message)
+         */
+        @Override
+        public boolean accept(Message message) {
+            mAcceptWasInvoked = true;
+            return RouterImpl.this.accept(message);
+        }
+
+        /**
+         * @see MessageReceiver#close()
+         */
+        @Override
+        public void close() {
+            RouterImpl.this.close();
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            if (!mAcceptWasInvoked) {
+                // We close the pipe here as a way of signaling to the calling application that an
+                // error condition occurred. Without this the calling application would have no
+                // way of knowing it should stop waiting for a response.
+                RouterImpl.this.closeOnHandleThread();
+            }
+            super.finalize();
+        }
+    }
+
+    /**
+     * 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>();
+
+    /**
+     * An Executor that will run on the thread associated with the MessagePipe to which
+     * this Router is bound. This may be {@code Null} if the MessagePipeHandle passed
+     * in to the constructor is not valid.
+     */
+    private final Executor mExecutor;
+
+    /**
+     * 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 HandleIncomingMessageThunk());
+        Core core = messagePipeHandle.getCore();
+        if (core != null) {
+            mExecutor = ExecutorFactory.getExecutorForCurrentThread(core);
+        } else {
+            mExecutor = null;
+        }
+    }
+
+    /**
+     * @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.
+        // TODO(lhchavez): Remove this hack. See b/28986534 for details.
+        synchronized (mResponders) {
+            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, new ResponderThunk());
+            }
+            // 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;
+            // TODO(lhchavez): Remove this hack. See b/28986534 for details.
+            synchronized (mResponders) {
+                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();
+        }
+    }
+
+    /**
+     * Invokes {@link #close()} asynchronously on the thread associated with
+     * this Router's Handle. If this Router was constructed with an invalid
+     * handle then this method does nothing.
+     */
+    private void closeOnHandleThread() {
+        if (mExecutor != null) {
+            mExecutor.execute(new Runnable() {
+
+                @Override
+                public void run() {
+                    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..313dc6a
--- /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.LITTLE_ENDIAN);
+            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..85cc97c
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.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.Core;
+
+/**
+ * Base class for all mojo structs.
+ */
+public abstract class Struct {
+    /**
+     * The base size of the encoded struct.
+     */
+    private final int mEncodedBaseSize;
+
+    /**
+     * The version of the struct.
+     */
+    private final int mVersion;
+
+    /**
+     * Constructor.
+     */
+    protected Struct(int encodedBaseSize, int version) {
+        mEncodedBaseSize = encodedBaseSize;
+        mVersion = version;
+    }
+
+    /**
+     * Returns the version of the struct. It is the max version of the struct in the mojom if it has
+     * been created locally, and the version of the received struct if it has been deserialized.
+     */
+    public int getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Returns the serialization of the struct. This method can close Handles.
+     *
+     * @param core the |Core| implementation used to generate handles. Only used if the data
+     *            structure 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);
+    }
+
+    /**
+     * Use the given encoder to serialize this data structure.
+     */
+    protected abstract void encode(Encoder encoder);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java
new file mode 100644
index 0000000..90b40ea
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Core;
+
+/**
+ * Base class for all mojo unions.
+ */
+public abstract class Union {
+    /**
+     * Returns the serialization of the union. This method can close Handles.
+     *
+     * @param core the |Core| implementation used to generate handles. Only used if the data
+     *            structure being encoded contains interfaces, can be |null| otherwise.
+     */
+    public Message serialize(Core core) {
+        Encoder encoder = new Encoder(core, BindingsHelper.UNION_SIZE);
+        encoder.claimMemory(16);
+        encode(encoder, 0);
+        return encoder.getMessage();
+    }
+
+    /**
+     * Serializes this data structure using the given encoder.
+     */
+    protected abstract void encode(Encoder encoder, int offset);
+}
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..ba0e5c6
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -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.
+
+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.
+         */
+        public 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;
+        private static final int FLAG_PEER_CLOSED = 1 << 2;
+
+        /**
+         * 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);
+        }
+
+        /**
+         * Change the peer closed bit of this signal.
+         *
+         * @param peerClosed the new value of the peer closed bit.
+         * @return this.
+         */
+        public HandleSignals setPeerClosed(boolean peerClosed) {
+            return setFlag(FLAG_PEER_CLOSED, peerClosed);
+        }
+
+        /**
+         * Returns a signal with no bit set.
+         */
+        public static HandleSignals none() {
+            return new HandleSignals(FLAG_NONE);
+        }
+
+    }
+
+    /**
+     * Returns a platform-dependent monotonically increasing tick count representing "right now."
+     */
+    public long getTimeTicksNow();
+
+    /**
+     * Returned by wait functions to indicate the signaling state of handles.
+     */
+    public static class HandleSignalsState {
+        /**
+         * Signals that were satisfied at some time // before the call returned.
+         */
+        private final HandleSignals mSatisfiedSignals;
+
+        /**
+         * Signals that are possible to satisfy. For example, if the return value was
+         * |MOJO_RESULT_FAILED_PRECONDITION|, you can use this field to determine which, if any, of
+         * the signals can still be satisfied.
+         */
+        private final HandleSignals mSatisfiableSignals;
+
+        /**
+         * Constructor.
+         */
+        public HandleSignalsState(
+                HandleSignals satisfiedSignals, HandleSignals satisfiableSignals) {
+            mSatisfiedSignals = satisfiedSignals;
+            mSatisfiableSignals = satisfiableSignals;
+        }
+
+        /**
+         * Returns the satisfiedSignals.
+         */
+        public HandleSignals getSatisfiedSignals() {
+            return mSatisfiedSignals;
+        }
+
+        /**
+         * Returns the satisfiableSignals.
+         */
+        public HandleSignals getSatisfiableSignals() {
+            return mSatisfiableSignals;
+        }
+    }
+
+    /**
+     * Result for the |wait| method.
+     */
+    public static class WaitResult {
+        /**
+         * The result of the wait method.
+         * <p>
+         * |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 closed).
+         */
+        private int mMojoResult;
+
+        /**
+         * The signaling state of handles.
+         */
+        private HandleSignalsState mHandleSignalsState;
+
+        /**
+         * Returns the mojoResult.
+         */
+        public int getMojoResult() {
+            return mMojoResult;
+        }
+
+        /**
+         * @param mojoResult the mojoResult to set
+         */
+        public void setMojoResult(int mojoResult) {
+            mMojoResult = mojoResult;
+        }
+
+        /**
+         * Returns the handleSignalsState.
+         */
+        public HandleSignalsState getHandleSignalsState() {
+            return mHandleSignalsState;
+        }
+
+        /**
+         * @param handleSignalsState the handleSignalsState to set
+         */
+        public void setHandleSignalsState(HandleSignalsState handleSignalsState) {
+            mHandleSignalsState = handleSignalsState;
+        }
+    }
+
+    /**
+     * Waits on the given |handle| until the state indicated by |signals| is satisfied or until
+     * |deadline| has passed.
+     *
+     * @return a |WaitResult|.
+     */
+    public WaitResult 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;
+
+        /**
+         * The signaling state of handles. Will not be set if |mojoResult| is
+         * |MOJO_RESULT_INVALID_ARGUMENT| or |MOJO_RESULT_RESOURCE_EXHAUSTED|
+         */
+        private List<HandleSignalsState> mSignalStates;
+
+        /**
+         * Returns the mojoResult.
+         */
+        public int getMojoResult() {
+            return mMojoResult;
+        }
+
+        /**
+         * @param mojoResult the mojoResult to set
+         */
+        public void setMojoResult(int mojoResult) {
+            mMojoResult = mojoResult;
+        }
+
+        /**
+         * Returns the handleIndex.
+         */
+        public int getHandleIndex() {
+            return mHandleIndex;
+        }
+
+        /**
+         * @param handleIndex the handleIndex to set
+         */
+        public void setHandleIndex(int handleIndex) {
+            mHandleIndex = handleIndex;
+        }
+
+        /**
+         * Returns the signalStates.
+         */
+        public List<HandleSignalsState> getSignalStates() {
+            return mSignalStates;
+        }
+
+        /**
+         * @param signalStates the signalStates to set
+         */
+        public void setSignalStates(List<HandleSignalsState> signalStates) {
+            mSignalStates = signalStates;
+        }
+    }
+
+    /**
+     * 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();
+
+    /**
+     * Returns a new run loop.
+     */
+    public RunLoop createDefaultRunLoop();
+
+    /**
+     * Returns the current run loop if it exists.
+     */
+    public RunLoop getCurrentRunLoop();
+}
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..4deaf09
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
@@ -0,0 +1,334 @@
+// Copyright 2014 The Chromium 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;
+
+        /**
+         * 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 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;
+        private static final int FLAG_PEEK = 1 << 3;
+
+        /**
+         * 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 |discard| 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);
+        }
+
+        /**
+         * Change the peek bit of this flag. If set, read the requested number of elements, and
+         * leave those elements in the pipe. A later read will return the same data.
+         * Mutually exclusive with |discard| and |query|.
+         *
+         * @param peek the new value of the peek bit.
+         * @return this.
+         */
+        public ReadFlags peek(boolean peek) {
+            return setFlag(FLAG_PEEK, peek);
+        }
+
+        /**
+         * @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 ResultAnd<Integer> 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 ResultAnd<Integer> 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..30ff07f
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
@@ -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.
+
+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;
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return mFlags;
+    }
+
+    /**
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        Flags<?> other = (Flags<?>) obj;
+        if (mFlags != other.mFlags) return false;
+        return true;
+    }
+}
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..6181669
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+import org.chromium.mojo.system.Core.WaitResult;
+
+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 WaitResult 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..9c20fdd
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+import org.chromium.mojo.system.Core.HandleSignals;
+import org.chromium.mojo.system.Core.WaitResult;
+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 WaitResult 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 0;
+    }
+
+    /**
+     * @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 ResultAnd<Integer> 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 ResultAnd<Integer> 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 ResultAnd<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..deb6ac0
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
@@ -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.
+
+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 {
+        /**
+         * 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 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).
+     */
+    ResultAnd<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..4e0e3e9
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
@@ -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.
+
+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;
+    }
+
+    /**
+     * Constructor.
+     */
+    public MojoException(Throwable cause) {
+        super(cause);
+        mCode = MojoResult.UNKNOWN;
+    }
+
+    /**
+     * 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..2602cb5
--- /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/ResultAnd.java b/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java
new file mode 100644
index 0000000..656d0d6
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+/**
+ * Container that contains a mojo result and a value.
+ *
+ * @param <A> the type of the value.
+ */
+public class ResultAnd<A> {
+    private final int mMojoResult;
+    private final A mValue;
+
+    public ResultAnd(int result, A value) {
+        this.mMojoResult = result;
+        this.mValue = value;
+    }
+
+    /**
+     * Returns the mojo result.
+     */
+    public int getMojoResult() {
+        return mMojoResult;
+    }
+
+    /**
+     * Returns the value.
+     */
+    public A getValue() {
+        return mValue;
+    }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java b/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java
new file mode 100644
index 0000000..4038b29
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+import java.io.Closeable;
+
+/**
+ * Definition of a run loop.
+ */
+public interface RunLoop extends Closeable {
+    /**
+     * Start the run loop. It will continue until quit() is called.
+     */
+    public void run();
+
+    /**
+     * Start the run loop and stop it as soon as no task is present in the work queue.
+     */
+    public void runUntilIdle();
+
+    /*
+     * Quit the currently running run loop.
+     */
+    public void quit();
+
+    /**
+     * Add a runnable to the queue of tasks.
+     * @param runnable Callback to be executed by the run loop.
+     * @param delay Delay, in MojoTimeTicks (microseconds) before the callback should
+     * be executed.
+     */
+    public void postDelayedTask(Runnable runnable, long delay);
+
+    /**
+     * Destroy the run loop and deregister it from Core.
+     */
+    @Override
+    public abstract void close();
+}
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/BUILD.gn b/mojo/public/js/BUILD.gn
new file mode 100644
index 0000000..eda7e04
--- /dev/null
+++ b/mojo/public/js/BUILD.gn
@@ -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.
+
+source_set("js") {
+  sources = [
+    "constants.cc",
+    "constants.h",
+  ]
+}
+
+group("bindings") {
+  data = [
+    "bindings.js",
+    "buffer.js",
+    "codec.js",
+    "connection.js",
+    "connector.js",
+    "constants.cc",
+    "constants.h",
+    "core.js",
+    "router.js",
+    "support.js",
+    "threading.js",
+    "unicode.js",
+    "validator.js",
+  ]
+}
+
+group("tests") {
+  testonly = true
+
+  data = [
+    "codec_unittests.js",
+    "core_unittests.js",
+    "struct_unittests.js",
+    "test/validation_test_input_parser.js",
+    "union_unittests.js",
+    "validation_unittests.js",
+    "//mojo/public/interfaces/bindings/tests/data/validation/",
+  ]
+  public_deps = [
+    ":bindings",
+  ]
+}
diff --git a/mojo/public/js/bindings.js b/mojo/public/js/bindings.js
new file mode 100644
index 0000000..2fdcae3
--- /dev/null
+++ b/mojo/public/js/bindings.js
@@ -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.
+
+define("mojo/public/js/bindings", [
+  "mojo/public/js/router",
+  "mojo/public/js/core",
+], function(router, core) {
+
+  var Router = router.Router;
+
+  var kProxyProperties = Symbol("proxyProperties");
+  var kStubProperties = Symbol("stubProperties");
+
+  // Public proxy class properties that are managed at runtime by the JS
+  // bindings. See ProxyBindings below.
+  function ProxyProperties(receiver) {
+    this.receiver = receiver;
+  }
+
+  // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom.
+  ProxyProperties.prototype.getLocalDelegate = function() {
+    return this.local && StubBindings(this.local).delegate;
+  }
+
+  // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom.
+  ProxyProperties.prototype.setLocalDelegate = function(impl) {
+    if (this.local)
+      StubBindings(this.local).delegate = impl;
+    else
+      throw new Error("no stub object");
+  }
+
+  ProxyProperties.prototype.close = function() {
+    this.connection.close();
+  }
+
+  // Public stub class properties that are managed at runtime by the JS
+  // bindings. See StubBindings below.
+  function StubProperties(delegate) {
+    this.delegate = delegate;
+  }
+
+  StubProperties.prototype.close = function() {
+    this.connection.close();
+  }
+
+  // The base class for generated proxy classes.
+  function ProxyBase(receiver) {
+    this[kProxyProperties] = new ProxyProperties(receiver);
+
+    // TODO(hansmuller): Temporary, for Chrome backwards compatibility.
+    if (receiver instanceof Router)
+      this.receiver_ = receiver;
+  }
+
+  // The base class for generated stub classes.
+  function StubBase(delegate) {
+    this[kStubProperties] = new StubProperties(delegate);
+  }
+
+  // TODO(hansmuller): remove everything except the connection property doc
+  // after 'Client=' has been removed from Mojom.
+
+  // Provides access to properties added to a proxy object without risking
+  // Mojo interface name collisions. Unless otherwise specified, the initial
+  // value of all properties is undefined.
+  //
+  // ProxyBindings(proxy).connection - The Connection object that links the
+  //   proxy for a remote Mojo service to an optional local stub for a local
+  //   service. The value of ProxyBindings(proxy).connection.remote == proxy.
+  //
+  // ProxyBindings(proxy).local  - The "local" stub object whose delegate
+  //   implements the proxy's Mojo client interface.
+  //
+  // ProxyBindings(proxy).setLocalDelegate(impl) - Sets the implementation
+  //   delegate of the proxy's client stub object. This is just shorthand
+  //   for |StubBindings(ProxyBindings(proxy).local).delegate = impl|.
+  //
+  // ProxyBindings(proxy).getLocalDelegate() - Returns the implementation
+  //   delegate of the proxy's client stub object. This is just shorthand
+  //   for |StubBindings(ProxyBindings(proxy).local).delegate|.
+
+  function ProxyBindings(proxy) {
+    return (proxy instanceof ProxyBase) ? proxy[kProxyProperties] : proxy;
+  }
+
+  // TODO(hansmuller): remove the remote doc after 'Client=' has been
+  // removed from Mojom.
+
+  // Provides access to properties added to a stub object without risking
+  // Mojo interface name collisions. Unless otherwise specified, the initial
+  // value of all properties is undefined.
+  //
+  // StubBindings(stub).delegate - The optional implementation delegate for
+  //  the Mojo interface stub.
+  //
+  // StubBindings(stub).connection - The Connection object that links an
+  //   optional proxy for a remote service to this stub. The value of
+  //   StubBindings(stub).connection.local == stub.
+  //
+  // StubBindings(stub).remote - A proxy for the the stub's Mojo client
+  //   service.
+
+  function StubBindings(stub) {
+    return stub instanceof StubBase ?  stub[kStubProperties] : stub;
+  }
+
+  var exports = {};
+  exports.EmptyProxy = ProxyBase;
+  exports.EmptyStub = StubBase;
+  exports.ProxyBase = ProxyBase;
+  exports.ProxyBindings = ProxyBindings;
+  exports.StubBase = StubBase;
+  exports.StubBindings = StubBindings;
+  return exports;
+});
diff --git a/mojo/public/js/buffer.js b/mojo/public/js/buffer.js
new file mode 100644
index 0000000..e35f695
--- /dev/null
+++ b/mojo/public/js/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/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/codec.js b/mojo/public/js/codec.js
new file mode 100644
index 0000000..4003b51
--- /dev/null
+++ b/mojo/public/js/codec.js
@@ -0,0 +1,879 @@
+// Copyright 2014 The Chromium 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/codec", [
+  "mojo/public/js/unicode",
+  "mojo/public/js/buffer",
+], function(unicode, buffer) {
+
+  var kErrorUnsigned = "Passing negative value to unsigned";
+  var kErrorArray = "Passing non Array for array type";
+  var kErrorString = "Passing non String for string type";
+  var kErrorMap = "Passing non Map for map type";
+
+  // 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 = 24;
+  var kMessageWithRequestIDHeaderSize = 32;
+  var kMapStructPayloadSize = 16;
+
+  var kStructHeaderNumBytesOffset = 0;
+  var kStructHeaderVersionOffset = 4;
+
+  var kEncodedInvalidHandleValue = 0xFFFFFFFF;
+
+  // Decoder ------------------------------------------------------------------
+
+  function Decoder(buffer, handles, base) {
+    this.buffer = buffer;
+    this.handles = handles;
+    this.base = base;
+    this.next = base;
+  }
+
+  Decoder.prototype.align = function() {
+    this.next = align(this.next);
+  };
+
+  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()] || null;
+  };
+
+  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();
+  };
+
+  Decoder.prototype.decodeMap = function(keyClass, valueClass) {
+    this.skip(4); // numberOfBytes
+    this.skip(4); // version
+    var keys = this.decodeArrayPointer(keyClass);
+    var values = this.decodeArrayPointer(valueClass);
+    var val = new Map();
+    for (var i = 0; i < keys.length; i++)
+      val.set(keys[i], values[i]);
+    return val;
+  };
+
+  Decoder.prototype.decodeMapPointer = function(keyClass, valueClass) {
+    var pointer = this.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    var decoder = this.decodeAndCreateDecoder(pointer);
+    return decoder.decodeMap(keyClass, valueClass);
+  };
+
+  // Encoder ------------------------------------------------------------------
+
+  function Encoder(buffer, handles, base) {
+    this.buffer = buffer;
+    this.handles = handles;
+    this.base = base;
+    this.next = base;
+  }
+
+  Encoder.prototype.align = function() {
+    this.next = align(this.next);
+  };
+
+  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;
+    if (!Number.isSafeInteger(numberOfElements) || numberOfElements < 0)
+      throw new Error(kErrorArray);
+
+    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;
+    }
+    // Only accepts string primivites, not String Objects like new String("foo")
+    if (typeof(val) !== "string") {
+      throw new Error(kErrorString);
+    }
+    var encodedSize = kArrayHeaderSize + unicode.utf8Length(val);
+    var encoder = this.createAndEncodeEncoder(encodedSize);
+    encoder.encodeString(val);
+  };
+
+  Encoder.prototype.encodeMap = function(keyClass, valueClass, val) {
+    var keys = new Array(val.size);
+    var values = new Array(val.size);
+    var i = 0;
+    val.forEach(function(value, key) {
+      values[i] = value;
+      keys[i++] = key;
+    });
+    this.writeUint32(kStructHeaderSize + kMapStructPayloadSize);
+    this.writeUint32(0);  // version
+    this.encodeArrayPointer(keyClass, keys);
+    this.encodeArrayPointer(valueClass, values);
+  }
+
+  Encoder.prototype.encodeMapPointer = function(keyClass, valueClass, val) {
+    if (val == null) {
+      // Also handles undefined, since undefined == null.
+      this.encodePointer(val);
+      return;
+    }
+    if (!(val instanceof Map)) {
+      throw new Error(kErrorMap);
+    }
+    var encodedSize = kStructHeaderSize + kMapStructPayloadSize;
+    var encoder = this.createAndEncodeEncoder(encodedSize);
+    encoder.encodeMap(keyClass, valueClass, val);
+  };
+
+  // Message ------------------------------------------------------------------
+
+  var kMessageInterfaceIdOffset = kStructHeaderSize;
+  var kMessageNameOffset = kMessageInterfaceIdOffset + 4;
+  var kMessageFlagsOffset = kMessageNameOffset + 4;
+  var kMessageRequestIDOffset = kMessageFlagsOffset + 8;
+
+  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.getHeaderVersion = function() {
+    return this.buffer.getUint32(kStructHeaderVersionOffset);
+  };
+
+  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(0);  // version.
+    encoder.writeUint32(0);  // interface ID.
+    encoder.writeUint32(messageName);
+    encoder.writeUint32(0);  // flags.
+    encoder.writeUint32(0);  // padding.
+  }
+
+  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(1);  // version.
+    encoder.writeUint32(0);  // interface ID.
+    encoder.writeUint32(messageName);
+    encoder.writeUint32(flags);
+    encoder.writeUint32(0);  // padding.
+    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 version = this.decoder.readUint32();
+    var interface_id = this.decoder.readUint32();
+    if (interface_id != 0) {
+      throw new Error("Receiving non-zero interface ID. Associated interfaces " +
+                      "are not yet supported.");
+    }
+    this.messageName = this.decoder.readUint32();
+    this.flags = this.decoder.readUint32();
+    // Skip the padding.
+    this.decoder.skip(4);
+    if (version >= 1)
+      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, length) {
+    this.cls = cls;
+    this.length = length || 0;
+  }
+
+  ArrayOf.prototype.encodedSize = 8;
+
+  ArrayOf.prototype.dimensions = function() {
+    return [this.length].concat(
+      (this.cls instanceof ArrayOf) ? this.cls.dimensions() : []);
+  }
+
+  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;
+
+  function Interface() {
+  }
+
+  Interface.encodedSize = 8;
+
+  Interface.decode = function(decoder) {
+    var handle = decoder.decodeHandle();
+    // Ignore the version field for now.
+    decoder.readUint32();
+
+    return handle;
+  };
+
+  Interface.encode = function(encoder, val) {
+    encoder.encodeHandle(val);
+    // Set the version field to 0 for now.
+    encoder.writeUint32(0);
+  };
+
+  function NullableInterface() {
+  }
+
+  NullableInterface.encodedSize = Interface.encodedSize;
+
+  NullableInterface.decode = Interface.decode;
+
+  NullableInterface.encode = Interface.encode;
+
+  function MapOf(keyClass, valueClass) {
+    this.keyClass = keyClass;
+    this.valueClass = valueClass;
+  }
+
+  MapOf.prototype.encodedSize = 8;
+
+  MapOf.prototype.decode = function(decoder) {
+    return decoder.decodeMapPointer(this.keyClass, this.valueClass);
+  };
+
+  MapOf.prototype.encode = function(encoder, val) {
+    encoder.encodeMapPointer(this.keyClass, this.valueClass, val);
+  };
+
+  function NullableMapOf(keyClass, valueClass) {
+    MapOf.call(this, keyClass, valueClass);
+  }
+
+  NullableMapOf.prototype = Object.create(MapOf.prototype);
+
+  var exports = {};
+  exports.align = align;
+  exports.isAligned = isAligned;
+  exports.Message = Message;
+  exports.MessageBuilder = MessageBuilder;
+  exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder;
+  exports.MessageReader = MessageReader;
+  exports.kArrayHeaderSize = kArrayHeaderSize;
+  exports.kMapStructPayloadSize = kMapStructPayloadSize;
+  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;
+  exports.Interface = Interface;
+  exports.NullableInterface = NullableInterface;
+  exports.MapOf = MapOf;
+  exports.NullableMapOf = NullableMapOf;
+  return exports;
+});
diff --git a/mojo/public/js/codec_unittests.js b/mojo/public/js/codec_unittests.js
new file mode 100644
index 0000000..b610d9a
--- /dev/null
+++ b/mojo/public/js/codec_unittests.js
@@ -0,0 +1,296 @@
+// Copyright 2014 The Chromium 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/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();
+  testTypedPointerValidation();
+  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([
+      24, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+      42, 0, 0, 0,
+       0, 0, 0, 0,
+       0, 0, 0, 0,
+
+      16, 0, 0, 0,
+       0, 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: */   24,    0,    0,    0,    0,    0,    0,    0,
+      /*  8: */    0,    0,    0,    0,   31,    0,    0,    0,
+      /* 16: */    0,    0,    0,    0,    0,    0,    0,    0,
+      /* 24: */   96,    0,    0,    0,    0,    0,    0,    0,
+      /* 32: */ 0xD5, 0xB4, 0x12, 0x02, 0x93, 0x6E, 0x01,    0,
+      /* 40: */    5,    0,    0,    0,    0,    0,    0,    0,
+      /* 48: */   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: */   24,    0,    0,    0,    0,    0,    0,    0,
+      /*  8: */    0,    0,    0,    0,   42,    0,    0,    0,
+      /* 16: */    0,    0,    0,    0,    0,    0,    0,    0,
+      /* 24: */    8,    0,    0,    0,    0,    0,    0,    0,
+      /* 32: */   14,    0,    0,    0,    6,    0,    0,    0,
+      /* 40: */ 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);
+  }
+
+  function testTypedPointerValidation() {
+    var encoder = new codec.MessageBuilder(42, 24).createEncoder(8);
+    function DummyClass() {};
+    var testCases = [
+      // method, args, invalid examples, valid examples
+      [encoder.encodeArrayPointer, [DummyClass], [75],
+          [[], null, undefined, new Uint8Array([])]],
+      [encoder.encodeStringPointer, [], [75, new String("foo")],
+          ["", "bar", null, undefined]],
+      [encoder.encodeMapPointer, [DummyClass, DummyClass], [75],
+          [new Map(), null, undefined]],
+    ];
+
+    testCases.forEach(function(test) {
+      var method = test[0];
+      var baseArgs = test[1];
+      var invalidExamples = test[2];
+      var validExamples = test[3];
+
+      var encoder = new codec.MessageBuilder(42, 24).createEncoder(8);
+      invalidExamples.forEach(function(invalid) {
+        expect(function() {
+          method.apply(encoder, baseArgs.concat(invalid));
+        }).toThrow();
+      });
+
+      validExamples.forEach(function(valid) {
+        var encoder = new codec.MessageBuilder(42, 24).createEncoder(8);
+        method.apply(encoder, baseArgs.concat(valid));
+      });
+    });
+  }
+});
diff --git a/mojo/public/js/connection.js b/mojo/public/js/connection.js
new file mode 100644
index 0000000..3f7e839
--- /dev/null
+++ b/mojo/public/js/connection.js
@@ -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.
+
+define("mojo/public/js/connection", [
+  "mojo/public/js/bindings",
+  "mojo/public/js/connector",
+  "mojo/public/js/core",
+  "mojo/public/js/router",
+], function(bindings, connector, core, router) {
+
+  var Router = router.Router;
+  var EmptyProxy = bindings.EmptyProxy;
+  var EmptyStub = bindings.EmptyStub;
+  var ProxyBindings = bindings.ProxyBindings;
+  var StubBindings = bindings.StubBindings;
+  var TestConnector = connector.TestConnector;
+  var TestRouter = router.TestRouter;
+
+  // TODO(hansmuller): the proxy receiver_ property should be receiver$
+
+  function BaseConnection(localStub, remoteProxy, router) {
+    this.router_ = router;
+    this.local = localStub;
+    this.remote = remoteProxy;
+
+    this.router_.setIncomingReceiver(localStub);
+    this.router_.setErrorHandler(function() {
+      if (StubBindings(this.local) &&
+          StubBindings(this.local).connectionErrorHandler)
+        StubBindings(this.local).connectionErrorHandler();
+    }.bind(this));
+    if (this.remote)
+      this.remote.receiver_ = router;
+
+    // Validate incoming messages: remote responses and local requests.
+    var validateRequest = localStub && localStub.validator;
+    var validateResponse = remoteProxy && remoteProxy.validator;
+    var payloadValidators = [];
+    if (validateRequest)
+      payloadValidators.push(validateRequest);
+    if (validateResponse)
+      payloadValidators.push(validateResponse);
+    this.router_.setPayloadValidators(payloadValidators);
+  }
+
+  BaseConnection.prototype.close = function() {
+    this.router_.close();
+    this.router_ = null;
+    this.local = null;
+    this.remote = null;
+  };
+
+  BaseConnection.prototype.encounteredError = function() {
+    return this.router_.encounteredError();
+  };
+
+  function Connection(
+      handle, localFactory, remoteFactory, routerFactory, connectorFactory) {
+    var routerClass = routerFactory || Router;
+    var router = new routerClass(handle, connectorFactory);
+    var remoteProxy = remoteFactory && new remoteFactory(router);
+    var localStub = localFactory && new localFactory(remoteProxy);
+    BaseConnection.call(this, localStub, remoteProxy, router);
+  }
+
+  Connection.prototype = Object.create(BaseConnection.prototype);
+
+  // The TestConnection subclass is only intended to be used in unit tests.
+  function TestConnection(handle, localFactory, remoteFactory) {
+    Connection.call(this,
+                    handle,
+                    localFactory,
+                    remoteFactory,
+                    TestRouter,
+                    TestConnector);
+  }
+
+  TestConnection.prototype = Object.create(Connection.prototype);
+
+  // Return a handle for a message pipe that's connected to a proxy
+  // for remoteInterface. Used by generated code for outgoing interface&
+  // (request) parameters: the caller is given the generated proxy via
+  // |proxyCallback(proxy)| and the generated code sends the handle
+  // returned by this function.
+  function bindProxy(proxyCallback, remoteInterface) {
+    var messagePipe = core.createMessagePipe();
+    if (messagePipe.result != core.RESULT_OK)
+      throw new Error("createMessagePipe failed " + messagePipe.result);
+
+    var proxy = new remoteInterface.proxyClass;
+    var router = new Router(messagePipe.handle0);
+    var connection = new BaseConnection(undefined, proxy, router);
+    ProxyBindings(proxy).connection = connection;
+    if (proxyCallback)
+      proxyCallback(proxy);
+
+    return messagePipe.handle1;
+  }
+
+  // Return a handle for a message pipe that's connected to a stub for
+  // localInterface. Used by generated code for outgoing interface
+  // parameters: the caller  is given the generated stub via
+  // |stubCallback(stub)| and the generated code sends the handle
+  // returned by this function. The caller is responsible for managing
+  // the lifetime of the stub and for setting it's implementation
+  // delegate with: StubBindings(stub).delegate = myImpl;
+  function bindImpl(stubCallback, localInterface) {
+    var messagePipe = core.createMessagePipe();
+    if (messagePipe.result != core.RESULT_OK)
+      throw new Error("createMessagePipe failed " + messagePipe.result);
+
+    var stub = new localInterface.stubClass;
+    var router = new Router(messagePipe.handle0);
+    var connection = new BaseConnection(stub, undefined, router);
+    StubBindings(stub).connection = connection;
+    if (stubCallback)
+      stubCallback(stub);
+
+    return messagePipe.handle1;
+  }
+
+  // Return a remoteInterface proxy for handle. Used by generated code
+  // for converting incoming interface parameters to proxies.
+  function bindHandleToProxy(handle, remoteInterface) {
+    if (!core.isHandle(handle))
+      throw new Error("Not a handle " + handle);
+
+    var proxy = new remoteInterface.proxyClass;
+    var router = new Router(handle);
+    var connection = new BaseConnection(undefined, proxy, router);
+    ProxyBindings(proxy).connection = connection;
+    return proxy;
+  }
+
+  // Return a localInterface stub for handle. Used by generated code
+  // for converting incoming interface& request parameters to localInterface
+  // stubs. The caller can specify the stub's implementation of localInterface
+  // like this: StubBindings(stub).delegate = myStubImpl.
+  function bindHandleToStub(handle, localInterface) {
+    if (!core.isHandle(handle))
+      throw new Error("Not a handle " + handle);
+
+    var stub = new localInterface.stubClass;
+    var router = new Router(handle);
+    var connection = new BaseConnection(stub, undefined, router);
+    StubBindings(stub).connection = connection;
+    return stub;
+  }
+
+  /**
+   * Creates a messape pipe and links one end of the pipe to the given object.
+   * @param {!Object} obj The object to create a handle for. Must be a subclass
+   *     of an auto-generated stub class.
+   * @return {!MojoHandle} The other (not yet connected) end of the message
+   *     pipe.
+   */
+  function bindStubDerivedImpl(obj) {
+    var pipe = core.createMessagePipe();
+    var router = new Router(pipe.handle0);
+    var connection = new BaseConnection(obj, undefined, router);
+    obj.connection = connection;
+    return pipe.handle1;
+  }
+
+  var exports = {};
+  exports.Connection = Connection;
+  exports.TestConnection = TestConnection;
+
+  exports.bindProxy = bindProxy;
+  exports.bindImpl = bindImpl;
+  exports.bindHandleToProxy = bindHandleToProxy;
+  exports.bindHandleToStub = bindHandleToStub;
+  exports.bindStubDerivedImpl = bindStubDerivedImpl;
+  return exports;
+});
diff --git a/mojo/public/js/connector.js b/mojo/public/js/connector.js
new file mode 100644
index 0000000..674f36b
--- /dev/null
+++ b/mojo/public/js/connector.js
@@ -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.
+
+define("mojo/public/js/connector", [
+  "mojo/public/js/buffer",
+  "mojo/public/js/codec",
+  "mojo/public/js/core",
+  "mojo/public/js/support",
+], function(buffer, codec, core, support) {
+
+  function Connector(handle) {
+    if (!core.isHandle(handle))
+      throw new Error("Connector: not a handle " + handle);
+    this.handle_ = handle;
+    this.dropWrites_ = false;
+    this.error_ = false;
+    this.incomingReceiver_ = null;
+    this.readWatcher_ = null;
+    this.errorHandler_ = null;
+
+    if (handle) {
+      this.readWatcher_ = support.watch(handle,
+                                        core.HANDLE_SIGNAL_READABLE,
+                                        this.readMore_.bind(this));
+    }
+  }
+
+  Connector.prototype.close = function() {
+    if (this.readWatcher_) {
+      support.cancelWatch(this.readWatcher_);
+      this.readWatcher_ = 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.readMore_ = function(result) {
+    for (;;) {
+      var read = core.readMessage(this.handle_,
+                                  core.READ_MESSAGE_FLAG_NONE);
+      if (this.handle_ == null) // The connector has been closed.
+        return;
+      if (read.result == core.RESULT_SHOULD_WAIT)
+        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
+  // doesn't automatically listen for input messages. Instead, you need to
+  // call waitForNextMessage to block and wait for the next incoming message.
+  function TestConnector(handle) {
+    Connector.call(this, handle);
+  }
+
+  TestConnector.prototype = Object.create(Connector.prototype);
+
+  TestConnector.prototype.waitForNextMessage = function() {
+    var wait = core.wait(this.handle_, core.HANDLE_SIGNAL_READABLE,
+                         core.DEADLINE_INDEFINITE);
+    this.readMore_(wait.result);
+  }
+
+  var exports = {};
+  exports.Connector = Connector;
+  exports.TestConnector = TestConnector;
+  return exports;
+});
diff --git a/mojo/public/js/constants.cc b/mojo/public/js/constants.cc
new file mode 100644
index 0000000..d29f5cb
--- /dev/null
+++ b/mojo/public/js/constants.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/js/constants.h"
+
+namespace mojo {
+
+const char kBindingsModuleName[] = "mojo/public/js/bindings";
+const char kBufferModuleName[] = "mojo/public/js/buffer";
+const char kCodecModuleName[] = "mojo/public/js/codec";
+const char kConnectionModuleName[] = "mojo/public/js/connection";
+const char kConnectorModuleName[] = "mojo/public/js/connector";
+const char kUnicodeModuleName[] = "mojo/public/js/unicode";
+const char kRouterModuleName[] = "mojo/public/js/router";
+const char kValidatorModuleName[] = "mojo/public/js/validator";
+
+}  // namespace mojo
diff --git a/mojo/public/js/constants.h b/mojo/public/js/constants.h
new file mode 100644
index 0000000..de75a90
--- /dev/null
+++ b/mojo/public/js/constants.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_JS_BINDINGS_CONSTANTS_H_
+#define MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
+
+namespace mojo {
+
+// JavaScript module names:
+extern const char kBindingsModuleName[];
+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/core.js b/mojo/public/js/core.js
new file mode 100644
index 0000000..b89a956
--- /dev/null
+++ b/mojo/public/js/core.js
@@ -0,0 +1,238 @@
+// Copyright 2014 The Chromium 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/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;
+var HANDLE_SIGNAL_PEER_CLOSED;
+
+/**
+ * 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;
+
+/*
+ * 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;
+var READ_DATA_FLAG_PEEK;
+
+/**
+ * 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] }
+
+/**
+ * True if the argument is a message or data pipe handle.
+ *
+ * @param {value} an arbitrary JS value.
+ * @return true or false
+ */
+function isHandle(value) { [native code] }
diff --git a/mojo/public/js/core_unittests.js b/mojo/public/js/core_unittests.js
new file mode 100644
index 0000000..12364dc
--- /dev/null
+++ b/mojo/public/js/core_unittests.js
@@ -0,0 +1,198 @@
+// Copyright 2014 The Chromium 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/core",
+    "gc",
+  ], function(expect, core, gc) {
+
+  var HANDLE_SIGNAL_READWRITABLE = core.HANDLE_SIGNAL_WRITABLE |
+                                   core.HANDLE_SIGNAL_READABLE;
+  var HANDLE_SIGNAL_ALL = core.HANDLE_SIGNAL_WRITABLE |
+                          core.HANDLE_SIGNAL_READABLE |
+                          core.HANDLE_SIGNAL_PEER_CLOSED;
+
+  runWithMessagePipe(testNop);
+  runWithMessagePipe(testReadAndWriteMessage);
+  runWithMessagePipeWithOptions(testNop);
+  runWithMessagePipeWithOptions(testReadAndWriteMessage);
+  runWithDataPipe(testNop);
+  runWithDataPipe(testReadAndWriteDataPipe);
+  runWithDataPipeWithOptions(testNop);
+  runWithDataPipeWithOptions(testReadAndWriteDataPipe);
+  runWithMessagePipe(testIsHandleMessagePipe);
+  runWithDataPipe(testIsHandleDataPipe);
+  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 wait = core.waitMany([], [], 0);
+    expect(wait.result).toBe(core.RESULT_INVALID_ARGUMENT);
+    expect(wait.index).toBe(null);
+    expect(wait.signalsState).toBe(null);
+
+    wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_READABLE, 0);
+    expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED);
+    expect(wait.signalsState.satisfiedSignals).toBe(
+           core.HANDLE_SIGNAL_WRITABLE);
+    expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+    wait = core.waitMany(
+                  [pipe.handle0, pipe.handle1],
+                  [core.HANDLE_SIGNAL_READABLE,core.HANDLE_SIGNAL_READABLE],
+                  0);
+    expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED);
+    expect(wait.index).toBe(null);
+    expect(wait.signalsState[0].satisfiedSignals).toBe(
+           core.HANDLE_SIGNAL_WRITABLE);
+    expect(wait.signalsState[0].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+    expect(wait.signalsState[1].satisfiedSignals).toBe(
+           core.HANDLE_SIGNAL_WRITABLE);
+    expect(wait.signalsState[1].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+    wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0);
+    expect(wait.result).toBe(core.RESULT_OK);
+    expect(wait.signalsState.satisfiedSignals).toBe(
+           core.HANDLE_SIGNAL_WRITABLE);
+    expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+    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);
+
+    wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0);
+    expect(wait.result).toBe(core.RESULT_OK);
+    expect(wait.signalsState.satisfiedSignals).toBe(
+        core.HANDLE_SIGNAL_WRITABLE);
+    expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+    wait = core.wait(pipe.handle1, core.HANDLE_SIGNAL_READABLE,
+                     core.DEADLINE_INDEFINITE);
+    expect(wait.result).toBe(core.RESULT_OK);
+    expect(wait.signalsState.satisfiedSignals).toBe(HANDLE_SIGNAL_READWRITABLE);
+    expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+    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 wait = core.wait(pipe.consumerHandle, core.HANDLE_SIGNAL_READABLE,
+                         core.DEADLINE_INDEFINITE);
+    expect(wait.result).toBe(core.RESULT_OK);
+    var peeked = core.readData(
+         pipe.consumerHandle,
+         core.READ_DATA_FLAG_PEEK | core.READ_DATA_FLAG_ALL_OR_NONE);
+    expect(peeked.result).toBe(core.RESULT_OK);
+    expect(peeked.buffer.byteLength).toBe(42);
+
+    var peeked_memory = new Uint8Array(peeked.buffer);
+    for (var i = 0; i < peeked_memory.length; ++i)
+      expect(peeked_memory[i]).toBe((i * i) & 0xFF);
+
+    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);
+  }
+
+  function testIsHandleMessagePipe(pipe) {
+    expect(core.isHandle(123).toBeFalsy);
+    expect(core.isHandle("123").toBeFalsy);
+    expect(core.isHandle({}).toBeFalsy);
+    expect(core.isHandle([]).toBeFalsy);
+    expect(core.isHandle(undefined).toBeFalsy);
+    expect(core.isHandle(pipe).toBeFalsy);
+    expect(core.isHandle(pipe.handle0)).toBeTruthy();
+    expect(core.isHandle(pipe.handle1)).toBeTruthy();
+    expect(core.isHandle(null)).toBeTruthy();
+  }
+
+  function testIsHandleDataPipe(pipe) {
+    expect(core.isHandle(pipe.consumerHandle)).toBeTruthy();
+    expect(core.isHandle(pipe.producerHandle)).toBeTruthy();
+  }
+
+});
diff --git a/mojo/public/js/router.js b/mojo/public/js/router.js
new file mode 100644
index 0000000..e3db0a6
--- /dev/null
+++ b/mojo/public/js/router.js
@@ -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.
+
+define("mojo/public/js/router", [
+  "mojo/public/js/codec",
+  "mojo/public/js/core",
+  "mojo/public/js/connector",
+  "mojo/public/js/validator",
+], function(codec, core, connector, validator) {
+
+  var Connector = connector.Connector;
+  var MessageReader = codec.MessageReader;
+  var Validator = validator.Validator;
+
+  function Router(handle, connectorFactory) {
+    if (!core.isHandle(handle))
+      throw new Error("Router constructor: Not a handle");
+    if (connectorFactory === undefined)
+      connectorFactory = Connector;
+    this.connector_ = new connectorFactory(handle);
+    this.incomingReceiver_ = null;
+    this.errorHandler_ = null;
+    this.nextRequestID_ = 0;
+    this.completers_ = new Map();
+    this.payloadValidators_ = [];
+
+    this.connector_.setIncomingReceiver({
+        accept: this.handleIncomingMessage_.bind(this),
+    });
+    this.connector_.setErrorHandler({
+        onError: this.handleConnectionError_.bind(this),
+    });
+  }
+
+  Router.prototype.close = function() {
+    this.completers_.clear();  // 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.acceptAndExpectResponse = function(message) {
+    // 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);
+    if (!result)
+      return Promise.reject(Error("Connection error"));
+
+    var completer = {};
+    this.completers_.set(requestID, completer);
+    return new Promise(function(resolve, reject) {
+      completer.resolve = resolve;
+      completer.reject = reject;
+    });
+  };
+
+  Router.prototype.setIncomingReceiver = function(receiver) {
+    this.incomingReceiver_ = receiver;
+  };
+
+  Router.prototype.setPayloadValidators = function(payloadValidators) {
+    this.payloadValidators_ = payloadValidators;
+  };
+
+  Router.prototype.setErrorHandler = function(handler) {
+    this.errorHandler_ = handler;
+  };
+
+  Router.prototype.encounteredError = function() {
+    return this.connector_.encounteredError();
+  };
+
+  Router.prototype.handleIncomingMessage_ = function(message) {
+    var noError = validator.validationError.NONE;
+    var messageValidator = new 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 MessageReader(message);
+      var requestID = reader.requestID;
+      var completer = this.completers_.get(requestID);
+      this.completers_.delete(requestID);
+      completer.resolve(message);
+    } else {
+      if (this.incomingReceiver_)
+        this.incomingReceiver_.accept(message);
+    }
+  }
+
+  Router.prototype.handleInvalidIncomingMessage_ = function(message, error) {
+    this.close();
+  }
+
+  Router.prototype.handleConnectionError_ = function(result) {
+    this.completers_.forEach(function(value) {
+      value.reject(result);
+    });
+    if (this.errorHandler_)
+      this.errorHandler_();
+    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/struct_unittests.js b/mojo/public/js/struct_unittests.js
new file mode 100644
index 0000000..691d51b
--- /dev/null
+++ b/mojo/public/js/struct_unittests.js
@@ -0,0 +1,279 @@
+// Copyright 2014 The Chromium 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",
+    "mojo/public/js/codec",
+    "mojo/public/js/validator",
+], function(expect,
+            rect,
+            testStructs,
+            codec,
+            validator) {
+
+  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);
+  }
+
+  function testScopedConstants() {
+    expect(testStructs.ScopedConstants.TEN).toEqual(10);
+    expect(testStructs.ScopedConstants.ALSO_TEN).toEqual(10);
+
+    expect(testStructs.ScopedConstants.EType.E0).toEqual(0);
+    expect(testStructs.ScopedConstants.EType.E1).toEqual(1);
+    expect(testStructs.ScopedConstants.EType.E2).toEqual(10);
+    expect(testStructs.ScopedConstants.EType.E3).toEqual(10);
+    expect(testStructs.ScopedConstants.EType.E4).toEqual(11);
+
+    var s = new testStructs.ScopedConstants();
+    expect(s.f0).toEqual(0);
+    expect(s.f1).toEqual(1);
+    expect(s.f2).toEqual(10);
+    expect(s.f3).toEqual(10);
+    expect(s.f4).toEqual(11);
+    expect(s.f5).toEqual(10);
+    expect(s.f6).toEqual(10);
+  }
+
+  function structEncodeDecode(struct) {
+    var structClass = struct.constructor;
+    var builder = new codec.MessageBuilder(1234, structClass.encodedSize);
+    builder.encodeStruct(structClass, struct);
+    var message = builder.finish();
+
+    var messageValidator = new validator.Validator(message);
+    var err = structClass.validate(messageValidator, codec.kMessageHeaderSize);
+    expect(err).toEqual(validator.validationError.NONE);
+
+    var reader = new codec.MessageReader(message);
+    return reader.decodeStruct(structClass);
+  }
+
+  function testMapKeyTypes() {
+    var mapFieldsStruct = new testStructs.MapKeyTypes({
+      f0: new Map([[true, false], [false, true]]),  // map<bool, bool>
+      f1: new Map([[0, 0], [1, 127], [-1, -128]]),  // map<int8, int8>
+      f2: new Map([[0, 0], [1, 127], [2, 255]]),  // map<uint8, uint8>
+      f3: new Map([[0, 0], [1, 32767], [2, -32768]]),  // map<int16, int16>
+      f4: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]),  // map<uint16, uint16>
+      f5: new Map([[0, 0], [1, 32767], [2, -32768]]),  // map<int32, int32>
+      f6: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]),  // map<uint32, uint32>
+      f7: new Map([[0, 0], [1, 32767], [2, -32768]]),  // map<int64, int64>
+      f8: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]),  // map<uint64, uint64>
+      f9: new Map([[1000.5, -50000], [100.5, 5000]]),  // map<float, float>
+      f10: new Map([[-100.5, -50000], [0, 50000000]]),  // map<double, double>
+      f11: new Map([["one", "two"], ["free", "four"]]),  // map<string, string>
+    });
+    var decodedStruct = structEncodeDecode(mapFieldsStruct);
+    expect(decodedStruct.f0).toEqual(mapFieldsStruct.f0);
+    expect(decodedStruct.f1).toEqual(mapFieldsStruct.f1);
+    expect(decodedStruct.f2).toEqual(mapFieldsStruct.f2);
+    expect(decodedStruct.f3).toEqual(mapFieldsStruct.f3);
+    expect(decodedStruct.f4).toEqual(mapFieldsStruct.f4);
+    expect(decodedStruct.f5).toEqual(mapFieldsStruct.f5);
+    expect(decodedStruct.f6).toEqual(mapFieldsStruct.f6);
+    expect(decodedStruct.f7).toEqual(mapFieldsStruct.f7);
+    expect(decodedStruct.f8).toEqual(mapFieldsStruct.f8);
+    expect(decodedStruct.f9).toEqual(mapFieldsStruct.f9);
+    expect(decodedStruct.f10).toEqual(mapFieldsStruct.f10);
+    expect(decodedStruct.f11).toEqual(mapFieldsStruct.f11);
+  }
+
+  function testMapValueTypes() {
+    var mapFieldsStruct = new testStructs.MapValueTypes({
+      // map<string, array<string>>
+      f0: new Map([["a", ["b", "c"]], ["d", ["e"]]]),
+      // map<string, array<string>?>
+      f1: new Map([["a", null], ["b", ["c", "d"]]]),
+      // map<string, array<string?>>
+      f2: new Map([["a", [null]], ["b", [null, "d"]]]),
+      // map<string, array<string,2>>
+      f3: new Map([["a", ["1", "2"]], ["b", ["1", "2"]]]),
+      // map<string, array<array<string, 2>?>>
+      f4: new Map([["a", [["1", "2"]]], ["b", [null]]]),
+      // map<string, array<array<string, 2>, 1>>
+      f5: new Map([["a", [["1", "2"]]]]),
+      // map<string, Rect?>
+      f6: new Map([["a", null]]),
+      // map<string, map<string, string>>
+      f7: new Map([["a", new Map([["b", "c"]])]]),
+      // map<string, array<map<string, string>>>
+      f8: new Map([["a", [new Map([["b", "c"]])]]]),
+      // map<string, handle>
+      f9: new Map([["a", 1234]]),
+      // map<string, array<handle>>
+      f10: new Map([["a", [1234, 5678]]]),
+      // map<string, map<string, handle>>
+      f11: new Map([["a", new Map([["b", 1234]])]]),
+    });
+    var decodedStruct = structEncodeDecode(mapFieldsStruct);
+    expect(decodedStruct.f0).toEqual(mapFieldsStruct.f0);
+    expect(decodedStruct.f1).toEqual(mapFieldsStruct.f1);
+    expect(decodedStruct.f2).toEqual(mapFieldsStruct.f2);
+    expect(decodedStruct.f3).toEqual(mapFieldsStruct.f3);
+    expect(decodedStruct.f4).toEqual(mapFieldsStruct.f4);
+    expect(decodedStruct.f5).toEqual(mapFieldsStruct.f5);
+    expect(decodedStruct.f6).toEqual(mapFieldsStruct.f6);
+    expect(decodedStruct.f7).toEqual(mapFieldsStruct.f7);
+    expect(decodedStruct.f8).toEqual(mapFieldsStruct.f8);
+    expect(decodedStruct.f9).toEqual(mapFieldsStruct.f9);
+    expect(decodedStruct.f10).toEqual(mapFieldsStruct.f10);
+    expect(decodedStruct.f11).toEqual(mapFieldsStruct.f11);
+  }
+
+  function testFloatNumberValues() {
+    var decodedStruct = structEncodeDecode(new testStructs.FloatNumberValues);
+    expect(decodedStruct.f0).toEqual(testStructs.FloatNumberValues.V0);
+    expect(decodedStruct.f1).toEqual(testStructs.FloatNumberValues.V1);
+    expect(decodedStruct.f2).toEqual(testStructs.FloatNumberValues.V2);
+    expect(decodedStruct.f3).toEqual(testStructs.FloatNumberValues.V3);
+    expect(decodedStruct.f4).toEqual(testStructs.FloatNumberValues.V4);
+    expect(decodedStruct.f5).toEqual(testStructs.FloatNumberValues.V5);
+    expect(decodedStruct.f6).toEqual(testStructs.FloatNumberValues.V6);
+    expect(decodedStruct.f7).toEqual(testStructs.FloatNumberValues.V7);
+    expect(decodedStruct.f8).toEqual(testStructs.FloatNumberValues.V8);
+    expect(decodedStruct.f9).toEqual(testStructs.FloatNumberValues.V9);
+  }
+
+  function testIntegerNumberValues() {
+    var decodedStruct = structEncodeDecode(new testStructs.IntegerNumberValues);
+    expect(decodedStruct.f0).toEqual(testStructs.IntegerNumberValues.V0);
+    expect(decodedStruct.f1).toEqual(testStructs.IntegerNumberValues.V1);
+    expect(decodedStruct.f2).toEqual(testStructs.IntegerNumberValues.V2);
+    expect(decodedStruct.f3).toEqual(testStructs.IntegerNumberValues.V3);
+    expect(decodedStruct.f4).toEqual(testStructs.IntegerNumberValues.V4);
+    expect(decodedStruct.f5).toEqual(testStructs.IntegerNumberValues.V5);
+    expect(decodedStruct.f6).toEqual(testStructs.IntegerNumberValues.V6);
+    expect(decodedStruct.f7).toEqual(testStructs.IntegerNumberValues.V7);
+    expect(decodedStruct.f8).toEqual(testStructs.IntegerNumberValues.V8);
+    expect(decodedStruct.f9).toEqual(testStructs.IntegerNumberValues.V9);
+    expect(decodedStruct.f10).toEqual(testStructs.IntegerNumberValues.V10);
+    expect(decodedStruct.f11).toEqual(testStructs.IntegerNumberValues.V11);
+    expect(decodedStruct.f12).toEqual(testStructs.IntegerNumberValues.V12);
+    expect(decodedStruct.f13).toEqual(testStructs.IntegerNumberValues.V13);
+    expect(decodedStruct.f14).toEqual(testStructs.IntegerNumberValues.V14);
+    expect(decodedStruct.f15).toEqual(testStructs.IntegerNumberValues.V15);
+    expect(decodedStruct.f16).toEqual(testStructs.IntegerNumberValues.V16);
+    expect(decodedStruct.f17).toEqual(testStructs.IntegerNumberValues.V17);
+    expect(decodedStruct.f18).toEqual(testStructs.IntegerNumberValues.V18);
+    expect(decodedStruct.f19).toEqual(testStructs.IntegerNumberValues.V19);
+  }
+
+  function testUnsignedNumberValues() {
+    var decodedStruct =
+        structEncodeDecode(new testStructs.UnsignedNumberValues);
+    expect(decodedStruct.f0).toEqual(testStructs.UnsignedNumberValues.V0);
+    expect(decodedStruct.f1).toEqual(testStructs.UnsignedNumberValues.V1);
+    expect(decodedStruct.f2).toEqual(testStructs.UnsignedNumberValues.V2);
+    expect(decodedStruct.f3).toEqual(testStructs.UnsignedNumberValues.V3);
+    expect(decodedStruct.f4).toEqual(testStructs.UnsignedNumberValues.V4);
+    expect(decodedStruct.f5).toEqual(testStructs.UnsignedNumberValues.V5);
+    expect(decodedStruct.f6).toEqual(testStructs.UnsignedNumberValues.V6);
+    expect(decodedStruct.f7).toEqual(testStructs.UnsignedNumberValues.V7);
+    expect(decodedStruct.f8).toEqual(testStructs.UnsignedNumberValues.V8);
+    expect(decodedStruct.f9).toEqual(testStructs.UnsignedNumberValues.V9);
+    expect(decodedStruct.f10).toEqual(testStructs.UnsignedNumberValues.V10);
+    expect(decodedStruct.f11).toEqual(testStructs.UnsignedNumberValues.V11);
+  }
+
+
+  function testBitArrayValues() {
+    var bitArraysStruct = new testStructs.BitArrayValues({
+      // array<bool, 1> f0;
+      f0: [true],
+      // array<bool, 7> f1;
+      f1: [true, false, true, false, true, false, true],
+      // array<bool, 9> f2;
+      f2: [true, false, true, false, true, false, true, false, true],
+      // array<bool> f3;
+      f3: [true, false, true, false, true, false, true, false],
+      // array<array<bool>> f4;
+      f4: [[true], [false], [true, false], [true, false, true, false]],
+      // array<array<bool>?> f5;
+      f5: [[true], null, null, [true, false, true, false]],
+      // array<array<bool, 2>?> f6;
+      f6: [[true, false], [true, false], [true, false]],
+    });
+    var decodedStruct = structEncodeDecode(bitArraysStruct);
+    expect(decodedStruct.f0).toEqual(bitArraysStruct.f0);
+    expect(decodedStruct.f1).toEqual(bitArraysStruct.f1);
+    expect(decodedStruct.f2).toEqual(bitArraysStruct.f2);
+    expect(decodedStruct.f3).toEqual(bitArraysStruct.f3);
+    expect(decodedStruct.f4).toEqual(bitArraysStruct.f4);
+    expect(decodedStruct.f5).toEqual(bitArraysStruct.f5);
+    expect(decodedStruct.f6).toEqual(bitArraysStruct.f6);
+  }
+
+  testConstructors();
+  testNoDefaultFieldValues();
+  testDefaultFieldValues();
+  testScopedConstants();
+  testMapKeyTypes();
+  testMapValueTypes();
+  testFloatNumberValues();
+  testIntegerNumberValues();
+  testUnsignedNumberValues();
+  testBitArrayValues();
+  this.result = "PASS";
+});
diff --git a/mojo/public/js/support.js b/mojo/public/js/support.js
new file mode 100644
index 0000000..7e27504
--- /dev/null
+++ b/mojo/public/js/support.js
@@ -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.
+
+// Module "mojo/public/js/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);
+
+/* @deprecated Please use watch()/cancelWatch() instead of
+ *     asyncWait()/cancelWait().
+ *
+ * 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] }
+
+/* @deprecated Please use watch()/cancelWatch() instead of
+ *     asyncWait()/cancelWait().
+ *
+ * Cancels the asyncWait operation specified by the given |waitId|.
+ *
+ * @param {MojoWaitId} waitId The waitId returned by asyncWait.
+ */
+function cancelWait(waitId) { [native code] }
+
+/* Begins watching a handle for |signals| to be satisfied or unsatisfiable.
+ *
+  * @param {MojoHandle} handle The handle to watch.
+  * @param {MojoHandleSignals} signals The signals to watch.
+  * @param {function (mojoResult)} calback Called with a result any time
+  *     the watched signals become satisfied or unsatisfiable.
+  *
+  * @param {MojoWatchId} watchId An opaque identifier that identifies this
+  *     watch.
+  */
+function watch(handle, signals, callback) { [native code] }
+
+/* Cancels a handle watch initiated by watch().
+ *
+ * @param {MojoWatchId} watchId The watch identifier returned by watch().
+ */
+function cancelWatch(watchId) { [native code] }
diff --git a/mojo/public/js/test/validation_test_input_parser.js b/mojo/public/js/test/validation_test_input_parser.js
new file mode 100644
index 0000000..f5a57f9
--- /dev/null
+++ b/mojo/public/js/test/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/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/threading.js b/mojo/public/js/threading.js
new file mode 100644
index 0000000..cfe5037
--- /dev/null
+++ b/mojo/public/js/threading.js
@@ -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/public/js/threading"
+//
+// 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 a way for a Mojo application implemented in JS
+// to exit by quitting the current message loop. This module is not
+// intended to be used by Mojo JS application started by the JS
+// content handler.
+
+while (1);
+
+/**
+ * Quits the current message loop, esssentially:
+ * base::MessageLoop::current()->QuitNow();
+*/
+function quit() { [native code] }
diff --git a/mojo/public/js/unicode.js b/mojo/public/js/unicode.js
new file mode 100644
index 0000000..be2ba0e
--- /dev/null
+++ b/mojo/public/js/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/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/union_unittests.js b/mojo/public/js/union_unittests.js
new file mode 100644
index 0000000..5dcda7d
--- /dev/null
+++ b/mojo/public/js/union_unittests.js
@@ -0,0 +1,184 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define([
+    "gin/test/expect",
+    "mojo/public/interfaces/bindings/tests/test_unions.mojom",
+    "mojo/public/js/codec",
+    "mojo/public/js/validator",
+], function(expect,
+            unions,
+            codec,
+            validator) {
+  function testConstructors() {
+    var u = new unions.PodUnion();
+    expect(u.$data).toEqual(null);
+    expect(u.$tag).toBeUndefined();
+
+    u.f_uint32 = 32;
+
+    expect(u.f_uint32).toEqual(32);
+    expect(u.$tag).toEqual(unions.PodUnion.Tags.f_uint32);
+
+    var u = new unions.PodUnion({f_uint64: 64});
+    expect(u.f_uint64).toEqual(64);
+    expect(u.$tag).toEqual(unions.PodUnion.Tags.f_uint64);
+    expect(function() {var v = u.f_uint32;}).toThrow();
+
+    expect(function() {
+      var u = new unions.PodUnion({
+        f_uint64: 64,
+        f_uint32: 32,
+      });
+    }).toThrow();
+
+    expect(function() {
+      var u = new unions.PodUnion({ foo: 64 }); }).toThrow();
+
+    expect(function() {
+      var u = new unions.PodUnion([1,2,3,4]); }).toThrow();
+  }
+
+  function structEncodeDecode(struct) {
+    var structClass = struct.constructor;
+    var builder = new codec.MessageBuilder(1234, structClass.encodedSize);
+    builder.encodeStruct(structClass, struct);
+
+    var message = builder.finish();
+
+    var messageValidator = new validator.Validator(message);
+    var err = structClass.validate(messageValidator, codec.kMessageHeaderSize);
+    expect(err).toEqual(validator.validationError.NONE);
+
+    var reader = new codec.MessageReader(message);
+    var view = reader.decoder.buffer.dataView;
+
+    return reader.decodeStruct(structClass);
+  }
+
+  function testBasicEncoding() {
+    var s = new unions.WrapperStruct({
+      pod_union: new unions.PodUnion({
+        f_uint64: 64})});
+
+    var decoded = structEncodeDecode(s);
+    expect(decoded).toEqual(s);
+
+    var s = new unions.WrapperStruct({
+      object_union: new unions.ObjectUnion({
+        f_dummy: new unions.DummyStruct({
+          f_int8: 8})})});
+
+    var decoded = structEncodeDecode(s);
+    expect(decoded).toEqual(s);
+
+    var s = new unions.WrapperStruct({
+      object_union: new unions.ObjectUnion({
+        f_array_int8: [1, 2, 3]})});
+
+    var decoded = structEncodeDecode(s);
+    expect(decoded).toEqual(s);
+
+    var s = new unions.WrapperStruct({
+      object_union: new unions.ObjectUnion({
+        f_map_int8: new Map([
+          ["first", 1],
+          ["second", 2],
+        ])})});
+
+    var decoded = structEncodeDecode(s);
+    expect(decoded).toEqual(s);
+
+    // Encoding a union with no member set is an error.
+    var s = new unions.WrapperStruct({
+      object_union: new unions.ObjectUnion()});
+    expect(function() {
+      structEncodeDecode(s); }).toThrow();
+  }
+
+  function testUnionsInArrayEncoding() {
+    var s = new unions.SmallStruct({
+      pod_union_array: [
+        new unions.PodUnion({f_uint32: 32}),
+        new unions.PodUnion({f_uint64: 64}),
+      ]
+    });
+
+    var decoded = structEncodeDecode(s);
+    expect(decoded).toEqual(s);
+  }
+
+  function testUnionsInMapEncoding() {
+    var s = new unions.SmallStruct({
+      pod_union_map: new Map([
+        ["thirty-two", new unions.PodUnion({f_uint32: 32})],
+        ["sixty-four", new unions.PodUnion({f_uint64: 64})],
+      ])
+    });
+
+    var decoded = structEncodeDecode(s);
+    expect(decoded).toEqual(s);
+  }
+
+  function testNestedUnionsEncoding() {
+    var s = new unions.WrapperStruct({
+      object_union: new unions.ObjectUnion({
+        f_pod_union: new unions.PodUnion({f_uint32: 32})
+      })});
+    var decoded = structEncodeDecode(s);
+    expect(decoded).toEqual(s);
+  }
+
+  function structValidate(struct) {
+    var structClass = struct.constructor;
+    var builder = new codec.MessageBuilder(1234, structClass.encodedSize);
+    builder.encodeStruct(structClass, struct);
+
+    var message = builder.finish();
+
+    var messageValidator = new validator.Validator(message);
+    return structClass.validate(messageValidator, codec.kMessageHeaderSize);
+  }
+
+  function testNullUnionMemberValidation() {
+    var s = new unions.WrapperStruct({
+      object_union: new unions.ObjectUnion({
+        f_dummy: null})});
+
+    var err = structValidate(s);
+    expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_POINTER);
+
+    var s = new unions.WrapperStruct({
+      object_union: new unions.ObjectUnion({
+        f_nullable: null})});
+
+    var err = structValidate(s);
+    expect(err).toEqual(validator.validationError.NONE);
+  }
+
+  function testNullUnionValidation() {
+    var s = new unions.SmallStructNonNullableUnion({
+      pod_union: null});
+
+    var err = structValidate(s);
+    expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_UNION);
+
+    var s = new unions.WrapperStruct({
+      object_union: new unions.ObjectUnion({
+        f_pod_union: null})
+      });
+
+    var err = structValidate(s);
+    expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_UNION);
+  }
+
+  testConstructors();
+  testBasicEncoding();
+  testUnionsInArrayEncoding();
+  testUnionsInMapEncoding();
+  testNestedUnionsEncoding();
+  testNullUnionMemberValidation();
+  testNullUnionValidation();
+  this.result = "PASS";
+});
diff --git a/mojo/public/js/validation_unittests.js b/mojo/public/js/validation_unittests.js
new file mode 100644
index 0000000..817d42c
--- /dev/null
+++ b/mojo/public/js/validation_unittests.js
@@ -0,0 +1,349 @@
+// Copyright 2014 The Chromium 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/buffer",
+    "mojo/public/js/codec",
+    "mojo/public/js/connection",
+    "mojo/public/js/connector",
+    "mojo/public/js/core",
+    "mojo/public/js/test/validation_test_input_parser",
+    "mojo/public/js/router",
+    "mojo/public/js/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(prefix) {
+    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" && s.indexOf(prefix) == 0;
+    }).map(function(s) {
+      return testDir + s.slice(0, -5);
+    });
+  }
+
+  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(prefix, filters) {
+    var testFiles = getMessageTestFiles(prefix);
+    expect(testFiles.length).toBeGreaterThan(0);
+
+    for (var i = 0; i < testFiles.length; i++) {
+      // TODO(hansmuller) Temporarily skipping array pointer overflow tests
+      // because JS numbers are limited to 53 bits.
+      // TODO(yzshen) Skipping struct versioning tests (tests with "mthd11"
+      // in the name) because the feature is not supported in JS yet.
+      // TODO(yzshen) Skipping enum validation tests (tests with "enum" in the
+      // name) because the feature is not supported in JS yet. crbug.com/581390
+      // TODO(rudominer): Temporarily skipping 'no-such-method',
+      // 'invalid_request_flags', and 'invalid_response_flags' until additional
+      // logic in *RequestValidator and *ResponseValidator is ported from
+      // cpp to js.
+      if (testFiles[i].indexOf("overflow") != -1 ||
+          testFiles[i].indexOf("mthd11") != -1 ||
+          testFiles[i].indexOf("enum") != -1 ||
+          testFiles[i].indexOf("no_such_method") != -1 ||
+          testFiles[i].indexOf("invalid_request_flags") != -1 ||
+          testFiles[i].indexOf("invalid_response_flags") != -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.ConformanceTestInterface.validateRequest]);
+  }
+
+  function testBoundsCheckMessageValidation() {
+    testMessageValidation("boundscheck_", [
+        testInterface.BoundsCheckTestInterface.validateRequest]);
+  }
+
+  function testResponseConformanceMessageValidation() {
+    testMessageValidation("resp_conformance_", [
+        testInterface.ConformanceTestInterface.validateResponse]);
+  }
+
+  function testResponseBoundsCheckMessageValidation() {
+    testMessageValidation("resp_boundscheck_", [
+        testInterface.BoundsCheckTestInterface.validateResponse]);
+  }
+
+  function testIntegratedMessageValidation(testFilesPattern,
+                                           localFactory,
+                                           remoteFactory) {
+    var testFiles = getMessageTestFiles(testFilesPattern);
+    expect(testFiles.length).toBeGreaterThan(0);
+
+    var testMessagePipe = core.createMessagePipe();
+    expect(testMessagePipe.result).toBe(core.RESULT_OK);
+    var testConnection = new connection.TestConnection(
+        testMessagePipe.handle1, localFactory, remoteFactory);
+
+    for (var i = 0; i < testFiles.length; i++) {
+      var testMessage = readTestMessage(testFiles[i]);
+      var handles = new Array(testMessage.handleCount);
+
+      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 validationError = noError;
+      testConnection.router_.validationErrorHandler = function(err) {
+        validationError = err;
+      }
+
+      testConnection.router_.connector_.waitForNextMessage();
+      checkValidationResult(testFiles[i], validationError);
+    }
+
+    testConnection.close();
+    expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK);
+  }
+
+  function testIntegratedMessageHeaderValidation() {
+    testIntegratedMessageValidation(
+        "integration_msghdr",
+        testInterface.IntegrationTestInterface.stubClass,
+        undefined);
+    testIntegratedMessageValidation(
+        "integration_msghdr",
+        undefined,
+        testInterface.IntegrationTestInterface.proxyClass);
+  }
+
+  function testIntegratedRequestMessageValidation() {
+    testIntegratedMessageValidation(
+        "integration_intf_rqst",
+        testInterface.IntegrationTestInterface.stubClass,
+        undefined);
+  }
+
+  function testIntegratedResponseMessageValidation() {
+    testIntegratedMessageValidation(
+        "integration_intf_resp",
+        undefined,
+        testInterface.IntegrationTestInterface.proxyClass);
+  }
+
+  expect(checkTestMessageParser()).toBeNull();
+  testConformanceMessageValidation();
+  testBoundsCheckMessageValidation();
+  testResponseConformanceMessageValidation();
+  testResponseBoundsCheckMessageValidation();
+  testIntegratedMessageHeaderValidation();
+  testIntegratedResponseMessageValidation();
+  testIntegratedRequestMessageValidation();
+
+  this.result = "PASS";
+});
diff --git a/mojo/public/js/validator.js b/mojo/public/js/validator.js
new file mode 100644
index 0000000..cbf7521
--- /dev/null
+++ b/mojo/public/js/validator.js
@@ -0,0 +1,415 @@
+// Copyright 2014 The Chromium 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/validator", [
+  "mojo/public/js/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_FLAGS:
+        'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS',
+    MESSAGE_HEADER_MISSING_REQUEST_ID:
+        'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID',
+    DIFFERENT_SIZED_ARRAYS_IN_MAP:
+        'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP',
+    INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE',
+    UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION',
+  };
+
+  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 isInterfaceClass(cls) {
+    return cls === codec.Interface || cls === codec.NullableInterface;
+  }
+
+  function isNullable(type) {
+    return type === codec.NullableString || type === codec.NullableHandle ||
+        type === codec.NullableInterface ||
+        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.validateInterface = function(offset, nullable) {
+    return this.validateHandle(offset, nullable);
+  }
+
+  Validator.prototype.validateStructHeader =
+      function(offset, minNumBytes, minVersion) {
+    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 version = this.message.buffer.getUint32(offset + 4);
+
+    // Backward compatibility is not yet supported.
+    if (numBytes < minNumBytes || version < minVersion)
+      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, 0);
+    if (err != validationError.NONE)
+      return err;
+
+    var numBytes = this.message.getHeaderNumBytes();
+    var version = this.message.getHeaderVersion();
+
+    var validVersionAndNumBytes =
+        (version == 0 && numBytes == codec.kMessageHeaderSize) ||
+        (version == 1 &&
+         numBytes == codec.kMessageWithRequestIDHeaderSize) ||
+        (version > 1 &&
+         numBytes >= codec.kMessageWithRequestIDHeaderSize);
+    if (!validVersionAndNumBytes)
+      return validationError.UNEXPECTED_STRUCT_HEADER;
+
+    var expectsResponse = this.message.expectsResponse();
+    var isResponse = this.message.isResponse();
+
+    if (version == 0 && (expectsResponse || isResponse))
+      return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
+
+    if (isResponse && expectsResponse)
+      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+
+    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.decodeUnionSize = function(offset) {
+    return this.message.buffer.getUint32(offset);
+  };
+
+  Validator.prototype.decodeUnionTag = function(offset) {
+    return this.message.buffer.getUint32(offset + 4);
+  };
+
+  Validator.prototype.validateArrayPointer = function(
+      offset, elementSize, elementType, nullable, expectedDimensionSizes,
+      currentDimension) {
+    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, elementType,
+                              expectedDimensionSizes, currentDimension);
+  }
+
+  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.validateUnion = function(
+      offset, unionClass, nullable) {
+    var size = this.message.buffer.getUint32(offset);
+    if (size == 0) {
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
+    }
+
+    return unionClass.validate(this, offset);
+  }
+
+  Validator.prototype.validateNestedUnion = function(
+      offset, unionClass, nullable) {
+    var unionOffset = this.decodePointer(offset);
+    if (unionOffset === null)
+      return validationError.ILLEGAL_POINTER;
+
+    if (unionOffset === NULL_MOJO_POINTER)
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
+
+    return this.validateUnion(unionOffset, unionClass, nullable);
+  }
+
+  // This method assumes that the array at arrayPointerOffset has
+  // been validated.
+
+  Validator.prototype.arrayLength = function(arrayPointerOffset) {
+    var arrayOffset = this.decodePointer(arrayPointerOffset);
+    return this.message.buffer.getUint32(arrayOffset + 4);
+  }
+
+  Validator.prototype.validateMapPointer = function(
+      offset, mapIsNullable, keyClass, valueClass, valueIsNullable) {
+    // Validate the implicit map struct:
+    // struct {array<keyClass> keys; array<valueClass> values};
+    var structOffset = this.decodePointer(offset);
+    if (structOffset === null)
+      return validationError.ILLEGAL_POINTER;
+
+    if (structOffset === NULL_MOJO_POINTER)
+      return mapIsNullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+    var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize;
+    var err = this.validateStructHeader(structOffset, mapEncodedSize, 0);
+    if (err !== validationError.NONE)
+        return err;
+
+    // Validate the keys array.
+    var keysArrayPointerOffset = structOffset + codec.kStructHeaderSize;
+    err = this.validateArrayPointer(
+        keysArrayPointerOffset, keyClass.encodedSize, keyClass, false, [0], 0);
+    if (err !== validationError.NONE)
+        return err;
+
+    // Validate the values array.
+    var valuesArrayPointerOffset = keysArrayPointerOffset + 8;
+    var valuesArrayDimensions = [0]; // Validate the actual length below.
+    if (valueClass instanceof codec.ArrayOf)
+      valuesArrayDimensions =
+          valuesArrayDimensions.concat(valueClass.dimensions());
+    var err = this.validateArrayPointer(valuesArrayPointerOffset,
+                                        valueClass.encodedSize,
+                                        valueClass,
+                                        valueIsNullable,
+                                        valuesArrayDimensions,
+                                        0);
+    if (err !== validationError.NONE)
+        return err;
+
+    // Validate the lengths of the keys and values arrays.
+    var keysArrayLength = this.arrayLength(keysArrayPointerOffset);
+    var valuesArrayLength = this.arrayLength(valuesArrayPointerOffset);
+    if (keysArrayLength != valuesArrayLength)
+      return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP;
+
+    return validationError.NONE;
+  }
+
+  Validator.prototype.validateStringPointer = function(offset, nullable) {
+    return this.validateArrayPointer(
+        offset, codec.Uint8.encodedSize, codec.Uint8, nullable, [0], 0);
+  }
+
+  // Similar to Array_Data<T>::Validate()
+  // mojo/public/cpp/bindings/lib/array_internal.h
+
+  Validator.prototype.validateArray =
+      function (offset, elementSize, elementType, expectedDimensionSizes,
+                currentDimension) {
+    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 (expectedDimensionSizes[currentDimension] != 0 &&
+        numElements != expectedDimensionSizes[currentDimension]) {
+      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 (isInterfaceClass(elementType))
+      return this.validateInterfaceElements(
+          elementsOffset, numElements, nullable);
+    if (isStringClass(elementType))
+      return this.validateArrayElements(
+          elementsOffset, numElements, codec.Uint8, nullable, [0], 0);
+    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,
+          expectedDimensionSizes, currentDimension + 1);
+
+    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;
+  }
+
+  Validator.prototype.validateInterfaceElements =
+      function(offset, numElements, nullable) {
+    var elementSize = codec.Interface.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateInterface(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,
+               expectedDimensionSizes, currentDimension) {
+    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, elementClass, nullable,
+          expectedDimensionSizes, currentDimension);
+      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/mojo_application.gni b/mojo/public/mojo_application.gni
new file mode 100644
index 0000000..28c8a8d
--- /dev/null
+++ b/mojo/public/mojo_application.gni
@@ -0,0 +1,270 @@
+# Copyright 2014 The Chromium 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/toolchain/toolchain.gni")
+import("//mojo/public/mojo_constants.gni")
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+  import("//build/config/zip.gni")
+}
+
+# Generate a binary Mojo application in a self-named directory.
+# Application resources are copied to a "resources" directory alongside the app.
+# The parameters of this template are those of a shared library.
+template("mojo_native_application") {
+  base_target_name = target_name
+  if (defined(invoker.output_name)) {
+    base_target_name = invoker.output_name
+  }
+
+  final_target_name = target_name
+
+  mojo_deps = []
+  if (defined(invoker.deps)) {
+    mojo_deps += invoker.deps
+  }
+
+  mojo_data_deps = []
+
+  if (defined(invoker.resources)) {
+    copy_step_name = "${base_target_name}__copy_resources"
+    copy(copy_step_name) {
+      sources = invoker.resources
+      outputs = [
+        "${root_out_dir}/${mojo_application_subdir}/${base_target_name}/resources/{{source_file_part}}",
+      ]
+      if (defined(invoker.testonly)) {
+        testonly = invoker.testonly
+      }
+      deps = mojo_deps
+    }
+    mojo_data_deps += [ ":$copy_step_name" ]
+  }
+
+  output = base_target_name + ".mojo"
+  library_target_name = base_target_name + "_library"
+  library_name = "${shlib_prefix}${library_target_name}${shlib_extension}"
+
+  shared_library(library_target_name) {
+    if (defined(invoker.cflags)) {
+      cflags = invoker.cflags
+    }
+    if (defined(invoker.cflags_c)) {
+      cflags_c = invoker.cflags_c
+    }
+    if (defined(invoker.cflags_cc)) {
+      cflags_cc = invoker.cflags_cc
+    }
+    if (defined(invoker.cflags_objc)) {
+      cflags_objc = invoker.cflags_objc
+    }
+    if (defined(invoker.cflags_objcc)) {
+      cflags_objcc = invoker.cflags_objcc
+    }
+    if (defined(invoker.defines)) {
+      defines = invoker.defines
+    }
+    if (defined(invoker.include_dirs)) {
+      include_dirs = invoker.include_dirs
+    }
+    if (defined(invoker.ldflags)) {
+      ldflags = invoker.ldflags
+    }
+    if (defined(invoker.lib_dirs)) {
+      lib_dirs = invoker.lib_dirs
+    }
+    if (defined(invoker.libs)) {
+      libs = invoker.libs
+    }
+
+    data_deps = []
+    if (!defined(invoker.avoid_runner_cycle) || !invoker.avoid_runner_cycle) {
+      # Give the user an out; as some mojo services are depended on by the
+      # runner.
+      data_deps += [ "//services/shell/standalone" ]
+    }
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
+    }
+    data_deps += mojo_data_deps
+
+    deps = [
+      "//mojo/public/c/system:set_thunks_for_app",
+      "//services/shell/public/cpp:application_support",
+    ]
+
+    deps += mojo_deps
+    if (defined(invoker.public_deps)) {
+      public_deps = invoker.public_deps
+    }
+    if (defined(invoker.all_dependent_configs)) {
+      all_dependent_configs = invoker.all_dependent_configs
+    }
+    if (defined(invoker.public_configs)) {
+      public_configs = invoker.public_configs
+    }
+    if (defined(invoker.check_includes)) {
+      check_includes = invoker.check_includes
+    }
+    if (defined(invoker.configs)) {
+      configs += invoker.configs
+    }
+    if (defined(invoker.data)) {
+      data = invoker.data
+    }
+    if (defined(invoker.inputs)) {
+      inputs = invoker.inputs
+    }
+    if (defined(invoker.public)) {
+      public = invoker.public
+    }
+    if (defined(invoker.sources)) {
+      sources = invoker.sources
+    }
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+  }
+
+  copy(final_target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "testonly",
+                             "visibility",
+                           ])
+    deps = [
+      ":${library_target_name}",
+    ]
+
+    sources = [
+      "${root_shlib_dir}/${library_name}",
+    ]
+    outputs = [
+      "${root_out_dir}/${mojo_application_subdir}/${base_target_name}/${output}",
+    ]
+  }
+
+  if (is_android) {
+    android_assets("${final_target_name}_assets") {
+      forward_variables_from(invoker, [ "testonly" ])
+      deps = [
+        ":${library_target_name}",
+      ]
+      if (defined(invoker.deps)) {
+        deps += invoker.deps
+      }
+      renaming_sources = [ "${root_shlib_dir}/${library_name}" ]
+      renaming_destinations = [ "${base_target_name}/${output}" ]
+      if (defined(invoker.resources)) {
+        renaming_sources += invoker.resources
+        renaming_destinations += process_file_template(
+                invoker.resources,
+                [ "$base_target_name/resources/{{source_file_part}}" ])
+      }
+    }
+  }
+}
+
+if (is_android) {
+  # Declares an Android Mojo application consisting of an .so file and a
+  # corresponding .dex.jar file.
+  #
+  # Variables:
+  #   input_so: the .so file to bundle
+  #   input_dex_jar: the .dex.jar file to bundle
+  #   deps / public_deps / data_deps (optional):
+  #       Dependencies. The targets that generate the .so/jar inputs should be
+  #       listed in either deps or public_deps.
+  #   output_name (optional): override for the output file name
+  template("mojo_android_application") {
+    assert(defined(invoker.input_so))
+    assert(defined(invoker.input_dex_jar))
+
+    base_target_name = target_name
+    if (defined(invoker.output_name)) {
+      base_target_name = invoker.output_name
+    }
+
+    mojo_data_deps = []
+    if (defined(invoker.resources)) {
+      copy_step_name = "${base_target_name}__copy_resources"
+      copy(copy_step_name) {
+        sources = invoker.resources
+        outputs = [
+          "${root_out_dir}/${mojo_application_subdir}/${base_target_name}/resources/{{source_file_part}}",
+        ]
+        if (defined(invoker.testonly)) {
+          testonly = invoker.testonly
+        }
+        if (defined(invoker.deps)) {
+          deps = invoker.deps
+        }
+      }
+      mojo_data_deps += [ ":$copy_step_name" ]
+    }
+
+    zip_action_name = "${target_name}_zip"
+    zip_action_output = "$target_gen_dir/${target_name}.zip"
+    prepend_action_name = target_name
+    zip(zip_action_name) {
+      visibility = [ ":$prepend_action_name" ]
+      inputs = [
+        invoker.input_so,
+        invoker.input_dex_jar,
+      ]
+      output = zip_action_output
+      forward_variables_from(invoker,
+                             [
+                               "deps",
+                               "public_deps",
+                               "data_deps",
+                             ])
+    }
+
+    _mojo_output = "${root_out_dir}/${mojo_application_subdir}/${base_target_name}/${base_target_name}.mojo"
+
+    action(target_name) {
+      script = "//mojo/public/tools/prepend.py"
+
+      input = zip_action_output
+      inputs = [
+        input,
+      ]
+
+      outputs = [
+        _mojo_output,
+      ]
+
+      rebase_input = rebase_path(input, root_build_dir)
+      rebase_output = rebase_path(_mojo_output, root_build_dir)
+      args = [
+        "--input=$rebase_input",
+        "--output=$rebase_output",
+        "--line=#!mojo mojo:android_handler",
+      ]
+
+      data_deps = mojo_data_deps
+
+      public_deps = [
+        ":$zip_action_name",
+      ]
+    }
+
+    android_assets("${target_name}_assets") {
+      forward_variables_from(invoker, [ "testonly" ])
+      deps = [
+        ":$prepend_action_name",
+      ]
+      renaming_sources = [ _mojo_output ]
+      renaming_destinations = [ "${base_target_name}/${base_target_name}.mojo" ]
+      if (defined(invoker.resources)) {
+        renaming_sources += invoker.resources
+        renaming_destinations += process_file_template(
+                invoker.resources,
+                [ "$base_target_name/resources/{{source_file_part}}" ])
+      }
+    }
+  }
+}
diff --git a/mojo/public/mojo_application_manifest.gni b/mojo/public/mojo_application_manifest.gni
new file mode 100644
index 0000000..6184417
--- /dev/null
+++ b/mojo/public/mojo_application_manifest.gni
@@ -0,0 +1,139 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/mojo_constants.gni")
+
+# Used to produce a Mojo Application Manifest for an application.
+#
+# Parameters:
+#
+#   source
+#       The manifest file template for this application, must be valid JSON with
+#       a valid 'url' key matching application_name.
+#
+#   base_manifest (optional)
+#       A manifest file template to use as a base for |source|. Any properties
+#       defined in |source| will overwrite or be merged with properties defined
+#       in |base_manifest|.
+#
+#   application_name
+#       The host portion of the mojo: URL of the application. The script
+#       validates that the value of this parameter matches the host name portion
+#       of the 'url' property set in the manifest and throws a ValueError if
+#       they do not.
+#
+#   base_deps (optional)
+#       Dependencies required to generate |base_manifest| if applicable.
+#
+#   deps (optional)
+#       An array of dependent instances of this template. This template enforces
+#       that dependencies can only be instances of this template.
+#
+#   packaged_applications (optional)
+#       An array of application_names of the dependent applications.
+#
+#   type (default is mojo)
+#       Possible values are 'mojo' and 'exe'. Default is 'mojo'.
+#
+# Outputs:
+#
+#   An instantiation of this template produces in
+#       $outdir/<application_name>/manifest.json
+#   a meta manifest from the source template and the output manifest of all
+#   dependent children.
+#
+template("mojo_application_manifest") {
+  assert(defined(invoker.source),
+         "\"source\" must be defined for the $target_name template")
+  assert(defined(invoker.application_name),
+         "\"application_name\" must be defined for the $target_name template")
+  if (defined(invoker.deps)) {
+    assert(defined(invoker.packaged_applications),
+           "\"packaged_applications\" listing the directory containing the " +
+               "manifest.json of dependent applications must be provided.")
+  }
+  if (defined(invoker.packaged_applications)) {
+    assert(defined(invoker.deps),
+           "\"deps\" building the dependent packaged applications must be " +
+               "provided.")
+  }
+  if (defined(invoker.type)) {
+    assert(invoker.type == "mojo" || invoker.type == "exe",
+           "\"type\" must be one of \"mojo\" or \"exe\".")
+  }
+
+  action(target_name) {
+    script = "//mojo/public/tools/manifest/manifest_collator.py"
+
+    type = "mojo"
+    if (defined(invoker.type)) {
+      type = invoker.type
+    }
+
+    application_name = invoker.application_name
+    inputs = [
+      invoker.source,
+    ]
+
+    if (type == "mojo") {
+      output = "$root_out_dir/$mojo_application_subdir/$application_name/manifest.json"
+    } else {
+      output = "$root_out_dir/${application_name}_manifest.json"
+    }
+    outputs = [
+      output,
+    ]
+
+    rebase_parent = rebase_path(invoker.source, root_build_dir)
+    rebase_output = rebase_path(output, root_build_dir)
+
+    args = [
+      "--application-name=$application_name",
+      "--parent=$rebase_parent",
+      "--output=$rebase_output",
+    ]
+
+    if (defined(invoker.base_manifest)) {
+      rebase_base = rebase_path(invoker.base_manifest, root_build_dir)
+      args += [ "--base-manifest=$rebase_base" ]
+    }
+
+    if (defined(invoker.packaged_applications)) {
+      foreach(application_name, invoker.packaged_applications) {
+        input = "$root_out_dir/$mojo_application_subdir/$application_name/manifest.json"
+        inputs += [ input ]
+        args += [ rebase_path(input, root_build_dir) ]
+      }
+    }
+    deps = []
+    data_deps = []
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+      data_deps += invoker.deps
+    }
+    if (defined(invoker.base_deps)) {
+      deps += invoker.base_deps
+      data_deps += invoker.base_deps
+    }
+  }
+
+  all_deps = []
+  if (defined(invoker.deps)) {
+    all_deps += invoker.deps
+  }
+
+  group("${target_name}__is_mojo_application_manifest") {
+  }
+
+  # Explicitly ensure that all dependencies are mojo_application_manifest
+  # targets themselves.
+  group("${target_name}__check_deps_are_all_mojo_application_manifest") {
+    deps = []
+    foreach(d, all_deps) {
+      name = get_label_info(d, "label_no_toolchain")
+      toolchain = get_label_info(d, "toolchain")
+      deps += [ "${name}__is_mojo_application_manifest(${toolchain})" ]
+    }
+  }
+}
diff --git a/mojo/public/mojo_application_manifest.gypi b/mojo/public/mojo_application_manifest.gypi
new file mode 100644
index 0000000..9b89abb
--- /dev/null
+++ b/mojo/public/mojo_application_manifest.gypi
@@ -0,0 +1,56 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'variables': {
+      'application_name%': '<(application_name)',
+      'application_type%': '<(application_type)',
+      'base_manifest%': 'none',
+      'packaged_manifests%': []
+    },
+    'application_type%': '<(application_type)',
+    'application_name%': '<(application_name)',
+    'base_manifest%': '<(base_manifest)',
+    'manifest_collator_script%':
+        '<(DEPTH)/mojo/public/tools/manifest/manifest_collator.py',
+    'packaged_manifests%': '<(packaged_manifests)',
+    'source_manifest%': '<(source_manifest)',
+    'conditions': [
+      ['application_type=="mojo"', {
+        'output_manifest%': '<(PRODUCT_DIR)/Mojo Applications/<(application_name)/manifest.json',
+      }, {
+        'output_manifest%': '<(PRODUCT_DIR)/<(application_name)_manifest.json',
+      }],
+      ['base_manifest!="none"', {
+        'extra_args%': [
+          '--base-manifest=<(base_manifest)',
+          '<@(packaged_manifests)',
+        ],
+      }, {
+        'extra_args%': [
+          '<@(packaged_manifests)',
+        ],
+      }]
+    ],
+  },
+  'actions': [{
+    'action_name': '<(_target_name)_collation',
+    'inputs': [
+      '<(manifest_collator_script)',
+      '<(source_manifest)',
+    ],
+    'outputs': [
+      '<(output_manifest)',
+    ],
+    'action': [
+      'python',
+      '<(manifest_collator_script)',
+      '--application-name', '<(application_name)',
+      '--parent=<(source_manifest)',
+      '--output=<(output_manifest)',
+      '<@(extra_args)',
+    ],
+  }],
+}
diff --git a/mojo/public/mojo_constants.gni b/mojo/public/mojo_constants.gni
new file mode 100644
index 0000000..3165a8d
--- /dev/null
+++ b/mojo/public/mojo_constants.gni
@@ -0,0 +1,8 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+declare_args() {
+  # Mojo application directories are created within this subdirectory.
+  mojo_application_subdir = "Mojo Applications"
+}
diff --git a/mojo/public/tests/test_support_private.cc b/mojo/public/tests/test_support_private.cc
new file mode 100644
index 0000000..0081984
--- /dev/null
+++ b/mojo/public/tests/test_support_private.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/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,
+                                  const char* sub_test_name,
+                                  double value,
+                                  const char* units) {
+  if (g_test_support) {
+    g_test_support->LogPerfResult(test_name, sub_test_name, value, units);
+  } else {
+    if (sub_test_name) {
+      printf("[no test runner]\t%s/%s\t%g\t%s\n", test_name, sub_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..2742605
--- /dev/null
+++ b/mojo/public/tests/test_support_private.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_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 TestSupport {
+ public:
+  virtual ~TestSupport();
+
+  static void Init(TestSupport* test_support);
+  static TestSupport* Get();
+  static void Reset();
+
+  virtual void LogPerfResult(const char* test_name,
+                             const char* sub_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/BUILD.gn b/mojo/public/tools/bindings/BUILD.gn
new file mode 100644
index 0000000..eeea5c5
--- /dev/null
+++ b/mojo/public/tools/bindings/BUILD.gn
@@ -0,0 +1,71 @@
+# Copyright 2016 The Chromium 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")
+
+action("precompile_templates") {
+  sources = mojom_generator_sources
+  sources += [
+    "$mojom_generator_root/generators/cpp_templates/enum_macros.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/enum_serialization_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/interface_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/interface_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/interface_macros.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/interface_proxy_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/interface_stub_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/module-internal.h.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/module.cc.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/module.h.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/struct_data_view_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/struct_data_view_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/struct_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/struct_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/struct_macros.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/struct_serialization_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/union_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/union_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/union_serialization_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/union_serialization_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/validation_macros.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/wrapper_class_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/wrapper_class_template_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_definition.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_template_definition.tmpl",
+    "$mojom_generator_root/generators/java_templates/constant_definition.tmpl",
+    "$mojom_generator_root/generators/java_templates/constants.java.tmpl",
+    "$mojom_generator_root/generators/java_templates/data_types_definition.tmpl",
+    "$mojom_generator_root/generators/java_templates/enum.java.tmpl",
+    "$mojom_generator_root/generators/java_templates/enum_definition.tmpl",
+    "$mojom_generator_root/generators/java_templates/header.java.tmpl",
+    "$mojom_generator_root/generators/java_templates/interface.java.tmpl",
+    "$mojom_generator_root/generators/java_templates/interface_definition.tmpl",
+    "$mojom_generator_root/generators/java_templates/interface_internal.java.tmpl",
+    "$mojom_generator_root/generators/java_templates/struct.java.tmpl",
+    "$mojom_generator_root/generators/java_templates/union.java.tmpl",
+    "$mojom_generator_root/generators/js_templates/enum_definition.tmpl",
+    "$mojom_generator_root/generators/js_templates/interface_definition.tmpl",
+    "$mojom_generator_root/generators/js_templates/module.amd.tmpl",
+    "$mojom_generator_root/generators/js_templates/module_definition.tmpl",
+    "$mojom_generator_root/generators/js_templates/struct_definition.tmpl",
+    "$mojom_generator_root/generators/js_templates/union_definition.tmpl",
+    "$mojom_generator_root/generators/js_templates/validation_macros.tmpl",
+  ]
+  script = mojom_generator_script
+  outputs = [
+    "$target_gen_dir/cpp_templates.zip",
+    "$target_gen_dir/java_templates.zip",
+    "$target_gen_dir/js_templates.zip",
+  ]
+  args = [
+    "--use_bundled_pylibs",
+    "precompile",
+    "-o",
+    rebase_path(target_gen_dir),
+  ]
+}
diff --git a/mojo/public/tools/bindings/bindings.gyp b/mojo/public/tools/bindings/bindings.gyp
new file mode 100644
index 0000000..0f00114
--- /dev/null
+++ b/mojo/public/tools/bindings/bindings.gyp
@@ -0,0 +1,83 @@
+# Copyright 2016 The Chromium 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': [
+    '../../../mojom_bindings_generator_variables.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'precompile_mojom_bindings_generator_templates',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'precompile_mojom_bindings_generator_templates',
+          'inputs': [
+            '<@(mojom_bindings_generator_sources)',
+            'generators/cpp_templates/enum_macros.tmpl',
+            'generators/cpp_templates/enum_serialization_declaration.tmpl',
+            'generators/cpp_templates/interface_declaration.tmpl',
+            'generators/cpp_templates/interface_definition.tmpl',
+            'generators/cpp_templates/interface_macros.tmpl',
+            'generators/cpp_templates/interface_proxy_declaration.tmpl',
+            'generators/cpp_templates/interface_request_validator_declaration.tmpl',
+            'generators/cpp_templates/interface_response_validator_declaration.tmpl',
+            'generators/cpp_templates/interface_stub_declaration.tmpl',
+            'generators/cpp_templates/module.cc.tmpl',
+            'generators/cpp_templates/module.h.tmpl',
+            'generators/cpp_templates/module-internal.h.tmpl',
+            'generators/cpp_templates/struct_data_view_declaration.tmpl',
+            'generators/cpp_templates/struct_data_view_definition.tmpl',
+            'generators/cpp_templates/struct_declaration.tmpl',
+            'generators/cpp_templates/struct_definition.tmpl',
+            'generators/cpp_templates/struct_macros.tmpl',
+            'generators/cpp_templates/struct_serialization_declaration.tmpl',
+            'generators/cpp_templates/struct_serialization_definition.tmpl',
+            'generators/cpp_templates/union_declaration.tmpl',
+            'generators/cpp_templates/union_definition.tmpl',
+            'generators/cpp_templates/union_serialization_declaration.tmpl',
+            'generators/cpp_templates/union_serialization_definition.tmpl',
+            'generators/cpp_templates/validation_macros.tmpl',
+            'generators/cpp_templates/wrapper_class_declaration.tmpl',
+            'generators/cpp_templates/wrapper_class_definition.tmpl',
+            'generators/cpp_templates/wrapper_class_template_definition.tmpl',
+            'generators/cpp_templates/wrapper_union_class_declaration.tmpl',
+            'generators/cpp_templates/wrapper_union_class_definition.tmpl',
+            'generators/cpp_templates/wrapper_union_class_template_definition.tmpl',
+            'generators/java_templates/constant_definition.tmpl',
+            'generators/java_templates/constants.java.tmpl',
+            'generators/java_templates/data_types_definition.tmpl',
+            'generators/java_templates/enum_definition.tmpl',
+            'generators/java_templates/enum.java.tmpl',
+            'generators/java_templates/header.java.tmpl',
+            'generators/java_templates/interface_definition.tmpl',
+            'generators/java_templates/interface_internal.java.tmpl',
+            'generators/java_templates/interface.java.tmpl',
+            'generators/java_templates/struct.java.tmpl',
+            'generators/java_templates/union.java.tmpl',
+            'generators/js_templates/enum_definition.tmpl',
+            'generators/js_templates/interface_definition.tmpl',
+            'generators/js_templates/module_definition.tmpl',
+            'generators/js_templates/module.amd.tmpl',
+            'generators/js_templates/struct_definition.tmpl',
+            'generators/js_templates/union_definition.tmpl',
+            'generators/js_templates/validation_macros.tmpl',
+          ],
+          'outputs': [
+            '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/cpp_templates.zip',
+            '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/java_templates.zip',
+            '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/js_templates.zip',
+          ],
+          'action': [
+            'python', '<@(mojom_bindings_generator)',
+            '--use_bundled_pylibs', 'precompile',
+            '-o', '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings',
+          ],
+        }
+      ],
+      'hard_dependency': 1,
+    },
+  ],
+}
+
diff --git a/mojo/public/tools/bindings/blink_bindings_configuration.gni b/mojo/public/tools/bindings/blink_bindings_configuration.gni
new file mode 100644
index 0000000..ef19cc3
--- /dev/null
+++ b/mojo/public/tools/bindings/blink_bindings_configuration.gni
@@ -0,0 +1,29 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+variant = "blink"
+
+for_blink = true
+
+_typemap_imports = [
+  "//mojo/public/cpp/bindings/tests/blink_typemaps.gni",
+  "//third_party/WebKit/Source/platform/mojo/blink_typemaps.gni",
+]
+_typemaps = []
+
+foreach(typemap_import, _typemap_imports) {
+  _imported = read_file(typemap_import, "scope")
+  _typemaps += _imported.typemaps
+}
+
+typemaps = []
+foreach(typemap, _typemaps) {
+  typemaps += [ read_file(typemap, "scope") ]
+}
+
+blacklist = [
+  # TODO(sammc): Remove the following once |for_blink| bindings support WTF
+  # maps with enum keys. See https://crbug.com/583738.
+  "//mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom",
+]
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
new file mode 100644
index 0000000..44c2ae6
--- /dev/null
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -0,0 +1,30 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+_typemap_imports = [
+  "//cc/ipc/typemaps.gni",
+  "//device/bluetooth/public/interfaces/typemaps.gni",
+  "//components/arc/common/typemaps.gni",
+  "//components/typemaps.gni",
+  "//gpu/ipc/common/typemaps.gni",
+  "//media/mojo/interfaces/typemaps.gni",
+  "//mojo/common/typemaps.gni",
+  "//mojo/public/cpp/bindings/tests/chromium_typemaps.gni",
+  "//skia/public/interfaces/typemaps.gni",
+  "//ui/events/devices/mojo/typemaps.gni",
+  "//ui/events/mojo/typemaps.gni",
+  "//ui/gfx/typemaps.gni",
+  "//url/mojo/typemaps.gni",
+]
+_typemaps = []
+
+foreach(typemap_import, _typemap_imports) {
+  _imported = read_file(typemap_import, "scope")
+  _typemaps += _imported.typemaps
+}
+
+typemaps = []
+foreach(typemap, _typemaps) {
+  typemaps += [ read_file(typemap, "scope") ]
+}
diff --git a/mojo/public/tools/bindings/format_typemap_generator_args.py b/mojo/public/tools/bindings/format_typemap_generator_args.py
new file mode 100755
index 0000000..5057d6c
--- /dev/null
+++ b/mojo/public/tools/bindings/format_typemap_generator_args.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium 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
+
+# This utility converts mojom dependencies into their corresponding typemap
+# paths and formats them to be consumed by generate_type_mappings.py.
+
+
+def FormatTypemap(typemap_filename):
+  # A simple typemap is valid Python with a minor alteration.
+  with open(typemap_filename) as f:
+    typemap_content = f.read().replace('=\n', '=')
+  typemap = {}
+  exec typemap_content in typemap
+
+  for header in typemap.get('public_headers', []):
+    yield 'public_headers=%s' % header
+  for header in typemap.get('traits_headers', []):
+    yield 'traits_headers=%s' % header
+  for header in typemap.get('type_mappings', []):
+    yield 'type_mappings=%s' % header
+
+
+def main():
+  typemaps = sys.argv[1:]
+  print ' '.join('--start-typemap %s' % ' '.join(FormatTypemap(typemap))
+                 for typemap in typemaps)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/mojo/public/tools/bindings/generate_type_mappings.py b/mojo/public/tools/bindings/generate_type_mappings.py
new file mode 100755
index 0000000..e2fbf31
--- /dev/null
+++ b/mojo/public/tools/bindings/generate_type_mappings.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium 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 JSON typemap from its command-line arguments and dependencies.
+
+Each typemap should be specified in an command-line argument of the form
+key=value, with an argument of "--start-typemap" preceding each typemap.
+
+For example,
+generate_type_mappings.py --output=foo.typemap --start-typemap \\
+    public_headers=foo.h traits_headers=foo_traits.h \\
+    type_mappings=mojom.Foo=FooImpl
+
+generates a foo.typemap containing
+{
+  "c++": {
+    "mojom.Foo": {
+      "typename": "FooImpl",
+      "traits_headers": [
+        "foo_traits.h"
+      ],
+      "public_headers": [
+        "foo.h"
+      ]
+    }
+  }
+}
+
+Then,
+generate_type_mappings.py --dependency foo.typemap --output=bar.typemap \\
+    --start-typemap public_headers=bar.h traits_headers=bar_traits.h \\
+    type_mappings=mojom.Bar=BarImpl
+
+generates a bar.typemap containing
+{
+  "c++": {
+    "mojom.Bar": {
+      "typename": "BarImpl",
+      "traits_headers": [
+        "bar_traits.h"
+      ],
+      "public_headers": [
+        "bar.h"
+      ]
+    },
+    "mojom.Foo": {
+      "typename": "FooImpl",
+      "traits_headers": [
+        "foo_traits.h"
+      ],
+      "public_headers": [
+        "foo.h"
+      ]
+    }
+  }
+}
+"""
+
+import argparse
+import json
+import os
+import re
+
+
+def ReadTypemap(path):
+  with open(path) as f:
+    return json.load(f)['c++']
+
+
+def ParseTypemapArgs(args):
+  typemaps = [s for s in '\n'.join(args).split('--start-typemap\n') if s]
+  result = {}
+  for typemap in typemaps:
+    result.update(ParseTypemap(typemap))
+  return result
+
+
+def ParseTypemap(typemap):
+  values = {'type_mappings': [], 'public_headers': [], 'traits_headers': []}
+  for line in typemap.split('\n'):
+    if not line:
+      continue
+    key, _, value = line.partition('=')
+    values[key].append(value.lstrip('/'))
+  result = {}
+  mapping_pattern = \
+      re.compile(r"""^([^=]+)           # mojom type
+                     =
+                     ([^[]+)            # native type
+                     (?:\[([^]]+)\])?$  # optional attribute in square brackets
+                 """, re.X)
+  for typename in values['type_mappings']:
+    match_result = mapping_pattern.match(typename)
+    assert match_result, (
+        "Cannot parse entry in the \"type_mappings\" section: %s" % typename)
+
+    mojom_type = match_result.group(1)
+    native_type = match_result.group(2)
+    # The only attribute supported currently is "move_only".
+    move_only = match_result.group(3) and match_result.group(3) == "move_only"
+
+    assert mojom_type not in result, (
+        "Cannot map multiple native types (%s, %s) to the same mojom type: %s" %
+        (result[mojom_type]['typename'], native_type, mojom_type))
+
+    result[mojom_type] = {
+        'typename': native_type,
+        'move_only': move_only,
+        'public_headers': values['public_headers'],
+        'traits_headers': values['traits_headers'],
+    }
+  return result
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description=__doc__,
+      formatter_class=argparse.RawDescriptionHelpFormatter)
+  parser.add_argument(
+      '--dependency',
+      type=str,
+      action='append',
+      default=[],
+      help=('A path to another JSON typemap to merge into the output. '
+            'This may be repeated to merge multiple typemaps.'))
+  parser.add_argument('--output',
+                      type=str,
+                      required=True,
+                      help='The path to which to write the generated JSON.')
+  params, typemap_params = parser.parse_known_args()
+  typemaps = ParseTypemapArgs(typemap_params)
+  missing = [path for path in params.dependency if not os.path.exists(path)]
+  if missing:
+    raise IOError('Missing dependencies: %s' % ', '.join(missing))
+  for path in params.dependency:
+    typemaps.update(ReadTypemap(path))
+  with open(params.output, 'w') as f:
+    json.dump({'c++': typemaps}, f, indent=2)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
new file mode 100644
index 0000000..0a446a7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
@@ -0,0 +1,83 @@
+{#---
+  Macro for enum definition, and the declaration of associated functions.
+---#}
+{%- macro enum_decl(enum) %}
+enum class {{enum.name}} : int32_t {
+{%- for field in enum.fields %}
+{%-    if field.value %}
+  {{field.name}} = {{field.value|expression_to_text}},
+{%-    else %}
+  {{field.name}},
+{%-    endif %}
+{%- endfor %}
+};
+{%- endmacro %}
+
+{%- macro enum_data_decl(enum) %}
+struct {{enum.name}}_Data {
+ public:
+  static bool const kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
+
+  static bool IsKnownValue(int32_t value) {
+{%- if enum.fields %}
+    switch (value) {
+{%-   for enum_field in enum.fields|groupby('numeric_value') %}
+      case {{enum_field[0]}}:
+{%-   endfor %}
+        return true;
+    }
+{%- endif %}
+    return false;
+  }
+
+  static bool Validate(int32_t value,
+                       mojo::internal::ValidationContext* validation_context) {
+    if (kIsExtensible || IsKnownValue(value))
+      return true;
+
+    ReportValidationError(validation_context,
+                          mojo::internal::VALIDATION_ERROR_UNKNOWN_ENUM_VALUE);
+    return false;
+  }
+};
+{%- endmacro %}
+
+{#--- macros for enum-associated functions. Namely:
+  * operator<<(): outputs the given enum value.
+  * IsKnownEnumValue(): returns true if the given enum value exists in this
+        generated version of enum.
+---#}
+
+{%- macro enum_stream_operator(enum) %}
+inline std::ostream& operator<<(std::ostream& os, {{enum|get_name_for_kind}} value) {
+  switch(value) {
+{%- for _, values in enum.fields|groupby('numeric_value') %}
+    case {{enum|get_name_for_kind}}::{{values[0].name}}:
+      return os << "{{enum|get_name_for_kind}}::
+{%-   if values|length > 1 -%}
+      {{'{'}}
+{%-   endif -%}
+      {{values|map(attribute='name')|join(', ')}}
+{%-   if values|length > 1 -%}
+      {{'}'}}
+{%-   endif -%}
+      ";
+{%- endfor %}
+    default:
+      return os << "Unknown {{enum|get_name_for_kind}} value: " << static_cast<int32_t>(value);
+  }
+}
+{%- endmacro %}
+
+{%- macro is_known_enum_value(enum) %}
+inline bool IsKnownEnumValue({{enum|get_name_for_kind}} value) {
+  return {{enum|get_qualified_name_for_kind(internal=True)}}::IsKnownValue(
+      static_cast<int32_t>(value));
+}
+{%- endmacro %}
+
+{%- macro enum_hash(enum) %}
+template <>
+struct hash<{{enum|get_qualified_name_for_kind}}>
+    : public mojo::internal::EnumHashImpl<{{enum|get_qualified_name_for_kind}}> {};
+{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
new file mode 100644
index 0000000..e42128d
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
@@ -0,0 +1,28 @@
+{%- set mojom_type = enum|get_qualified_name_for_kind %}
+
+template <>
+struct EnumTraits<{{mojom_type}}, {{mojom_type}}> {
+  static {{mojom_type}} ToMojom({{mojom_type}} input) { return input; }
+  static bool FromMojom({{mojom_type}} input, {{mojom_type}}* output) {
+    *output = input;
+    return true;
+  }
+};
+
+namespace internal {
+
+template <typename MaybeConstUserType>
+struct Serializer<{{mojom_type}}, MaybeConstUserType> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = EnumTraits<{{mojom_type}}, UserType>;
+
+  static void Serialize(UserType input, int32_t* output) {
+    *output = static_cast<int32_t>(Traits::ToMojom(input));
+  }
+
+  static bool Deserialize(int32_t input, UserType* output) {
+    return Traits::FromMojom(static_cast<{{mojom_type}}>(input), output);
+  }
+};
+
+}  // namespace internal
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..0249ef3
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
@@ -0,0 +1,62 @@
+{%- 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 %}
+
+class {{interface.name}} {
+ public:
+  static const char Name_[];
+  static const uint32_t Version_ = {{interface.version}};
+  static const bool PassesAssociatedKinds_ = {% if interface|passes_associated_kinds %}true{% else %}false{% endif %};
+  static const bool HasSyncMethods_ = {% if interface|has_sync_methods %}true{% else %}false{% endif %};
+
+  using Proxy_ = {{interface.name}}Proxy;
+  using Stub_ = {{interface.name}}Stub;
+
+  using RequestValidator_ = {{interface.name}}RequestValidator;
+{%- if interface|has_callbacks %}
+  using ResponseValidator_ = {{interface.name}}ResponseValidator;
+{%- else %}
+  using ResponseValidator_ = mojo::PassThroughFilter;
+{%- endif %}
+
+{#--- Enums #}
+{% from "enum_macros.tmpl" import enum_decl -%}
+{%- for enum in interface.enums %}
+{%-   if enum|is_native_only_kind %}
+  using {{enum.name}} = mojo::NativeEnum;
+{%-   else %}
+  {{enum_decl(enum)|indent(2)}}
+{%-   endif %}
+{%- endfor %}
+
+{#--- Constants #}
+{%- for constant in interface.constants %}
+{%-   if constant.kind|is_integral_kind %}
+  static const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%-   else %}
+  static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%-   endif %}
+{%- endfor %}
+
+{#--- Methods #}
+  virtual ~{{interface.name}}() {}
+
+{%- for method in interface.methods %}
+{%    if method.response_parameters != None %}
+{%-     if method.sync %}
+  // Sync method. This signature is used by the client side; the service side
+  // should implement the signature with callback below.
+  virtual bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}});
+{%-     endif %}
+
+  using {{method.name}}Callback = {{interface_macros.declare_callback(method,
+      for_blink, use_new_wrapper_types)}};
+{%-   endif %}
+  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..4017fc5
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -0,0 +1,473 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+{%- import "struct_macros.tmpl" as struct_macros %}
+
+{%- set class_name = interface.name %}
+{%- set proxy_name = interface.name ~ "Proxy" %}
+{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}
+
+{%- macro alloc_params(struct, params, message, serialization_context,
+                       description) %}
+  ({{serialization_context}})->handles.Swap(({{message}})->mutable_handles());
+  bool success = true;
+{%-   for param in struct.packed.packed_fields_in_ordinal_order %}
+  {{param.field.kind|cpp_wrapper_type}} p_{{param.field.name}}{};
+{%-   endfor %}
+  {{struct.name}}DataView input_data_view({{params}},
+                                          {{serialization_context}});
+  {{struct_macros.deserialize(struct, "input_data_view", "p_%s", "success")}}
+  if (!success) {
+    mojo::internal::ValidationContext validation_context(
+        {{message}}->data(), {{message}}->data_num_bytes(),
+        {{message}}->handles()->size(), {{message}},
+        "{{description}} deserializer");
+    ReportValidationError(
+        &validation_context,
+        mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED);
+    return false;
+  }
+{%- endmacro %}
+
+{%- macro pass_params(parameters) %}
+{%-   for param in parameters %}
+std::move(p_{{param.name}})
+{%-     if not loop.last %}, {% endif %}
+{%-   endfor %}
+{%- endmacro %}
+
+{%- macro build_message(struct, input_pattern, struct_display_name,
+                        serialization_context) -%}
+  {{struct_macros.serialize(struct, struct_display_name, input_pattern,
+                            "params", "builder.buffer()",
+                            serialization_context)}}
+  ({{serialization_context}})->handles.Swap(
+      builder.message()->mutable_handles());
+{%- endmacro %}
+
+{#--- Begin #}
+const char {{class_name}}::Name_[] = "{{namespace_as_string}}::{{class_name}}";
+const uint32_t {{class_name}}::Version_;
+
+{#--- Constants #}
+{%-  for constant in interface.constants %}
+{%-   if constant.kind|is_integral_kind %}
+const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}};
+{%-   else %}
+const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}} = {{constant|constant_value}};
+{%-   endif %}
+{%- endfor %}
+
+
+{%- for method in interface.methods %}
+{%-   if method.sync %}
+bool {{class_name}}::{{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) {
+  NOTREACHED();
+  return false;
+}
+{%-   endif %}
+{%- endfor %}
+
+{#--- ForwardToCallback definition #}
+{%- for method in interface.methods -%}
+{%-   if method.response_parameters != None %}
+{%-     if method.sync %}
+class {{class_name}}_{{method.name}}_HandleSyncResponse
+    : public mojo::MessageReceiver {
+ public:
+  {{class_name}}_{{method.name}}_HandleSyncResponse(
+      scoped_refptr<mojo::AssociatedGroupController> group_controller,
+      bool* result
+{%-       for param in method.response_parameters -%}
+      , {{param.kind|cpp_wrapper_type}}* out_{{param.name}}
+{%-       endfor %})
+      : serialization_context_(std::move(group_controller)), result_(result)
+{%-       for param in method.response_parameters -%}
+        , out_{{param.name}}_(out_{{param.name}})
+{%-       endfor %} {
+    DCHECK(!*result_);
+  }
+  bool Accept(mojo::Message* message) override;
+ private:
+  mojo::internal::SerializationContext serialization_context_;
+  bool* result_;
+{%-       for param in method.response_parameters %}
+  {{param.kind|cpp_wrapper_type}}* out_{{param.name}}_;
+{%-       endfor -%}
+  DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_HandleSyncResponse);
+};
+bool {{class_name}}_{{method.name}}_HandleSyncResponse::Accept(
+    mojo::Message* message) {
+  internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params =
+      reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
+          message->mutable_payload());
+
+  {{alloc_params(method.response_param_struct, "params", "message",
+                 "&serialization_context_",
+                 "{{class_name}}::{{method.name}} response")}}
+
+{%-       for param in method.response_parameters %}
+  *out_{{param.name}}_ = std::move(p_{{param.name}});
+{%-       endfor %}
+  *result_ = true;
+  return true;
+}
+{%-     endif %}
+
+class {{class_name}}_{{method.name}}_ForwardToCallback
+    : public mojo::MessageReceiver {
+ public:
+  {{class_name}}_{{method.name}}_ForwardToCallback(
+      const {{class_name}}::{{method.name}}Callback& callback,
+      scoped_refptr<mojo::AssociatedGroupController> group_controller)
+      : callback_(callback),
+        serialization_context_(std::move(group_controller)) {
+  }
+  bool Accept(mojo::Message* message) override;
+ private:
+  {{class_name}}::{{method.name}}Callback callback_;
+  mojo::internal::SerializationContext serialization_context_;
+  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());
+
+  {{alloc_params(method.response_param_struct, "params", "message",
+                 "&serialization_context_",
+                 "{{class_name}}_{{method.name}} response")}}
+  if (!callback_.is_null())
+    callback_.Run({{pass_params(method.response_parameters)}});
+  return true;
+}
+{%-   endif %}
+{%- endfor %}
+
+{{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver)
+    : ControlMessageProxy(receiver) {
+}
+
+{#--- Proxy definitions #}
+
+{%- for method in interface.methods %}
+{%-   set message_name =
+          "internal::k%s_%s_Name"|format(interface.name, method.name) %}
+{%-   set params_struct = method.param_struct %}
+{%-   set params_description =
+          "%s.%s request"|format(interface.name, method.name) %}
+{%-   if method.sync %}
+bool {{proxy_name}}::{{method.name}}(
+    {{interface_macros.declare_sync_method_params("param_", method)}}) {
+  {{struct_macros.get_serialized_size(params_struct, "param_%s",
+                                      "&serialization_context_")}}
+
+  mojo::internal::RequestMessageBuilder builder({{message_name}}, size,
+                                                mojo::Message::kFlagIsSync);
+
+  {{build_message(params_struct, "param_%s", params_description,
+                  "&serialization_context_")}}
+
+  bool result = false;
+  mojo::MessageReceiver* responder =
+      new {{class_name}}_{{method.name}}_HandleSyncResponse(
+          serialization_context_.group_controller, &result
+{%-     for param in method.response_parameters -%}
+          , param_{{param.name}}
+{%-     endfor %});
+  if (!receiver_->AcceptWithResponder(builder.message(), responder))
+    delete responder;
+  return result;
+}
+{%-   endif %}
+
+void {{proxy_name}}::{{method.name}}(
+    {{interface_macros.declare_request_params("in_", method)}}) {
+  {{struct_macros.get_serialized_size(params_struct, "in_%s",
+                                      "&serialization_context_")}}
+
+{%- if method.response_parameters != None %}
+  mojo::internal::RequestMessageBuilder builder({{message_name}}, size);
+{%- else %}
+  mojo::internal::MessageBuilder builder({{message_name}}, size);
+{%- endif %}
+
+  {{build_message(params_struct, "in_%s", params_description,
+                  "&serialization_context_")}}
+
+{%- if method.response_parameters != None %}
+  mojo::MessageReceiver* responder =
+      new {{class_name}}_{{method.name}}_ForwardToCallback(
+          callback, serialization_context_.group_controller);
+  if (!receiver_->AcceptWithResponder(builder.message(), responder))
+    delete responder;
+{%- else %}
+  bool ok = receiver_->Accept(builder.message());
+  // This return value may be ignored as !ok implies the Connector has
+  // encountered an error, which will be visible through other means.
+  ALLOW_UNUSED_LOCAL(ok);
+{%- 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 response_params_struct = method.response_param_struct %}
+{%-     set params_description =
+            "%s.%s response"|format(interface.name, method.name) %}
+class {{class_name}}_{{method.name}}_ProxyToResponder
+    : public base::RefCountedThreadSafe<
+          {{class_name}}_{{method.name}}_ProxyToResponder> {
+ public:
+  static {{class_name}}::{{method.name}}Callback CreateCallback(
+      uint64_t request_id,
+      bool is_sync,
+      mojo::MessageReceiverWithStatus* responder,
+      scoped_refptr<mojo::AssociatedGroupController>
+          group_controller) {
+    scoped_refptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy
+        = new {{class_name}}_{{method.name}}_ProxyToResponder(
+            request_id, is_sync, responder, group_controller);
+    return base::Bind(&{{class_name}}_{{method.name}}_ProxyToResponder::Run,
+                      proxy);
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<
+      {{class_name}}_{{method.name}}_ProxyToResponder>;
+
+  {{class_name}}_{{method.name}}_ProxyToResponder(
+      uint64_t request_id,
+      bool is_sync,
+      mojo::MessageReceiverWithStatus* responder,
+      scoped_refptr<mojo::AssociatedGroupController> group_controller)
+      : request_id_(request_id),
+        is_sync_(is_sync),
+        responder_(responder),
+        serialization_context_(std::move(group_controller)) {
+  }
+
+  ~{{class_name}}_{{method.name}}_ProxyToResponder() {
+#if DCHECK_IS_ON()
+    if (responder_) {
+      // Is the Mojo application destroying the callback without running it
+      // and without first closing the pipe?
+      responder_->DCheckInvalid("The callback passed to "
+          "{{class_name}}::{{method.name}}() was never run.");
+    }
+#endif
+    // If the Callback was dropped then deleting the responder will close
+    // the pipe so the calling application knows to stop waiting for a reply.
+    delete responder_;
+  }
+
+  void Run(
+      {{interface_macros.declare_responder_params(
+          "in_", method.response_parameters, for_blink,
+          use_new_wrapper_types)}});
+
+  uint64_t request_id_;
+  bool is_sync_;
+  mojo::MessageReceiverWithStatus* responder_;
+  // TODO(yzshen): maybe I should use a ref to the original one?
+  mojo::internal::SerializationContext serialization_context_;
+
+  DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder);
+};
+
+void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
+    {{interface_macros.declare_responder_params(
+        "in_", method.response_parameters, for_blink,
+        use_new_wrapper_types)}}) {
+  {{struct_macros.get_serialized_size(response_params_struct, "in_%s",
+                                      "&serialization_context_")}}
+  mojo::internal::ResponseMessageBuilder builder(
+      {{message_name}}, size, request_id_,
+      is_sync_ ? mojo::Message::kFlagIsSync : 0);
+  {{build_message(response_params_struct, "in_%s", params_description,
+                  "&serialization_context_")}}
+  bool ok = responder_->Accept(builder.message());
+  ALLOW_UNUSED_LOCAL(ok);
+  // 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),
+      control_message_handler_({{interface.name}}::Version_) {
+}
+
+{{class_name}}Stub::~{{interface.name}}Stub() {}
+
+{#--- Stub definition #}
+
+bool {{class_name}}Stub::Accept(mojo::Message* message) {
+  if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
+    return control_message_handler_.Accept(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());
+
+      {{alloc_params(method.param_struct, "params", "message",
+          "&serialization_context_", "{{class_name}}::{{method.name}}")
+              |indent(4)}}
+      // A null |sink_| means no implementation was bound.
+      assert(sink_);
+      TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}");
+      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::MessageReceiverWithStatus* responder) {
+  if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
+    return control_message_handler_.AcceptWithResponder(message, 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());
+
+      {{alloc_params(method.param_struct, "params", "message",
+          "&serialization_context_", "{{class_name}}::{{method.name}}")|
+              indent(4)}}
+      {{class_name}}::{{method.name}}Callback callback =
+          {{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback(
+              message->request_id(),
+              message->has_flag(mojo::Message::kFlagIsSync),
+              responder,
+              serialization_context_.group_controller);
+      // A null |sink_| means no implementation was bound.
+      assert(sink_);
+      TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}");
+      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) {
+  assert(sink_);
+
+  mojo::internal::ValidationContext validation_context(
+    message->data(), message->data_num_bytes(), message->handles()->size(),
+    message, "{{class_name}} RequestValidator");
+
+  if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
+    if (!mojo::internal::ValidateControlRequest(message, &validation_context))
+      return false;
+    return sink_->Accept(message);
+  }
+
+  switch (message->header()->name) {
+{%- for method in interface.methods %}
+    case internal::k{{class_name}}_{{method.name}}_Name: {
+{%-   if method.response_parameters != None %}
+      if (!mojo::internal::ValidateMessageIsRequestExpectingResponse(
+              message, &validation_context)) {
+        return false;
+      }
+{%-   else %}
+      if (!mojo::internal::ValidateMessageIsRequestWithoutResponse(
+              message, &validation_context)) {
+        return false;
+      }
+{%-   endif %}
+      if (!mojo::internal::ValidateMessagePayload<
+               internal::{{class_name}}_{{method.name}}_Params_Data>(
+                  message, &validation_context)) {
+        return false;
+      }
+      return sink_->Accept(message);
+    }
+{%- endfor %}
+    default:
+      break;
+  }
+
+  // Unrecognized message.
+  ReportValidationError(
+      &validation_context,
+      mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
+  return false;
+}
+
+{#--- 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) {
+  assert(sink_);
+
+  mojo::internal::ValidationContext validation_context(
+    message->data(), message->data_num_bytes(), message->handles()->size(),
+    message, "{{class_name}} ResponseValidator");
+
+  if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
+    if (!mojo::internal::ValidateControlResponse(message, &validation_context))
+      return false;
+    return sink_->Accept(message);
+  }
+
+  if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context))
+    return false;
+  switch (message->header()->name) {
+{%- for method in interface.methods if method.response_parameters != None %}
+    case internal::k{{class_name}}_{{method.name}}_Name: {
+      if (!mojo::internal::ValidateMessagePayload<
+               internal::{{class_name}}_{{method.name}}_ResponseParams_Data>(
+                    message, &validation_context)) {
+        return false;
+      }
+      return sink_->Accept(message);
+    }
+{%- endfor %}
+    default:
+      break;
+  }
+
+  // Unrecognized message.
+  ReportValidationError(
+      &validation_context,
+      mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
+  return false;
+}
+{%- 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..4bec4c6
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
@@ -0,0 +1,53 @@
+{%- macro declare_params(prefix, parameters) %}
+{%-   for param in parameters -%}
+{{param.kind|cpp_wrapper_param_type}} {{prefix}}{{param.name}}
+{%- if not loop.last %}, {% endif %}
+{%-   endfor %}
+{%- endmacro %}
+
+{%- macro declare_responder_params(prefix, parameters, for_blink, use_new_wrapper_types) %}
+{%-   for param in parameters -%}
+{%-     if (not param.kind|is_string_kind) or for_blink or
+           use_new_wrapper_types -%}
+{{param.kind|cpp_wrapper_param_type}} {{prefix}}{{param.name}}
+{%-     else %}
+mojo::String {{prefix}}{{param.name}}
+{%-     endif %}
+{%- if not loop.last %}, {% endif %}
+{%-   endfor %}
+{%- endmacro %}
+
+{%- macro declare_callback(method, for_blink, use_new_wrapper_types) -%}
+base::Callback<void(
+{%-   for param in method.response_parameters -%}
+{#- TODO(yzshen): For historical reasons, we use mojo::String here (instead of
+    const mojo::String&) inconsistently. Preserve the behavior temporarily. #}
+{%-     if (not param.kind|is_string_kind) or for_blink or
+           use_new_wrapper_types -%}
+{{param.kind|cpp_wrapper_param_type}}
+{%-     else -%}
+mojo::String
+{%-     endif %}
+{%-     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 {{method.name}}Callback& callback
+{%-   endif -%}
+{%- endmacro -%}
+
+{%- macro declare_sync_method_params(prefix, method) -%}
+{{declare_params(prefix, method.parameters)}}
+{%-   if method.response_parameters %}
+{%-     if method.parameters %}, {% endif %}
+{%-     for param in method.response_parameters -%}
+{{param.kind|cpp_wrapper_type}}* {{prefix}}{{param.name}}
+{%-       if not loop.last %}, {% endif %}
+{%-     endfor %}
+{%-   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..477116b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
@@ -0,0 +1,21 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{interface.name}}Proxy
+    : public {{interface.name}},
+      public mojo::internal::ControlMessageProxy {
+ public:
+  explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver);
+
+{%- for method in interface.methods %}
+{%-   if method.sync %}
+  bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) override;
+{%-   endif %}
+  void {{method.name}}({{interface_macros.declare_request_params("", method)}}) override;
+{%- endfor %}
+
+  mojo::internal::SerializationContext* serialization_context() {
+    return &serialization_context_;
+  }
+
+ private:
+  mojo::internal::SerializationContext serialization_context_;
+};
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..29917ea
--- /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);
+
+  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..5893bfd
--- /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);
+
+  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..30b5de7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -0,0 +1,19 @@
+class {{interface.name}}Stub : public mojo::MessageReceiverWithResponderStatus {
+ public:
+  {{interface.name}}Stub();
+  ~{{interface.name}}Stub() override;
+  void set_sink({{interface.name}}* sink) { sink_ = sink; }
+  {{interface.name}}* sink() { return sink_; }
+  mojo::internal::SerializationContext* serialization_context() {
+    return &serialization_context_;
+  }
+
+  bool Accept(mojo::Message* message) override;
+  bool AcceptWithResponder(mojo::Message* message,
+      mojo::MessageReceiverWithStatus* responder) override;
+
+ private:
+  {{interface.name}}* sink_;
+  mojo::internal::SerializationContext serialization_context_;
+  mojo::internal::ControlMessageHandler control_message_handler_;
+};
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..5256e75
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
@@ -0,0 +1,126 @@
+// 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 variant -%}
+{%-   set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%-   set variant_path = module.path -%}
+{%- endif -%}
+
+{%- set header_guard = "%s_INTERNAL_H_"|format(
+        variant_path|upper|replace("/","_")|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"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/union_accessor.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+
+{%- for import in imports %}
+{%-   if variant %}
+#include "{{"%s-%s-internal.h"|format(import.module.path, variant)}}"
+{%-   else %}
+#include "{{import.module.path}}-internal.h"
+{%-   endif %}
+{%- endfor %}
+
+namespace mojo {
+namespace internal {
+class ValidationContext;
+}
+}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+{%- if variant %}
+namespace {{variant}} {
+{%- endif %}
+
+{#--- Wrapper forward declarations #}
+{%  for struct in structs %}
+{%-   if struct|is_native_only_kind %}
+using {{struct.name}} = mojo::NativeStruct;
+{%-   else %}
+class {{struct.name}};
+{%-   endif %}
+{%- endfor %}
+
+{#--- Wrapper forward declarations for unions #}
+{%  for union in unions %}
+class {{union.name}};
+{%- endfor %}
+
+namespace internal {
+
+{#--- Internal forward declarations #}
+{%  for struct in structs %}
+{%-   if struct|is_native_only_kind %}
+using {{struct.name}}_Data = mojo::internal::NativeStruct_Data;
+{%-   else %}
+class {{struct.name}}_Data;
+{%-   endif %}
+{%- endfor %}
+
+{%  for union in unions %}
+class {{union.name}}_Data;
+{%- endfor %}
+
+{#--- Enums #}
+{% from "enum_macros.tmpl" import enum_data_decl -%}
+{%- for enum in enums %}
+{%-   if enum|is_native_only_kind %}
+  using {{enum.name}}_Data = mojo::internal::NativeEnum_Data;
+{%-   else %}
+  {{enum_data_decl(enum)}}
+{%-   endif %}
+{%- endfor %}
+
+#pragma pack(push, 1)
+
+{#--- Unions must be declared first because they can be members of structs #}
+{#--- Union class declarations #}
+{%  for union in unions %}
+{%    include "union_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Struct class declarations #}
+{%  for struct in structs %}
+{%-   if not struct|is_native_only_kind %}
+{%      include "struct_declaration.tmpl" %}
+{%-   endif %}
+{%- endfor %}
+
+{#--- Interface class declarations. They are needed only when they contain
+    enums. #}
+{%- for interface in interfaces %}
+{%-   if interface.enums %}
+class {{interface.name}}_Data {
+ public:
+{%-     for enum in interface.enums %}
+{%-       if enum|is_native_only_kind %}
+  using {{enum.name}}_Data = mojo::internal::NativeEnum_Data;
+{%-       else %}
+  {{enum_data_decl(enum)|indent(2)}}
+{%-       endif %}
+{%-     endfor %}
+};
+{%-   endif %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+}  // namespace internal
+{%- if variant %}
+}  // namespace {{variant}}
+{%- endif %}
+{%- 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..efb9db6
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -0,0 +1,174 @@
+// 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 variant -%}
+{%-   set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%-   set variant_path = module.path -%}
+{%- endif %}
+
+#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:4065)
+#pragma warning(disable:4756)
+#endif
+
+#include "{{variant_path}}.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
+
+{%- if for_blink %}
+#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
+{%- endif %}
+
+{%- for header in extra_traits_headers %}
+#include "{{header}}"
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+{%- if variant %}
+namespace {{variant}} {
+{%- endif %}
+
+{#--- Constants #}
+{%- for constant in module.constants %}
+{%-   if not constant.kind|is_integral_kind %}
+const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%-   endif %}
+{%- 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.param_struct %}
+{%      include "struct_declaration.tmpl" %}
+{%-     include "struct_definition.tmpl" %}
+{%-     if method.response_parameters != None %}
+{%-       set struct = method.response_param_struct %}
+{%        include "struct_declaration.tmpl" %}
+{%-       include "struct_definition.tmpl" %}
+{%-     endif %}
+{%-   endfor %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+}  // namespace
+
+{#--- Struct definitions #}
+{%  for struct in structs %}
+{%-   if not struct|is_native_only_kind %}
+{%-     include "struct_definition.tmpl" %}
+{%-   endif %}
+{%- endfor %}
+
+{#--- Union definitions #}
+{%  for union in unions %}
+{%-   include "union_definition.tmpl" %}
+{%- endfor %}
+
+}  // namespace internal
+
+namespace {
+
+{#--- Interface parameter data view definitions #}
+{%- for interface in interfaces %}
+{%-   for method in interface.methods %}
+{%      set struct = method.param_struct %}
+{%      include "struct_data_view_declaration.tmpl" %}
+{%      include "struct_data_view_definition.tmpl" %}
+{%-     if method.response_parameters != None %}
+{%-       set struct = method.response_param_struct %}
+{%        include "struct_data_view_declaration.tmpl" %}
+{%        include "struct_data_view_definition.tmpl" %}
+{%-     endif %}
+{%-   endfor %}
+{%- endfor %}
+
+}  // namespace
+
+{#--- Struct Constants #}
+{%- for struct in structs %}
+{%-   for constant in struct.constants %}
+{%-     if constant.kind|is_integral_kind %}
+const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}};
+{%-     else %}
+const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}} = {{constant|constant_value}};
+{%-     endif %}
+{%-   endfor %}
+{%- endfor %}
+
+{#--- Struct builder definitions #}
+{%- for struct in structs %}
+{%-   if not struct|is_native_only_kind %}
+{%-     include "wrapper_class_definition.tmpl" %}
+{%-     include "struct_data_view_definition.tmpl" %}
+{%-   endif %}
+{%- endfor %}
+
+{#--- Union builder definitions #}
+{%- for union in unions %}
+{%-   include "wrapper_union_class_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces %}
+{%-   include "interface_definition.tmpl" %}
+{%- endfor %}
+
+{%- if variant %}
+}  // namespace {{variant}}
+{%- endif %}
+{%- for namespace in namespaces_as_array|reverse %}
+}  // namespace {{namespace}}
+{%- endfor %}
+
+namespace mojo {
+
+{#--- Struct Serialization Helpers -#}
+{%  for struct in structs %}
+{%-   if not struct|is_native_only_kind %}
+{%      include "struct_serialization_definition.tmpl" %}
+{%-   endif %}
+{%- endfor %}
+
+{#--- Union Serialization Helpers #}
+{%- for union in unions %}
+{%-   include "union_serialization_definition.tmpl" %}
+{%- endfor %}
+
+}  // namespace mojo
+
+
+#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..c4f3e07
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -0,0 +1,312 @@
+// 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 variant -%}
+{%-   set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%-   set variant_path = module.path -%}
+{%- endif -%}
+
+{%- set header_guard = "%s_H_"|format(
+        variant_path|upper|replace("/","_")|replace(".","_")|
+            replace("-", "_")) %}
+
+{%- from "enum_macros.tmpl" import enum_decl %}
+{%- from "enum_macros.tmpl" import enum_stream_operator %}
+{%- from "enum_macros.tmpl" import is_known_enum_value %}
+{%- from "enum_macros.tmpl" import enum_hash %}
+
+{%- macro namespace_begin() %}
+{%-   for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%-   endfor %}
+{%-   if variant %}
+namespace {{variant}} {
+{%-   endif %}
+{%- endmacro %}
+
+{%- macro namespace_end() %}
+{%-   if variant %}
+}  // namespace {{variant}}
+{%-   endif %}
+{%-   for namespace in namespaces_as_array|reverse %}
+}  // namespace {{namespace}}
+{%-   endfor %}
+{%- endmacro %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include <stdint.h>
+
+#include <functional>
+#include <ostream>
+#include <type_traits>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
+#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/bindings/native_enum.h"
+#include "mojo/public/cpp/bindings/native_struct.h"
+#include "mojo/public/cpp/bindings/no_interface.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "{{variant_path}}-internal.h"
+{%- for import in imports %}
+{%-   if variant %}
+#include "{{"%s-%s.h"|format(import.module.path, variant)}}"
+{%-   else %}
+#include "{{import.module.path}}.h"
+{%-   endif %}
+{%- endfor %}
+{%- if not for_blink %}
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/string.h"
+{%- else %}
+#include "mojo/public/cpp/bindings/wtf_array.h"
+#include "mojo/public/cpp/bindings/wtf_map.h"
+#include "third_party/WebKit/Source/wtf/Optional.h"
+#include "third_party/WebKit/Source/wtf/text/WTFString.h"
+{%- endif %}
+
+{%- for header in extra_public_headers %}
+#include "{{header}}"
+{%- endfor %}
+
+{#--- Enums #}
+{%- if enums %}
+{{namespace_begin()}}
+{%-   for enum in enums %}
+{%-     if enum|is_native_only_kind %}
+using {{enum.name}} = mojo::NativeEnum;
+{%-     else %}
+{{enum_decl(enum)}}
+{{enum_stream_operator(enum)}}
+{{is_known_enum_value(enum)}}
+{%-     endif %}
+{%-   endfor %}
+{{namespace_end()}}
+
+namespace std {
+
+{%-   for enum in enums %}
+{%-     if not enum|is_native_only_kind %}
+{{enum_hash(enum)}}
+{%-     endif %}
+{%-   endfor %}
+
+}  // namespace std
+{%- endif %}
+
+{{namespace_begin()}}
+
+{#--- Constants #}
+{%- for constant in module.constants %}
+{#-   To be consistent with constants defined inside interfaces, only make
+      integral types compile-time constants. #}
+{%-   if constant.kind|is_integral_kind %}
+const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%-   else %}
+extern const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%-   endif %}
+{%- endfor %}
+
+{#--- Interface Forward Declarations -#}
+{%  for interface in interfaces %}
+class {{interface.name}};
+using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>;
+using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>;
+using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>;
+using {{interface.name}}AssociatedPtr =
+    mojo::AssociatedInterfacePtr<{{interface.name}}>;
+using {{interface.name}}AssociatedPtrInfo =
+    mojo::AssociatedInterfacePtrInfo<{{interface.name}}>;
+using {{interface.name}}AssociatedRequest =
+    mojo::AssociatedInterfaceRequest<{{interface.name}}>;
+{%  endfor %}
+
+{#--- Struct Forward Declarations -#}
+{%  for struct in structs %}
+{%-   if struct|is_native_only_kind %}
+using {{struct.name}} = mojo::NativeStruct;
+using {{struct.name}}Ptr = mojo::NativeStructPtr;
+{%-   else %}
+class {{struct.name}};
+class {{struct.name}}DataView;
+{%-     if struct|should_inline %}
+using {{struct.name}}Ptr = mojo::InlinedStructPtr<{{struct.name}}>;
+{%-     else %}
+using {{struct.name}}Ptr = mojo::StructPtr<{{struct.name}}>;
+{%-     endif %}
+{%-   endif %}
+{%  endfor %}
+
+{#--- Union Forward Declarations -#}
+{%  for union in unions %}
+class {{union.name}};
+{%    if union|should_inline_union %}
+typedef mojo::InlinedStructPtr<{{union.name}}> {{union.name}}Ptr;
+{%    else %}
+typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr;
+{%    endif %}
+{%- endfor %}
+
+{#--- Interfaces -#}
+{%  for interface in interfaces %}
+{%    include "interface_declaration.tmpl" %}
+
+{%-   if interface.enums %}
+{{namespace_end()}}
+namespace std {
+
+{%-     for enum in interface.enums %}
+{%-       if not enum|is_native_only_kind %}
+{{enum_hash(enum)}}
+{%-       endif %}
+{%-     endfor %}
+
+}  // namespace std
+{{namespace_begin()}}
+{%-   endif %}
+
+{%-   for enum in interface.enums %}
+{%-     if not enum|is_native_only_kind %}
+{{enum_stream_operator(enum)}}
+{{is_known_enum_value(enum)}}
+{%-     endif %}
+{%-   endfor %}
+{%- 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 %}
+
+{#--- NOTE: Unions and 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 and not struct|is_native_only_kind %}
+{%      include "wrapper_class_declaration.tmpl" %}
+{%      include "struct_data_view_declaration.tmpl" %}
+{%    endif %}
+{%- endfor %}
+
+{#--- Unions must be declared before non-inlined structs because they can be
+      members of structs. #}
+{#--- Unions #}
+{%  for union in unions %}
+{%    include "wrapper_union_class_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Non-inlined structs #}
+{%  for struct in structs %}
+{%    if not struct|should_inline and not struct|is_native_only_kind %}
+{%      include "wrapper_class_declaration.tmpl" %}
+{%      include "struct_data_view_declaration.tmpl" %}
+{%    endif %}
+{%- endfor %}
+
+{%- for union in unions %}
+{%    include "wrapper_union_class_template_definition.tmpl" %}
+{%- endfor %}
+
+{%- for struct in structs %}
+{%-   if not struct|is_native_only_kind %}
+{%      include "wrapper_class_template_definition.tmpl" %}
+{%-   endif %}
+
+{%-   if struct.enums %}
+{{namespace_end()}}
+namespace std {
+
+{%-     for enum in struct.enums %}
+{%-       if not enum|is_native_only_kind %}
+{{enum_hash(enum)}}
+{%-       endif %}
+{%-     endfor %}
+
+}  // namespace std
+{{namespace_begin()}}
+{%-   endif %}
+
+{%-   for enum in struct.enums %}
+{%-     if not enum|is_native_only_kind %}
+{{enum_stream_operator(enum)}}
+{{is_known_enum_value(enum)}}
+{%-     endif %}
+{%-   endfor %}
+{%- endfor %}
+
+{{namespace_end()}}
+
+namespace mojo {
+
+{#--- Enum Serialization Helpers -#}
+{%- for enum in enums %}
+{%-   if not enum|is_native_only_kind %}
+{%      include "enum_serialization_declaration.tmpl" %}
+{%-   endif %}
+{%- endfor %}
+
+{%- for struct in structs %}
+{%-   for enum in struct.enums %}
+{%-     if not enum|is_native_only_kind %}
+{%        include "enum_serialization_declaration.tmpl" %}
+{%-     endif %}
+{%-   endfor %}
+{%- endfor %}
+
+{%- for interface in interfaces %}
+{%-   for enum in interface.enums %}
+{%-     if not enum|is_native_only_kind %}
+{%        include "enum_serialization_declaration.tmpl" %}
+{%-     endif %}
+{%-   endfor %}
+{%- endfor %}
+
+{#--- Struct Serialization Helpers -#}
+{%  for struct in structs %}
+{%-   if not struct|is_native_only_kind %}
+{%      include "struct_serialization_declaration.tmpl" %}
+{%-   endif %}
+{%- endfor %}
+
+{#--- Union Serialization Helpers -#}
+{%  if unions %}
+{%-   for union in unions %}
+{%      include "union_serialization_declaration.tmpl" %}
+{%-   endfor %}
+{%- endif %}
+
+}  // namespace mojo
+
+#endif  // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl
new file mode 100644
index 0000000..7c34939
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl
@@ -0,0 +1,53 @@
+class {{struct.name}}DataView {
+ public:
+  {{struct.name}}DataView(
+      internal::{{struct.name}}_Data* data,
+      mojo::internal::SerializationContext* context);
+
+{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%-   set kind = pf.field.kind -%}
+{%-   set name = pf.field.name -%}
+{%-   if kind|is_struct_kind or kind|is_array_kind or kind|is_string_kind
+         or kind|is_map_kind %}
+  template <typename UserType>
+  bool Read{{name|under_to_camel}}(UserType* value) {
+{%-  if pf.min_version != 0 %}
+    auto pointer = data_->header_.version >= {{pf.min_version}}
+                   ? data_->{{name}}.Get() : nullptr;
+{%-  else %}
+    auto pointer = data_->{{name}}.Get();
+{%-  endif %}
+    return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+        pointer, value, context_);
+  }
+
+{%-   elif kind|is_enum_kind %}
+  template <typename UserType>
+  bool Read{{name|under_to_camel}}(UserType* value) const {
+{%-     if pf.min_version != 0 %}
+    auto data_value = data_->header_.version >= {{pf.min_version}}
+                      ? data_->{{name}} : 0;
+{%-     else %}
+    auto data_value = data_->{{name}};
+{%-     endif %}
+    return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+        data_value, value);
+  }
+
+  {{kind|get_qualified_name_for_kind}} {{name}}() const;
+
+{%-   elif kind|is_union_kind %}
+  bool Read{{name|under_to_camel}}({{kind|cpp_wrapper_type}}* value);
+
+{%-   elif kind|is_any_handle_or_interface_kind %}
+  {{kind|cpp_wrapper_type}} Take{{name|under_to_camel}}();
+
+{%-   else %}
+  {{kind|cpp_wrapper_type}} {{name}}() const;
+{%-   endif %}
+{%- endfor %}
+ private:
+  internal::{{struct.name}}_Data* data_;
+  mojo::internal::SerializationContext* context_;
+};
+
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl
new file mode 100644
index 0000000..2be92b3
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl
@@ -0,0 +1,60 @@
+{{struct.name}}DataView::{{struct.name}}DataView(
+    internal::{{struct.name}}_Data*  data,
+    mojo::internal::SerializationContext* context)
+    : data_(data), context_(context) {
+  DCHECK(data_);
+}
+
+{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%-   set kind = pf.field.kind -%}
+{%-   set name = pf.field.name -%}
+{%-   if kind|is_struct_kind or kind|is_array_kind or kind|is_string_kind or
+         kind|is_map_kind %}
+{#-     Does nothing. They are already defined in the class declaration. #}
+
+{%-   elif kind|is_enum_kind %}
+{{kind|get_qualified_name_for_kind}} {{struct.name}}DataView::{{name}}() const {
+{%-     if pf.min_version != 0 %}
+  if (data_->header_.version < {{pf.min_version}})
+    return {{kind|get_qualified_name_for_kind}}{};
+{%-     endif %}
+  return static_cast<{{kind|get_qualified_name_for_kind}}>(data_->{{name}});
+}
+
+{%-   elif kind|is_union_kind %}
+bool {{struct.name}}DataView::Read{{name|under_to_camel}}(
+    {{kind|cpp_wrapper_type}}* value) {
+{%-     if pf.min_version != 0 %}
+    auto pointer = data_->header_.version >= {{pf.min_version}}
+                   ? &data_->{{name}} : nullptr;
+{%-     else %}
+    auto pointer = &data_->{{name}};
+{%-     endif %}
+  return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+      pointer, value, context_);
+}
+
+{%-   elif kind|is_any_handle_or_interface_kind %}
+{{kind|cpp_wrapper_type}} {{struct.name}}DataView::Take{{name|under_to_camel}}() {
+  {{kind|cpp_wrapper_type}} result;
+{%-     if pf.min_version != 0 %}
+  if (data_->header_.version < {{pf.min_version}})
+    return result;
+{%-     endif %}
+  bool ret = mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+      &data_->{{name}}, &result, context_);
+  DCHECK(ret);
+  return result;
+}
+
+{%-   else %}
+{{kind|cpp_wrapper_type}} {{struct.name}}DataView::{{name}}() const {
+{%-     if pf.min_version != 0 %}
+  if (data_->header_.version < {{pf.min_version}})
+    return {{kind|cpp_wrapper_type}}{};
+{%-     endif %}
+  return data_->{{name}};
+}
+{%-   endif %}
+{%- endfor %}
+
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..dd87be7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
@@ -0,0 +1,53 @@
+{%- 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::ValidationContext* validation_context);
+
+{% from "enum_macros.tmpl" import enum_data_decl -%}
+{#--- Enums #}
+{%- for enum in struct.enums -%}
+{%-   if enum|is_native_only_kind %}
+  using {{enum.name}}_Data = mojo::internal::NativeEnum_Data;
+{%-   else %}
+  {{enum_data_decl(enum)|indent(2)}}
+{%-   endif %}
+{%- endfor %}
+
+  mojo::internal::StructHeader header_;
+{%- for packed_field in struct.packed.packed_fields %}
+{%-   set name = packed_field.field.name %}
+{%-   set kind = packed_field.field.kind %}
+{%-   if kind.spec == 'b' %}
+  uint8_t {{name}} : 1;
+{%-   else %}
+  {{kind|cpp_field_type}} {{name}};
+{%-   endif %}
+{%-   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.versions[-1].num_fields %}
+{%- 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 %}
+
+ private:
+  {{class_name}}();
+  ~{{class_name}}() = delete;
+};
+static_assert(sizeof({{class_name}}) == {{struct.versions[-1].num_bytes}},
+              "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..a2e5708
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
@@ -0,0 +1,79 @@
+{%- import "validation_macros.tmpl" as validation_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::ValidationContext* validation_context) {
+  if (!data)
+    return true;
+
+  if (!ValidateStructHeaderAndClaimMemory(data, validation_context))
+    return false;
+
+  // NOTE: The memory backing |object| may be smaller than |sizeof(*object)| if
+  // the message comes from an older version.
+  const {{class_name}}* object = static_cast<const {{class_name}}*>(data);
+
+  static const struct {
+    uint32_t version;
+    uint32_t num_bytes;
+  } kVersionSizes[] = {
+{%- for version in struct.versions -%}
+    { {{version.version}}, {{version.num_bytes}} }{% if not loop.last %}, {% endif -%}
+{%- endfor -%}
+  };
+
+  if (object->header_.version <=
+          kVersionSizes[arraysize(kVersionSizes) - 1].version) {
+    // Scan in reverse order to optimize for more recent versions.
+    for (int i = arraysize(kVersionSizes) - 1; i >= 0; --i) {
+      if (object->header_.version >= kVersionSizes[i].version) {
+        if (object->header_.num_bytes == kVersionSizes[i].num_bytes)
+          break;
+
+        ReportValidationError(
+            validation_context,
+            mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+        return false;
+      }
+    }
+  } else if (object->header_.num_bytes <
+                 kVersionSizes[arraysize(kVersionSizes) - 1].num_bytes) {
+    ReportValidationError(
+        validation_context,
+        mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+    return false;
+  }
+
+{#- Before validating fields introduced at a certain version, we need to add
+    a version check, which makes sure we skip further validation if |object|
+    is from an earlier version. |last_checked_version| records the last
+    version that we have added such version check. #}
+{%- set last_checked_version = 0 %}
+{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %}
+{%-   set kind = packed_field.field.kind %}
+{%-   if kind|is_object_kind or kind|is_any_handle_or_interface_kind or
+         kind|is_enum_kind %}
+{%-     if packed_field.min_version > last_checked_version %}
+{%-       set last_checked_version = packed_field.min_version %}
+  if (object->header_.version < {{packed_field.min_version}})
+    return true;
+{%-     endif %}
+{%-     set field_expr = "object->" ~ packed_field.field.name %}
+{{validation_macros.validate_field(packed_field.field, field_expr, struct.name, true)}}
+{%-   endif %}
+{%- endfor %}
+
+  return true;
+}
+
+{{class_name}}::{{class_name}}() {
+  header_.num_bytes = sizeof(*this);
+  header_.version = {{struct.versions[-1].version}};
+}
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..c59f317
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -0,0 +1,157 @@
+{#  TODO(yzshen): Make these templates more readable. #}
+
+{#  Computes the serialized size for the specified struct.
+    |struct| is the struct definition.
+    |input_field_pattern| should be a pattern that contains one string
+    placeholder, for example, "input->%s", "p_%s". The placeholder will be
+    substituted with struct field names to refer to the input fields.
+    |context| is the name of the serialization context.
+    |input_may_be_temp| indicates whether any input may be temporary obejcts.
+    We need to assign temporary objects to local variables before passing it to
+    Serializer, because it is illegal to pass temporary objects as non-const
+    references.
+    This macro is expanded to compute seriailized size for both:
+    - user-defined structs: the input is an instance of the corresponding struct
+      wrapper class.
+    - method parameters/response parameters: the input is a list of
+      arguments.
+    It declares |size| of type size_t to store the resulting size. #}
+{%- macro get_serialized_size(struct, input_field_pattern, context,
+                              input_may_be_temp=False) -%}
+  size_t size = sizeof({{struct|get_qualified_name_for_kind(internal=True)}});
+{%-   for pf in struct.packed.packed_fields_in_ordinal_order if pf.field.kind|is_object_kind %}
+{%-     set name = pf.field.name -%}
+{%-     set kind = pf.field.kind -%}
+{%-     set original_input_field = input_field_pattern|format(name) %}
+{%-     set input_field = "in_%s"|format(name) if input_may_be_temp
+                                               else original_input_field %}
+{%-     if input_may_be_temp %}
+  decltype({{original_input_field}}) in_{{name}} = {{original_input_field}};
+{%-     endif %}
+
+{%-     set serializer_type = kind|unmapped_type_for_serializer %}
+{%-     if kind|is_union_kind %}
+  size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+      {{input_field}}, true, {{context}});
+{%-     else %}
+  size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+      {{input_field}}, {{context}});
+{%-     endif %}
+{%-   endfor %}
+{%- endmacro -%}
+
+{#  Serializes the specified struct.
+    |struct| is the struct definition.
+    |struct_display_name| is the display name for the struct that can be showed
+    in error/log messages, for example, "FooStruct", "FooMethod request".
+    |input_field_pattern| should be a pattern that contains one string
+    placeholder, for example, "input->%s", "p_%s". The placeholder will be
+    substituted with struct field names to refer to the input fields.
+    |output| is the name of the output struct instance.
+    |buffer| is the name of the Buffer instance used.
+    |context| is the name of the serialization context.
+    |input_may_be_temp|: please see the comments of get_serialized_size.
+    This macro is expanded to do serialization for both:
+    - user-defined structs: the input is an instance of the corresponding struct
+      wrapper class.
+    - method parameters/response parameters: the input is a list of
+      arguments. #}
+{%- macro serialize(struct, struct_display_name, input_field_pattern, output,
+                    buffer, context, input_may_be_temp=False) -%}
+  auto {{output}} =
+      {{struct|get_qualified_name_for_kind(internal=True)}}::New({{buffer}});
+  ALLOW_UNUSED_LOCAL({{output}});
+{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%-   set input_field = input_field_pattern|format(pf.field.name) %}
+{%-   set name = pf.field.name %}
+{%-   set kind = pf.field.kind %}
+{%-   set serializer_type = kind|unmapped_type_for_serializer %}
+
+{%-   if kind|is_object_kind or kind|is_any_handle_or_interface_kind %}
+{%-     set original_input_field = input_field_pattern|format(name) %}
+{%-     set input_field = "in_%s"|format(name) if input_may_be_temp
+                                               else original_input_field %}
+{%-     if input_may_be_temp %}
+  decltype({{original_input_field}}) in_{{name}} = {{original_input_field}};
+{%-     endif %}
+{%-   endif %}
+
+{%-   if kind|is_object_kind %}
+{%-     if kind|is_array_kind or kind|is_map_kind %}
+  typename decltype({{output}}->{{name}})::BaseType* {{name}}_ptr;
+  const mojo::internal::ContainerValidateParams {{name}}_validate_params(
+      {{kind|get_container_validate_params_ctor_args|indent(10)}});
+  mojo::internal::Serialize<{{serializer_type}}>(
+      {{input_field}}, {{buffer}}, &{{name}}_ptr, &{{name}}_validate_params,
+      {{context}});
+  {{output}}->{{name}}.Set({{name}}_ptr);
+{%-     elif kind|is_union_kind %}
+  auto {{name}}_ptr = &{{output}}->{{name}};
+  mojo::internal::Serialize<{{serializer_type}}>(
+      {{input_field}}, {{buffer}}, &{{name}}_ptr, true, {{context}});
+{%-     else %}
+  typename decltype({{output}}->{{name}})::BaseType* {{name}}_ptr;
+  mojo::internal::Serialize<{{serializer_type}}>(
+      {{input_field}}, {{buffer}}, &{{name}}_ptr, {{context}});
+  {{output}}->{{name}}.Set({{name}}_ptr);
+{%-     endif %}
+{%-     if not kind|is_nullable_kind %}
+  MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+      {{output}}->{{name}}.is_null(),
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+      "null {{name}} in {{struct_display_name}}");
+{%-     endif %}
+
+{%-   elif kind|is_any_handle_or_interface_kind %}
+  mojo::internal::Serialize<{{serializer_type}}>(
+      {{input_field}}, &{{output}}->{{name}}, {{context}});
+{%-     if not kind|is_nullable_kind %}
+  MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+      !mojo::internal::IsHandleOrInterfaceValid({{output}}->{{name}}),
+{%-       if kind|is_associated_kind %}
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
+{%-       else %}
+      mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+{%-       endif %}
+      "invalid {{name}} in {{struct_display_name}}");
+{%-     endif %}
+
+{%-   elif kind|is_enum_kind %}
+  mojo::internal::Serialize<{{serializer_type}}>(
+      {{input_field}}, &{{output}}->{{name}});
+
+{%-   else %}
+  {{output}}->{{name}} = {{input_field}};
+{%-   endif %}
+{%- endfor %}
+{%- endmacro -%}
+
+{#  Deserializes the specified struct.
+    |struct| is the struct definition.
+    |input| is the name of the input struct data view. It is expected to be
+    non-null.
+    |output_field_pattern| should be a pattern that contains one string
+    placeholder, for example, "result->%s", "p_%s". The placeholder will be
+    substituted with struct field names to refer to the output fields.
+    |context| is the name of the serialization context.
+    |success| is the name of a bool variable to track success of the operation.
+    This macro is expanded to do deserialization for both:
+    - user-defined structs: the output is an instance of the corresponding
+      struct wrapper class.
+    - method parameters/response parameters: the output is a list of
+      arguments. #}
+{%- macro deserialize(struct, input, output_field_pattern, success) -%}
+{%-   for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%-     set output_field = output_field_pattern|format(pf.field.name) %}
+{%-     set name = pf.field.name %}
+{%-     set kind = pf.field.kind %}
+{%-     if kind|is_object_kind or kind|is_enum_kind %}
+  if (!{{input}}.Read{{name|under_to_camel}}(&{{output_field}}))
+    {{success}} = false;
+{%-     elif kind|is_any_handle_or_interface_kind %}
+  {{output_field}} = {{input}}.Take{{name|under_to_camel}}();
+{%-     else %}
+  {{output_field}} = {{input}}.{{name}}();
+{%-     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..aede1a7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
@@ -0,0 +1,81 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set mojom_type = struct|get_qualified_name_for_kind %}
+{%- set data_type = struct|get_qualified_name_for_kind(internal=True) %}
+
+template <>
+struct StructTraits<{{mojom_type}}, {{mojom_type}}Ptr> {
+  static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; }
+  static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); }
+
+{%- for field in struct.fields %}
+{%-   set return_ref = field.kind|is_object_kind or
+                       field.kind|is_any_handle_or_interface_kind %}
+{%-   if return_ref %}
+  static decltype({{mojom_type}}::{{field.name}})& {{field.name}}(
+      {{mojom_type}}Ptr& input) {
+    return input->{{field.name}};
+  }
+{%-   else %}
+  static decltype({{mojom_type}}::{{field.name}}) {{field.name}}(
+      const {{mojom_type}}Ptr& input) {
+    return input->{{field.name}};
+  }
+{%-   endif %}
+{%- endfor %}
+
+  static bool Read({{mojom_type}}DataView input, {{mojom_type}}Ptr* output);
+};
+
+namespace internal {
+
+template <typename MaybeConstUserType>
+struct Serializer<{{mojom_type}}Ptr, MaybeConstUserType> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+  using Traits = StructTraits<{{mojom_type}}, UserType>;
+
+  static size_t PrepareToSerialize(MaybeConstUserType& input,
+                                   SerializationContext* context) {
+    if (CallIsNullIfExists<Traits>(input))
+      return 0;
+
+    void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
+    ALLOW_UNUSED_LOCAL(custom_context);
+
+    {{struct_macros.get_serialized_size(
+          struct, "CallWithContext(Traits::%s, input, custom_context)",
+          "context", True)|indent(2)}}
+    return size;
+  }
+
+  static void Serialize(MaybeConstUserType& input,
+                        Buffer* buffer,
+                        {{data_type}}** output,
+                        SerializationContext* context) {
+    if (CallIsNullIfExists<Traits>(input)) {
+      *output = nullptr;
+      return;
+    }
+
+    void* custom_context = CustomContextHelper<Traits>::GetNext(context);
+
+    {{struct_macros.serialize(
+          struct, struct.name ~ " struct",
+          "CallWithContext(Traits::%s, input, custom_context)", "result",
+          "buffer", "context", True)|indent(4)}}
+    *output = result;
+
+    CustomContextHelper<Traits>::TearDown(input, custom_context);
+  }
+
+  static bool Deserialize({{data_type}}* input,
+                          UserType* output,
+                          SerializationContext* context) {
+    if (!input)
+      return CallSetToNullIfExists<Traits>(output);
+
+    {{mojom_type}}DataView data_view(input, context);
+    return Traits::Read(data_view, output);
+  }
+};
+
+}  // namespace internal
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..7421abc
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
@@ -0,0 +1,14 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set mojom_type = struct|get_qualified_name_for_kind %}
+
+// static
+bool StructTraits<{{mojom_type}}, {{mojom_type}}Ptr>::Read(
+    {{mojom_type}}DataView input,
+    {{mojom_type}}Ptr* output) {
+  bool success = true;
+  {{mojom_type}}Ptr result({{mojom_type}}::New());
+  {{struct_macros.deserialize(struct, "input", "result->%s",
+                              "success")|indent(4)}}
+  *output = std::move(result);
+  return success;
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
new file mode 100644
index 0000000..be2db4c
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
@@ -0,0 +1,49 @@
+{%- set class_name = union.name ~ "_Data" -%}
+{%- set enum_name = union.name ~ "_Tag" -%}
+{%- import "struct_macros.tmpl" as struct_macros %}
+
+class {{class_name}} {
+ public:
+  // Used to identify Mojom Union Data Classes.
+  typedef void MojomUnionDataType;
+  static {{class_name}}* New(mojo::internal::Buffer* buf);
+  {{class_name}}();
+  // Do nothing in the destructor since it won't be called.
+  ~{{class_name}}() {}
+
+  static bool Validate(const void* data,
+                       mojo::internal::ValidationContext* validation_context,
+                       bool inlined);
+
+  bool is_null() const {
+    return size == 0;
+  }
+
+  void set_null();
+
+  enum class {{enum_name}} : uint32_t {
+{%  for field in union.fields %}
+    {{field.name|upper}},
+{%- endfor %}
+  };
+
+  // A note on layout:
+  // "Each non-static data member is allocated as if it were the sole member of
+  // a struct." - Section 9.5.2 ISO/IEC 14882:2011 (The C++ Spec)
+  union MOJO_ALIGNAS(8) Union_ {
+{%- for field in union.fields %}
+{%-   if field.kind.spec == 'b' %}
+    uint8_t f_{{field.name}} : 1;
+{%-   else %}
+    {{field.kind|cpp_union_field_type}} f_{{field.name}};
+{%-   endif %}
+{%- endfor %}
+    uint64_t unknown;
+  };
+
+  uint32_t size;
+  {{enum_name}} tag;
+  Union_ data;
+};
+static_assert(sizeof({{class_name}}) == mojo::internal::kUnionDataSize,
+              "Bad sizeof({{class_name}})");
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
new file mode 100644
index 0000000..fc6eddf
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
@@ -0,0 +1,50 @@
+{%- import "validation_macros.tmpl" as validation_macros %}
+{%- set class_name = union.name ~ "_Data" %}
+{%- set enum_name = union.name ~ "_Tag" -%}
+
+// 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::ValidationContext* validation_context,
+    bool inlined) {
+  if (!data)
+    return true;
+
+  if (!ValidateUnionHeaderAndClaimMemory(data, inlined, validation_context))
+    return false;
+
+  const {{class_name}}* object = static_cast<const {{class_name}}*>(data);
+  ALLOW_UNUSED_LOCAL(object);
+
+  switch (object->tag) {
+{%  for field in union.fields %}
+    case {{enum_name}}::{{field.name|upper}}: {
+{%-   set field_expr = "object->data.f_" ~ field.name %}
+{{validation_macros.validate_field(field, field_expr, union.name, false)|indent(4)}}
+      return true;
+    }
+{%- endfor %}
+    default: {
+      ReportValidationError(
+          validation_context,
+          mojo::internal::VALIDATION_ERROR_UNKNOWN_UNION_TAG,
+          "unknown tag in {{union.name}}");
+      return false;
+    }
+  }
+}
+
+void {{class_name}}::set_null() {
+  size = 0U;
+  tag = static_cast<{{enum_name}}>(0);
+  data.unknown = 0U;
+}
+
+{{class_name}}::{{class_name}}() {
+}
+
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
new file mode 100644
index 0000000..bab9045
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
@@ -0,0 +1,37 @@
+{%- set mojom_type = union|get_qualified_name_for_kind %}
+{%- set data_type = union|get_qualified_name_for_kind(internal=True) %}
+
+namespace internal {
+
+template <typename MojomType>
+struct UnionSerializerImpl;
+
+template <>
+struct UnionSerializerImpl<{{mojom_type}}Ptr> {
+  static size_t PrepareToSerialize({{mojom_type}}Ptr& input,
+                                   bool inlined,
+                                   SerializationContext* context);
+
+  static void Serialize({{mojom_type}}Ptr& input,
+                        Buffer* buffer,
+                        {{data_type}}** output,
+                        bool inlined,
+                        SerializationContext* context);
+
+  static bool Deserialize({{data_type}}* input,
+                          {{mojom_type}}Ptr* output,
+                          SerializationContext* context);
+};
+
+template <typename MaybeConstUserType>
+struct Serializer<{{mojom_type}}Ptr, MaybeConstUserType>
+    : public UnionSerializerImpl<{{mojom_type}}Ptr> {
+  using UserType = typename std::remove_const<MaybeConstUserType>::type;
+
+  static_assert(std::is_same<MaybeConstUserType, UserType>::value,
+                "Only support serialization of non-const Unions.");
+  static_assert(std::is_same<UserType, {{mojom_type}}Ptr>::value,
+                "Custom mapping of mojom union is not supported.");
+};
+
+}  // namespace internal
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl
new file mode 100644
index 0000000..5a11ff7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl
@@ -0,0 +1,164 @@
+{%- set mojom_type = union|get_qualified_name_for_kind %}
+{%- set data_type = union|get_qualified_name_for_kind(internal=True) %}
+
+namespace internal {
+
+// static
+size_t UnionSerializerImpl<{{mojom_type}}Ptr>::PrepareToSerialize(
+    {{mojom_type}}Ptr& input,
+    bool inlined,
+    SerializationContext* context) {
+  size_t size = inlined ? 0 : sizeof({{data_type}});
+
+  if (!input)
+    return size;
+
+  UnionAccessor<{{mojom_type}}> input_acc(input.get());
+  switch (input->which()) {
+{%  for field in union.fields %}
+{%    if field.kind|is_object_kind %}
+{%-     set serializer_type = field.kind|unmapped_type_for_serializer %}
+    case {{mojom_type}}::Tag::{{field.name|upper}}:
+{%      if field.kind|is_union_kind %}
+      size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+          *(input_acc.data()->{{field.name}}), false, context);
+{%      else %}
+      size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+          *(input_acc.data()->{{field.name}}), context);
+{%      endif %}
+      break;
+{%-   endif %}
+{%- endfor %}
+    default:
+      break;
+  }
+  return size;
+}
+
+// static
+void UnionSerializerImpl<{{mojom_type}}Ptr>::Serialize(
+    {{mojom_type}}Ptr& input,
+    Buffer* buf,
+    {{data_type}}** output,
+    bool inlined,
+    SerializationContext* context) {
+  {{data_type}}* result = *output;
+  if (input) {
+    if (!inlined)
+      result = {{data_type}}::New(buf);
+    UnionAccessor<{{mojom_type}}> input_acc(input.get());
+    // TODO(azani): Handle unknown and objects.
+    // Set the not-null flag.
+    result->size = 16;
+    result->tag = input->which();
+    switch (input->which()) {
+{%- for field in union.fields %}
+      case {{mojom_type}}::Tag::{{field.name|upper}}: {
+{%-   set serializer_type = field.kind|unmapped_type_for_serializer %}
+{%-   if field.kind|is_object_kind %}
+        typename decltype(result->data.f_{{field.name}})::BaseType* ptr;
+{%-     if field.kind|is_union_kind %}
+        mojo::internal::Serialize<{{serializer_type}}>(
+            *(input_acc.data()->{{field.name}}), buf, &ptr, false, context);
+{%-     elif field.kind|is_array_kind or field.kind|is_map_kind %}
+        const ContainerValidateParams {{field.name}}_validate_params(
+            {{field.kind|get_container_validate_params_ctor_args|indent(16)}});
+        mojo::internal::Serialize<{{serializer_type}}>(
+            *(input_acc.data()->{{field.name}}), buf, &ptr,
+            &{{field.name}}_validate_params, context);
+{%-     else %}
+        mojo::internal::Serialize<{{serializer_type}}>(
+            *(input_acc.data()->{{field.name}}), buf, &ptr, context);
+{%-     endif %}
+        result->data.f_{{field.name}}.Set(ptr);
+{%-     if not field.kind|is_nullable_kind %}
+        MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+            !ptr, mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+            "null {{field.name}} in {{union.name}} union");
+{%-     endif %}
+
+{%-   elif field.kind|is_any_handle_or_interface_kind %}
+        mojo::internal::Serialize<{{serializer_type}}>(
+            *input_acc.data()->{{field.name}}, &result->data.f_{{field.name}},
+            context);
+{%-     if not field.kind|is_nullable_kind %}
+        MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+            !mojo::internal::IsHandleOrInterfaceValid(result->data.f_{{field.name}}),
+{%-       if field.kind|is_associated_kind %}
+            mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
+{%-       else %}
+            mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+{%-       endif %}
+            "invalid {{field.name}} in {{union.name}} union");
+{%-     endif %}
+
+{%-   elif field.kind|is_enum_kind %}
+        mojo::internal::Serialize<{{serializer_type}}>(
+            input_acc.data()->{{field.name}}, &result->data.f_{{field.name}});
+
+{%-   else %}
+        result->data.f_{{field.name}} = input_acc.data()->{{field.name}};
+{%-   endif %}
+        break;
+      }
+{%- endfor %}
+    }
+  } else if (inlined) {
+    result->set_null();
+  } else {
+    result = nullptr;
+  }
+  *output = result;
+}
+
+// static
+bool UnionSerializerImpl<{{mojom_type}}Ptr>::Deserialize(
+    {{data_type}}* input,
+    {{mojom_type}}Ptr* output,
+    SerializationContext* context) {
+  bool success = true;
+  if (input && !input->is_null()) {
+    {{mojom_type}}Ptr result({{mojom_type}}::New());
+    UnionAccessor<{{mojom_type}}> result_acc(result.get());
+    switch (input->tag) {
+{%- for field in union.fields %}
+      case {{mojom_type}}::Tag::{{field.name|upper}}: {
+{%-   set serializer_type = field.kind|unmapped_type_for_serializer %}
+{%-   if field.kind|is_object_kind %}
+        result_acc.SwitchActive({{mojom_type}}::Tag::{{field.name|upper}});
+        if (!mojo::internal::Deserialize<{{serializer_type}}>(
+                input->data.f_{{field.name}}.Get(),
+                result_acc.data()->{{field.name}}, context))
+          success = false;
+
+{%-   elif field.kind|is_any_handle_or_interface_kind %}
+        typename std::remove_reference<
+            decltype(result->get_{{field.name}}())>::type result_{{field.name}};
+        bool ret = mojo::internal::Deserialize<{{serializer_type}}>(
+            &input->data.f_{{field.name}}, &result_{{field.name}}, context);
+        DCHECK(ret);
+        result->set_{{field.name}}(std::move(result_{{field.name}}));
+
+{%-   elif field.kind|is_enum_kind %}
+        decltype(result->get_{{field.name}}()) result_{{field.name}};
+        if (!mojo::internal::Deserialize<{{serializer_type}}>(
+                input->data.f_{{field.name}}, &result_{{field.name}}))
+           success = false;
+        else
+           result->set_{{field.name}}(result_{{field.name}});
+
+{%-   else %}
+        result->set_{{field.name}}(input->data.f_{{field.name}});
+{%-   endif %}
+        break;
+      }
+{%- endfor %}
+    }
+    *output = std::move(result);
+  } else {
+    output->reset();
+  }
+  return success;
+}
+
+}  // namespace internal
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
new file mode 100644
index 0000000..367be6d
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
@@ -0,0 +1,82 @@
+{#- Validates the specified field, which is supposed to be an object
+    (struct/array/string/map/union). If it is a union, |union_is_inlined|
+    indicates whether the union is inlined. (Nested unions are not inlined.)
+    This macro is expanded by the Validate() method. #}
+{%- macro validate_object(field, field_expr, object_name, union_is_inlined) %}
+{%-   set name = field.name %}
+{%-   set kind = field.kind %}
+{%-   if not kind|is_nullable_kind %}
+{%-     if kind|is_union_kind and union_is_inlined %}
+  if (!mojo::internal::ValidateInlinedUnionNonNullable(
+          {{field_expr}}, "null {{name}} field in {{object_name}}",
+          validation_context)) {
+    return false;
+  }
+{%-     else %}
+  if (!mojo::internal::ValidatePointerNonNullable(
+          {{field_expr}}, "null {{name}} field in {{object_name}}",
+          validation_context)) {
+    return false;
+  }
+{%-     endif %}
+{%-   endif %}
+{%-   if kind|is_array_kind or kind|is_string_kind or kind|is_map_kind %}
+  const mojo::internal::ContainerValidateParams {{name}}_validate_params(
+      {{kind|get_container_validate_params_ctor_args|indent(6)}});
+  if (!mojo::internal::ValidateContainer({{field_expr}}, validation_context,
+                                         &{{name}}_validate_params)) {
+    return false;
+  }
+{%-   elif kind|is_struct_kind %}
+  if (!mojo::internal::ValidateStruct({{field_expr}}, validation_context))
+    return false;
+{%-   elif kind|is_union_kind %}
+{%-     if union_is_inlined %}
+  if (!mojo::internal::ValidateInlinedUnion({{field_expr}}, validation_context))
+    return false;
+{%-     else %}
+  if (!mojo::internal::ValidateNonInlinedUnion({{field_expr}},
+                                               validation_context))
+    return false;
+{%-     endif %}
+{%-   else %}
+#error Not reached!
+{%-   endif %}
+{%- endmacro %}
+
+{#- Validates the specified field, which is supposed to be a handle,
+    an interface, an associated interface or an associated interface request.
+    This macro is expanded by the Validate() method. #}
+{%- macro validate_handle_or_interface(field, field_expr, object_name) %}
+{%-   set name = field.name %}
+{%-   set kind = field.kind %}
+{%-   if not kind|is_nullable_kind %}
+  if (!mojo::internal::ValidateHandleOrInterfaceNonNullable(
+          {{field_expr}},
+          "invalid {{name}} field in {{object_name}}", validation_context)) {
+    return false;
+  }
+{%-   endif %}
+  if (!mojo::internal::ValidateHandleOrInterface({{field_expr}},
+                                                 validation_context)) {
+    return false;
+  }
+{%- endmacro %}
+
+{#- Validates the specified field, which is supposed to be an enum.
+    This macro is expanded by the Validate() method. #}
+{%- macro validate_enum(field, field_expr) %}
+  if (!{{field.kind|get_qualified_name_for_kind(internal=True)}}
+        ::Validate({{field_expr}}, validation_context))
+    return false;
+{%- endmacro %}
+
+{%- macro validate_field(field, field_expr, object_name, union_is_inlined) %}
+{%-   if field.kind|is_object_kind -%}
+{{validate_object(field, field_expr, object_name, union_is_inlined)}}
+{%-   elif field.kind|is_any_handle_or_interface_kind -%}
+{{validate_handle_or_interface(field, field_expr, object_name)}}
+{%-   elif field.kind|is_enum_kind %}
+{{validate_enum(field, field_expr)}}
+{%-   endif %}
+{%- endmacro %}
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..98ae23c
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -0,0 +1,79 @@
+{% from "enum_macros.tmpl" import enum_decl -%}
+
+class {{struct.name}} {
+ public:
+  using DataView = {{struct.name}}DataView;
+  using Data_ = internal::{{struct.name}}_Data;
+
+{#--- Enums #}
+{%- for enum in struct.enums -%}
+{%-   if enum|is_native_only_kind %}
+  using {{enum.name}} = mojo::NativeEnum;
+{%-   else %}
+  {{enum_decl(enum)|indent(2)}}
+{%-   endif %}
+{%- endfor %}
+
+{#--- Constants #}
+{%- for constant in struct.constants %}
+{%-   if constant.kind|is_integral_kind %}
+  static const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%-   else %}
+  static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%-   endif %}
+{%- 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);
+  }
+
+  template <typename U>
+  U To() const {
+    return mojo::TypeConverter<U, {{struct.name}}>::Convert(*this);
+  }
+
+  {{struct.name}}();
+  ~{{struct.name}}();
+
+  // Clone() is a template so it is only instantiated if it is used. Thus, the
+  // bindings generator does not need to know whether Clone() or copy
+  // constructor/assignment are available for members.
+  template <typename StructPtrType = {{struct.name}}Ptr>
+  {{struct.name}}Ptr Clone() const;
+
+  // Equals() is a template so it is only instantiated if it is used. Thus, the
+  // bindings generator does not need to know whether Equals() or == operator
+  // are available for members.
+  template <typename T,
+            typename std::enable_if<std::is_same<
+                T, {{struct.name}}>::value>::type* = nullptr>
+  bool Equals(const T& other) const;
+
+{%- set serialization_result_type = "mojo::WTFArray<uint8_t>"
+        if for_blink else "mojo::Array<uint8_t>" %}
+
+  template <typename UserType>
+  static {{serialization_result_type}} Serialize(UserType* input) {
+    return mojo::internal::StructSerializeImpl<
+        {{struct.name}}Ptr, {{serialization_result_type}}>(input);
+  }
+
+  template <typename UserType>
+  static bool Deserialize(const {{serialization_result_type}}& input,
+                          UserType* output) {
+    return mojo::internal::StructDeserializeImpl<
+        {{struct.name}}Ptr, {{serialization_result_type}}>(
+            input, output);
+  }
+
+{#--- Struct members #}
+{%  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..0bb1cda
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
@@ -0,0 +1,15 @@
+// static
+{{struct.name}}Ptr {{struct.name}}::New() {
+  {{struct.name}}Ptr rv;
+  mojo::internal::StructHelper<{{struct.name}}>::Initialize(&rv);
+  return rv;
+}
+
+{{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}}() {
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
new file mode 100644
index 0000000..f4aa314
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
@@ -0,0 +1,21 @@
+template <typename StructPtrType>
+{{struct.name}}Ptr {{struct.name}}::Clone() const {
+  // Use StructPtrType to prevent the compiler from trying to compile this
+  // without being asked.
+  StructPtrType rv(New());
+{%- for field in struct.fields %}
+  rv->{{field.name}} = mojo::internal::Clone({{field.name}});
+{%- endfor %}
+  return rv;
+}
+
+template <typename T,
+          typename std::enable_if<std::is_same<
+              T, {{struct.name}}>::value>::type*>
+bool {{struct.name}}::Equals(const T& other) const {
+{%- for field in struct.fields %}
+  if (!mojo::internal::Equals(this->{{field.name}}, other.{{field.name}}))
+    return false;
+{%- endfor %}
+  return true;
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
new file mode 100644
index 0000000..f62bc72
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
@@ -0,0 +1,65 @@
+class {{union.name}} {
+ public:
+  using Data_ = internal::{{union.name}}_Data;
+  using Tag = Data_::{{union.name}}_Tag;
+
+  static {{union.name}}Ptr New();
+
+  template <typename U>
+  static {{union.name}}Ptr From(const U& u) {
+    return mojo::TypeConverter<{{union.name}}Ptr, U>::Convert(u);
+  }
+
+  template <typename U>
+  U To() const {
+    return mojo::TypeConverter<U, {{union.name}}>::Convert(*this);
+  }
+
+  {{union.name}}();
+  ~{{union.name}}();
+
+  // Clone() is a template so it is only instantiated if it is used. Thus, the
+  // bindings generator does not need to know whether Clone() or copy
+  // constructor/assignment are available for members.
+  template <typename UnionPtrType = {{union.name}}Ptr>
+  {{union.name}}Ptr Clone() const;
+
+  // Equals() is a template so it is only instantiated if it is used. Thus, the
+  // bindings generator does not need to know whether Equals() or == operator
+  // are available for members.
+  template <typename T,
+            typename std::enable_if<std::is_same<
+                T, {{union.name}}>::value>::type* = nullptr>
+  bool Equals(const T& other) const;
+
+  Tag which() const {
+    return tag_;
+  }
+
+{%  for field in union.fields %}
+  bool is_{{field.name}}() const;
+  {{field.kind|cpp_union_getter_return_type}} get_{{field.name}}() const;
+  void set_{{field.name}}({{field.kind|cpp_wrapper_param_type}} {{field.name}});
+{%- endfor %}
+
+ private:
+  friend class mojo::internal::UnionAccessor<{{union.name}}>;
+  union Union_ {
+    Union_() {}
+    ~Union_() {}
+
+{%- for field in union.fields %}
+{%-   if field.kind|is_object_kind or
+         field.kind|is_any_handle_or_interface_kind %}
+    {{field.kind|cpp_wrapper_type}}* {{field.name}};
+{%-   else %}
+    {{field.kind|cpp_wrapper_type}} {{field.name}};
+{%-   endif %}
+{%- endfor %}
+  };
+  void SwitchActive(Tag new_active);
+  void SetActive(Tag new_active);
+  void DestroyActive();
+  Tag tag_;
+  Union_ data_;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
new file mode 100644
index 0000000..85cc4e6
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
@@ -0,0 +1,81 @@
+// static
+{{union.name}}Ptr {{union.name}}::New() {
+  {{union.name}}Ptr rv;
+  mojo::internal::StructHelper<{{union.name}}>::Initialize(&rv);
+  return rv;
+}
+
+{{union.name}}::{{union.name}}() {
+  // TODO(azani): Implement default values here when/if we support them.
+  // TODO(azani): Set to UNKNOWN when unknown is implemented.
+  SetActive(static_cast<Tag>(0));
+}
+
+{{union.name}}::~{{union.name}}() {
+  DestroyActive();
+}
+
+{%  for field in union.fields %}
+bool {{union.name}}::is_{{field.name}}() const {
+  return tag_ == Tag::{{field.name|upper}};
+}
+
+{{field.kind|cpp_union_getter_return_type}} {{union.name}}::get_{{field.name}}() const {
+  DCHECK(tag_ == Tag::{{field.name|upper}});
+{%    if field.kind|is_object_kind or
+         field.kind|is_any_handle_or_interface_kind %}
+  return *(data_.{{field.name}});
+{%-   else %}
+  return data_.{{field.name}};
+{%-   endif %}
+}
+
+void {{union.name}}::set_{{field.name}}({{field.kind|cpp_wrapper_param_type}} {{field.name}}) {
+  SwitchActive(Tag::{{field.name|upper}});
+{%    if field.kind|is_string_kind %}
+  *(data_.{{field.name}}) = {{field.name}};
+{%    elif field.kind|is_object_kind or
+           field.kind|is_any_handle_or_interface_kind %}
+  *(data_.{{field.name}}) = std::move({{field.name}});
+{%-   else %}
+  data_.{{field.name}} = {{field.name}};
+{%-   endif %}
+}
+{%- endfor %}
+
+void {{union.name}}::SwitchActive(Tag new_active) {
+  if (new_active == tag_) {
+    return;
+  }
+
+  DestroyActive();
+  SetActive(new_active);
+}
+
+void {{union.name}}::SetActive(Tag new_active) {
+  switch (new_active) {
+{%  for field in union.fields %}
+    case Tag::{{field.name|upper}}:
+{%    if field.kind|is_object_kind or
+         field.kind|is_any_handle_or_interface_kind %}
+      data_.{{field.name}} = new {{field.kind|cpp_wrapper_type}}();
+{%-   endif %}
+      break;
+{%- endfor %}
+  }
+
+  tag_ = new_active;
+}
+
+void {{union.name}}::DestroyActive() {
+  switch (tag_) {
+{%  for field in union.fields %}
+    case Tag::{{field.name|upper}}:
+{%    if field.kind|is_object_kind or
+         field.kind|is_any_handle_or_interface_kind %}
+      delete data_.{{field.name}};
+{%-   endif %}
+      break;
+{%- endfor %}
+  }
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
new file mode 100644
index 0000000..d3371d9
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
@@ -0,0 +1,41 @@
+template <typename UnionPtrType>
+{{union.name}}Ptr {{union.name}}::Clone() const {
+  // Use UnionPtrType to prevent the compiler from trying to compile this
+  // without being asked.
+  UnionPtrType rv(New());
+  switch (tag_) {
+{%- for field in union.fields %}
+    case Tag::{{field.name|upper}}:
+{%-   if field.kind|is_object_kind or
+         field.kind|is_any_handle_or_interface_kind %}
+      rv->set_{{field.name}}(mojo::internal::Clone(*data_.{{field.name}}));
+{%-   else %}
+      rv->set_{{field.name}}(mojo::internal::Clone(data_.{{field.name}}));
+{%-   endif %}
+      break;
+{%- endfor %}
+  };
+  return rv;
+}
+
+template <typename T,
+          typename std::enable_if<std::is_same<
+              T, {{union.name}}>::value>::type*>
+bool {{union.name}}::Equals(const T& other) const {
+  if (tag_ != other.which())
+    return false;
+
+  switch (tag_) {
+{%- for field in union.fields %}
+    case Tag::{{field.name|upper}}:
+{%-   if field.kind|is_object_kind or
+         field.kind|is_any_handle_or_interface_kind %}
+      return mojo::internal::Equals(*(data_.{{field.name}}), *(other.data_.{{field.name}}));
+{%-   else %}
+      return mojo::internal::Equals(data_.{{field.name}}, other.data_.{{field.name}});
+{%-   endif %}
+{%- endfor %}
+  };
+
+  return false;
+}
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/data_types_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
new file mode 100644
index 0000000..5cf9a68
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
@@ -0,0 +1,399 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% from "enum_definition.tmpl" import enum_def %}
+
+{%- macro equality(kind, v1, v2, ne=False) -%}
+{%-   if kind|is_reference_kind -%}
+{%-     if kind|is_array_kind -%}
+{%-       if kind.kind|is_reference_kind -%}
+{% if ne %}!{% endif %}java.util.Arrays.deepEquals({{v1}}, {{v2}})
+{%-       else -%}
+{% if ne %}!{% endif %}java.util.Arrays.equals({{v1}}, {{v2}})
+{%-       endif -%}
+{%-     else -%}
+{% if ne %}!{% endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}})
+{%-     endif -%}
+{%-   else -%}
+{{v1}} {% if ne %}!={% else %}=={% endif %} {{v2}}
+{%-   endif -%}
+{%- endmacro -%}
+
+{%- macro hash(kind, v) -%}
+{%-   if kind|is_array_kind -%}
+{%-     if kind.kind|is_reference_kind -%}
+java.util.Arrays.deepHashCode({{v}})
+{%-     else -%}
+java.util.Arrays.hashCode({{v}})
+{%-     endif -%}
+{%-   else -%}
+org.chromium.mojo.bindings.BindingsHelper.hashCode({{v}})
+{%-   endif -%}
+{%- endmacro -%}
+
+{%- macro array_element_size(kind) -%}
+{%-  if kind|is_union_kind %}
+org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE
+{%-  else -%}
+org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE
+{%-  endif -%}
+{%- endmacro -%}
+
+{% macro encode(variable, kind, offset, bit, level=0, check_for_null=True) %}
+{% if kind|is_pointer_array_kind or kind|is_union_array_kind %}
+{% set sub_kind = kind.kind %}
+{%   if check_for_null %}
+if ({{variable}} == null) {
+    encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
+} else {
+{%   else %}
+{
+{%   endif %}
+{%   if kind|is_pointer_array_kind %}
+{%     set encodePointer = 'encodePointerArray' %}
+{%   else %}
+{%     set encodePointer = 'encodeUnionArray' %}
+{%   endif %}
+    org.chromium.mojo.bindings.Encoder encoder{{level + 1}} = encoder{{level}}.{{encodePointer}}({{variable}}.length, {{offset}}, {{kind|array_expected_length}});
+    for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) {
+        {{encode(variable~'[i'~level~']', sub_kind, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + ' ~ array_element_size(sub_kind) ~ ' * i'~level, 0, level+1)|indent(8)}}
+    }
+}
+{% elif kind|is_map_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}}.encoderForMap({{offset}});
+    int size{{level}} = {{variable}}.size();
+    {{kind.key_kind|java_type}}[] keys{{level}} = {{kind.key_kind|array|new_array('size'~level)}};
+    {{kind.value_kind|java_type}}[] values{{level}} = {{kind.value_kind|array|new_array('size'~level)}};
+    int index{{level}} = 0;
+    for (java.util.Map.Entry<{{kind.key_kind|java_type(true)}}, {{kind.value_kind|java_type(true)}}> entry{{level}} : {{variable}}.entrySet()) {
+        keys{{level}}[index{{level}}] = entry{{level}}.getKey();
+        values{{level}}[index{{level}}] = entry{{level}}.getValue();
+        ++index{{level}};
+    }
+    {{encode('keys'~level, kind.key_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0, level+1, False)|indent(4)}}
+    {{encode('values'~level, kind.value_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE', 0, level+1, False)|indent(4)}}
+}
+{% 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 or
+      kind|is_union_array_kind or
+      kind|is_map_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 or is_map_kind #}
+{%     if kind|is_nullable_kind %}
+if (decoder{{level+1}} == null) {
+    {{variable}} = null;
+} else {
+{%     else %}
+{
+{%     endif %}
+{%     if kind|is_map_kind %}
+    decoder{{level+1}}.readDataHeaderForMap();
+    {{kind.key_kind|java_type}}[] keys{{level}};
+    {{kind.value_kind|java_type}}[] values{{level}};
+    {
+        {{decode('keys'~level, kind.key_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0, level+1)|indent(8)}}
+    }
+    {
+        {{decode('values'~level, kind.value_kind|array('keys'~level~'.length'), 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE', 0, level+1)|indent(8)}}
+    }
+    {{variable}} = new java.util.HashMap<{{kind.key_kind|java_type(true)}}, {{kind.value_kind|java_type(true)}}>();
+    for (int index{{level}} = 0; index{{level}} < keys{{level}}.length; ++index{{level}}) {
+        {{variable}}.put(keys{{level}}[index{{level}}],  values{{level}}[index{{level}}]);
+    }
+{%     else %}
+    org.chromium.mojo.bindings.DataHeader si{{level+1}} = decoder{{level+1}}.readDataHeaderForPointerArray({{kind|array_expected_length}});
+    {{variable}} = {{kind|new_array('si'~(level+1)~'.elementsOrVersion')}};
+    for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.elementsOrVersion; ++i{{level+1}}) {
+        {{decode(variable~'[i'~(level+1)~']', kind.kind, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + ' ~ array_element_size(kind.kind) ~' * i'~(level+1), 0, level+1)|indent(8)}}
+    }
+{%     endif %}
+}
+{%   endif %}
+{% elif kind|is_union_kind %}
+{{variable}} = {{kind|java_type}}.decode(decoder{{level}}, {{offset}});
+{% else %}
+{{variable}} = decoder{{level}}.{{kind|decode_method(offset, bit)}};
+{%   if kind|is_array_kind and kind.kind|is_enum_kind %}
+{%     if kind|is_nullable_kind %}
+if ({{variable}} != null) {
+{%     else %}
+{
+{%     endif %}
+    for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) {
+        {{kind.kind|java_class_for_enum}}.validate({{variable}}[i{{level}}]);
+    }
+}
+{%   elif kind|is_enum_kind %}
+    {{kind|java_class_for_enum}}.validate({{variable}});
+{%   endif %}
+{% 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.versions[-1].num_bytes}};
+    private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {
+{%-   for version in struct.versions -%}
+        new org.chromium.mojo.bindings.DataHeader({{version.num_bytes}}, {{version.version}}){% if not loop.last %}, {% endif -%}
+{%-   endfor -%}
+    };
+    private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[{{struct.versions|length - 1}}];
+{%  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 %}
+
+    private {{struct|name}}(int version) {
+        super(STRUCT_SIZE, version);
+{% for field in struct.fields %}
+{%   if field.default %}
+        {{field|name}} = {{field|default_value}};
+{%   elif field.kind|is_any_handle_kind and not field.kind|is_interface_request_kind %}
+        {{field|name}} = org.chromium.mojo.system.InvalidHandle.INSTANCE;
+{%   endif %}
+{% endfor %}
+    }
+
+    public {{struct|name}}() {
+        this({{struct.versions[-1].version}});
+    }
+
+    public static {{struct|name}} deserialize(org.chromium.mojo.bindings.Message message) {
+        return decode(new org.chromium.mojo.bindings.Decoder(message));
+    }
+
+    @SuppressWarnings("unchecked")
+    public static {{struct|name}} decode(org.chromium.mojo.bindings.Decoder decoder0) {
+        if (decoder0 == null) {
+            return null;
+        }
+        org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY);
+        {{struct|name}} result = new {{struct|name}}(mainDataHeader.elementsOrVersion);
+{% for byte in struct.bytes %}
+{%   for packed_field in byte.packed_fields %}
+        if (mainDataHeader.elementsOrVersion >= {{packed_field.min_version}}) {
+            {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(12)}}
+        }
+{%   endfor %}
+{% endfor %}
+        return result;
+    }
+
+    @SuppressWarnings("unchecked")
+    @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 %}
+    }
+
+    /**
+     * @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;
+{% if struct.fields|length %}
+        {{struct|name}} other = ({{struct|name}}) object;
+{%   for field in struct.fields %}
+        if ({{equality(field.kind, 'this.'~field|name, 'other.'~field|name, True)}})
+            return false;
+{%   endfor %}
+{% endif %}
+        return true;
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = prime + getClass().hashCode();
+{% for field in struct.fields %}
+        result = prime * result + {{hash(field.kind, field|name)}};
+{% endfor %}
+        return result;
+    }
+}
+{% endmacro %}
+
+
+{% macro union_def(union) %}
+public final class {{union|name}} extends org.chromium.mojo.bindings.Union {
+
+    public static final class Tag {
+{%   for field in union.fields %}
+        public static final int {{field|ucc}} = {{loop.index0}};
+{%   endfor %}
+    };
+
+    private int mTag_ = -1;
+{%   for field in union.fields %}
+    private {{field.kind|java_type}} m{{field|ucc}};
+{%   endfor %}
+
+    public int which() {
+      return mTag_;
+    }
+
+    public boolean isUnknown() {
+      return mTag_ == -1;
+    }
+{%   for field in union.fields %}
+
+    // TODO(rockot): Fix the findbugs error and remove this suppression.
+    // See http://crbug.com/570386.
+    @SuppressFBWarnings("EI_EXPOSE_REP2")
+    public void set{{field|ucc}}({{field.kind|java_type}} {{field|name}}) {
+        mTag_ = Tag.{{field|ucc}};
+        m{{field|ucc}} = {{field|name}};
+    }
+
+    // TODO(rockot): Fix the findbugs error and remove this suppression.
+    // See http://crbug.com/570386.
+    @SuppressFBWarnings("EI_EXPOSE_REP")
+    public {{field.kind|java_type}} get{{field|ucc}}() {
+        assert mTag_ == Tag.{{field|ucc}};
+        return m{{field|ucc}};
+    }
+{%   endfor %}
+
+
+    @Override
+    protected final void encode(org.chromium.mojo.bindings.Encoder encoder0, int offset) {
+        encoder0.encode(org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE, offset);
+        encoder0.encode(mTag_, offset + 4);
+        switch (mTag_) {
+{%   for field in union.fields %}
+            case Tag.{{field|ucc}}: {
+{%     if field.kind|is_union_kind %}
+                if (m{{field|ucc}} == null) {
+                    encoder0.encodeNullPointer(offset + 8, {{field.kind|is_nullable_kind|java_true_false}});
+                } else {
+                    m{{field|ucc}}.encode(encoder0.encoderForUnionPointer(offset + 8), 0);
+                }
+{%     else %}
+                {{encode('m' ~ field|ucc, field.kind, 'offset + 8', 0)|indent(16)}}
+{%     endif %}
+                break;
+            }
+{%   endfor %}
+            default: {
+                break;
+            }
+        }
+    }
+
+    public static {{union|name}} deserialize(org.chromium.mojo.bindings.Message message) {
+        return decode(new org.chromium.mojo.bindings.Decoder(message).decoderForSerializedUnion(), 0);
+    }
+
+    public static final {{union|name}} decode(org.chromium.mojo.bindings.Decoder decoder0, int offset) {
+        org.chromium.mojo.bindings.DataHeader dataHeader = decoder0.readDataHeaderForUnion(offset);
+        if (dataHeader.size == 0) {
+            return null;
+        }
+        {{union|name}} result = new {{union|name}}();
+        switch (dataHeader.elementsOrVersion) {
+{%   for field in union.fields %}
+            case Tag.{{field|ucc}}: {
+{%     if field.kind|is_union_kind %}
+                org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, {{field.kind|is_nullable_kind|java_true_false}});
+                if (decoder1 != null) {
+                    result.m{{field|ucc}} = {{field.kind|name}}.decode(decoder1, 0);
+                }
+{%     else %}
+                {{decode('result.m'~field|ucc, field.kind, 'offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0)|indent(16)}}
+{%     endif %}
+                result.mTag_ = Tag.{{field|ucc}};
+                break;
+            }
+{%   endfor %}
+            default: {
+                break;
+            }
+        }
+        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;
+        {{union|name}} other = ({{union|name}}) object;
+        if (mTag_ != other.mTag_)
+            return false;
+        switch (mTag_) {
+{%   for field in union.fields %}
+            case Tag.{{field|ucc}}:
+                return {{equality(field.kind, 'm'~field|ucc, 'other.m'~field|ucc)}};
+{%   endfor %}
+            default:
+                break;
+        }
+        return false;
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = prime + getClass().hashCode();
+        result = prime * result + org.chromium.mojo.bindings.BindingsHelper.hashCode(mTag_);
+        switch (mTag_) {
+{%   for field in union.fields %}
+            case Tag.{{field|ucc}}: {
+                result = prime * result + {{hash(field.kind, 'm'~field|ucc)}};
+                break;
+            }
+{%   endfor %}
+            default: {
+                break;
+            }
+        }
+        return result;
+    }
+}
+{% endmacro %}
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..d37288a
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl
@@ -0,0 +1,42 @@
+{%- 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 static final boolean IS_EXTENSIBLE = {% if enum.extensible %}true{% else %}false{% endif %};
+
+    public static boolean isKnownValue(int value) {
+{%- if enum.fields %}
+        switch (value) {
+{%-   for enum_field in enum.fields|groupby('numeric_value') %}
+            case {{enum_field[0]}}:
+{%-   endfor %}
+                return true;
+        }
+{%- endif %}
+        return false;
+    }
+
+    public static void validate(int value) {
+        if (IS_EXTENSIBLE || isKnownValue(value))
+            return;
+
+        throw new DeserializationException("Invalid enum value.");
+    }
+
+    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..1d67890
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl
@@ -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.
+
+// This file is autogenerated by:
+//     mojo/public/tools/bindings/mojom_bindings_generator.py
+// For:
+//     {{module.path}}
+//
+
+package {{package}};
+
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.mojo.bindings.DeserializationException;
\ No newline at end of file
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..a13be3e
--- /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) }}
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..3928e0c
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
@@ -0,0 +1,292 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% from "enum_definition.tmpl" import enum_def %}
+{% from "data_types_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 flags_for_method(method, is_request) -%}
+{{flags(method.response_parameters != None, is_request)}}
+{%- endmacro -%}
+
+{%- macro flags(use_response_flag, is_request) -%}
+{%-  if not use_response_flag -%}
+org.chromium.mojo.bindings.MessageHeader.NO_FLAG
+{%-   elif is_request: -%}
+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, fully_qualified=False) -%}
+{% if fully_qualified %}org.chromium.mojo.bindings.Interface.{% endif %}Manager<{{interface|name}}, {{interface|name}}.Proxy>
+{%- endmacro -%}
+
+{%- macro manager_def(interface) -%}
+public static final {{manager_class(interface, True)}} MANAGER =
+        new {{manager_class(interface, True)}}() {
+
+    public String getName() {
+        return "{{namespace|replace(".","::")}}::{{interface.name}}";
+    }
+
+    public int getVersion() {
+      return {{interface.version}};
+    }
+
+    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];
+    }
+};
+{%- endmacro -%}
+
+{%- macro accept_body(interface, 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()) {
+{%   if with_response %}
+        case org.chromium.mojo.bindings.InterfaceControlMessagesConstants.RUN_MESSAGE_ID:
+            return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRun(
+                    getCore(), {{interface|name}}_Internal.MANAGER, messageWithHeader, receiver);
+{%   else %}
+        case org.chromium.mojo.bindings.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID:
+            return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRunOrClosePipe(
+                    {{interface|name}}_Internal.MANAGER, messageWithHeader);
+{%   endif %}
+{%   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.param_struct %}
+{%       if with_response %}
+{%         set response_struct = method.response_param_struct %}
+{%       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 %}
+            try {
+                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 %});
+            } catch (RuntimeException e) {
+                // TODO(lhchavez): Remove this hack. See b/28814913 for details.
+                android.util.Log.wtf("{{namespace}}.{{interface.name}}", "Uncaught runtime exception", e);
+            }
+            return true;
+        }
+{%     endif %}
+{%   endfor %}
+        default:
+            return false;
+    }
+} catch (org.chromium.mojo.bindings.DeserializationException e) {
+    System.err.println(e.toString());
+    return false;
+}
+{%- endmacro -%}
+
+{% macro interface_def(interface) %}
+public interface {{interface|name}} extends org.chromium.mojo.bindings.Interface {
+{%  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}}, org.chromium.mojo.bindings.Interface.Proxy {
+    }
+
+    {{manager_class(interface)}} 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) %}
+class {{interface|name}}_Internal {
+
+    {{manager_def(interface)|indent(4)}}
+
+{% for method in interface.methods %}
+    private static final int {{method|method_ordinal_name}} = {{method.ordinal}};
+{% endfor %}
+
+    static final class Proxy extends org.chromium.mojo.bindings.Interface.AbstractProxy 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.param_struct %}
+            {{request_struct|name}} _message = new {{request_struct|name}}();
+{%   for param in method.parameters %}
+            _message.{{param|name}} = {{param|name}};
+{%   endfor %}
+{%   if method.response_parameters != None %}
+            getProxyHandler().getMessageReceiver().acceptWithResponder(
+                    _message.serializeWithHeader(
+                            getProxyHandler().getCore(),
+                            new org.chromium.mojo.bindings.MessageHeader(
+                                    {{method|method_ordinal_name}},
+                                    {{flags_for_method(method, True)}},
+                                    0)),
+                    new {{method.response_param_struct|name}}ForwardToCallback(callback));
+{%   else %}
+            getProxyHandler().getMessageReceiver().accept(
+                    _message.serializeWithHeader(
+                            getProxyHandler().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.param_struct, True)|indent(4) }}
+{%   if method.response_parameters != None %}
+{%   set response_struct = method.response_param_struct %}
+
+    {{ 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..50c7a7b
--- /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) }}
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..e28ba19
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl
@@ -0,0 +1,4 @@
+{% from "data_types_definition.tmpl" import struct_def %}
+{% include "header.java.tmpl" %}
+
+{{ struct_def(struct) }}
diff --git a/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl
new file mode 100644
index 0000000..b8cd4aa
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl
@@ -0,0 +1,4 @@
+{% from "data_types_definition.tmpl" import union_def %}
+{% include "header.java.tmpl" %}
+
+{{ union_def(union) }}
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..4ae0a9b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
@@ -0,0 +1,13 @@
+{%- macro enum_def(enum_name, enum) -%}
+  {{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..1b5cafa
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -0,0 +1,188 @@
+{%- for method in interface.methods %}
+  var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}};
+{%- endfor %}
+
+  function {{interface.name}}Proxy(receiver) {
+    bindings.ProxyBase.call(this, receiver);
+  }
+  {{interface.name}}Proxy.prototype = Object.create(bindings.ProxyBase.prototype);
+
+{%- 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|js_proxy_method_parameter_value}};
+{%- 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_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct({{interface.name}}_{{method.name}}_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+{%- endif %}
+  };
+{%- endfor %}
+
+  function {{interface.name}}Stub(delegate) {
+    bindings.StubBase.call(this, delegate);
+  }
+  {{interface.name}}Stub.prototype = Object.create(bindings.StubBase.prototype);
+
+{%- for method in interface.methods %}
+{%-   set js_method_name = method.name|stylize_method %}
+{%-   set delegate_expr =  "bindings.StubBindings(this).delegate" %}
+  {{interface.name}}Stub.prototype.{{js_method_name}} = function({{method.parameters|map(attribute='name')|join(', ')}}) {
+    return {{delegate_expr}} && {{delegate_expr}}.{{js_method_name}} && {{delegate_expr}}.{{js_method_name}}({{method.parameters|map('js_stub_method_parameter_value')|join(', ')}});
+  }
+{%- endfor %}
+
+  {{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 %}
+  }
+
+  var {{interface.name}} = {
+    name: '{{namespace|replace(".","::")}}::{{interface.name}}',
+    proxyClass: {{interface.name}}Proxy,
+    stubClass: {{interface.name}}Stub,
+    validateRequest: validate{{interface.name}}Request,
+{%- if interface|has_callbacks %}
+    validateResponse: validate{{interface.name}}Response,
+{%- else %}
+    validateResponse: null,
+{%- endif %}
+  };
+{#--- Interface Constants #}
+{%- for constant in interface.constants %}
+  {{interface.name}}.{{constant.name}} = {{constant.value|expression_to_text}},
+{%-   endfor %}
+{#--- Interface Enums #}
+{%- from "enum_definition.tmpl" import enum_def -%}
+{%- for enum in interface.enums %}
+  {{ enum_def("%s.%s"|format(interface.name, enum.name), enum) }}
+{%-  endfor %}
+  {{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 %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
new file mode 100644
index 0000000..6d7a1a2
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
@@ -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.
+
+define("{{module.path}}", [
+    "mojo/public/js/bindings",
+    "mojo/public/js/codec",
+    "mojo/public/js/connection",
+    "mojo/public/js/core",
+    "mojo/public/js/validator",
+{%- for import in imports %}
+    "{{import.module.path}}",
+{%- endfor %}
+], function(bindings, codec, connection, core, validator
+{%- for import in imports -%}
+    , {{import.unique_name}}
+{%- endfor -%}
+) {
+
+{%- include "module_definition.tmpl" %}
+
+  return exports;
+});
diff --git a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
new file mode 100644
index 0000000..86ea2ce
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
@@ -0,0 +1,49 @@
+{#--- 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) }}
+{%-  endfor %}
+
+{#--- Struct definitions #}
+{%  for struct in structs %}
+{%-   include "struct_definition.tmpl" %}
+{%- endfor -%}
+
+{#--- Union definitions #}
+{%- from "union_definition.tmpl" import union_def %}
+{%- for union in unions %}
+{{union_def(union)|indent(2)}}
+{%- 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 union in unions %}
+  exports.{{union.name}} = {{union.name}};
+{%- endfor %}
+{%- for interface in interfaces %}
+  exports.{{interface.name}} = {{interface.name}};
+{#--- Interface Client #}
+{%-   if interface.client in interfaces|map(attribute='name') %}
+  exports.{{interface.name}}.client = {{interface.client}};
+{%-   elif interface.client in imported_interfaces %}
+  exports.{{interface.name}}.client = {{imported_interfaces[interface.client]}};
+{%-   endif %}
+{%- endfor %}
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..ca80d67
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -0,0 +1,100 @@
+{#--- 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)}}
+{%-  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;
+    err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.versions[-1].version}});
+    if (err !== validator.validationError.NONE)
+        return err;
+
+{%- from "validation_macros.tmpl" import validate_struct_field %}
+{%- for packed_field in struct.packed.packed_fields %}
+{%-   set offset = packed_field|field_offset %}
+{%-   set field = packed_field.field %}
+{%-   set name = struct.name ~ '.' ~ field.name %}
+{{validate_struct_field(field, offset, name)|indent(4)}}
+{%- 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 version = 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.versions[-1].version}});
+
+{%- 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/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
new file mode 100644
index 0000000..3c903bc
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
@@ -0,0 +1,146 @@
+{%- macro union_def(union) %}
+function {{union.name}}(value) {
+  this.initDefault_();
+  this.initValue_(value);
+}
+
+{{tags(union)}}
+
+{{union.name}}.prototype.initDefault_ = function() {
+  this.$data = null;
+  this.$tag = undefined;
+}
+
+{{union.name}}.prototype.initValue_ = function(value) {
+  if (value == undefined) {
+    return;
+  }
+
+  var keys = Object.keys(value);
+  if (keys.length == 0) {
+    return;
+  }
+
+  if (keys.length > 1) {
+    throw new TypeError("You may set only one member on a union.");
+  }
+
+  var fields = [
+{%-   for field in union.fields %}
+      "{{field.name}}",
+{%-   endfor %}
+  ];
+
+  if (fields.indexOf(keys[0]) < 0) {
+    throw new ReferenceError(keys[0] + " is not a {{union.name}} member.");
+
+  }
+
+  this[keys[0]] = value[keys[0]];
+}
+
+{%-   for field in union.fields %}
+Object.defineProperty({{union.name}}.prototype, "{{field.name}}", {
+  get: function() {
+    if (this.$tag != {{union.name}}.Tags.{{field.name}}) {
+      throw new ReferenceError(
+          "{{union.name}}.{{field.name}} is not currently set.");
+    }
+    return this.$data;
+  },
+
+  set: function(value) {
+    this.$tag = {{union.name}}.Tags.{{field.name}};
+    this.$data = value;
+  }
+});
+{%-   endfor %}
+
+{{encode(union)|indent(2)}}
+
+{{decode(union)|indent(2)}}
+
+{{validate(union)|indent(2)}}
+
+{{union.name}}.encodedSize = 16;
+{%- endmacro %}
+
+{%- macro tags(union) %}
+{{union.name}}.Tags = {
+{%-   for field in union.fields %}
+  {{field.name}}: {{field.ordinal}},
+{%-   endfor %}
+};
+{%- endmacro %}
+
+{%- macro encode(union) %}
+{{union.name}}.encode = function(encoder, val) {
+  if (val == null) {
+    encoder.writeUint64(0);
+    encoder.writeUint64(0);
+    return;
+  }
+  if (val.$tag == undefined) {
+    throw new TypeError("Cannot encode unions with an unknown member set.");
+  }
+
+  encoder.writeUint32(16);
+  encoder.writeUint32(val.$tag);
+  switch (val.$tag) {
+{%-   for field in union.fields %}
+    case {{union.name}}.Tags.{{field.name}}:
+      encoder.{{field.kind|union_encode_snippet}}val.{{field.name}});
+      break;
+{%-   endfor %}
+  }
+  encoder.align();
+};
+{%- endmacro %}
+
+{%- macro decode(union) %}
+{{union.name}}.decode = function(decoder) {
+  var size = decoder.readUint32();
+  if (size == 0) {
+    decoder.readUint32();
+    decoder.readUint64();
+    return null;
+  }
+
+  var result = new {{union.name}}();
+  var tag = decoder.readUint32();
+  switch (tag) {
+{%-   for field in union.fields %}
+    case {{union.name}}.Tags.{{field.name}}:
+      result.{{field.name}} = decoder.{{field.kind|union_decode_snippet}};
+      break;
+{%-   endfor %}
+  }
+  decoder.align();
+
+  return result;
+};
+{%- endmacro %}
+
+{%- from "validation_macros.tmpl" import validate_union_field %}
+{%- macro validate(union) %}
+{{union.name}}.validate = function(messageValidator, offset) {
+  var size = messageValidator.decodeUnionSize(offset);
+  if (size != 16) {
+    return validator.validationError.INVALID_UNION_SIZE;
+  }
+
+  var tag = messageValidator.decodeUnionTag(offset);
+  var data_offset = offset + 8;
+  var err;
+  switch (tag) {
+{%-   for field in union.fields %}
+{%-     set name = union.name ~ '.' ~ field.name %}
+    case {{union.name}}.Tags.{{field.name}}:
+      {{validate_union_field(field, "data_offset", name)}}
+      break;
+{%-   endfor %}
+  }
+
+  return validator.validationError.NONE;
+};
+{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
new file mode 100644
index 0000000..7a39749
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
@@ -0,0 +1,52 @@
+{% macro _check_err() -%}
+if (err !== validator.validationError.NONE)
+    return err;
+{%- endmacro %}
+
+{%- macro _validate_field(field, offset, name) %}
+{%-   if field|is_string_pointer_field %}
+// validate {{name}}
+err = messageValidator.validateStringPointer({{offset}}, {{field|validate_string_params}})
+{{_check_err()}}
+{%-   elif field|is_array_pointer_field %}
+// validate {{name}}
+err = messageValidator.validateArrayPointer({{offset}}, {{field|validate_array_params}});
+{{_check_err()}}
+{%-   elif field|is_struct_pointer_field %}
+// validate {{name}}
+err = messageValidator.validateStructPointer({{offset}}, {{field|validate_struct_params}});
+{{_check_err()}}
+{%-   elif field|is_map_pointer_field %}
+// validate {{name}}
+err = messageValidator.validateMapPointer({{offset}}, {{field|validate_map_params}});
+{{_check_err()}}
+{%-   elif field|is_interface_field %}
+// validate {{name}}
+err = messageValidator.validateInterface({{offset}}, {{field|validate_interface_params}});
+{{_check_err()}}
+{%-   elif field|is_handle_field %}
+// validate {{name}}
+err = messageValidator.validateHandle({{offset}}, {{field|validate_handle_params}})
+{{_check_err()}}
+{%-   endif %}
+{%- endmacro %}
+
+{%- macro validate_struct_field(field, offset, name) %}
+{%-   if field|is_union_field %}
+// validate {{name}}
+err = messageValidator.validateUnion({{offset}}, {{field|validate_union_params}});
+{{_check_err()}}
+{%-   else %}
+{{_validate_field(field, offset, name)}}
+{%-   endif %}
+{%- endmacro %}
+
+{%- macro validate_union_field(field, offset, name) %}
+{%-   if field|is_union_field %}
+// validate {{name}}
+err = messageValidator.validateNestedUnion({{offset}}, {{field|validate_union_params}});
+{{_check_err()}}
+{%-   else %}
+{{_validate_field(field, offset, name)}}
+{%-   endif %}
+{%- endmacro %}
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..38b219c
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -0,0 +1,509 @@
+# 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.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",
+}
+
+# TODO(rockot): Get rid of these globals. This requires some refactoring of the
+# generator library code so that filters can use the generator as context.
+_current_typemap = {}
+_for_blink = False
+_use_new_wrapper_types = False
+# TODO(rockot, yzshen): The variant handling is kind of a hack currently. Make
+# it right.
+_variant = None
+
+
+class _NameFormatter(object):
+  """A formatter for the names of kinds or values."""
+
+  def __init__(self, token, variant):
+    self._token = token
+    self._variant = variant
+
+  def Format(self, separator, prefixed=False, internal=False,
+             include_variant=False, add_same_module_namespaces=False):
+    parts = []
+    if self._ShouldIncludeNamespace(add_same_module_namespaces):
+      if prefixed:
+        parts.append("")
+      parts.extend(self._GetNamespace())
+      if include_variant and self._variant:
+        parts.append(self._variant)
+    parts.extend(self._GetName(internal))
+    return separator.join(parts)
+
+  def FormatForCpp(self, add_same_module_namespaces=False, internal=False):
+    return self.Format(
+        "::", prefixed=True,
+        add_same_module_namespaces=add_same_module_namespaces,
+        internal=internal, include_variant=True)
+
+  def FormatForMojom(self):
+    return self.Format(".", add_same_module_namespaces=True)
+
+  def _MapKindName(self, token, internal):
+    if not internal:
+      return token.name
+    if (mojom.IsStructKind(token) or mojom.IsUnionKind(token) or
+        mojom.IsInterfaceKind(token) or mojom.IsEnumKind(token)):
+      return token.name + "_Data"
+    return token.name
+
+  def _GetName(self, internal):
+    name = []
+    if internal:
+      name.append("internal")
+    if self._token.parent_kind:
+      name.append(self._MapKindName(self._token.parent_kind, internal))
+    # Both variable and enum constants are constructed like:
+    # Namespace::Struct::CONSTANT_NAME
+    # For enums, CONSTANT_NAME is EnumName::ENUM_VALUE.
+    if isinstance(self._token, mojom.EnumValue):
+      name.extend([self._token.enum.name, self._token.name])
+    else:
+      name.append(self._MapKindName(self._token, internal))
+    return name
+
+  def _ShouldIncludeNamespace(self, add_same_module_namespaces):
+    return add_same_module_namespaces or self._token.imported_from
+
+  def _GetNamespace(self):
+    if self._token.imported_from:
+      return NamespaceToArray(self._token.imported_from["namespace"])
+    elif hasattr(self._token, "module"):
+      return NamespaceToArray(self._token.module.namespace)
+    return []
+
+
+def ConstantValue(constant):
+  return ExpressionToText(constant.value, kind=constant.kind)
+
+# TODO(yzshen): Revisit the default value feature. It was designed prior to
+# custom type mapping.
+def DefaultValue(field):
+  if field.default:
+    if mojom.IsStructKind(field.kind):
+      assert field.default == "default"
+      if not IsTypemappedKind(field.kind):
+        return "%s::New()" % GetNameForKind(field.kind)
+    return ExpressionToText(field.default, kind=field.kind)
+  if not _use_new_wrapper_types:
+    if mojom.IsArrayKind(field.kind) or mojom.IsMapKind(field.kind):
+      return "nullptr";
+    if mojom.IsStringKind(field.kind):
+      return "" if _for_blink else "nullptr"
+  return ""
+
+def NamespaceToArray(namespace):
+  return namespace.split(".") if namespace else []
+
+def GetNameForKind(kind, internal=False):
+  return _NameFormatter(kind, _variant).FormatForCpp(internal=internal)
+
+def GetQualifiedNameForKind(kind, internal=False):
+  return _NameFormatter(kind, _variant).FormatForCpp(
+      internal=internal, add_same_module_namespaces=True)
+
+def GetFullMojomNameForKind(kind):
+  return _NameFormatter(kind, _variant).FormatForMojom()
+
+def IsTypemappedKind(kind):
+  return hasattr(kind, "name") and \
+      GetFullMojomNameForKind(kind) in _current_typemap
+
+def IsNativeOnlyKind(kind):
+  return (mojom.IsStructKind(kind) or mojom.IsEnumKind(kind)) and \
+      kind.native_only
+
+def GetNativeTypeName(typemapped_kind):
+  return _current_typemap[GetFullMojomNameForKind(typemapped_kind)]["typename"]
+
+def GetCppPodType(kind):
+  if mojom.IsStringKind(kind):
+    return "char*"
+  return _kind_to_cpp_type[kind]
+
+def GetCppWrapperType(kind):
+  if IsTypemappedKind(kind):
+    return GetNativeTypeName(kind)
+  if mojom.IsEnumKind(kind):
+    return GetNameForKind(kind)
+  if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsArrayKind(kind):
+    pattern = None
+    if _use_new_wrapper_types:
+      if mojom.IsNullableKind(kind):
+        pattern = ("WTF::Optional<WTF::Vector<%s>>" if _for_blink else
+            "base::Optional<std::vector<%s>>")
+      else:
+        pattern = "WTF::Vector<%s>" if _for_blink else "std::vector<%s>"
+    else:
+      pattern = "mojo::WTFArray<%s>" if _for_blink else "mojo::Array<%s>"
+    return pattern % GetCppWrapperType(kind.kind)
+  if mojom.IsMapKind(kind):
+    pattern = None
+    if _use_new_wrapper_types:
+      if mojom.IsNullableKind(kind):
+        pattern = ("WTF::Optional<WTF::HashMap<%s, %s>>" if _for_blink else
+                   "base::Optional<std::unordered_map<%s, %s>>")
+      else:
+        pattern = ("WTF::HashMap<%s, %s>" if _for_blink else
+                   "std::unordered_map<%s, %s>")
+    else:
+      pattern = "mojo::WTFMap<%s, %s>" if _for_blink else "mojo::Map<%s, %s>"
+    return pattern % (GetCppWrapperType(kind.key_kind),
+                      GetCppWrapperType(kind.value_kind))
+  if mojom.IsInterfaceKind(kind):
+    return "%sPtr" % GetNameForKind(kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    return "%sRequest" % GetNameForKind(kind.kind)
+  if mojom.IsAssociatedInterfaceKind(kind):
+    return "%sAssociatedPtrInfo" % GetNameForKind(kind.kind)
+  if mojom.IsAssociatedInterfaceRequestKind(kind):
+    return "%sAssociatedRequest" % GetNameForKind(kind.kind)
+  if mojom.IsStringKind(kind):
+    if _for_blink:
+      return "WTF::String"
+    if not _use_new_wrapper_types:
+      return "mojo::String"
+    return ("base::Optional<std::string>" if mojom.IsNullableKind(kind) else
+            "std::string")
+  if mojom.IsGenericHandleKind(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:
+    raise Exception("Unrecognized kind %s" % kind.spec)
+  return _kind_to_cpp_type[kind]
+
+def IsMoveOnlyKind(kind):
+  if IsTypemappedKind(kind):
+    if mojom.IsEnumKind(kind):
+      return False
+    return _current_typemap[GetFullMojomNameForKind(kind)]["move_only"]
+  if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
+    return True
+  if mojom.IsArrayKind(kind):
+    return IsMoveOnlyKind(kind.kind) if _use_new_wrapper_types else True
+  if mojom.IsMapKind(kind):
+    return IsMoveOnlyKind(kind.value_kind) if _use_new_wrapper_types else True
+  if mojom.IsAnyHandleOrInterfaceKind(kind):
+    return True
+  return False
+
+def ShouldPassParamByValue(kind):
+  return (not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind)
+
+def GetCppWrapperParamType(kind):
+  cpp_wrapper_type = GetCppWrapperType(kind)
+  return (cpp_wrapper_type if ShouldPassParamByValue(kind)
+                           else "const %s&" % cpp_wrapper_type)
+
+def GetCppFieldType(kind):
+  if mojom.IsStructKind(kind):
+    return ("mojo::internal::Pointer<%s>" %
+        GetNameForKind(kind, internal=True))
+  if mojom.IsUnionKind(kind):
+    return "%s" % GetNameForKind(kind, internal=True)
+  if mojom.IsArrayKind(kind):
+    return ("mojo::internal::Pointer<mojo::internal::Array_Data<%s>>" %
+            GetCppFieldType(kind.kind))
+  if mojom.IsMapKind(kind):
+    return ("mojo::internal::Pointer<mojo::internal::Map_Data<%s, %s>>" %
+            (GetCppFieldType(kind.key_kind), GetCppFieldType(kind.value_kind)))
+  if mojom.IsInterfaceKind(kind):
+    return "mojo::internal::Interface_Data"
+  if mojom.IsInterfaceRequestKind(kind):
+    return "mojo::internal::Handle_Data"
+  if mojom.IsAssociatedInterfaceKind(kind):
+    return "mojo::internal::AssociatedInterface_Data"
+  if mojom.IsAssociatedInterfaceRequestKind(kind):
+    return "mojo::internal::AssociatedInterfaceRequest_Data"
+  if mojom.IsEnumKind(kind):
+    return "int32_t"
+  if mojom.IsStringKind(kind):
+    return "mojo::internal::Pointer<mojo::internal::String_Data>"
+  if mojom.IsAnyHandleKind(kind):
+    return "mojo::internal::Handle_Data"
+  return _kind_to_cpp_type[kind]
+
+def GetCppUnionFieldType(kind):
+  if mojom.IsUnionKind(kind):
+    return ("mojo::internal::Pointer<%s>" % GetNameForKind(kind, internal=True))
+  return GetCppFieldType(kind)
+
+def GetUnionGetterReturnType(kind):
+  if mojom.IsReferenceKind(kind):
+    return "%s&" % GetCppWrapperType(kind)
+  return GetCppWrapperType(kind)
+
+def GetUnmappedTypeForSerializer(kind):
+  if mojom.IsEnumKind(kind):
+    return GetQualifiedNameForKind(kind)
+  if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
+    return "%sPtr" % GetQualifiedNameForKind(kind)
+  if mojom.IsArrayKind(kind):
+    return "mojo::Array<%s>" % GetUnmappedTypeForSerializer(kind.kind)
+  if mojom.IsMapKind(kind):
+    return "mojo::Map<%s, %s>" % (
+        GetUnmappedTypeForSerializer(kind.key_kind),
+        GetUnmappedTypeForSerializer(kind.value_kind))
+  if mojom.IsInterfaceKind(kind):
+    return "%sPtr" % GetQualifiedNameForKind(kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    return "%sRequest" % GetQualifiedNameForKind(kind.kind)
+  if mojom.IsAssociatedInterfaceKind(kind):
+    return "%sAssociatedPtrInfo" % GetQualifiedNameForKind(kind.kind)
+  if mojom.IsAssociatedInterfaceRequestKind(kind):
+    return "%sAssociatedRequest" % GetQualifiedNameForKind(kind.kind)
+  if mojom.IsStringKind(kind):
+    return "mojo::String"
+  if mojom.IsGenericHandleKind(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 TranslateConstants(token, kind):
+  if isinstance(token, mojom.NamedValue):
+    return _NameFormatter(token, _variant).FormatForCpp()
+
+  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";
+
+  # Per C++11, 2.14.2, the type of an integer literal is the first of the
+  # corresponding list in Table 6 in which its value can be represented. In this
+  # case, the list for decimal constants with no suffix is:
+  #   int, long int, long long int
+  # The standard considers a program ill-formed if it contains an integer
+  # literal that cannot be represented by any of the allowed types.
+  #
+  # As it turns out, MSVC doesn't bother trying to fall back to long long int,
+  # so the integral constant -2147483648 causes it grief: it decides to
+  # represent 2147483648 as an unsigned integer, and then warns that the unary
+  # minus operator doesn't make sense on unsigned types. Doh!
+  if kind == mojom.INT32 and token == "-2147483648":
+    return "(-%d - 1) /* %s */" % (
+        2**31 - 1, "Workaround for MSVC bug; see https://crbug.com/445618")
+
+  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.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind):
+      return False
+  return True
+
+def ShouldInlineUnion(union):
+  return not any(
+      mojom.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind)
+           for field in union.fields)
+
+def GetContainerValidateParamsCtorArgs(kind):
+  if mojom.IsStringKind(kind):
+    expected_num_elements = 0
+    element_is_nullable = False
+    key_validate_params = "nullptr"
+    element_validate_params = "nullptr"
+    enum_validate_func = "nullptr"
+  elif mojom.IsMapKind(kind):
+    expected_num_elements = 0
+    element_is_nullable = False
+    key_validate_params = GetNewContainerValidateParams(mojom.Array(
+        kind=kind.key_kind))
+    element_validate_params = GetNewContainerValidateParams(mojom.Array(
+        kind=kind.value_kind))
+    enum_validate_func = "nullptr"
+  else:  # mojom.IsArrayKind(kind)
+    expected_num_elements = generator.ExpectedArraySize(kind) or 0
+    element_is_nullable = mojom.IsNullableKind(kind.kind)
+    key_validate_params = "nullptr"
+    element_validate_params = GetNewContainerValidateParams(kind.kind)
+    if mojom.IsEnumKind(kind.kind):
+      enum_validate_func = ("%s::Validate" %
+                            GetQualifiedNameForKind(kind.kind, internal=True))
+    else:
+      enum_validate_func = "nullptr"
+
+  if enum_validate_func == "nullptr":
+    if key_validate_params == "nullptr":
+      return "%d, %s, %s" % (expected_num_elements,
+                             "true" if element_is_nullable else "false",
+                             element_validate_params)
+    else:
+      return "%s, %s" % (key_validate_params, element_validate_params)
+  else:
+    return "%d, %s" % (expected_num_elements, enum_validate_func)
+
+def GetNewContainerValidateParams(kind):
+  if (not mojom.IsArrayKind(kind) and not mojom.IsMapKind(kind) and
+      not mojom.IsStringKind(kind)):
+    return "nullptr"
+
+  return "new mojo::internal::ContainerValidateParams(%s)" % (
+      GetContainerValidateParamsCtorArgs(kind))
+
+class Generator(generator.Generator):
+
+  cpp_filters = {
+    "constant_value": ConstantValue,
+    "cpp_wrapper_param_type": GetCppWrapperParamType,
+    "cpp_field_type": GetCppFieldType,
+    "cpp_union_field_type": GetCppUnionFieldType,
+    "cpp_pod_type": GetCppPodType,
+    "cpp_union_getter_return_type": GetUnionGetterReturnType,
+    "cpp_wrapper_type": GetCppWrapperType,
+    "default_value": DefaultValue,
+    "expression_to_text": ExpressionToText,
+    "get_container_validate_params_ctor_args":
+    GetContainerValidateParamsCtorArgs,
+    "get_name_for_kind": GetNameForKind,
+    "get_pad": pack.GetPad,
+    "get_qualified_name_for_kind": GetQualifiedNameForKind,
+    "has_callbacks": mojom.HasCallbacks,
+    "has_sync_methods": mojom.HasSyncMethods,
+    "should_inline": ShouldInlineStruct,
+    "should_inline_union": ShouldInlineUnion,
+    "is_array_kind": mojom.IsArrayKind,
+    "is_enum_kind": mojom.IsEnumKind,
+    "is_integral_kind": mojom.IsIntegralKind,
+    "is_native_only_kind": IsNativeOnlyKind,
+    "is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind,
+    "is_associated_kind": mojom.IsAssociatedKind,
+    "is_map_kind": mojom.IsMapKind,
+    "is_nullable_kind": mojom.IsNullableKind,
+    "is_object_kind": mojom.IsObjectKind,
+    "is_string_kind": mojom.IsStringKind,
+    "is_struct_kind": mojom.IsStructKind,
+    "is_typemapped_kind": IsTypemappedKind,
+    "is_union_kind": mojom.IsUnionKind,
+    "passes_associated_kinds": mojom.PassesAssociatedKinds,
+    "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
+    "stylize_method": generator.StudlyCapsToCamel,
+    "under_to_camel": generator.UnderToCamel,
+    "unmapped_type_for_serializer": GetUnmappedTypeForSerializer,
+  }
+
+  def GetExtraTraitsHeaders(self):
+    extra_headers = set()
+    for entry in self.typemap.itervalues():
+      extra_headers.update(entry.get("traits_headers", []))
+    return list(extra_headers)
+
+  def GetExtraPublicHeaders(self):
+    extra_headers = set()
+    for entry in self.typemap.itervalues():
+      extra_headers.update(entry.get("public_headers", []))
+    return list(extra_headers)
+
+  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(),
+      "unions": self.GetUnions(),
+      "interfaces": self.GetInterfaces(),
+      "variant": self.variant,
+      "extra_traits_headers": self.GetExtraTraitsHeaders(),
+      "extra_public_headers": self.GetExtraPublicHeaders(),
+      "for_blink": self.for_blink,
+      "use_new_wrapper_types": self.use_new_wrapper_types,
+    }
+
+  @staticmethod
+  def GetTemplatePrefix():
+    return "cpp_templates"
+
+  @classmethod
+  def GetFilters(cls):
+    return cls.cpp_filters
+
+  @UseJinja("module.h.tmpl")
+  def GenerateModuleHeader(self):
+    return self.GetJinjaExports()
+
+  @UseJinja("module-internal.h.tmpl")
+  def GenerateModuleInternalHeader(self):
+    return self.GetJinjaExports()
+
+  @UseJinja("module.cc.tmpl")
+  def GenerateModuleSource(self):
+    return self.GetJinjaExports()
+
+  def GenerateFiles(self, args):
+    global _current_typemap
+    _current_typemap = self.typemap
+    global _for_blink
+    _for_blink = self.for_blink
+    global _use_new_wrapper_types
+    _use_new_wrapper_types = self.use_new_wrapper_types
+    global _variant
+    _variant = self.variant
+    suffix = "-%s" % self.variant if self.variant else ""
+    self.Write(self.GenerateModuleHeader(),
+        self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix)))
+    self.Write(self.GenerateModuleInternalHeader(),
+        self.MatchMojomFilePath("%s%s-internal.h" % (self.module.name, suffix)))
+    self.Write(self.GenerateModuleSource(),
+        self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix)))
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..481efbc
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -0,0 +1,553 @@
+# Copyright 2014 The Chromium 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.fileutil as fileutil
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+from mojom.generate.template_expander import UseJinja
+
+
+GENERATOR_PREFIX = 'java'
+
+_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) or mojom.IsUnionKind(element)):
+    return UpperCamelCase(element.name)
+  if mojom.IsInterfaceRequestKind(element) or mojom.IsAssociatedKind(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.IsArrayKind(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.IsArrayKind(kind):
+      params.append(GetArrayNullabilityFlags(kind))
+    else:
+      params.append(GetJavaTrueFalse(mojom.IsNullableKind(kind)))
+  if mojom.IsArrayKind(kind):
+    params.append(GetArrayExpectedLength(kind))
+  if mojom.IsInterfaceKind(kind):
+    params.append('%s.MANAGER' % GetJavaType(context, kind))
+  if mojom.IsArrayKind(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.IsArrayKind(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'
+    if mojom.IsAssociatedInterfaceRequestKind(kind):
+      return 'readAssociatedInterfaceRequestNotSupported'
+    if mojom.IsAssociatedInterfaceKind(kind):
+      return 'readAssociatedServiceInterfaceNotSupported'
+    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 module.attributes and '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)
+
+@contextfilter
+def GetJavaClassForEnum(context, kind):
+  return GetNameForKind(context, kind)
+
+def GetBoxedJavaType(context, kind, with_generics=True):
+  unboxed_type = GetJavaType(context, kind, False, with_generics)
+  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, with_generics=True):
+  if boxed:
+    return GetBoxedJavaType(context, kind)
+  if (mojom.IsStructKind(kind) or
+      mojom.IsInterfaceKind(kind) or
+      mojom.IsUnionKind(kind)):
+    return GetNameForKind(context, kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    return ('org.chromium.mojo.bindings.InterfaceRequest<%s>' %
+            GetNameForKind(context, kind.kind))
+  if mojom.IsAssociatedInterfaceKind(kind):
+    return 'org.chromium.mojo.bindings.AssociatedInterfaceNotSupported'
+  if mojom.IsAssociatedInterfaceRequestKind(kind):
+    return 'org.chromium.mojo.bindings.AssociatedInterfaceRequestNotSupported'
+  if mojom.IsMapKind(kind):
+    if with_generics:
+      return 'java.util.Map<%s, %s>' % (
+          GetBoxedJavaType(context, kind.key_kind),
+          GetBoxedJavaType(context, kind.value_kind))
+    else:
+      return 'java.util.Map'
+  if mojom.IsArrayKind(kind):
+    return '%s[]' % GetJavaType(context, kind.kind, boxed, with_generics)
+  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.IsArrayKind(kind.kind):
+    return NewArray(context, kind.kind, size) + '[]'
+  return 'new %s[%s]' % (
+      GetJavaType(context, kind.kind, boxed=False, with_generics=False), 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 GetArrayKind(kind, size = None):
+  if size is None:
+    return mojom.Array(kind)
+  else:
+    array = mojom.Array(kind, 0)
+    array.java_map_size = size
+    return array
+
+def GetArrayExpectedLength(kind):
+  if mojom.IsArrayKind(kind) and kind.length is not None:
+    return getattr(kind, 'java_map_size', str(kind.length))
+  else:
+    return 'org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH'
+
+def IsPointerArrayKind(kind):
+  if not mojom.IsArrayKind(kind):
+    return False
+  sub_kind = kind.kind
+  return mojom.IsObjectKind(sub_kind) and not mojom.IsUnionKind(sub_kind)
+
+def IsUnionArrayKind(kind):
+  if not mojom.IsArrayKind(kind):
+    return False
+  sub_kind = kind.kind
+  return mojom.IsUnionKind(sub_kind)
+
+def GetConstantsMainEntityName(module):
+  if module.attributes and '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 = {
+    'array_expected_length': GetArrayExpectedLength,
+    'array': GetArrayKind,
+    'constant_value': ConstantValue,
+    'decode_method': DecodeMethod,
+    'default_value': DefaultValue,
+    'encode_method': EncodeMethod,
+    'expression_to_text': ExpressionToText,
+    'has_method_without_response': HasMethodWithoutResponse,
+    'has_method_with_response': HasMethodWithResponse,
+    'interface_response_name': GetInterfaceResponseName,
+    'is_array_kind': mojom.IsArrayKind,
+    'is_any_handle_kind': mojom.IsAnyHandleKind,
+    "is_enum_kind": mojom.IsEnumKind,
+    'is_interface_request_kind': mojom.IsInterfaceRequestKind,
+    'is_map_kind': mojom.IsMapKind,
+    'is_nullable_kind': mojom.IsNullableKind,
+    'is_pointer_array_kind': IsPointerArrayKind,
+    'is_reference_kind': mojom.IsReferenceKind,
+    'is_struct_kind': mojom.IsStructKind,
+    'is_union_array_kind': IsUnionArrayKind,
+    'is_union_kind': mojom.IsUnionKind,
+    'java_class_for_enum': GetJavaClassForEnum,
+    'java_true_false': GetJavaTrueFalse,
+    'java_type': GetJavaType,
+    'method_ordinal_name': GetMethodOrdinalName,
+    'name': GetNameForElement,
+    'new_array': NewArray,
+    'ucc': lambda x: UpperCamelCase(x.name),
+  }
+
+  def GetJinjaExports(self):
+    return {
+      'package': GetPackage(self.module),
+    }
+
+  @staticmethod
+  def GetTemplatePrefix():
+    return "java_templates"
+
+  @classmethod
+  def GetFilters(cls):
+    return cls.java_filters
+
+  def GetJinjaExportsForInterface(self, interface):
+    exports = self.GetJinjaExports()
+    exports.update({'interface': interface})
+    return exports
+
+  @UseJinja('enum.java.tmpl')
+  def GenerateEnumSource(self, enum):
+    exports = self.GetJinjaExports()
+    exports.update({'enum': enum})
+    return exports
+
+  @UseJinja('struct.java.tmpl')
+  def GenerateStructSource(self, struct):
+    exports = self.GetJinjaExports()
+    exports.update({'struct': struct})
+    return exports
+
+  @UseJinja('union.java.tmpl')
+  def GenerateUnionSource(self, union):
+    exports = self.GetJinjaExports()
+    exports.update({'union': union})
+    return exports
+
+  @UseJinja('interface.java.tmpl')
+  def GenerateInterfaceSource(self, interface):
+    return self.GetJinjaExportsForInterface(interface)
+
+  @UseJinja('interface_internal.java.tmpl')
+  def GenerateInterfaceInternalSource(self, interface):
+    return self.GetJinjaExportsForInterface(interface)
+
+  @UseJinja('constants.java.tmpl')
+  def GenerateConstantsSource(self, module):
+    exports = self.GetJinjaExports()
+    exports.update({'main_entity': GetConstantsMainEntityName(module),
+                    'constants': module.constants})
+    return exports
+
+  def DoGenerateFiles(self):
+    fileutil.EnsureDirectoryExists(self.output_dir)
+
+    # 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 union in self.module.unions:
+      self.Write(self.GenerateUnionSource(union),
+                 '%s.java' % GetNameForElement(union))
+
+    for enum in self.module.enums:
+      self.Write(self.GenerateEnumSource(enum),
+                 '%s.java' % GetNameForElement(enum))
+
+    for interface in self.GetInterfaces():
+      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):
+    # TODO(rockot): Support variant output for Java.
+    if self.variant:
+      raise Exception("Variants not supported in Java bindings.")
+
+    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.
+    basename = self.MatchMojomFilePath("%s.srcjar" % self.module.name)
+    zip_filename = os.path.join(self.output_dir, basename)
+    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..e9a4883
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -0,0 +1,427 @@
+# 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.IsUnionKind(field.kind):
+    return "null"
+  if mojom.IsArrayKind(field.kind):
+    return "null"
+  if mojom.IsMapKind(field.kind):
+    return "null"
+  if mojom.IsInterfaceKind(field.kind) or \
+     mojom.IsInterfaceRequestKind(field.kind):
+    return _kind_to_javascript_default_value[mojom.MSGPIPE]
+  if mojom.IsAssociatedKind(field.kind):
+    return "null"
+  if mojom.IsEnumKind(field.kind):
+    return "0"
+  raise Exception("No valid default: %s" % field)
+
+
+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.IsUnionKind(kind):
+    return JavaScriptType(kind)
+  if mojom.IsArrayKind(kind):
+    array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf"
+    array_length = "" if kind.length is None else ", %d" % kind.length
+    element_type = ElementCodecType(kind.kind)
+    return "new codec.%s(%s%s)" % (array_type, element_type, array_length)
+  if mojom.IsInterfaceKind(kind):
+    return "codec.%s" % ("NullableInterface" if mojom.IsNullableKind(kind)
+        else "Interface")
+  if mojom.IsInterfaceRequestKind(kind):
+    return CodecType(mojom.MSGPIPE)
+  if mojom.IsAssociatedInterfaceKind(kind):
+    return "codec.AssociatedInterfaceNotSupported"
+  if mojom.IsAssociatedInterfaceRequestKind(kind):
+    return "codec.AssociatedInterfaceRequestNotSupported"
+  if mojom.IsEnumKind(kind):
+    return _kind_to_codec_type[mojom.INT32]
+  if mojom.IsMapKind(kind):
+    map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf"
+    key_type = ElementCodecType(kind.key_kind)
+    value_type = ElementCodecType(kind.value_kind)
+    return "new codec.%s(%s, %s)" % (map_type, key_type, value_type)
+  raise Exception("No codec type for %s" % kind)
+
+
+def ElementCodecType(kind):
+  return "codec.PackedBool" if mojom.IsBoolKind(kind) else CodecType(kind)
+
+def JavaScriptDecodeSnippet(kind):
+  if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
+      mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)):
+    return "decodeStruct(%s)" % CodecType(kind)
+  if mojom.IsStructKind(kind):
+    return "decodeStructPointer(%s)" % JavaScriptType(kind)
+  if mojom.IsMapKind(kind):
+    return "decodeMapPointer(%s, %s)" % \
+        (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind))
+  if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind):
+    return "decodeArrayPointer(codec.PackedBool)"
+  if mojom.IsArrayKind(kind):
+    return "decodeArrayPointer(%s)" % CodecType(kind.kind)
+  if mojom.IsUnionKind(kind):
+    return "decodeUnion(%s)" % CodecType(kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    return JavaScriptDecodeSnippet(mojom.MSGPIPE)
+  if mojom.IsEnumKind(kind):
+    return JavaScriptDecodeSnippet(mojom.INT32)
+  raise Exception("No decode snippet for %s" % kind)
+
+
+def JavaScriptEncodeSnippet(kind):
+  if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
+      mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)):
+    return "encodeStruct(%s, " % CodecType(kind)
+  if mojom.IsUnionKind(kind):
+    return "encodeStruct(%s, " % JavaScriptType(kind)
+  if mojom.IsStructKind(kind):
+    return "encodeStructPointer(%s, " % JavaScriptType(kind)
+  if mojom.IsMapKind(kind):
+    return "encodeMapPointer(%s, %s, " % \
+        (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind))
+  if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind):
+    return "encodeArrayPointer(codec.PackedBool, ";
+  if mojom.IsArrayKind(kind):
+    return "encodeArrayPointer(%s, " % CodecType(kind.kind)
+  if mojom.IsInterfaceRequestKind(kind):
+    return JavaScriptEncodeSnippet(mojom.MSGPIPE)
+  if mojom.IsEnumKind(kind):
+    return JavaScriptEncodeSnippet(mojom.INT32)
+  raise Exception("No encode snippet for %s" % kind)
+
+
+def JavaScriptUnionDecodeSnippet(kind):
+  if mojom.IsUnionKind(kind):
+    return "decodeStructPointer(%s)" % JavaScriptType(kind)
+  return JavaScriptDecodeSnippet(kind)
+
+
+def JavaScriptUnionEncodeSnippet(kind):
+  if mojom.IsUnionKind(kind):
+    return "encodeStructPointer(%s, " % JavaScriptType(kind)
+  return JavaScriptEncodeSnippet(kind)
+
+
+def JavaScriptFieldOffset(packed_field):
+  return "offset + codec.kStructHeaderSize + %s" % packed_field.offset
+
+
+def JavaScriptNullableParam(field):
+  return "true" if mojom.IsNullableKind(field.kind) else "false"
+
+
+def GetArrayExpectedDimensionSizes(kind):
+  expected_dimension_sizes = []
+  while mojom.IsArrayKind(kind):
+    expected_dimension_sizes.append(generator.ExpectedArraySize(kind) or 0)
+    kind = kind.kind
+  # Strings are serialized as variable-length arrays.
+  if (mojom.IsStringKind(kind)):
+    expected_dimension_sizes.append(0)
+  return expected_dimension_sizes
+
+
+def JavaScriptValidateArrayParams(field):
+  nullable = JavaScriptNullableParam(field)
+  element_kind = field.kind.kind
+  element_size = pack.PackedField.GetSizeForKind(element_kind)
+  expected_dimension_sizes = GetArrayExpectedDimensionSizes(
+      field.kind)
+  element_type = ElementCodecType(element_kind)
+  return "%s, %s, %s, %s, 0" % \
+      (element_size, element_type, nullable,
+       expected_dimension_sizes)
+
+
+def JavaScriptValidateStructParams(field):
+  nullable = JavaScriptNullableParam(field)
+  struct_type = JavaScriptType(field.kind)
+  return "%s, %s" % (struct_type, nullable)
+
+def JavaScriptValidateUnionParams(field):
+  nullable = JavaScriptNullableParam(field)
+  union_type = JavaScriptType(field.kind)
+  return "%s, %s" % (union_type, nullable)
+
+def JavaScriptValidateMapParams(field):
+  nullable = JavaScriptNullableParam(field)
+  keys_type = ElementCodecType(field.kind.key_kind)
+  values_kind = field.kind.value_kind;
+  values_type = ElementCodecType(values_kind)
+  values_nullable = "true" if mojom.IsNullableKind(values_kind) else "false"
+  return "%s, %s, %s, %s" % \
+      (nullable, keys_type, values_type, values_nullable)
+
+
+def JavaScriptValidateStringParams(field):
+  nullable = JavaScriptNullableParam(field)
+  return "%s" % (nullable)
+
+
+def JavaScriptValidateHandleParams(field):
+  nullable = JavaScriptNullableParam(field)
+  return "%s" % (nullable)
+
+def JavaScriptValidateInterfaceParams(field):
+  return JavaScriptValidateHandleParams(field)
+
+def JavaScriptProxyMethodParameterValue(parameter):
+  name = parameter.name;
+  if (mojom.IsInterfaceKind(parameter.kind)):
+   type = JavaScriptType(parameter.kind)
+   return "core.isHandle(%s) ? %s : connection.bindImpl" \
+       "(%s, %s)" % (name, name, name, type)
+  if (mojom.IsInterfaceRequestKind(parameter.kind)):
+   type = JavaScriptType(parameter.kind.kind)
+   return "core.isHandle(%s) ? %s : connection.bindProxy" \
+       "(%s, %s)" % (name, name, name, type)
+  return name;
+
+
+def JavaScriptStubMethodParameterValue(parameter):
+  name = parameter.name;
+  if (mojom.IsInterfaceKind(parameter.kind)):
+   type = JavaScriptType(parameter.kind)
+   return "connection.bindHandleToProxy(%s, %s)" % (name, type)
+  if (mojom.IsInterfaceRequestKind(parameter.kind)):
+   type = JavaScriptType(parameter.kind.kind)
+   return "connection.bindHandleToStub(%s, %s)" % (name, type)
+  return name;
+
+
+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.IsArrayKind(field.kind)
+
+def IsStringPointerField(field):
+  return mojom.IsStringKind(field.kind)
+
+def IsStructPointerField(field):
+  return mojom.IsStructKind(field.kind)
+
+def IsMapPointerField(field):
+  return mojom.IsMapKind(field.kind)
+
+def IsHandleField(field):
+  return mojom.IsAnyHandleKind(field.kind)
+
+def IsInterfaceField(field):
+  return mojom.IsInterfaceKind(field.kind)
+
+def IsUnionField(field):
+  return mojom.IsUnionKind(field.kind)
+
+
+class Generator(generator.Generator):
+
+  js_filters = {
+    "default_value": JavaScriptDefaultValue,
+    "payload_size": JavaScriptPayloadSize,
+    "decode_snippet": JavaScriptDecodeSnippet,
+    "encode_snippet": JavaScriptEncodeSnippet,
+    "union_decode_snippet": JavaScriptUnionDecodeSnippet,
+    "union_encode_snippet": JavaScriptUnionEncodeSnippet,
+    "expression_to_text": ExpressionToText,
+    "field_offset": JavaScriptFieldOffset,
+    "has_callbacks": mojom.HasCallbacks,
+    "is_array_pointer_field": IsArrayPointerField,
+    "is_map_pointer_field": IsMapPointerField,
+    "is_struct_pointer_field": IsStructPointerField,
+    "is_string_pointer_field": IsStringPointerField,
+    "is_union_field": IsUnionField,
+    "is_handle_field": IsHandleField,
+    "is_interface_field": IsInterfaceField,
+    "js_type": JavaScriptType,
+    "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue,
+    "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue,
+    "stylize_method": generator.StudlyCapsToCamel,
+    "validate_array_params": JavaScriptValidateArrayParams,
+    "validate_handle_params": JavaScriptValidateHandleParams,
+    "validate_interface_params": JavaScriptValidateInterfaceParams,
+    "validate_map_params": JavaScriptValidateMapParams,
+    "validate_string_params": JavaScriptValidateStringParams,
+    "validate_struct_params": JavaScriptValidateStructParams,
+    "validate_union_params": JavaScriptValidateUnionParams,
+  }
+
+  def GetParameters(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(),
+      "unions": self.GetUnions(),
+      "interfaces": self.GetInterfaces(),
+      "imported_interfaces": self.GetImportedInterfaces(),
+    }
+
+  @staticmethod
+  def GetTemplatePrefix():
+    return "js_templates"
+
+  @classmethod
+  def GetFilters(cls):
+    return cls.js_filters
+
+  @UseJinja("module.amd.tmpl")
+  def GenerateAMDModule(self):
+    return self.GetParameters()
+
+  def GenerateFiles(self, args):
+    if self.variant:
+      raise Exception("Variants not supported in JavaScript bindings.")
+
+    self.Write(self.GenerateAMDModule(),
+        self.MatchMojomFilePath("%s.js" % self.module.name))
+
+  def GetImports(self):
+    used_names = set()
+    for each_import in self.module.imports:
+      simple_name = each_import["module_name"].split(".")[0]
+
+      # Since each import is assigned a variable in JS, they need to have unique
+      # names.
+      unique_name = simple_name
+      counter = 0
+      while unique_name in used_names:
+        counter += 1
+        unique_name = simple_name + str(counter)
+
+      used_names.add(unique_name)
+      each_import["unique_name"] = unique_name + "$"
+      counter += 1
+    return self.module.imports
+
+  def GetImportedInterfaces(self):
+    interface_to_import = {};
+    for each_import in self.module.imports:
+      for each_interface in each_import["module"].interfaces:
+        name = each_interface.name
+        interface_to_import[name] = each_import["unique_name"] + "." + name
+    return interface_to_import;
+
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..d6f4f17
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -0,0 +1,425 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom_generator_root = "//mojo/public/tools/bindings"
+mojom_generator_script = "$mojom_generator_root/mojom_bindings_generator.py"
+mojom_generator_sources = [
+  "$mojom_generator_root/generators/mojom_cpp_generator.py",
+  "$mojom_generator_root/generators/mojom_js_generator.py",
+  "$mojom_generator_root/generators/mojom_java_generator.py",
+  "$mojom_generator_root/pylib/mojom/__init__.py",
+  "$mojom_generator_root/pylib/mojom/error.py",
+  "$mojom_generator_root/pylib/mojom/generate/__init__.py",
+  "$mojom_generator_root/pylib/mojom/generate/constant_resolver.py",
+  "$mojom_generator_root/pylib/mojom/generate/data.py",
+  "$mojom_generator_root/pylib/mojom/generate/generator.py",
+  "$mojom_generator_root/pylib/mojom/generate/module.py",
+  "$mojom_generator_root/pylib/mojom/generate/pack.py",
+  "$mojom_generator_root/pylib/mojom/generate/template_expander.py",
+  "$mojom_generator_root/pylib/mojom/parse/__init__.py",
+  "$mojom_generator_root/pylib/mojom/parse/ast.py",
+  "$mojom_generator_root/pylib/mojom/parse/lexer.py",
+  "$mojom_generator_root/pylib/mojom/parse/parser.py",
+  "$mojom_generator_root/pylib/mojom/parse/translate.py",
+  "$mojom_generator_script",
+]
+
+if (!is_ios) {
+  _bindings_configuration_files = [
+    "//mojo/public/tools/bindings/chromium_bindings_configuration.gni",
+    "//mojo/public/tools/bindings/blink_bindings_configuration.gni",
+  ]
+} else {
+  _bindings_configuration_files =
+      [ "//mojo/public/tools/bindings/chromium_bindings_configuration.gni" ]
+}
+_bindings_configurations = []
+foreach(config_file, _bindings_configuration_files) {
+  _bindings_configurations += [ read_file(config_file, "scope") ]
+}
+foreach(configuration, _bindings_configurations) {
+  foreach(typemap, configuration.typemaps) {
+    # Check that the mojom field of each typemap refers to a mojom that exists.
+    read_file(typemap.mojom, "")
+  }
+}
+
+# Generate C++/JavaScript/Java 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 (optional if one of the deps sets listed below is present)
+#       List of source .mojom files to compile.
+#
+#   deps (optional)
+#       Note: this can contain only other mojom targets.
+#
+#       DEPRECATED: This is synonymous with public_deps because all mojom
+#       dependencies must be public by design. Please use public_deps.
+#
+#   public_deps (optional)
+#       Note: this can contain only other mojom targets.
+#
+#   import_dirs (optional)
+#       List of import directories that will get added when processing sources.
+#
+#   testonly (optional)
+#
+#   visibility (optional)
+#
+#   use_new_wrapper_types (optional)
+#       If set to true, mojom array/map/string will be mapped to STL (for
+#       chromium variant) or WTF (for blink) types. Otherwise, they will be
+#       mapped to mojo::Array/Map/String/etc.
+#       Default value is false.
+#       TODO(yzshen):
+#         - flip the flag and make use_new_wrapper_types=true the default;
+#         - convert all users to use the new mode;
+#         - remove support for the old mode.
+template("mojom") {
+  assert(
+      defined(invoker.sources) || defined(invoker.deps) ||
+          defined(invoker.public_deps),
+      "\"sources\" or \"deps\" must be defined for the $target_name template.")
+
+  all_deps = []
+  if (defined(invoker.deps)) {
+    all_deps += invoker.deps
+  }
+  if (defined(invoker.public_deps)) {
+    all_deps += invoker.public_deps
+  }
+
+  group("${target_name}__is_mojom") {
+  }
+
+  # Explicitly ensure that all dependencies (invoker.deps and
+  # invoker.public_deps) are mojom targets.
+  group("${target_name}__check_deps_are_all_mojom") {
+    deps = []
+    foreach(d, all_deps) {
+      name = get_label_info(d, "label_no_toolchain")
+      toolchain = get_label_info(d, "toolchain")
+      deps += [ "${name}__is_mojom(${toolchain})" ]
+    }
+  }
+
+  foreach(bindings_configuration, _bindings_configurations) {
+    cpp_only = false
+    variant_suffix = ""
+    if (defined(bindings_configuration.variant)) {
+      variant = bindings_configuration.variant
+      variant_suffix = "_${variant}"
+      cpp_only = true
+    }
+    type_mappings_target_name = "${target_name}${variant_suffix}__type_mappings"
+    type_mappings_path =
+        "$target_gen_dir/${target_name}${variant_suffix}__type_mappings"
+    active_typemaps = []
+    cpp_sources_suffix = "cpp_sources"
+    cpp_sources_target_name =
+        "${target_name}${variant_suffix}_${cpp_sources_suffix}"
+    enabled_sources = []
+    if (defined(invoker.sources)) {
+      generator_cpp_outputs = []
+      generator_js_outputs = []
+      generator_java_outputs = []
+      variant_dash_suffix = ""
+      if (defined(variant)) {
+        variant_dash_suffix = "-${variant}"
+      }
+      generator_cpp_outputs += [
+        "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.cc",
+        "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.h",
+        "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}-internal.h",
+      ]
+      enabled_sources = []
+      if (defined(bindings_configuration.blacklist)) {
+        foreach(source, invoker.sources) {
+          blacklisted = false
+          foreach(blacklisted_source, bindings_configuration.blacklist) {
+            if (get_path_info(source, "abspath") == blacklisted_source) {
+              blacklisted = true
+            }
+          }
+          if (!blacklisted) {
+            enabled_sources += [ source ]
+          }
+        }
+      } else {
+        enabled_sources = invoker.sources
+      }
+      foreach(source, enabled_sources) {
+        # TODO(sammc): Use a map instead of a linear scan when GN supports maps.
+        foreach(typemap, bindings_configuration.typemaps) {
+          if (get_path_info(source, "abspath") == typemap.mojom) {
+            active_typemaps += [ typemap ]
+          }
+        }
+      }
+
+      if (!cpp_only) {
+        generator_js_outputs =
+            [ "{{source_gen_dir}}/{{source_name_part}}.mojom.js" ]
+        generator_java_outputs =
+            [ "{{source_gen_dir}}/{{source_name_part}}.mojom.srcjar" ]
+      }
+      generator_target_name = "${target_name}${variant_suffix}__generator"
+      action_foreach(generator_target_name) {
+        script = mojom_generator_script
+        inputs = mojom_generator_sources
+        sources = invoker.sources
+        deps = [
+          ":$type_mappings_target_name",
+          "//mojo/public/tools/bindings:precompile_templates",
+        ]
+        outputs = generator_cpp_outputs + generator_java_outputs +
+                  generator_js_outputs
+        args = [
+          "--use_bundled_pylibs",
+          "generate",
+          "{{source}}",
+          "-d",
+          rebase_path("//", root_build_dir),
+          "-I",
+          rebase_path("//", root_build_dir),
+          "-o",
+          rebase_path(root_gen_dir),
+          "--bytecode_path",
+          rebase_path("$root_gen_dir/mojo/public/tools/bindings"),
+        ]
+
+        if (defined(invoker.import_dirs)) {
+          foreach(import_dir, invoker.import_dirs) {
+            args += [
+              "-I",
+              rebase_path(import_dir, root_build_dir),
+            ]
+          }
+        }
+
+        if (cpp_only) {
+          args += [
+            "-g",
+            "c++",
+          ]
+        } else {
+          args += [
+            "-g",
+            "c++,javascript,java",
+          ]
+        }
+
+        if (defined(bindings_configuration.variant)) {
+          args += [
+            "--variant",
+            bindings_configuration.variant,
+          ]
+        }
+
+        args += [
+          "--typemap",
+          rebase_path(type_mappings_path, root_build_dir),
+        ]
+
+        if (defined(bindings_configuration.for_blink) &&
+            bindings_configuration.for_blink) {
+          args += [ "--for_blink" ]
+        }
+
+        if (defined(invoker.use_new_wrapper_types) &&
+            invoker.use_new_wrapper_types) {
+          args += [ "--use_new_wrapper_types" ]
+        }
+      }
+    }
+
+    action(type_mappings_target_name) {
+      inputs = _bindings_configuration_files
+      outputs = [
+        type_mappings_path,
+      ]
+      script = "$mojom_generator_root/generate_type_mappings.py"
+      deps = []
+      args = [
+        "--output",
+        rebase_path(type_mappings_path, root_build_dir),
+      ]
+
+      foreach(d, all_deps) {
+        name = get_label_info(d, "label_no_toolchain")
+        toolchain = get_label_info(d, "toolchain")
+        dependency_output = "${name}${variant_suffix}__type_mappings"
+        dependency_target = "${dependency_output}(${toolchain})"
+        deps += [ dependency_target ]
+        dependency_output_dir =
+            get_label_info(dependency_output, "target_gen_dir")
+        dependency_name = get_label_info(dependency_output, "name")
+        dependency_path =
+            rebase_path("$dependency_output_dir/${dependency_name}",
+                        root_build_dir)
+        args += [
+          "--dependency",
+          dependency_path,
+        ]
+      }
+
+      if (enabled_sources != []) {
+        # TODO(sammc): Pass the typemap description in a file to avoid command
+        # line length limitations.
+        typemap_description = []
+        foreach(typemap, active_typemaps) {
+          typemap_description += [ "--start-typemap" ]
+          if (defined(typemap.public_headers)) {
+            foreach(value, typemap.public_headers) {
+              typemap_description += [ "public_headers=$value" ]
+            }
+          }
+          if (defined(typemap.traits_headers)) {
+            foreach(value, typemap.traits_headers) {
+              typemap_description += [ "traits_headers=$value" ]
+            }
+          }
+          foreach(value, typemap.type_mappings) {
+            typemap_description += [ "type_mappings=$value" ]
+          }
+        }
+        args += typemap_description
+      }
+    }
+
+    source_set("${target_name}${variant_suffix}") {
+      if (defined(invoker.visibility)) {
+        visibility = invoker.visibility
+      }
+      if (defined(invoker.testonly)) {
+        testonly = invoker.testonly
+      }
+      if (defined(invoker.sources) && !defined(bindings_configuration.variant)) {
+        data = process_file_template(enabled_sources, generator_js_outputs)
+      }
+
+      public_deps = [
+        ":${cpp_sources_target_name}",
+        "//mojo/public/cpp/bindings",
+      ]
+      if (defined(invoker.deps)) {
+        public_deps += invoker.deps
+      }
+      if (defined(invoker.public_deps)) {
+        public_deps += invoker.public_deps
+      }
+
+      deps = []
+      if (defined(invoker.sources)) {
+        public_deps += [ ":$generator_target_name" ]
+      }
+    }
+
+    # The generated C++ source files. The main reason to introduce this target
+    # is so that mojo/public/cpp/bindings can depend on mojom interfaces without
+    # circular dependencies. It means that the target is missing the dependency
+    # on mojo/public/cpp/bindings. No external targets should depend directly on
+    # this target *except* mojo/public/cpp/bindings and other *_cpp_sources
+    # targets.
+    source_set(cpp_sources_target_name) {
+      if (defined(invoker.testonly)) {
+        testonly = invoker.testonly
+      }
+      if (enabled_sources != []) {
+        sources = process_file_template(enabled_sources, generator_cpp_outputs)
+      }
+      deps = [
+        "//mojo/public/cpp/bindings:struct_traits",
+        "//mojo/public/interfaces/bindings:bindings__generator",
+      ]
+      if (enabled_sources != []) {
+        deps += [ ":$generator_target_name" ]
+      }
+      public_deps = [
+        "//base",
+      ]
+      foreach(d, all_deps) {
+        # Resolve the name, so that a target //mojo/something becomes
+        # //mojo/something:something and we can append cpp_sources_suffix to
+        # get the cpp dependency name.
+        full_name = get_label_info("$d", "label_no_toolchain")
+        public_deps += [ "${full_name}${variant_suffix}_${cpp_sources_suffix}" ]
+      }
+      foreach(typemap, active_typemaps) {
+        if (defined(typemap.public_headers)) {
+          sources += typemap.public_headers
+        }
+        if (defined(typemap.traits_headers)) {
+          sources += typemap.traits_headers
+        }
+        if (defined(typemap.sources)) {
+          sources += typemap.sources
+        }
+        if (defined(typemap.public_deps)) {
+          public_deps += typemap.public_deps
+        }
+        if (defined(typemap.deps)) {
+          deps += typemap.deps
+        }
+      }
+      if (defined(bindings_configuration.for_blink) &&
+          bindings_configuration.for_blink) {
+        public_deps += [ "//mojo/public/cpp/bindings:wtf_support" ]
+      }
+    }
+
+    if (!cpp_only && is_android) {
+      import("//build/config/android/rules.gni")
+
+      java_srcjar_target_name = target_name + "_java_sources"
+      action(java_srcjar_target_name) {
+        script = "//mojo/public/tools/gn/zip.py"
+        inputs = []
+        if (enabled_sources != []) {
+          inputs =
+              process_file_template(enabled_sources, generator_java_outputs)
+        }
+        output = "$target_gen_dir/$target_name.srcjar"
+        outputs = [
+          output,
+        ]
+        rebase_inputs = rebase_path(inputs, root_build_dir)
+        rebase_output = rebase_path(output, root_build_dir)
+        args = [
+          "--zip-inputs=$rebase_inputs",
+          "--output=$rebase_output",
+        ]
+        deps = []
+        if (enabled_sources != []) {
+          deps = [
+            ":$generator_target_name",
+          ]
+        }
+      }
+
+      java_target_name = target_name + "_java"
+      android_library(java_target_name) {
+        deps = [
+          "//base:base_java",
+          "//mojo/public/java:bindings",
+          "//mojo/public/java:system",
+        ]
+
+        foreach(d, all_deps) {
+          # Resolve the name, so that a target //mojo/something becomes
+          # //mojo/something:something and we can append "_java" to get the java
+          # dependency name.
+          full_name = get_label_info(d, "label_no_toolchain")
+          deps += [ "${full_name}_java" ]
+        }
+
+        srcjar_deps = [ ":$java_srcjar_target_name" ]
+        run_findbugs_override = false
+      }
+    }
+  }
+}
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..12196ba
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -0,0 +1,306 @@
+#!/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 json
+import os
+import pprint
+import re
+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_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
+import mojom.fileutil as fileutil
+from mojom.generate.data import OrderedModuleFromData
+from mojom.generate import template_expander
+from mojom.parse.parser import Parse
+from mojom.parse.translate import Translate
+
+
+_BUILTIN_GENERATORS = {
+  "c++": "mojom_cpp_generator.py",
+  "javascript": "mojom_js_generator.py",
+  "java": "mojom_java_generator.py",
+}
+
+
+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(",")]:
+    language = generator_name.lower()
+    if language in _BUILTIN_GENERATORS:
+      generator_name = os.path.join(script_dir, "generators",
+                                    _BUILTIN_GENERATORS[language])
+    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[language] = 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)]))
+
+
+class RelativePath(object):
+  """Represents a path relative to the source tree."""
+  def __init__(self, path, source_root):
+    self.path = path
+    self.source_root = source_root
+
+  def relative_path(self):
+    return os.path.relpath(os.path.abspath(self.path),
+                           os.path.abspath(self.source_root))
+
+
+def FindImportFile(rel_dir, file_name, search_rel_dirs):
+  """Finds |file_name| in either |rel_dir| or |search_rel_dirs|. Returns a
+  RelativePath with first file found, or an arbitrary non-existent file
+  otherwise."""
+  for rel_search_dir in [rel_dir] + search_rel_dirs:
+    path = os.path.join(rel_search_dir.path, file_name)
+    if os.path.isfile(path):
+      return RelativePath(path, rel_search_dir.source_root)
+  return RelativePath(os.path.join(rel_dir.path, file_name),
+                      rel_dir.source_root)
+
+
+class MojomProcessor(object):
+  def __init__(self, should_generate):
+    self._should_generate = should_generate
+    self._processed_files = {}
+    self._parsed_files = {}
+    self._typemap = {}
+
+  def LoadTypemaps(self, typemaps):
+    # Support some very simple single-line comments in typemap JSON.
+    comment_expr = r"^\s*//.*$"
+    def no_comments(line):
+      return not re.match(comment_expr, line)
+    for filename in typemaps:
+      with open(filename) as f:
+        typemaps = json.loads("".join(filter(no_comments, f.readlines())))
+        for language, typemap in typemaps.iteritems():
+          language_map = self._typemap.get(language, {})
+          language_map.update(typemap)
+          self._typemap[language] = language_map
+
+  def ProcessFile(self, args, remaining_args, generator_modules, filename):
+    self._ParseFileAndImports(RelativePath(filename, args.depth),
+                              args.import_directories, [])
+
+    return self._GenerateModule(args, remaining_args, generator_modules,
+        RelativePath(filename, args.depth))
+
+  def _GenerateModule(self, args, remaining_args, generator_modules,
+                      rel_filename):
+    # Return the already-generated module.
+    if rel_filename.path in self._processed_files:
+      return self._processed_files[rel_filename.path]
+    tree = self._parsed_files[rel_filename.path]
+
+    dirname, name = os.path.split(rel_filename.path)
+    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']:
+      rel_import_file = FindImportFile(
+          RelativePath(dirname, rel_filename.source_root),
+          import_data['filename'], args.import_directories)
+      import_data['module'] = self._GenerateModule(
+          args, remaining_args, generator_modules, rel_import_file)
+
+    module = OrderedModuleFromData(mojom)
+
+    # Set the path as relative to the source root.
+    module.path = rel_filename.relative_path()
+
+    # Normalize to unix-style path here to keep the generators simpler.
+    module.path = module.path.replace('\\', '/')
+
+    if self._should_generate(rel_filename.path):
+      for language, generator_module in generator_modules.iteritems():
+        generator = generator_module.Generator(
+            module, args.output_dir, typemap=self._typemap.get(language, {}),
+            variant=args.variant, bytecode_path=args.bytecode_path,
+            for_blink=args.for_blink,
+            use_new_wrapper_types=args.use_new_wrapper_types)
+        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.
+    self._processed_files[rel_filename.path] = module
+    return module
+
+  def _ParseFileAndImports(self, rel_filename, import_directories,
+      imported_filename_stack):
+    # Ignore already-parsed files.
+    if rel_filename.path in self._parsed_files:
+      return
+
+    if rel_filename.path in imported_filename_stack:
+      print "%s: Error: Circular dependency" % rel_filename.path + \
+          MakeImportStackMessage(imported_filename_stack + [rel_filename.path])
+      sys.exit(1)
+
+    try:
+      with open(rel_filename.path) as f:
+        source = f.read()
+    except IOError as e:
+      print "%s: Error: %s" % (e.rel_filename.path, e.strerror) + \
+          MakeImportStackMessage(imported_filename_stack + [rel_filename.path])
+      sys.exit(1)
+
+    try:
+      tree = Parse(source, rel_filename.path)
+    except Error as e:
+      full_stack = imported_filename_stack + [rel_filename.path]
+      print str(e) + MakeImportStackMessage(full_stack)
+      sys.exit(1)
+
+    dirname = os.path.split(rel_filename.path)[0]
+    for imp_entry in tree.import_list:
+      import_file_entry = FindImportFile(
+          RelativePath(dirname, rel_filename.source_root),
+          imp_entry.import_filename, import_directories)
+      self._ParseFileAndImports(import_file_entry, import_directories,
+          imported_filename_stack + [rel_filename.path])
+
+    self._parsed_files[rel_filename.path] = tree
+
+
+def _Generate(args, remaining_args):
+  if args.variant == "none":
+    args.variant = None
+
+  for idx, import_dir in enumerate(args.import_directories):
+    tokens = import_dir.split(":")
+    if len(tokens) >= 2:
+      args.import_directories[idx] = RelativePath(tokens[0], tokens[1])
+    else:
+      args.import_directories[idx] = RelativePath(tokens[0], args.depth)
+  generator_modules = LoadGenerators(args.generators_string)
+
+  fileutil.EnsureDirectoryExists(args.output_dir)
+
+  processor = MojomProcessor(lambda filename: filename in args.filename)
+  processor.LoadTypemaps(set(args.typemaps))
+  for filename in args.filename:
+    processor.ProcessFile(args, remaining_args, generator_modules, filename)
+
+  return 0
+
+
+def _Precompile(args, _):
+  generator_modules = LoadGenerators(",".join(_BUILTIN_GENERATORS.keys()))
+
+  template_expander.PrecompileTemplates(generator_modules, args.output_dir)
+  return 0
+
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="Generate bindings from mojom files.")
+  parser.add_argument("--use_bundled_pylibs", action="store_true",
+                      help="use Python modules bundled in the SDK")
+
+  subparsers = parser.add_subparsers()
+  generate_parser = subparsers.add_parser(
+      "generate", description="Generate bindings from mojom files.")
+  generate_parser.add_argument("filename", nargs="+",
+                               help="mojom input file")
+  generate_parser.add_argument("-d", "--depth", dest="depth", default=".",
+                               help="depth from source root")
+  generate_parser.add_argument("-o", "--output_dir", dest="output_dir",
+                               default=".",
+                               help="output directory for generated files")
+  generate_parser.add_argument("--debug_print_intermediate",
+                               action="store_true",
+                               help="print the intermediate representation")
+  generate_parser.add_argument("-g", "--generators",
+                               dest="generators_string",
+                               metavar="GENERATORS",
+                               default="c++,javascript,java",
+                               help="comma-separated list of generators")
+  generate_parser.add_argument(
+      "-I", dest="import_directories", action="append", metavar="directory",
+      default=[],
+      help="add a directory to be searched for import files. The depth from "
+           "source root can be specified for each import by appending it after "
+           "a colon")
+  generate_parser.add_argument("--typemap", action="append", metavar="TYPEMAP",
+                               default=[], dest="typemaps",
+                               help="apply TYPEMAP to generated output")
+  generate_parser.add_argument("--variant", dest="variant", default=None,
+                               help="output a named variant of the bindings")
+  generate_parser.add_argument(
+      "--bytecode_path", type=str, required=True, help=(
+          "the path from which to load template bytecode; to generate template "
+          "bytecode, run %s precompile BYTECODE_PATH" % os.path.basename(
+              sys.argv[0])))
+  generate_parser.add_argument("--for_blink", action="store_true",
+                               help="Use WTF types as generated types for mojo "
+                               "string/array/map.")
+  generate_parser.add_argument(
+      "--use_new_wrapper_types", action="store_true",
+      help="Map mojom array/map/string to STL (for chromium variant) or WTF "
+      "(for blink variant) types directly.")
+  generate_parser.set_defaults(func=_Generate)
+
+  precompile_parser = subparsers.add_parser("precompile",
+      description="Precompile templates for the mojom bindings generator.")
+  precompile_parser.add_argument(
+      "-o", "--output_dir", dest="output_dir", default=".",
+      help="output directory for precompiled templates")
+  precompile_parser.set_defaults(func=_Precompile)
+
+  args, remaining_args = parser.parse_known_args()
+  return args.func(args, remaining_args)
+
+
+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/mojom_list_outputs.py b/mojo/public/tools/bindings/mojom_list_outputs.py
new file mode 100755
index 0000000..267bd80
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_list_outputs.py
@@ -0,0 +1,44 @@
+#!/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
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="GYP helper script for mapping mojoms => generated outputs.")
+  parser.add_argument("--basedir", required=True)
+  parser.add_argument("--variant", required=True)
+  parser.add_argument("mojom", nargs="*")
+
+  args = parser.parse_args()
+
+  variant = args.variant if args.variant != "none" else None
+
+  for mojom in args.mojom:
+    full = os.path.join("<(SHARED_INTERMEDIATE_DIR)", args.basedir, mojom)
+    base, ext = os.path.splitext(full)
+
+    # Ignore non-mojom files.
+    if ext != ".mojom":
+      continue
+
+    # Fix filename escaping issues on Windows.
+    base = base.replace("\\", "/")
+    if variant:
+      print base + ".mojom-%s.cc" % variant
+      print base + ".mojom-%s.h" % variant
+      print base + ".mojom-%s-internal.h" % variant
+    else:
+      print base + ".mojom.cc"
+      print base + ".mojom.h"
+      print base + ".mojom-internal.h"
+      print base + ".mojom.js"
+
+  return 0
+
+if __name__ == "__main__":
+  sys.exit(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/fileutil.py b/mojo/public/tools/bindings/pylib/mojom/fileutil.py
new file mode 100644
index 0000000..b321e9f
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/fileutil.py
@@ -0,0 +1,18 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import errno
+import os.path
+
+def EnsureDirectoryExists(path, always_try_to_create=False):
+  """A wrapper for os.makedirs that does not error if the directory already
+  exists. A different process could be racing to create this directory."""
+
+  if not os.path.exists(path) or always_try_to_create:
+    try:
+      os.makedirs(path)
+    except OSError as e:
+      # There may have been a race to create this directory.
+      if e.errno != errno.EEXIST:
+        raise
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/constant_resolver.py b/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py
new file mode 100644
index 0000000..c8b21f2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py
@@ -0,0 +1,91 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Resolves the values used for constants and enums."""
+
+from itertools import ifilter
+import mojom.generate.module as mojom
+
+def ResolveConstants(module, expression_to_text):
+  in_progress = set()
+  computed = set()
+
+  def GetResolvedValue(named_value):
+    assert isinstance(named_value, (mojom.EnumValue, mojom.ConstantValue))
+    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.resolved_value
+    else:
+      ResolveConstant(named_value.constant)
+      named_value.resolved_value = named_value.constant.resolved_value
+      return named_value.resolved_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)):
+      resolved_value = GetResolvedValue(constant.value)
+    else:
+      resolved_value = expression_to_text(constant.value)
+    constant.resolved_value = resolved_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):
+          resolved_value = GetResolvedValue(field.value)
+        elif isinstance(field.value, str):
+          resolved_value = int(field.value, 0)
+        else:
+          raise RuntimeError('Unexpected value: %s' % field.value)
+      else:
+        resolved_value = default_value
+      field.resolved_value = resolved_value
+      in_progress.remove(field)
+      computed.add(field)
+
+    current_value = 0
+    for field in enum.fields:
+      ResolveEnumField(enum, field, current_value)
+      current_value = field.resolved_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.resolved_value = GetResolvedValue(field.default)
+
+  for interface in module.interfaces:
+    for constant in interface.constants:
+      ResolveConstant(constant)
+    for enum in interface.enums:
+      ResolveEnum(enum)
+
+  return module
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..9ceb2e9
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
@@ -0,0 +1,530 @@
+# 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
+
+def AddOptional(dictionary, key, value):
+  if value is not None:
+    dictionary[key] = value;
+
+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('asso:'):
+    inner_kind = KindFromData(kinds, data[5:], scope)
+    if isinstance(inner_kind, mojom.InterfaceRequest):
+      kind = mojom.AssociatedInterfaceRequest(inner_kind)
+    else:
+      kind = mojom.AssociatedInterface(inner_kind)
+  elif data.startswith('a'):
+    colon = data.find(':')
+    length = int(data[1:colon])
+    kind = mojom.Array(KindFromData(kinds, data[colon+1:], scope), length)
+  elif data.startswith('r:'):
+    kind = mojom.InterfaceRequest(KindFromData(kinds, data[2:], scope))
+  elif data.startswith('m['):
+    # Isolate the two types from their brackets.
+
+    # It is not allowed to use map as key, so there shouldn't be nested ']'s
+    # inside the key type spec.
+    key_end = data.find(']')
+    assert key_end != -1 and key_end < len(data) - 1
+    assert data[key_end+1] == '[' and data[-1] == ']'
+
+    first_kind = data[2:key_end]
+    second_kind = data[key_end+2:-1]
+
+    kind = mojom.Map(KindFromData(kinds, first_kind, scope),
+                     KindFromData(kinds, second_kind, 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, Unions, 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.
+  importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface)
+  for kind in import_module.kinds.itervalues():
+    if (isinstance(kind, importable_kinds) 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):
+  data = {
+    istr(0, 'name'): struct.name,
+    istr(1, 'fields'): map(FieldToData, struct.fields),
+    # TODO(yzshen): EnumToData() and ConstantToData() are missing.
+    istr(2, 'enums'): [],
+    istr(3, 'constants'): []
+  }
+  AddOptional(data, istr(4, 'attributes'), struct.attributes)
+  return data
+
+def StructFromData(module, data):
+  struct = mojom.Struct(module=module)
+  struct.name = data['name']
+  struct.native_only = data['native_only']
+  struct.spec = 'x:' + module.namespace + '.' + struct.name
+  module.kinds[struct.spec] = struct
+  if struct.native_only:
+    struct.enums = []
+    struct.constants = []
+    struct.fields_data = []
+  else:
+    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']
+  struct.attributes = data.get('attributes')
+
+  # Enforce that a [Native] attribute is set to make native-only struct
+  # declarations more explicit.
+  if struct.native_only:
+    if not struct.attributes or not struct.attributes.get('Native', False):
+      raise Exception("Native-only struct declarations must include a " +
+                      "Native attribute.")
+
+  return struct
+
+def UnionToData(union):
+  data = {
+    istr(0, 'name'): union.name,
+    istr(1, 'fields'): map(FieldToData, union.fields)
+  }
+  AddOptional(data, istr(2, 'attributes'), union.attributes)
+  return data
+
+def UnionFromData(module, data):
+  union = mojom.Union(module=module)
+  union.name = data['name']
+  union.spec = 'x:' + module.namespace + '.' + union.name
+  module.kinds[union.spec] = union
+  # Stash fields data here temporarily.
+  union.fields_data = data['fields']
+  union.attributes = data.get('attributes')
+  return union
+
+def FieldToData(field):
+  data = {
+    istr(0, 'name'): field.name,
+    istr(1, 'kind'): KindToData(field.kind)
+  }
+  AddOptional(data, istr(2, 'ordinal'), field.ordinal)
+  AddOptional(data, istr(3, 'default'), field.default)
+  AddOptional(data, istr(4, 'attributes'), field.attributes)
+  return data
+
+def StructFieldFromData(module, data, struct):
+  field = mojom.StructField()
+  PopulateField(field, module, data, struct)
+  return field
+
+def UnionFieldFromData(module, data, union):
+  field = mojom.UnionField()
+  PopulateField(field, module, data, union)
+  return field
+
+def PopulateField(field, module, data, parent):
+  field.name = data['name']
+  field.kind = KindFromData(
+      module.kinds, data['kind'], (module.namespace, parent.name))
+  field.ordinal = data.get('ordinal')
+  field.default = FixupExpression(
+      module, data.get('default'), (module.namespace, parent.name), field.kind)
+  field.attributes = data.get('attributes')
+
+def ParameterToData(parameter):
+  data = {
+    istr(0, 'name'): parameter.name,
+    istr(1, 'kind'): parameter.kind.spec
+  }
+  AddOptional(data, istr(2, 'ordinal'), parameter.ordinal)
+  AddOptional(data, istr(3, 'default'), parameter.default)
+  AddOptional(data, istr(4, 'attributes'), parameter.attributes)
+  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')
+  parameter.attributes = data.get('attributes')
+  return parameter
+
+def MethodToData(method):
+  data = {
+    istr(0, 'name'):       method.name,
+    istr(1, 'parameters'): map(ParameterToData, method.parameters)
+  }
+  if method.response_parameters is not None:
+    data[istr(2, 'response_parameters')] = map(
+        ParameterToData, method.response_parameters)
+  AddOptional(data, istr(3, 'ordinal'), method.ordinal)
+  AddOptional(data, istr(4, 'attributes'), method.attributes)
+  return data
+
+def MethodFromData(module, data, interface):
+  method = mojom.Method(interface, data['name'], ordinal=data.get('ordinal'))
+  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'])
+  method.attributes = data.get('attributes')
+
+  # Enforce that only methods with response can have a [Sync] attribute.
+  if method.sync and method.response_parameters is None:
+    raise Exception("Only methods with response can include a [Sync] "
+                    "attribute. If no response parameters are needed, you "
+                    "could use an empty response parameter list, i.e., "
+                    "\"=> ()\".")
+
+  return method
+
+def InterfaceToData(interface):
+  data = {
+    istr(0, 'name'):    interface.name,
+    istr(1, 'methods'): map(MethodToData, interface.methods),
+    # TODO(yzshen): EnumToData() and ConstantToData() are missing.
+    istr(2, 'enums'): [],
+    istr(3, 'constants'): []
+  }
+  AddOptional(data, istr(4, 'attributes'), interface.attributes)
+  return data
+
+def InterfaceFromData(module, data):
+  interface = mojom.Interface(module=module)
+  interface.name = data['name']
+  interface.spec = 'x:' + module.namespace + '.' + interface.name
+  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']
+  interface.attributes = data.get('attributes')
+  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.get('value'), (module.namespace, parent_kind.name), enum)
+  else:
+    field.value = FixupExpression(
+        module, data.get('value'), (module.namespace, ), enum)
+  field.attributes = data.get('attributes')
+  value = mojom.EnumValue(module, enum, field)
+  module.values[value.GetSpec()] = value
+  return field
+
+def ResolveNumericEnumValues(enum_fields):
+  """
+  Given a reference to a list of mojom.EnumField, resolves and assigns their
+  values to EnumField.numeric_value.
+  """
+
+  # map of <name> -> integral value
+  resolved_enum_values = {}
+  prev_value = -1
+  for field in enum_fields:
+    # This enum value is +1 the previous enum value (e.g: BEGIN).
+    if field.value is None:
+      prev_value += 1
+
+    # Integral value (e.g: BEGIN = -0x1).
+    elif type(field.value) is str:
+      prev_value = int(field.value, 0)
+
+    # Reference to a previous enum value (e.g: INIT = BEGIN).
+    elif type(field.value) is mojom.EnumValue:
+      prev_value = resolved_enum_values[field.value.name]
+    else:
+      raise Exception("Unresolved enum value.")
+
+    resolved_enum_values[field.name] = prev_value
+    field.numeric_value = prev_value
+
+def EnumFromData(module, data, parent_kind):
+  enum = mojom.Enum(module=module)
+  enum.name = data['name']
+  enum.native_only = data['native_only']
+  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.attributes = data.get('attributes')
+  if enum.native_only:
+    enum.fields = []
+  else:
+    enum.fields = map(
+        lambda field: EnumFieldFromData(module, enum, field, parent_kind),
+        data['fields'])
+    ResolveNumericEnumValues(enum.fields)
+
+  module.kinds[enum.spec] = enum
+
+  # Enforce that a [Native] attribute is set to make native-only enum
+  # declarations more explicit.
+  if enum.native_only:
+    if not enum.attributes or not enum.attributes.get('Native', False):
+      raise Exception("Native-only enum declarations must include a " +
+                      "Native attribute.")
+
+  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.parent_kind = parent_kind
+  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):
+  data = {
+    istr(0, 'name'):       module.name,
+    istr(1, 'namespace'):  module.namespace,
+    # TODO(yzshen): Imports information is missing.
+    istr(2, 'imports'): [],
+    istr(3, 'structs'):    map(StructToData, module.structs),
+    istr(4, 'unions'):     map(UnionToData, module.unions),
+    istr(5, 'interfaces'): map(InterfaceToData, module.interfaces),
+    # TODO(yzshen): EnumToData() and ConstantToData() are missing.
+    istr(6, 'enums'): [],
+    istr(7, 'constants'): []
+  }
+  AddOptional(data, istr(8, 'attributes'), module.attributes)
+  return data
+
+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']
+  # 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'])
+  module.attributes = data.get('attributes')
+
+  # 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.unions = map(
+      lambda union: UnionFromData(module, union), data.get('unions', []))
+  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:
+        StructFieldFromData(module, field, struct), struct.fields_data)
+    del struct.fields_data
+  for union in module.unions:
+    union.fields = map(lambda field:
+        UnionFieldFromData(module, field, union), union.fields_data)
+    del union.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):
+  """Convert Mojom IR to a module.
+
+  Args:
+    data: The Mojom IR as a dict.
+
+  Returns:
+    A mojom.generate.module.Module object.
+  """
+  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..ccb8363
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -0,0 +1,148 @@
+# 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 mojom.fileutil as fileutil
+import pack
+
+def ExpectedArraySize(kind):
+  if mojom.IsArrayKind(kind):
+    return kind.length
+  return None
+
+def StudlyCapsToCamel(studly):
+  return studly[0].lower() + studly[1:]
+
+def UnderToCamel(under):
+  """Converts underscore_separated strings to CamelCase strings."""
+  return ''.join(word.capitalize() for word in under.split('_'))
+
+def WriteFile(contents, full_path):
+  # Make sure the containing directory exists.
+  full_dir = os.path.dirname(full_path)
+  fileutil.EnsureDirectoryExists(full_dir)
+
+  # Dump the data to disk.
+  with open(full_path, "w+") as f:
+    f.write(contents)
+
+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, typemap=None, variant=None,
+               bytecode_path=None, for_blink=False,
+               use_new_wrapper_types=False):
+    self.module = module
+    self.output_dir = output_dir
+    self.typemap = typemap or {}
+    self.variant = variant
+    self.bytecode_path = bytecode_path
+    self.for_blink = for_blink
+    self.use_new_wrapper_types = use_new_wrapper_types
+
+  def GetStructsFromMethods(self):
+    result = []
+    for interface in self.module.interfaces:
+      for method in interface.methods:
+        result.append(self._GetStructFromMethod(method))
+        if method.response_parameters != None:
+          result.append(self._GetResponseStructFromMethod(method))
+    return result
+
+  def GetStructs(self):
+    return map(partial(self._AddStructComputedData, True), self.module.structs)
+
+  def GetUnions(self):
+    return map(self._AddUnionComputedData, self.module.unions)
+
+  def GetInterfaces(self):
+    return map(self._AddInterfaceComputedData, self.module.interfaces)
+
+  # Prepend the filename with a directory that matches the directory of the
+  # original .mojom file, relative to the import root.
+  def MatchMojomFilePath(self, filename):
+    return os.path.join(os.path.dirname(self.module.path), filename)
+
+  def Write(self, contents, filename):
+    if self.output_dir is None:
+      print contents
+      return
+    full_path = os.path.join(self.output_dir, filename)
+    WriteFile(contents, full_path)
+
+  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 {}
+
+  def _AddStructComputedData(self, exported, struct):
+    """Adds computed data to the given struct. The data is computed once and
+    used repeatedly in the generation process."""
+    struct.packed = pack.PackedStruct(struct)
+    struct.bytes = pack.GetByteLayout(struct.packed)
+    struct.versions = pack.GetVersionInfo(struct.packed)
+    struct.exported = exported
+    return struct
+
+  def _AddUnionComputedData(self, union):
+    """Adds computed data to the given union. The data is computed once and
+    used repeatedly in the generation process."""
+    ordinal = 0
+    for field in union.fields:
+      if field.ordinal is not None:
+        ordinal = field.ordinal
+      field.ordinal = ordinal
+      ordinal += 1
+    return union
+
+  def _AddInterfaceComputedData(self, interface):
+    """Adds computed data to the given interface. The data is computed once and
+    used repeatedly in the generation process."""
+    interface.version = 0
+    for method in interface.methods:
+      if method.min_version is not None:
+        interface.version = max(interface.version, method.min_version)
+
+      method.param_struct = self._GetStructFromMethod(method)
+      interface.version = max(interface.version,
+                              method.param_struct.versions[-1].version)
+
+      if method.response_parameters is not None:
+        method.response_param_struct = self._GetResponseStructFromMethod(method)
+        interface.version = max(
+            interface.version,
+            method.response_param_struct.versions[-1].version)
+      else:
+        method.response_param_struct = None
+    return interface
+
+  def _GetStructFromMethod(self, 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,
+                      attributes=param.attributes)
+    return self._AddStructComputedData(False, struct)
+
+  def _GetResponseStructFromMethod(self, 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,
+                      attributes=param.attributes)
+    return self._AddStructComputedData(False, struct)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py
new file mode 100644
index 0000000..9966b0b
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py
@@ -0,0 +1,24 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import module as mojom
+import generator
+
+class TestGenerator(unittest.TestCase):
+
+  def testGetUnionsAddsOrdinals(self):
+    module = mojom.Module()
+    union = module.AddUnion('a')
+    union.AddField('a', mojom.BOOL)
+    union.AddField('b', mojom.BOOL)
+    union.AddField('c', mojom.BOOL, ordinal=10)
+    union.AddField('d', mojom.BOOL)
+
+    gen = generator.Generator(module)
+    union = gen.GetUnions()[0]
+    ordinals = [field.ordinal for field in union.fields]
+
+    self.assertEquals([0, 1, 10, 11], ordinals)
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..2775b6c
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -0,0 +1,840 @@
+# 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)
+
+
+# We use our own version of __repr__ when displaying the AST, as the
+# AST currently doesn't capture which nodes are reference (e.g. to
+# types) and which nodes are definitions. This allows us to e.g. print
+# the definition of a struct when it's defined inside a module, but
+# only print its name when it's referenced in e.g. a method parameter.
+def Repr(obj, as_ref=True):
+  """A version of __repr__ that can distinguish references.
+
+  Sometimes we like to print an object's full representation
+  (e.g. with its fields) and sometimes we just want to reference an
+  object that was printed in full elsewhere. This function allows us
+  to make that distinction.
+
+  Args:
+    obj: The object whose string representation we compute.
+    as_ref: If True, use the short reference representation.
+
+  Returns:
+    A str representation of |obj|.
+  """
+  if hasattr(obj, 'Repr'):
+    return obj.Repr(as_ref=as_ref)
+  # Since we cannot implement Repr for existing container types, we
+  # handle them here.
+  elif isinstance(obj, list):
+    if not obj:
+      return '[]'
+    else:
+      return ('[\n%s\n]' % (',\n'.join('    %s' % Repr(elem, as_ref).replace(
+          '\n', '\n    ') for elem in obj)))
+  elif isinstance(obj, dict):
+    if not obj:
+      return '{}'
+    else:
+      return ('{\n%s\n}' % (',\n'.join('    %s: %s' % (
+          Repr(key, as_ref).replace('\n', '\n    '),
+          Repr(val, as_ref).replace('\n', '\n    '))
+          for key, val in obj.iteritems())))
+  else:
+    return repr(obj)
+
+
+def GenericRepr(obj, names):
+  """Compute generic Repr for |obj| based on the attributes in |names|.
+
+  Args:
+    obj: The object to compute a Repr for.
+    names: A dict from attribute names to include, to booleans
+        specifying whether those attributes should be shown as
+        references or not.
+
+  Returns:
+    A str representation of |obj|.
+  """
+  def ReprIndent(name, as_ref):
+    return '    %s=%s' % (name, Repr(getattr(obj, name), as_ref).replace(
+        '\n', '\n    '))
+
+  return '%s(\n%s\n)' % (
+      obj.__class__.__name__,
+      ',\n'.join(ReprIndent(name, as_ref)
+                 for (name, as_ref) in names.iteritems()))
+
+
+class Kind(object):
+  """Kind represents a type (e.g. int8, string).
+
+  Attributes:
+    spec: A string uniquely identifying the type. May be None.
+    parent_kind: The enclosing type. For example, a struct defined
+        inside an interface has that interface as its parent. May be None.
+  """
+  def __init__(self, spec=None):
+    self.spec = spec
+    self.parent_kind = None
+
+  def Repr(self, as_ref=True):
+    return '<%s spec=%r>' % (self.__class__.__name__, self.spec)
+
+  def __repr__(self):
+    # Gives us a decent __repr__ for all kinds.
+    return self.Repr()
+
+
+class ReferenceKind(Kind):
+  """ReferenceKind represents pointer 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.
+
+  Attributes:
+    is_nullable: True if the type is nullable.
+  """
+
+  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 Repr(self, as_ref=True):
+    return '<%s spec=%r is_nullable=%r>' % (self.__class__.__name__, self.spec,
+                                            self.is_nullable)
+
+  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
+)
+
+
+ATTRIBUTE_MIN_VERSION = 'MinVersion'
+ATTRIBUTE_EXTENSIBLE = 'Extensible'
+ATTRIBUTE_SYNC = 'Sync'
+
+
+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, parent_kind=None):
+    self.name = name
+    self.kind = kind
+    self.value = value
+    self.parent_kind = parent_kind
+
+
+class Field(object):
+  def __init__(self, name=None, kind=None, ordinal=None, default=None,
+               attributes=None):
+    if self.__class__.__name__ == 'Field':
+      raise Exception()
+    self.name = name
+    self.kind = kind
+    self.ordinal = ordinal
+    self.default = default
+    self.attributes = attributes
+
+  def Repr(self, as_ref=True):
+    # Fields are only referenced by objects which define them and thus
+    # they are always displayed as non-references.
+    return GenericRepr(self, {'name': False, 'kind': True})
+
+  @property
+  def min_version(self):
+    return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+        if self.attributes else None
+
+
+class StructField(Field): pass
+
+
+class UnionField(Field): pass
+
+
+class Struct(ReferenceKind):
+  ReferenceKind.AddSharedProperty('name')
+  ReferenceKind.AddSharedProperty('native_only')
+  ReferenceKind.AddSharedProperty('module')
+  ReferenceKind.AddSharedProperty('imported_from')
+  ReferenceKind.AddSharedProperty('fields')
+  ReferenceKind.AddSharedProperty('attributes')
+
+  def __init__(self, name=None, module=None, attributes=None):
+    if name is not None:
+      spec = 'x:' + name
+    else:
+      spec = None
+    ReferenceKind.__init__(self, spec)
+    self.name = name
+    self.native_only = False
+    self.module = module
+    self.imported_from = None
+    self.fields = []
+    self.attributes = attributes
+
+  def Repr(self, as_ref=True):
+    if as_ref:
+      return '<%s name=%r imported_from=%s>' % (
+          self.__class__.__name__, self.name,
+          Repr(self.imported_from, as_ref=True))
+    else:
+      return GenericRepr(self, {'name': False, 'fields': False,
+                                'imported_from': True})
+
+  def AddField(self, name, kind, ordinal=None, default=None, attributes=None):
+    field = StructField(name, kind, ordinal, default, attributes)
+    self.fields.append(field)
+    return field
+
+
+class Union(ReferenceKind):
+  """A union of several kinds.
+
+  Attributes:
+    name: {str} The name of the union type.
+    module: {Module} The defining module.
+    imported_from: {dict} Information about where this union was
+        imported from.
+    fields: {List[UnionField]} The members of the union.
+    attributes: {dict} Additional information about the union, such as
+        which Java class name to use to represent it in the generated
+        bindings.
+  """
+  ReferenceKind.AddSharedProperty('name')
+  ReferenceKind.AddSharedProperty('module')
+  ReferenceKind.AddSharedProperty('imported_from')
+  ReferenceKind.AddSharedProperty('fields')
+  ReferenceKind.AddSharedProperty('attributes')
+
+  def __init__(self, name=None, module=None, attributes=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 = []
+    self.attributes = attributes
+
+  def Repr(self, as_ref=True):
+    if as_ref:
+      return '<%s spec=%r is_nullable=%r fields=%s>' % (
+          self.__class__.__name__, self.spec, self.is_nullable,
+          Repr(self.fields))
+    else:
+      return GenericRepr(self, {'fields': True, 'is_nullable': False})
+
+  def AddField(self, name, kind, ordinal=None, attributes=None):
+    field = UnionField(name, kind, ordinal, None, attributes)
+    self.fields.append(field)
+    return field
+
+
+class Array(ReferenceKind):
+  """An array.
+
+  Attributes:
+    kind: {Kind} The type of the elements. May be None.
+    length: The number of elements. None if unknown.
+  """
+
+  ReferenceKind.AddSharedProperty('kind')
+  ReferenceKind.AddSharedProperty('length')
+
+  def __init__(self, kind=None, length=None):
+    if kind is not None:
+      if length is not None:
+        spec = 'a%d:%s' % (length, kind.spec)
+      else:
+        spec = 'a:%s' % kind.spec
+
+      ReferenceKind.__init__(self, spec)
+    else:
+      ReferenceKind.__init__(self)
+    self.kind = kind
+    self.length = length
+
+  def Repr(self, as_ref=True):
+    if as_ref:
+      return '<%s spec=%r is_nullable=%r kind=%s length=%r>' % (
+          self.__class__.__name__, self.spec, self.is_nullable, Repr(self.kind),
+          self.length)
+    else:
+      return GenericRepr(self, {'kind': True, 'length': False,
+                                'is_nullable': False})
+
+
+class Map(ReferenceKind):
+  """A map.
+
+  Attributes:
+    key_kind: {Kind} The type of the keys. May be None.
+    value_kind: {Kind} The type of the elements. May be None.
+  """
+  ReferenceKind.AddSharedProperty('key_kind')
+  ReferenceKind.AddSharedProperty('value_kind')
+
+  def __init__(self, key_kind=None, value_kind=None):
+    if (key_kind is not None and value_kind is not None):
+      ReferenceKind.__init__(self,
+                             'm[' + key_kind.spec + '][' + value_kind.spec +
+                             ']')
+      if IsNullableKind(key_kind):
+        raise Exception("Nullable kinds cannot be keys in maps.")
+      if IsStructKind(key_kind):
+        # TODO(erg): It would sometimes be nice if we could key on struct
+        # values. However, what happens if the struct has a handle in it? Or
+        # non-copyable data like an array?
+        raise Exception("Structs cannot be keys in maps.")
+      if IsAnyHandleKind(key_kind):
+        raise Exception("Handles cannot be keys in maps.")
+      if IsInterfaceKind(key_kind):
+        raise Exception("Interfaces cannot be keys in maps.")
+      if IsArrayKind(key_kind):
+        raise Exception("Arrays cannot be keys in maps.")
+    else:
+      ReferenceKind.__init__(self)
+
+    self.key_kind = key_kind
+    self.value_kind = value_kind
+
+  def Repr(self, as_ref=True):
+    if as_ref:
+      return '<%s spec=%r is_nullable=%r key_kind=%s value_kind=%s>' % (
+          self.__class__.__name__, self.spec, self.is_nullable,
+          Repr(self.key_kind), Repr(self.value_kind))
+    else:
+      return GenericRepr(self, {'key_kind': True, 'value_kind': True})
+
+
+class InterfaceRequest(ReferenceKind):
+  ReferenceKind.AddSharedProperty('kind')
+
+  def __init__(self, kind=None):
+    if kind is not None:
+      if not isinstance(kind, Interface):
+        raise Exception(
+            "Interface request requires %r to be an interface." % kind.spec)
+      ReferenceKind.__init__(self, 'r:' + kind.spec)
+    else:
+      ReferenceKind.__init__(self)
+    self.kind = kind
+
+
+class AssociatedInterfaceRequest(ReferenceKind):
+  ReferenceKind.AddSharedProperty('kind')
+
+  def __init__(self, kind=None):
+    if kind is not None:
+      if not isinstance(kind, InterfaceRequest):
+        raise Exception(
+            "Associated interface request requires %r to be an interface "
+            "request." % kind.spec)
+      assert not kind.is_nullable
+      ReferenceKind.__init__(self, 'asso:' + kind.spec)
+    else:
+      ReferenceKind.__init__(self)
+    self.kind = kind.kind if kind is not None else None
+
+
+class Parameter(object):
+  def __init__(self, name=None, kind=None, ordinal=None, default=None,
+               attributes=None):
+    self.name = name
+    self.ordinal = ordinal
+    self.kind = kind
+    self.default = default
+    self.attributes = attributes
+
+  def Repr(self, as_ref=True):
+    return '<%s name=%r kind=%s>' % (self.__class__.__name__, self.name,
+                                     self.kind.Repr(as_ref=True))
+
+  @property
+  def min_version(self):
+    return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+        if self.attributes else None
+
+
+class Method(object):
+  def __init__(self, interface, name, ordinal=None, attributes=None):
+    self.interface = interface
+    self.name = name
+    self.ordinal = ordinal
+    self.parameters = []
+    self.response_parameters = None
+    self.attributes = attributes
+
+  def Repr(self, as_ref=True):
+    if as_ref:
+      return '<%s name=%r>' % (self.__class__.__name__, self.name)
+    else:
+      return GenericRepr(self, {'name': False, 'parameters': True,
+                                'response_parameters': True})
+
+  def AddParameter(self, name, kind, ordinal=None, default=None,
+                   attributes=None):
+    parameter = Parameter(name, kind, ordinal, default, attributes)
+    self.parameters.append(parameter)
+    return parameter
+
+  def AddResponseParameter(self, name, kind, ordinal=None, default=None,
+                           attributes=None):
+    if self.response_parameters == None:
+      self.response_parameters = []
+    parameter = Parameter(name, kind, ordinal, default, attributes)
+    self.response_parameters.append(parameter)
+    return parameter
+
+  @property
+  def min_version(self):
+    return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+        if self.attributes else None
+
+  @property
+  def sync(self):
+    return self.attributes.get(ATTRIBUTE_SYNC) \
+        if self.attributes else None
+
+
+class Interface(ReferenceKind):
+  ReferenceKind.AddSharedProperty('module')
+  ReferenceKind.AddSharedProperty('name')
+  ReferenceKind.AddSharedProperty('imported_from')
+  ReferenceKind.AddSharedProperty('methods')
+  ReferenceKind.AddSharedProperty('attributes')
+
+  def __init__(self, name=None, module=None, attributes=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.methods = []
+    self.attributes = attributes
+
+  def Repr(self, as_ref=True):
+    if as_ref:
+      return '<%s name=%r>' % (self.__class__.__name__, self.name)
+    else:
+      return GenericRepr(self, {'name': False, 'attributes': False,
+                                'methods': False})
+
+  def AddMethod(self, name, ordinal=None, attributes=None):
+    method = Method(self, name, ordinal, attributes)
+    self.methods.append(method)
+    return method
+
+  # TODO(451323): Remove when the language backends no longer rely on this.
+  @property
+  def client(self):
+    return None
+
+
+class AssociatedInterface(ReferenceKind):
+  ReferenceKind.AddSharedProperty('kind')
+
+  def __init__(self, kind=None):
+    if kind is not None:
+      if not isinstance(kind, Interface):
+        raise Exception(
+            "Associated interface requires %r to be an interface." % kind.spec)
+      assert not kind.is_nullable
+      ReferenceKind.__init__(self, 'asso:' + kind.spec)
+    else:
+      ReferenceKind.__init__(self)
+    self.kind = kind
+
+
+class EnumField(object):
+  def __init__(self, name=None, value=None, attributes=None,
+               numeric_value=None):
+    self.name = name
+    self.value = value
+    self.attributes = attributes
+    self.numeric_value = numeric_value
+
+  @property
+  def min_version(self):
+    return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+        if self.attributes else None
+
+
+class Enum(Kind):
+  def __init__(self, name=None, module=None, attributes=None):
+    self.module = module
+    self.name = name
+    self.native_only = False
+    self.imported_from = None
+    if name is not None:
+      spec = 'x:' + name
+    else:
+      spec = None
+    Kind.__init__(self, spec)
+    self.fields = []
+    self.attributes = attributes
+
+  def Repr(self, as_ref=True):
+    if as_ref:
+      return '<%s name=%r>' % (self.__class__.__name__, self.name)
+    else:
+      return GenericRepr(self, {'name': False, 'fields': False})
+
+  @property
+  def extensible(self):
+    return self.attributes.get(ATTRIBUTE_EXTENSIBLE, False) \
+        if self.attributes else False
+
+
+class Module(object):
+  def __init__(self, name=None, namespace=None, attributes=None):
+    self.name = name
+    self.path = name
+    self.namespace = namespace
+    self.structs = []
+    self.unions = []
+    self.interfaces = []
+    self.kinds = {}
+    self.attributes = attributes
+
+  def __repr__(self):
+    # Gives us a decent __repr__ for modules.
+    return self.Repr()
+
+  def Repr(self, as_ref=True):
+    if as_ref:
+      return '<%s name=%r namespace=%r>' % (
+          self.__class__.__name__, self.name, self.namespace)
+    else:
+      return GenericRepr(self, {'name': False, 'namespace': False,
+                                'attributes': False, 'structs': False,
+                                'interfaces': False, 'unions': False})
+
+  def AddInterface(self, name, attributes=None):
+    interface = Interface(name, self, attributes)
+    self.interfaces.append(interface)
+    return interface
+
+  def AddStruct(self, name, attributes=None):
+    struct = Struct(name, self, attributes)
+    self.structs.append(struct)
+    return struct
+
+  def AddUnion(self, name, attributes=None):
+    union = Union(name, self, attributes)
+    self.unions.append(union)
+    return union
+
+
+def IsBoolKind(kind):
+  return kind.spec == BOOL.spec
+
+
+def IsFloatKind(kind):
+  return kind.spec == FLOAT.spec
+
+
+def IsIntegralKind(kind):
+  return (kind.spec == BOOL.spec or
+          kind.spec == INT8.spec or
+          kind.spec == INT16.spec or
+          kind.spec == INT32.spec or
+          kind.spec == INT64.spec or
+          kind.spec == UINT8.spec or
+          kind.spec == UINT16.spec or
+          kind.spec == UINT32.spec or
+          kind.spec == UINT64.spec)
+
+
+def IsStringKind(kind):
+  return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec
+
+
+def IsGenericHandleKind(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 IsUnionKind(kind):
+  return isinstance(kind, Union)
+
+
+def IsArrayKind(kind):
+  return isinstance(kind, Array)
+
+
+def IsInterfaceKind(kind):
+  return isinstance(kind, Interface)
+
+
+def IsAssociatedInterfaceKind(kind):
+  return isinstance(kind, AssociatedInterface)
+
+
+def IsInterfaceRequestKind(kind):
+  return isinstance(kind, InterfaceRequest)
+
+
+def IsAssociatedInterfaceRequestKind(kind):
+  return isinstance(kind, AssociatedInterfaceRequest)
+
+
+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 IsMapKind(kind):
+  return isinstance(kind, Map)
+
+
+def IsObjectKind(kind):
+  return IsPointerKind(kind) or IsUnionKind(kind)
+
+
+def IsPointerKind(kind):
+  return (IsStructKind(kind) or IsArrayKind(kind) or IsStringKind(kind) or
+          IsMapKind(kind))
+
+
+# Please note that interface is not considered as handle kind, since it is an
+# aggregate type consisting of a handle and a version number.
+def IsAnyHandleKind(kind):
+  return (IsGenericHandleKind(kind) or
+          IsDataPipeConsumerKind(kind) or
+          IsDataPipeProducerKind(kind) or
+          IsMessagePipeKind(kind) or
+          IsSharedBufferKind(kind) or
+          IsInterfaceRequestKind(kind))
+
+
+def IsAnyHandleOrInterfaceKind(kind):
+  return (IsAnyHandleKind(kind) or IsInterfaceKind(kind) or
+          IsAssociatedKind(kind))
+
+
+def IsAssociatedKind(kind):
+  return (IsAssociatedInterfaceKind(kind) or
+          IsAssociatedInterfaceRequestKind(kind))
+
+
+def HasCallbacks(interface):
+  for method in interface.methods:
+    if method.response_parameters != None:
+      return True
+  return False
+
+
+# Finds out whether an interface passes associated interfaces and associated
+# interface requests.
+def PassesAssociatedKinds(interface):
+  def _ContainsAssociatedKinds(kind, visited_kinds):
+    if kind in visited_kinds:
+      # No need to examine the kind again.
+      return False
+    visited_kinds.add(kind)
+    if IsAssociatedKind(kind):
+      return True
+    if IsArrayKind(kind):
+      return _ContainsAssociatedKinds(kind.kind, visited_kinds)
+    if IsStructKind(kind) or IsUnionKind(kind):
+      for field in kind.fields:
+        if _ContainsAssociatedKinds(field.kind, visited_kinds):
+          return True
+    if IsMapKind(kind):
+      # No need to examine the key kind, only primitive kinds and non-nullable
+      # string are allowed to be key kinds.
+      return _ContainsAssociatedKinds(kind.value_kind, visited_kinds)
+    return False
+
+  visited_kinds = set()
+  for method in interface.methods:
+    for param in method.parameters:
+      if _ContainsAssociatedKinds(param.kind, visited_kinds):
+        return True
+    if method.response_parameters != None:
+      for param in method.response_parameters:
+        if _ContainsAssociatedKinds(param.kind, visited_kinds):
+          return True
+  return False
+
+
+def HasSyncMethods(interface):
+  for method in interface.methods:
+    if method.sync:
+      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..37dc8f3
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
@@ -0,0 +1,250 @@
+# 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).
+
+# Size of struct header in bytes: num_bytes [4B] + version [4B].
+HEADER_SIZE = 8
+
+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.Map, mojom.Struct,
+                         mojom.Interface, mojom.AssociatedInterface)):
+      return 8
+    if isinstance(kind, mojom.Union):
+      return 16
+    if isinstance(kind, mojom.InterfaceRequest):
+      kind = mojom.MSGPIPE
+    if isinstance(kind, mojom.AssociatedInterfaceRequest):
+      return 4
+    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]
+
+  @classmethod
+  def GetAlignmentForKind(cls, kind):
+    if isinstance(kind, (mojom.Interface, mojom.AssociatedInterface)):
+      return 4
+    if isinstance(kind, mojom.Union):
+      return 8
+    return cls.GetSizeForKind(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.alignment = self.GetAlignmentForKind(field.kind)
+    self.offset = None
+    self.bit = None
+    self.min_version = None
+
+
+def GetPad(offset, alignment):
+  """Returns the pad necessary to reserve space so that |offset + pad| equals to
+  some multiple of |alignment|."""
+  return (alignment - (offset % alignment)) % alignment
+
+
+def GetFieldOffset(field, last_field):
+  """Returns a 2-tuple of the field offset and bit (for BOOLs)."""
+  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.alignment)
+  return (offset + pad, 0)
+
+
+def GetPayloadSizeUpToField(field):
+  """Returns the payload size (not including struct header) if |field| is the
+  last field.
+  """
+  if not field:
+    return 0
+  offset = field.offset + field.size
+  pad = GetPad(offset, 8)
+  return offset + pad
+
+
+class PackedStruct(object):
+  def __init__(self, struct):
+    self.struct = struct
+    # |packed_fields| contains all the fields, in increasing offset order.
+    self.packed_fields = []
+    # |packed_fields_in_ordinal_order| refers to the same fields as
+    # |packed_fields|, but in ordinal order.
+    self.packed_fields_in_ordinal_order = []
+
+    # No fields.
+    if (len(struct.fields) == 0):
+      return
+
+    # Start by sorting by ordinal.
+    src_fields = self.packed_fields_in_ordinal_order
+    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)
+
+    # Set |min_version| for each field.
+    next_min_version = 0
+    for packed_field in src_fields:
+      if packed_field.field.min_version is None:
+        assert next_min_version == 0
+      else:
+        assert packed_field.field.min_version >= next_min_version
+        next_min_version = packed_field.field.min_version
+      packed_field.min_version = next_min_version
+
+      if (packed_field.min_version != 0 and
+          mojom.IsReferenceKind(packed_field.field.kind) and
+          not packed_field.field.kind.is_nullable):
+        raise Exception("Non-nullable fields are only allowed in version 0 of "
+                        "a struct. %s.%s is defined with [MinVersion=%d]."
+                            % (self.struct.name, packed_field.field.name,
+                               packed_field.min_version))
+
+    src_field = src_fields[0]
+    src_field.offset = 0
+    src_field.bit = 0
+    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)
+
+
+class ByteInfo(object):
+  def __init__(self):
+    self.is_padding = False
+    self.packed_fields = []
+
+
+def GetByteLayout(packed_struct):
+  total_payload_size = GetPayloadSizeUpToField(
+      packed_struct.packed_fields[-1] if packed_struct.packed_fields else None)
+  bytes = [ByteInfo() for i in xrange(total_payload_size)]
+
+  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
+
+
+class VersionInfo(object):
+  def __init__(self, version, num_fields, num_bytes):
+    self.version = version
+    self.num_fields = num_fields
+    self.num_bytes = num_bytes
+
+
+def GetVersionInfo(packed_struct):
+  """Get version information for a struct.
+
+  Args:
+    packed_struct: A PackedStruct instance.
+
+  Returns:
+    A non-empty list of VersionInfo instances, sorted by version in increasing
+    order.
+    Note: The version numbers may not be consecutive.
+  """
+  versions = []
+  last_version = 0
+  last_num_fields = 0
+  last_payload_size = 0
+
+  for packed_field in packed_struct.packed_fields_in_ordinal_order:
+    if packed_field.min_version != last_version:
+      versions.append(
+          VersionInfo(last_version, last_num_fields,
+                      last_payload_size + HEADER_SIZE))
+      last_version = packed_field.min_version
+
+    last_num_fields += 1
+    # The fields are iterated in ordinal order here. However, the size of a
+    # version is determined by the last field of that version in pack order,
+    # instead of ordinal order. Therefore, we need to calculate the max value.
+    last_payload_size = max(GetPayloadSizeUpToField(packed_field),
+                            last_payload_size)
+
+  assert len(versions) == 0 or last_num_fields != versions[-1].num_fields
+  versions.append(VersionInfo(last_version, last_num_fields,
+                              last_payload_size + HEADER_SIZE))
+  return versions
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..14f699d
--- /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.Array(length=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..9ea6cf8
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
@@ -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.
+
+# Based on:
+# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py
+
+import imp
+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, path_to_template, params, **kwargs):
+  loader = jinja2.ModuleLoader(os.path.join(
+      mojo_generator.bytecode_path, "%s.zip" % mojo_generator.GetTemplatePrefix(
+      )))
+  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())
+  jinja_env.filters.update(mojo_generator.GetFilters())
+  template = jinja_env.get_template(path_to_template)
+  return template.render(params)
+
+
+def UseJinja(path_to_template, **kwargs):
+  def RealDecorator(generator):
+    def GeneratorInternal(*args, **kwargs2):
+      parameters = generator(*args, **kwargs2)
+      return ApplyTemplate(args[0], path_to_template, parameters, **kwargs)
+    GeneratorInternal.func_name = generator.func_name
+    return GeneratorInternal
+  return RealDecorator
+
+
+def PrecompileTemplates(generator_modules, output_dir):
+  for module in generator_modules.values():
+    generator = module.Generator(None)
+    jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader([os.path.join(
+        os.path.dirname(module.__file__), generator.GetTemplatePrefix())]))
+    jinja_env.filters.update(generator.GetFilters())
+    jinja_env.compile_templates(
+        os.path.join(output_dir, "%s.zip" % generator.GetTemplatePrefix()),
+        extensions=["tmpl"],
+        zip="stored",
+        py_compile=True,
+        ignore_errors=False)
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..2c6b5fc
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
@@ -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.
+
+"""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, attribute_list, enum_value_list, **kwargs):
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    assert enum_value_list is None or isinstance(enum_value_list, EnumValueList)
+    super(Enum, self).__init__(name, **kwargs)
+    self.attribute_list = attribute_list
+    self.enum_value_list = enum_value_list
+
+  def __eq__(self, other):
+    return super(Enum, self).__eq__(other) and \
+           self.attribute_list == other.attribute_list and \
+           self.enum_value_list == other.enum_value_list
+
+
+class EnumValue(Definition):
+  """Represents a definition of an enum value."""
+
+  def __init__(self, name, attribute_list, value, **kwargs):
+    # The optional value is either an int (which is current a string) or a
+    # "wrapped identifier".
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    assert value is None or isinstance(value, (str, tuple))
+    super(EnumValue, self).__init__(name, **kwargs)
+    self.attribute_list = attribute_list
+    self.value = value
+
+  def __eq__(self, other):
+    return super(EnumValue, self).__eq__(other) and \
+           self.attribute_list == other.attribute_list 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, attribute_list, ordinal, parameter_list,
+               response_parameter_list, **kwargs):
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    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.attribute_list = attribute_list
+    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.attribute_list == other.attribute_list 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, attribute_list, ordinal, typename, **kwargs):
+    assert isinstance(name, str)
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    assert ordinal is None or isinstance(ordinal, Ordinal)
+    assert isinstance(typename, str)
+    super(Parameter, self).__init__(**kwargs)
+    self.name = name
+    self.attribute_list = attribute_list
+    self.ordinal = ordinal
+    self.typename = typename
+
+  def __eq__(self, other):
+    return super(Parameter, self).__eq__(other) and \
+           self.name == other.name and \
+           self.attribute_list == other.attribute_list 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) or body is None
+    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, attribute_list, ordinal, typename, default_value,
+               **kwargs):
+    assert isinstance(name, str)
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    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.attribute_list = attribute_list
+    self.ordinal = ordinal
+    self.typename = typename
+    self.default_value = default_value
+
+  def __eq__(self, other):
+    return super(StructField, self).__eq__(other) and \
+           self.attribute_list == other.attribute_list 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)
+
+
+class Union(Definition):
+  """Represents a union definition."""
+
+  def __init__(self, name, attribute_list, body, **kwargs):
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    assert isinstance(body, UnionBody)
+    super(Union, self).__init__(name, **kwargs)
+    self.attribute_list = attribute_list
+    self.body = body
+
+  def __eq__(self, other):
+    return super(Union, self).__eq__(other) and \
+           self.attribute_list == other.attribute_list and \
+           self.body == other.body
+
+
+class UnionField(Definition):
+
+  def __init__(self, name, attribute_list, ordinal, typename, **kwargs):
+    assert isinstance(name, str)
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
+    assert ordinal is None or isinstance(ordinal, Ordinal)
+    assert isinstance(typename, str)
+    super(UnionField, self).__init__(name, **kwargs)
+    self.attribute_list = attribute_list
+    self.ordinal = ordinal
+    self.typename = typename
+
+  def __eq__(self, other):
+    return super(UnionField, self).__eq__(other) and \
+           self.attribute_list == other.attribute_list and \
+           self.ordinal == other.ordinal and \
+           self.typename == other.typename
+
+
+class UnionBody(NodeListBase):
+
+  _list_item_type = UnionField
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..06354b1
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
@@ -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.
+
+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',
+    'UNION',
+    'INTERFACE',
+    'ENUM',
+    'CONST',
+    'TRUE',
+    'FALSE',
+    'DEFAULT',
+    'ARRAY',
+    'MAP',
+    'ASSOCIATED'
+  )
+
+  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..5cf20fe
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -0,0 +1,453 @@
+# Copyright 2014 The Chromium 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 : """
+    p[0] = ast.Mojom(None, ast.ImportList(), [])
+
+  def p_root_2(self, p):
+    """root : root module"""
+    if p[1].module is not None:
+      raise ParseError(self.filename,
+                       "Multiple \"module\" statements not allowed:",
+                       p[2].lineno, snippet=self._GetSnippet(p[2].lineno))
+    if p[1].import_list.items or p[1].definition_list:
+      raise ParseError(
+          self.filename,
+          "\"module\" statements must precede imports and definitions:",
+          p[2].lineno, snippet=self._GetSnippet(p[2].lineno))
+    p[0] = p[1]
+    p[0].module = p[2]
+
+  def p_root_3(self, p):
+    """root : root import"""
+    if p[1].definition_list:
+      raise ParseError(self.filename,
+                       "\"import\" statements must precede definitions:",
+                       p[2].lineno, snippet=self._GetSnippet(p[2].lineno))
+    p[0] = p[1]
+    p[0].import_list.Append(p[2])
+
+  def p_root_4(self, p):
+    """root : root definition"""
+    p[0] = p[1]
+    p[0].definition_list.append(p[2])
+
+  def p_import(self, p):
+    """import : IMPORT STRING_LITERAL SEMI"""
+    # '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]), filename=self.filename, lineno=p.lineno(2))
+
+  def p_module(self, p):
+    """module : attribute_section MODULE identifier_wrapped SEMI"""
+    p[0] = ast.Module(p[3], p[1], filename=self.filename, lineno=p.lineno(2))
+
+  def p_definition(self, p):
+    """definition : struct
+                  | union
+                  | 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_1(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_attribute_2(self, p):
+    """attribute : NAME"""
+    p[0] = ast.Attribute(p[1], True, filename=self.filename, lineno=p.lineno(1))
+
+  def p_evaled_literal(self, p):
+    """evaled_literal : literal"""
+    # 'eval' the literal to strip the quotes. Handle keywords "true" and "false"
+    # specially since they cannot directly be evaluated to python boolean
+    # values.
+    if p[1] == "true":
+      p[0] = True
+    elif p[1] == "false":
+      p[0] = False
+    else:
+      p[0] = eval(p[1])
+
+  def p_struct_1(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_2(self, p):
+    """struct : attribute_section STRUCT NAME SEMI"""
+    p[0] = ast.Struct(p[3], p[1], None)
+
+  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 : attribute_section typename NAME ordinal default SEMI"""
+    p[0] = ast.StructField(p[3], p[1], p[4], p[2], p[5])
+
+  def p_union(self, p):
+    """union : attribute_section UNION NAME LBRACE union_body RBRACE SEMI"""
+    p[0] = ast.Union(p[3], p[1], p[5])
+
+  def p_union_body_1(self, p):
+    """union_body : """
+    p[0] = ast.UnionBody()
+
+  def p_union_body_2(self, p):
+    """union_body : union_body union_field"""
+    p[0] = p[1]
+    p[1].Append(p[2])
+
+  def p_union_field(self, p):
+    """union_field : attribute_section typename NAME ordinal SEMI"""
+    p[0] = ast.UnionField(p[3], p[1], p[4], p[2])
+
+  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 : attribute_section NAME ordinal LPAREN parameter_list RPAREN \
+                    response SEMI"""
+    p[0] = ast.Method(p[2], p[1], p[3], p[5], p[7])
+
+  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 : attribute_section typename NAME ordinal"""
+    p[0] = ast.Parameter(p[3], p[1], p[4], p[2],
+                         filename=self.filename, lineno=p.lineno(3))
+
+  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
+                            | associative_array
+                            | interfacerequest"""
+    p[0] = p[1]
+
+  def p_basictypename(self, p):
+    """basictypename : identifier
+                     | ASSOCIATED identifier
+                     | handletype"""
+    if len(p) == 2:
+      p[0] = p[1]
+    else:
+      p[0] = "asso<" + p[2] + ">"
+
+  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_associative_array(self, p):
+    """associative_array : MAP LANGLE identifier COMMA typename RANGLE"""
+    p[0] = p[5] + "{" + p[3] + "}"
+
+  def p_interfacerequest(self, p):
+    """interfacerequest : identifier AMP
+                        | ASSOCIATED identifier AMP"""
+    if len(p) == 3:
+      p[0] = p[1] + "&"
+    else:
+      p[0] = "asso<" + p[2] + "&>"
+
+  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_1(self, p):
+    """enum : attribute_section ENUM NAME LBRACE nonempty_enum_value_list \
+                  RBRACE SEMI
+            | attribute_section ENUM NAME LBRACE nonempty_enum_value_list \
+                  COMMA RBRACE SEMI"""
+    p[0] = ast.Enum(p[3], p[1], p[5], filename=self.filename,
+                    lineno=p.lineno(2))
+
+  def p_enum_2(self, p):
+    """enum : attribute_section ENUM NAME SEMI"""
+    p[0] = ast.Enum(p[3], p[1], None, filename=self.filename,
+                    lineno=p.lineno(2))
+
+  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 : attribute_section NAME
+                  | attribute_section NAME EQUALS int
+                  | attribute_section NAME EQUALS identifier_wrapped"""
+    p[0] = ast.EnumValue(p[2], p[1], p[4] if len(p) == 5 else None,
+                         filename=self.filename, lineno=p.lineno(2))
+
+  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):
+  """Parse source file to AST.
+
+  Args:
+    source: The source text as a str.
+    filename: The filename that |source| originates from.
+
+  Returns:
+    The AST as a mojom.parse.ast.Mojom object.
+  """
+  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..66e8443
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
@@ -0,0 +1,236 @@
+# Copyright 2014 The Chromium 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 _DuplicateName(values):
+  """Returns the 'name' of the first entry in |values| whose 'name' has already
+     been encountered. If there are no duplicates, returns None."""
+  names = set()
+  for value in values:
+    if value['name'] in names:
+      return value['name']
+    names.add(value['name'])
+  return None
+
+def _MapTreeForType(func, tree, type_to_map, scope):
+  assert isinstance(type_to_map, type)
+  if not tree:
+    return []
+  result = [func(subtree)
+            for subtree in tree if isinstance(subtree, type_to_map)]
+  duplicate_name = _DuplicateName(result)
+  if duplicate_name:
+    raise Exception('Names in mojom must be unique within a scope. The name '
+                    '"%s" is used more than once within the scope "%s".' %
+                    (duplicate_name, scope))
+  return result
+
+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 = ('m', 's', 'h', 'a', 'r', 'x', 'asso')
+    if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:
+      raise Exception(
+          'A type (spec "%s") cannot be made nullable' % base_kind)
+    return '?' + base_kind
+  if kind.endswith('}'):
+    lbracket = kind.rfind('{')
+    value = kind[0:lbracket]
+    return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']'
+  if kind.endswith(']'):
+    lbracket = kind.rfind('[')
+    typename = kind[0:lbracket]
+    return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename)
+  if kind.endswith('&'):
+    return 'r:' + _MapKind(kind[0:-1])
+  if kind.startswith('asso<'):
+    assert kind.endswith('>')
+    return 'asso:' + _MapKind(kind[5:-1])
+  if kind in map_to_kind:
+    return map_to_kind[kind]
+  return 'x:' + kind
+
+def _AddOptional(dictionary, key, value):
+  if value is not None:
+    dictionary[key] = value;
+
+def _AttributeListToDict(attribute_list):
+  if attribute_list is None:
+    return None
+  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)
+    data = {'name': enum_value.name}
+    _AddOptional(data, 'value', enum_value.value)
+    _AddOptional(data, 'attributes',
+                 _AttributeListToDict(enum_value.attribute_list))
+    return data
+
+  assert isinstance(enum, ast.Enum)
+  data = {'name': enum.name,
+          'native_only': enum.enum_value_list is None }
+  if not data['native_only']:
+    data.update({'fields': map(EnumValueToDict, enum.enum_value_list)})
+  _AddOptional(data, 'attributes', _AttributeListToDict(enum.attribute_list))
+  return data
+
+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)
+        data = {'name': struct_field.name,
+                'kind': _MapKind(struct_field.typename)}
+        _AddOptional(data, 'ordinal',
+                     struct_field.ordinal.value
+                         if struct_field.ordinal else None)
+        _AddOptional(data, 'default', struct_field.default_value)
+        _AddOptional(data, 'attributes',
+                     _AttributeListToDict(struct_field.attribute_list))
+        return data
+
+      assert isinstance(struct, ast.Struct)
+      data = {'name': struct.name,
+              'native_only': struct.body is None}
+      if not data['native_only']:
+        data.update({
+            'fields': _MapTreeForType(StructFieldToDict, struct.body,
+                                      ast.StructField, struct.name),
+            'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum,
+                                     struct.name),
+            'constants': _MapTreeForType(_ConstToDict, struct.body,
+                                         ast.Const, struct.name)})
+      _AddOptional(data, 'attributes',
+                   _AttributeListToDict(struct.attribute_list))
+      return data
+
+    def UnionToDict(union):
+      def UnionFieldToDict(union_field):
+        assert isinstance(union_field, ast.UnionField)
+        data = {'name': union_field.name,
+                'kind': _MapKind(union_field.typename)}
+        _AddOptional(data, 'ordinal',
+                     union_field.ordinal.value
+                         if union_field.ordinal else None)
+        _AddOptional(data, 'attributes',
+                     _AttributeListToDict(union_field.attribute_list))
+        return data
+
+      assert isinstance(union, ast.Union)
+      data = {'name': union.name,
+              'fields': _MapTreeForType(UnionFieldToDict, union.body,
+                                        ast.UnionField, union.name)}
+      _AddOptional(data, 'attributes',
+                   _AttributeListToDict(union.attribute_list))
+      return data
+
+    def InterfaceToDict(interface):
+      def MethodToDict(method):
+        def ParameterToDict(param):
+          assert isinstance(param, ast.Parameter)
+          data = {'name': param.name,
+                  'kind': _MapKind(param.typename)}
+          _AddOptional(data, 'ordinal',
+                       param.ordinal.value if param.ordinal else None)
+          _AddOptional(data, 'attributes',
+                       _AttributeListToDict(param.attribute_list))
+          return data
+
+        assert isinstance(method, ast.Method)
+        data = {'name': method.name,
+                'parameters': map(ParameterToDict, method.parameter_list)}
+        if method.response_parameter_list is not None:
+          data['response_parameters'] = map(ParameterToDict,
+                                            method.response_parameter_list)
+        _AddOptional(data, 'ordinal',
+                     method.ordinal.value if method.ordinal else None)
+        _AddOptional(data, 'attributes',
+                     _AttributeListToDict(method.attribute_list))
+        return data
+
+      assert isinstance(interface, ast.Interface)
+      data = {'name': interface.name,
+              'methods': _MapTreeForType(MethodToDict, interface.body,
+                                         ast.Method, interface.name),
+              'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum,
+                                       interface.name),
+              'constants': _MapTreeForType(_ConstToDict, interface.body,
+                                           ast.Const, interface.name)}
+      _AddOptional(data, 'attributes',
+                   _AttributeListToDict(interface.attribute_list))
+      return data
+
+    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['structs'] = \
+        _MapTreeForType(StructToDict, tree.definition_list, ast.Struct, name)
+    self.mojom['unions'] = \
+        _MapTreeForType(UnionToDict, tree.definition_list, ast.Union, name)
+    self.mojom['interfaces'] = \
+        _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface,
+                        name)
+    self.mojom['enums'] = \
+        _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum, name)
+    self.mojom['constants'] = \
+        _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const, name)
+    _AddOptional(self.mojom, 'attributes',
+                 _AttributeListToDict(tree.module.attribute_list)
+                     if tree.module else None)
+    return self.mojom
+
+
+def Translate(tree, name):
+  """Translate AST to Mojom IR.
+
+  Args:
+    tree: The AST as a mojom.parse.ast.Mojom object.
+    name: The filename as a str.
+
+  Returns:
+    The Mojom IR as a dict.
+  """
+  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/fileutil_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py
new file mode 100644
index 0000000..d56faad
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py
@@ -0,0 +1,55 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import imp
+import os.path
+import shutil
+import sys
+import tempfile
+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"))
+from mojom import fileutil
+
+
+class FileUtilTest(unittest.TestCase):
+
+  def testEnsureDirectoryExists(self):
+    """Test that EnsureDirectoryExists fuctions correctly."""
+
+    temp_dir = tempfile.mkdtemp()
+    try:
+      self.assertTrue(os.path.exists(temp_dir))
+
+      # Directory does not exist, yet.
+      full = os.path.join(temp_dir, "foo", "bar")
+      self.assertFalse(os.path.exists(full))
+
+      # Create the directory.
+      fileutil.EnsureDirectoryExists(full)
+      self.assertTrue(os.path.exists(full))
+
+      # Trying to create it again does not cause an error.
+      fileutil.EnsureDirectoryExists(full)
+      self.assertTrue(os.path.exists(full))
+
+      # Bypass check for directory existence to tickle error handling that
+      # occurs in response to a race.
+      fileutil.EnsureDirectoryExists(full, always_try_to_create=True)
+      self.assertTrue(os.path.exists(full))
+    finally:
+      shutil.rmtree(temp_dir)
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py
new file mode 100644
index 0000000..70c92a3
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py
@@ -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.
+
+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"))
+from mojom.generate import data
+from mojom.generate import module as mojom
+
+
+class DataTest(unittest.TestCase):
+
+  def testStructDataConversion(self):
+    """Tests that a struct can be converted from data."""
+    module = mojom.Module('test_module', 'test_namespace')
+    struct_data = {
+        'name': 'SomeStruct',
+        'enums': [],
+        'constants': [],
+        'fields': [
+            {'name': 'field1', 'kind': 'i32'},
+            {'name': 'field2', 'kind': 'i32', 'ordinal': 10},
+            {'name': 'field3', 'kind': 'i32', 'default': 15}]}
+
+    struct = data.StructFromData(module, struct_data)
+    struct.fields = map(lambda field:
+        data.StructFieldFromData(module, field, struct), struct.fields_data)
+    self.assertEquals(struct_data, data.StructToData(struct))
+
+  def testUnionDataConversion(self):
+    """Tests that a union can be converted from data."""
+    module = mojom.Module('test_module', 'test_namespace')
+    union_data = {
+        'name': 'SomeUnion',
+        'fields': [
+            {'name': 'field1', 'kind': 'i32'},
+            {'name': 'field2', 'kind': 'i32', 'ordinal': 10}]}
+
+    union = data.UnionFromData(module, union_data)
+    union.fields = map(lambda field:
+        data.UnionFieldFromData(module, field, union), union.fields_data)
+    self.assertEquals(union_data, data.UnionToData(union))
+
+  def testImportFromDataNoMissingImports(self):
+    """Tests that unions, structs, interfaces and enums are imported."""
+    module = mojom.Module('test_module', 'test_namespace')
+    imported_module = mojom.Module('import_module', 'import_namespace')
+    #TODO(azani): Init values in module.py.
+    #TODO(azani): Test that values are imported.
+    imported_module.values = {}
+    imported_data = {'module' : imported_module}
+
+
+    struct = mojom.Struct('TestStruct', module=module)
+    imported_module.kinds[struct.spec] = struct
+
+    union = mojom.Union('TestUnion', module=module)
+    imported_module.kinds[union.spec] = union
+
+    interface = mojom.Interface('TestInterface', module=module)
+    imported_module.kinds[interface.spec] = interface
+
+    enum = mojom.Enum('TestEnum', module=module)
+    imported_module.kinds[enum.spec] = enum
+
+    data.ImportFromData(module, imported_data)
+
+    # Test that the kind was imported.
+    self.assertIn(struct.spec, module.kinds)
+    self.assertEquals(struct.name, module.kinds[struct.spec].name)
+
+    self.assertIn(union.spec, module.kinds)
+    self.assertEquals(union.name, module.kinds[union.spec].name)
+
+    self.assertIn(interface.spec, module.kinds)
+    self.assertEquals(interface.name, module.kinds[interface.spec].name)
+
+    self.assertIn(enum.spec, module.kinds)
+    self.assertEquals(enum.name, module.kinds[enum.spec].name)
+
+    # Test that the imported kind is a copy and not the original.
+    self.assertIsNot(struct, module.kinds[struct.spec])
+    self.assertIsNot(union, module.kinds[union.spec])
+    self.assertIsNot(interface, module.kinds[interface.spec])
+    self.assertIsNot(enum, module.kinds[enum.spec])
+
+  def testImportFromDataNoExtraneousImports(self):
+    """Tests that arrays, maps and interface requests are not imported."""
+    module = mojom.Module('test_module', 'test_namespace')
+    imported_module = mojom.Module('import_module', 'import_namespace')
+    #TODO(azani): Init values in module.py.
+    imported_module.values = {}
+    imported_data = {'module' : imported_module}
+
+    array = mojom.Array(mojom.INT16, length=20)
+    imported_module.kinds[array.spec] = array
+
+    map_kind = mojom.Map(mojom.INT16, mojom.INT16)
+    imported_module.kinds[map_kind.spec] = map_kind
+
+    interface = mojom.Interface('TestInterface', module=module)
+    imported_module.kinds[interface.spec] = interface
+
+    interface_req = mojom.InterfaceRequest(interface)
+    imported_module.kinds[interface_req.spec] = interface_req
+
+    data.ImportFromData(module, imported_data)
+
+    self.assertNotIn(array.spec, module.kinds)
+    self.assertNotIn(map_kind.spec, module.kinds)
+    self.assertNotIn(interface_req.spec, module.kinds)
+
+  def testNonInterfaceAsInterfaceRequest(self):
+    """Tests that a non-interface cannot be used for interface requests."""
+    module = mojom.Module('test_module', 'test_namespace')
+    interface = mojom.Interface('TestInterface', module=module)
+    method_dict = {
+        'name': 'Foo',
+        'parameters': [{'name': 'foo', 'kind': 'r:i32'}],
+    }
+    with self.assertRaises(Exception) as e:
+      data.MethodFromData(module, method_dict, interface)
+    self.assertEquals(e.exception.__str__(),
+                      'Interface request requires \'i32\' to be an interface.')
+
+  def testNonInterfaceAsAssociatedInterface(self):
+    """Tests that a non-interface type cannot be used for associated
+    interfaces."""
+    module = mojom.Module('test_module', 'test_namespace')
+    interface = mojom.Interface('TestInterface', module=module)
+    method_dict = {
+        'name': 'Foo',
+        'parameters': [{'name': 'foo', 'kind': 'asso:i32'}],
+    }
+    with self.assertRaises(Exception) as e:
+      data.MethodFromData(module, method_dict, interface)
+    self.assertEquals(
+        e.exception.__str__(),
+        'Associated interface requires \'i32\' to be an interface.')
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py
new file mode 100644
index 0000000..a684773
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.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.
+
+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"))
+from mojom.generate import generator
+
+
+class StringManipulationTest(unittest.TestCase):
+  """generator contains some string utilities, this tests only those."""
+
+  def testUnderToCamel(self):
+    """Tests UnderToCamel which converts underscore_separated to CamelCase."""
+    self.assertEquals("CamelCase", generator.UnderToCamel("camel_case"))
+    self.assertEquals("CamelCase", generator.UnderToCamel("CAMEL_CASE"))
+
+if __name__ == "__main__":
+  unittest.main()
+
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py
new file mode 100644
index 0000000..75b7cd5
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py
@@ -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.
+
+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"))
+from mojom.generate import module as mojom
+
+
+class ModuleTest(unittest.TestCase):
+
+  def testNonInterfaceAsInterfaceRequest(self):
+    """Tests that a non-interface cannot be used for interface requests."""
+    module = mojom.Module('test_module', 'test_namespace')
+    struct = mojom.Struct('TestStruct', module=module)
+    with self.assertRaises(Exception) as e:
+      mojom.InterfaceRequest(struct)
+    self.assertEquals(
+        e.exception.__str__(),
+        'Interface request requires \'x:TestStruct\' to be an interface.')
+
+  def testNonInterfaceAsAssociatedInterface(self):
+    """Tests that a non-interface type cannot be used for associated interfaces.
+    """
+    module = mojom.Module('test_module', 'test_namespace')
+    struct = mojom.Struct('TestStruct', module=module)
+    with self.assertRaises(Exception) as e:
+      mojom.AssociatedInterface(struct)
+    self.assertEquals(
+        e.exception.__str__(),
+        'Associated interface requires \'x:TestStruct\' to be an interface.')
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py
new file mode 100644
index 0000000..75f6d51
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py
@@ -0,0 +1,136 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+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"))
+from mojom.generate import pack
+from mojom.generate import module as mojom
+
+
+# TODO(yzshen): Move tests in pack_tests.py here.
+class PackTest(unittest.TestCase):
+  def _CheckPackSequence(self, kinds, fields, offsets):
+    """Checks the pack order and offsets of a sequence of mojom.Kinds.
+
+    Args:
+      kinds: A sequence of mojom.Kinds that specify the fields that are to be
+      created.
+      fields: The expected order of the resulting fields, with the integer "1"
+      first.
+      offsets: The expected order of offsets, with the integer "0" first.
+    """
+    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)
+    self.assertEquals(len(kinds), num_fields)
+    for i in xrange(num_fields):
+      self.assertEquals('%d' % fields[i], ps.packed_fields[i].field.name)
+      self.assertEquals(offsets[i], ps.packed_fields[i].offset)
+
+  def testMinVersion(self):
+    """Tests that |min_version| is properly set for packed fields."""
+    struct = mojom.Struct('test')
+    struct.AddField('field_2', mojom.BOOL, 2)
+    struct.AddField('field_0', mojom.INT32, 0)
+    struct.AddField('field_1', mojom.INT64, 1)
+    ps = pack.PackedStruct(struct)
+
+    self.assertEquals('field_0', ps.packed_fields[0].field.name)
+    self.assertEquals('field_2', ps.packed_fields[1].field.name)
+    self.assertEquals('field_1', ps.packed_fields[2].field.name)
+
+    self.assertEquals(0, ps.packed_fields[0].min_version)
+    self.assertEquals(0, ps.packed_fields[1].min_version)
+    self.assertEquals(0, ps.packed_fields[2].min_version)
+
+    struct.fields[0].attributes = {'MinVersion': 1}
+    ps = pack.PackedStruct(struct)
+
+    self.assertEquals(0, ps.packed_fields[0].min_version)
+    self.assertEquals(1, ps.packed_fields[1].min_version)
+    self.assertEquals(0, ps.packed_fields[2].min_version)
+
+  def testGetVersionInfoEmptyStruct(self):
+    """Tests that pack.GetVersionInfo() never returns an empty list, even for
+    empty structs.
+    """
+    struct = mojom.Struct('test')
+    ps = pack.PackedStruct(struct)
+
+    versions = pack.GetVersionInfo(ps)
+    self.assertEquals(1, len(versions))
+    self.assertEquals(0, versions[0].version)
+    self.assertEquals(0, versions[0].num_fields)
+    self.assertEquals(8, versions[0].num_bytes)
+
+  def testGetVersionInfoComplexOrder(self):
+    """Tests pack.GetVersionInfo() using a struct whose definition order,
+    ordinal order and pack order for fields are all different.
+    """
+    struct = mojom.Struct('test')
+    struct.AddField('field_3', mojom.BOOL, ordinal=3,
+                    attributes={'MinVersion': 3})
+    struct.AddField('field_0', mojom.INT32, ordinal=0)
+    struct.AddField('field_1', mojom.INT64, ordinal=1,
+                    attributes={'MinVersion': 2})
+    struct.AddField('field_2', mojom.INT64, ordinal=2,
+                    attributes={'MinVersion': 3})
+    ps = pack.PackedStruct(struct)
+
+    versions = pack.GetVersionInfo(ps)
+    self.assertEquals(3, len(versions))
+
+    self.assertEquals(0, versions[0].version)
+    self.assertEquals(1, versions[0].num_fields)
+    self.assertEquals(16, versions[0].num_bytes)
+
+    self.assertEquals(2, versions[1].version)
+    self.assertEquals(2, versions[1].num_fields)
+    self.assertEquals(24, versions[1].num_bytes)
+
+    self.assertEquals(3, versions[2].version)
+    self.assertEquals(4, versions[2].num_fields)
+    self.assertEquals(32, versions[2].num_bytes)
+
+  def testInterfaceAlignment(self):
+    """Tests that interfaces are aligned on 4-byte boundaries, although the size
+    of an interface is 8 bytes.
+    """
+    kinds = (mojom.INT32, mojom.Interface('test_interface'))
+    fields = (1, 2)
+    offsets = (0, 4)
+    self._CheckPackSequence(kinds, fields, offsets)
+
+  def testAssociatedInterfaceAlignment(self):
+    """Tests that associated interfaces are aligned on 4-byte boundaries,
+    although the size of an associated interface is 8 bytes.
+    """
+    kinds = (mojom.INT32,
+             mojom.AssociatedInterface(mojom.Interface('test_interface')))
+    fields = (1, 2)
+    offsets = (0, 4)
+    self._CheckPackSequence(kinds, fields, offsets)
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..6822cbc
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py
@@ -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.
+
+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("union"),
+                      _MakeLexTokenForKeyword("union"))
+    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"))
+    self.assertEquals(self._SingleTokenForInput("map"),
+                      _MakeLexTokenForKeyword("map"))
+    self.assertEquals(self._SingleTokenForInput("associated"),
+                      _MakeLexTokenForKeyword("associated"))
+
+  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..3f4ca87
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
@@ -0,0 +1,1497 @@
+# Copyright 2014 The Chromium 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"
+    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;
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a', None, None, 'int32', None),
+                 ast.StructField('b', None, None, 'double', None)]))])
+    self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+  def testSimpleStructWithoutModule(self):
+    """Tests a simple struct without an explict module statement."""
+
+    source = """\
+        struct MyStruct {
+          int32 a;
+          double b;
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a', None, None, 'int32', None),
+                 ast.StructField('b', None, 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',
+                          None,
+                          ast.EnumValueList(
+                              ast.EnumValue('VALUE', None, None))),
+                 ast.Const('kMyConst', 'double', '1.23'),
+                 ast.StructField('a', None, None, 'int32', None),
+                 ast.StructField('b', None, 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.
+
+        struct MyStruct {
+          int32 a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: Unexpected 'struct':\n"
+            r" *struct MyStruct {$"):
+      parser.Parse(source2, "my_file.mojom")
+
+  def testMultipleModuleStatements(self):
+    """Tests an (invalid) .mojom with multiple module statements."""
+
+    source = """\
+        module foo;
+        module bar;
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Multiple \"module\" statements not "
+            r"allowed:\n *module bar;$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testModuleStatementAfterImport(self):
+    """Tests an (invalid) .mojom with a module statement after an import."""
+
+    source = """\
+        import "foo.mojom";
+        module foo;
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: \"module\" statements must precede imports "
+            r"and definitions:\n *module foo;$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testModuleStatementAfterDefinition(self):
+    """Tests an (invalid) .mojom with a module statement after a definition."""
+
+    source = """\
+        struct MyStruct {
+          int32 a;
+        };
+        module foo;
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: \"module\" statements must precede imports "
+            r"and definitions:\n *module foo;$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testImportStatementAfterDefinition(self):
+    """Tests an (invalid) .mojom with an import statement after a definition."""
+
+    source = """\
+        struct MyStruct {
+          int32 a;
+        };
+        import "foo.mojom";
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: \"import\" statements must precede "
+            r"definitions:\n *import \"foo.mojom\";$"):
+      parser.Parse(source, "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.
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Enum(
+            'MyEnum1',
+            None,
+            ast.EnumValueList([ast.EnumValue('VALUE1', None, None),
+                               ast.EnumValue('VALUE2', None, None)])),
+         ast.Enum(
+            'MyEnum2',
+            None,
+            ast.EnumValueList([ast.EnumValue('VALUE1', None, '-1'),
+                               ast.EnumValue('VALUE2', None, '0'),
+                               ast.EnumValue('VALUE3', None, '+987'),
+                               ast.EnumValue('VALUE4', None, '0xAF12'),
+                               ast.EnumValue('VALUE5', None, '-0x09bcd'),
+                               ast.EnumValue('VALUE6', None, ('IDENTIFIER',
+                                                        'VALUE5')),
+                               ast.EnumValue('VALUE7', None, 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;
+        };
+        """
+    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', None, 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
+        };
+        """
+    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;
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a0', None, ast.Ordinal(0), 'int32', None),
+                 ast.StructField('a1', None, ast.Ordinal(1), 'int32', None),
+                 ast.StructField('a2', None, ast.Ordinal(2), 'int32', None),
+                 ast.StructField('a9', None, ast.Ordinal(9), 'int32', None),
+                 ast.StructField('a10', None, ast.Ordinal(10), 'int32', None),
+                 ast.StructField('a11', None, ast.Ordinal(11), 'int32', None),
+                 ast.StructField('a29', None, ast.Ordinal(29), 'int32', None),
+                 ast.StructField('a1234567890', None, 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@;
+        };
+        """
+    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;
+        };
+        """
+    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;
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my.mod'), None),
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(ast.StructField('a', None, 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, None, 'handle', None),
+                 ast.StructField('b', None, None, 'handle<data_pipe_consumer>',
+                                 None),
+                 ast.StructField('c', None, None, 'handle<data_pipe_producer>',
+                                 None),
+                 ast.StructField('d', None, None, 'handle<message_pipe>', None),
+                 ast.StructField('e', None, 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, None, 'int16', '0'),
+                 ast.StructField('a1', None, None, 'uint16', '0x0'),
+                 ast.StructField('a2', None, None, 'uint16', '0x00'),
+                 ast.StructField('a3', None, None, 'uint16', '0x01'),
+                 ast.StructField('a4', None, None, 'uint16', '0xcd'),
+                 ast.StructField('a5' , None, None, 'int32', '12345'),
+                 ast.StructField('a6', None, None, 'int64', '-12345'),
+                 ast.StructField('a7', None, None, 'int64', '+12345'),
+                 ast.StructField('a8', None, None, 'uint32', '0x12cd3'),
+                 ast.StructField('a9', None, None, 'uint32', '-0x12cD3'),
+                 ast.StructField('a10', None, None, 'uint32', '+0x12CD3'),
+                 ast.StructField('a11', None, None, 'bool', 'true'),
+                 ast.StructField('a12', None, None, 'bool', 'false'),
+                 ast.StructField('a13', None, None, 'float', '1.2345'),
+                 ast.StructField('a14', None, None, 'float', '-1.2345'),
+                 ast.StructField('a15', None, None, 'float', '+1.2345'),
+                 ast.StructField('a16', None, None, 'float', '123.'),
+                 ast.StructField('a17', None, None, 'float', '.123'),
+                 ast.StructField('a18', None, None, 'double', '1.23E10'),
+                 ast.StructField('a19', None, None, 'double', '1.E-10'),
+                 ast.StructField('a20', None, None, 'double', '.5E+10'),
+                 ast.StructField('a21', None, None, 'double', '-1.23E10'),
+                 ast.StructField('a22', None, 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;
+          array<array<array<int32, 1>>, 2> nested_arrays;
+        };
+        """
+    expected = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('normal_array', None, None, 'int32[]', None),
+                 ast.StructField('fixed_size_array_one_entry', None, None,
+                                 'int32[1]', None),
+                 ast.StructField('fixed_size_array_ten_entries', None, None,
+                                 'int32[10]', None),
+                 ast.StructField('nested_arrays', None, None,
+                                 'int32[1][][2]', 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, 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 testValidAssociativeArrays(self):
+    """Tests that we can parse valid associative array structures."""
+
+    source1 = "struct MyStruct { map<string, uint8> data; };"
+    expected1 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('data', None, None, 'uint8{string}', None)]))])
+    self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+    source2 = "interface MyInterface { MyMethod(map<string, uint8> a); };"
+    expected2 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Interface(
+            'MyInterface',
+            None,
+            ast.InterfaceBody(
+                ast.Method(
+                    'MyMethod',
+                    None,
+                    None,
+                    ast.ParameterList(
+                        ast.Parameter('a', None, None, 'uint8{string}')),
+                    None)))])
+    self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+    source3 = "struct MyStruct { map<string, array<uint8>> data; };"
+    expected3 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('data', None, None, 'uint8[]{string}',
+                                 None)]))])
+    self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+  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,
+                    None,
+                    ast.ParameterList(ast.Parameter('a', None, 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',
+                    None,
+                    ast.Ordinal(0),
+                    ast.ParameterList([ast.Parameter('a', None, ast.Ordinal(0),
+                                                     'int32'),
+                                       ast.Parameter('b', None, ast.Ordinal(1),
+                                                     'int64')]),
+                    None),
+                  ast.Method(
+                    'MyMethod2',
+                    None,
+                    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,
+                    None,
+                    ast.ParameterList(ast.Parameter('a', None, None, 'string')),
+                    ast.ParameterList([ast.Parameter('a', None, None, 'int32'),
+                                       ast.Parameter('b', None, 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',
+                          None,
+                          ast.EnumValueList(
+                              ast.EnumValue('VALUE', None, None))),
+                 ast.Const('kMyConst', 'int32', '123'),
+                 ast.Method(
+                    'MyMethod',
+                    None,
+                    None,
+                    ast.ParameterList(ast.Parameter('x', None, None, 'int32')),
+                    ast.ParameterList(ast.Parameter('y', None, 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)
+
+    # Various places that attribute list is allowed.
+    source4 = """\
+        [Attr0=0] module my_module;
+
+        [Attr1=1] struct MyStruct {
+          [Attr2=2] int32 a;
+        };
+        [Attr3=3] union MyUnion {
+          [Attr4=4] int32 a;
+        };
+        [Attr5=5] enum MyEnum {
+          [Attr6=6] a
+        };
+        [Attr7=7] interface MyInterface {
+          [Attr8=8] MyMethod([Attr9=9] int32 a) => ([Attr10=10] bool b);
+        };
+        """
+    expected4 = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'),
+                   ast.AttributeList([ast.Attribute("Attr0", 0)])),
+        ast.ImportList(),
+        [ast.Struct(
+             'MyStruct',
+             ast.AttributeList(ast.Attribute("Attr1", 1)),
+             ast.StructBody(
+                 ast.StructField(
+                     'a', ast.AttributeList([ast.Attribute("Attr2", 2)]),
+                     None, 'int32', None))),
+         ast.Union(
+             'MyUnion',
+             ast.AttributeList(ast.Attribute("Attr3", 3)),
+             ast.UnionBody(
+                 ast.UnionField(
+                     'a', ast.AttributeList([ast.Attribute("Attr4", 4)]), None,
+                     'int32'))),
+         ast.Enum(
+             'MyEnum',
+             ast.AttributeList(ast.Attribute("Attr5", 5)),
+             ast.EnumValueList(
+                 ast.EnumValue(
+                     'VALUE', ast.AttributeList([ast.Attribute("Attr6", 6)]),
+                     None))),
+         ast.Interface(
+            'MyInterface',
+            ast.AttributeList(ast.Attribute("Attr7", 7)),
+            ast.InterfaceBody(
+                ast.Method(
+                    'MyMethod',
+                    ast.AttributeList(ast.Attribute("Attr8", 8)),
+                    None,
+                    ast.ParameterList(
+                        ast.Parameter(
+                            'a', ast.AttributeList([ast.Attribute("Attr9", 9)]),
+                            None, 'int32')),
+                    ast.ParameterList(
+                        ast.Parameter(
+                            'b',
+                            ast.AttributeList([ast.Attribute("Attr10", 10)]),
+                            None, 'bool')))))])
+    self.assertEquals(parser.Parse(source4, "my_file.mojom"), expected4)
+
+    # 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 = """\
+        module my_module;
+        import "somedir/my1.mojom";
+        import "somedir/my2.mojom";
+        """
+    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.
+        struct MyStruct {
+          int32 a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+            r" *struct MyStruct {$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    source3 = """\
+        import "foo.mojom"  // Missing semicolon.
+        struct MyStruct {
+          int32 a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+            r" *struct MyStruct {$"):
+      parser.Parse(source3, "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, None,'int32?', None),
+                 ast.StructField('b', None, None,'string?', None),
+                 ast.StructField('c', None, None,'int32[]?', None),
+                 ast.StructField('d', None, None,'string?[]?', None),
+                 ast.StructField('e', None, None,'int32[]?[]?', None),
+                 ast.StructField('f', None, None,'int32[1]?', None),
+                 ast.StructField('g', None, None,'string?[1]?', None),
+                 ast.StructField('h', None, None,'some_struct?', None),
+                 ast.StructField('i', None, None,'handle?', None),
+                 ast.StructField('j', None, None,'handle<data_pipe_consumer>?',
+                                 None),
+                 ast.StructField('k', None, None,'handle<data_pipe_producer>?',
+                                 None),
+                 ast.StructField('l', None, None,'handle<message_pipe>?', None),
+                 ast.StructField('m', None, None,'handle<shared_buffer>?',
+                                 None),
+                 ast.StructField('n', None, 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")
+
+  def testSimpleUnion(self):
+    """Tests a simple .mojom source that just defines a union."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          int32 a;
+          double b;
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Union(
+          'MyUnion',
+          None,
+          ast.UnionBody([
+            ast.UnionField('a', None, None, 'int32'),
+            ast.UnionField('b', None, None, 'double')
+            ]))])
+    actual = parser.Parse(source, "my_file.mojom")
+    self.assertEquals(actual, expected)
+
+  def testUnionWithOrdinals(self):
+    """Test that ordinals are assigned to fields."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          int32 a @10;
+          double b @30;
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Union(
+          'MyUnion',
+          None,
+          ast.UnionBody([
+            ast.UnionField('a', None, ast.Ordinal(10), 'int32'),
+            ast.UnionField('b', None, ast.Ordinal(30), 'double')
+            ]))])
+    actual = parser.Parse(source, "my_file.mojom")
+    self.assertEquals(actual, expected)
+
+  def testUnionWithStructMembers(self):
+    """Test that struct members are accepted."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          SomeStruct s;
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Union(
+          'MyUnion',
+          None,
+          ast.UnionBody([
+            ast.UnionField('s', None, None, 'SomeStruct')
+            ]))])
+    actual = parser.Parse(source, "my_file.mojom")
+    self.assertEquals(actual, expected)
+
+  def testUnionWithArrayMember(self):
+    """Test that array members are accepted."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          array<int32> a;
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Union(
+          'MyUnion',
+          None,
+          ast.UnionBody([
+            ast.UnionField('a', None, None, 'int32[]')
+            ]))])
+    actual = parser.Parse(source, "my_file.mojom")
+    self.assertEquals(actual, expected)
+
+  def testUnionWithMapMember(self):
+    """Test that map members are accepted."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          map<int32, string> m;
+        };
+        """
+    expected = ast.Mojom(
+        ast.Module(('IDENTIFIER', 'my_module'), None),
+        ast.ImportList(),
+        [ast.Union(
+          'MyUnion',
+          None,
+          ast.UnionBody([
+            ast.UnionField('m', None, None, 'string{int32}')
+            ]))])
+    actual = parser.Parse(source, "my_file.mojom")
+    self.assertEquals(actual, expected)
+
+  def testUnionDisallowNestedStruct(self):
+    """Tests that structs cannot be nested in unions."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          struct MyStruct {
+            int32 a;
+          };
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: Unexpected 'struct':\n"
+        r" *struct MyStruct {$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testUnionDisallowNestedInterfaces(self):
+    """Tests that interfaces cannot be nested in unions."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          interface MyInterface {
+            MyMethod(int32 a);
+          };
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: Unexpected 'interface':\n"
+        r" *interface MyInterface {$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testUnionDisallowNestedUnion(self):
+    """Tests that unions cannot be nested in unions."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          union MyOtherUnion {
+            int32 a;
+          };
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: Unexpected 'union':\n"
+        r" *union MyOtherUnion {$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testUnionDisallowNestedEnum(self):
+    """Tests that enums cannot be nested in unions."""
+    source = """\
+        module my_module;
+
+        union MyUnion {
+          enum MyEnum {
+            A,
+          };
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:4: Error: Unexpected 'enum':\n"
+        r" *enum MyEnum {$"):
+      parser.Parse(source, "my_file.mojom")
+
+  def testValidAssociatedKinds(self):
+    """Tests parsing associated interfaces and requests."""
+    source1 = """\
+        struct MyStruct {
+          associated MyInterface a;
+          associated MyInterface& b;
+          associated MyInterface? c;
+          associated MyInterface&? d;
+        };
+        """
+    expected1 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Struct(
+            'MyStruct',
+            None,
+            ast.StructBody(
+                [ast.StructField('a', None, None,'asso<MyInterface>', None),
+                 ast.StructField('b', None, None,'asso<MyInterface&>', None),
+                 ast.StructField('c', None, None,'asso<MyInterface>?', None),
+                 ast.StructField('d', None, None,'asso<MyInterface&>?',
+                                 None)]))])
+    self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+    source2 = """\
+        interface MyInterface {
+          MyMethod(associated A a) =>(associated B& b);
+        };"""
+    expected2 = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Interface(
+            'MyInterface',
+            None,
+            ast.InterfaceBody(
+                ast.Method(
+                    'MyMethod',
+                    None,
+                    None,
+                    ast.ParameterList(
+                        ast.Parameter('a', None, None, 'asso<A>')),
+                    ast.ParameterList(
+                        ast.Parameter('b', None, None, 'asso<B&>')))))])
+    self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+  def testInvalidAssociatedKinds(self):
+    """Tests that invalid associated interfaces and requests are correctly
+    detected."""
+    source1 = """\
+        struct MyStruct {
+          associated associated SomeInterface a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'associated':\n"
+            r" *associated associated SomeInterface a;$"):
+      parser.Parse(source1, "my_file.mojom")
+
+    source2 = """\
+        struct MyStruct {
+          associated handle a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected 'handle':\n"
+            r" *associated handle a;$"):
+      parser.Parse(source2, "my_file.mojom")
+
+    source3 = """\
+        struct MyStruct {
+          associated? MyInterface& a;
+        };
+        """
+    with self.assertRaisesRegexp(
+        parser.ParseError,
+        r"^my_file\.mojom:2: Error: Unexpected '\?':\n"
+            r" *associated\? MyInterface& 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/parse/translate_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py
new file mode 100644
index 0000000..2520332
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py
@@ -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 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"))
+from mojom.parse import ast
+from mojom.parse import translate
+
+
+class TranslateTest(unittest.TestCase):
+  """Tests |parser.Parse()|."""
+
+  def testSimpleArray(self):
+    """Tests a simple int32[]."""
+    # pylint: disable=W0212
+    self.assertEquals(translate._MapKind("int32[]"), "a:i32")
+
+  def testAssociativeArray(self):
+    """Tests a simple uint8{string}."""
+    # pylint: disable=W0212
+    self.assertEquals(translate._MapKind("uint8{string}"), "m[s][u8]")
+
+  def testLeftToRightAssociativeArray(self):
+    """Makes sure that parsing is done from right to left on the internal kinds
+       in the presence of an associative array."""
+    # pylint: disable=W0212
+    self.assertEquals(translate._MapKind("uint8[]{string}"), "m[s][a:u8]")
+
+  def testTranslateSimpleUnions(self):
+    """Makes sure that a simple union is translated correctly."""
+    tree = ast.Mojom(
+        None,
+        ast.ImportList(),
+        [ast.Union("SomeUnion", None, ast.UnionBody(
+          [ast.UnionField("a", None, None, "int32"),
+           ast.UnionField("b", None, None, "string")]))])
+    expected = [{
+      "name": "SomeUnion",
+      "fields": [{"kind": "i32", "name": "a"},
+                 {"kind": "s", "name": "b"}]}]
+    actual = translate.Translate(tree, "mojom_tree")
+    self.assertEquals(actual["unions"], expected)
+
+  def testMapTreeForTypeRaisesWithDuplicate(self):
+    """Verifies _MapTreeForType() raises when passed two values with the same
+       name."""
+    methods = [ast.Method('dup', None, None, ast.ParameterList(), None),
+               ast.Method('dup', None, None, ast.ParameterList(), None)]
+    self.assertRaises(Exception, translate._MapTreeForType,
+                      (lambda x: x, methods, '', 'scope'))
+
+  def testAssociatedKinds(self):
+    """Tests type spec translation of associated interfaces and requests."""
+    # pylint: disable=W0212
+    self.assertEquals(translate._MapKind("asso<SomeInterface>?"),
+                      "?asso:x:SomeInterface")
+    self.assertEquals(translate._MapKind("asso<SomeInterface&>?"),
+                      "?asso:r:x:SomeInterface")
+
+
+if __name__ == "__main__":
+  unittest.main()
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/public/tools/chrome_ipc/generate_mojom.py b/mojo/public/tools/chrome_ipc/generate_mojom.py
new file mode 100755
index 0000000..04e933b
--- /dev/null
+++ b/mojo/public/tools/chrome_ipc/generate_mojom.py
@@ -0,0 +1,453 @@
+#! /usr/bin/env python
+# Copyright 2016 The Chromium 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 generator of mojom interfaces and typemaps from Chrome IPC messages.
+
+For example,
+generate_mojom.py content/common/file_utilities_messages.h
+    --output_mojom=content/common/file_utilities.mojom
+    --output_typemap=content/common/file_utilities.typemap
+"""
+
+import argparse
+import logging
+import os
+import re
+import subprocess
+import sys
+
+_MESSAGE_PATTERN = re.compile(
+    r'(?:\n|^)IPC_(SYNC_)?MESSAGE_(ROUTED|CONTROL)(\d_)?(\d)')
+_VECTOR_PATTERN = re.compile(r'std::(vector|set)<(.*)>')
+_MAP_PATTERN = re.compile(r'std::map<(.*), *(.*)>')
+_NAMESPACE_PATTERN = re.compile(r'([a-z_]*?)::([A-Z].*)')
+
+_unused_arg_count = 0
+
+
+def _git_grep(pattern, paths_pattern):
+  try:
+    args = ['git', 'grep', '-l', '-e', pattern, '--'] + paths_pattern
+    result = subprocess.check_output(args).strip().splitlines()
+    logging.debug('%s => %s', ' '.join(args), result)
+    return result
+  except subprocess.CalledProcessError:
+    logging.debug('%s => []', ' '.join(args))
+    return []
+
+
+def _git_multigrep(patterns, paths):
+  """Find a list of files that match all of the provided patterns."""
+  if isinstance(paths, str):
+    paths = [paths]
+  if isinstance(patterns, str):
+    patterns = [patterns]
+  for pattern in patterns:
+    # Search only the files that matched previous patterns.
+    paths = _git_grep(pattern, paths)
+    if not paths:
+      return []
+  return paths
+
+
+class Typemap(object):
+
+  def __init__(self, typemap_files):
+    self._typemap_files = typemap_files
+    self._custom_mappings = {}
+    self._new_custom_mappings = {}
+    self._imports = set()
+    self._public_includes = set()
+    self._traits_includes = set()
+    self._enums = set()
+
+  def load_typemaps(self):
+    for typemap in self._typemap_files:
+      self.load_typemap(typemap)
+
+  def load_typemap(self, path):
+    typemap = {}
+    with open(path) as f:
+      content = f.read().replace('=\n', '=')
+    exec content in typemap
+    for mapping in typemap['type_mappings']:
+      mojom, native = mapping.split('=')
+      self._custom_mappings[native] = {'name': mojom,
+                                       'mojom': typemap['mojom'].strip('/')}
+
+  def generate_typemap(self, output_mojom, input_filename, namespace):
+    new_mappings = sorted(self._format_new_mappings(namespace))
+    if not new_mappings:
+      return
+    yield """# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""
+    yield 'mojom = "//%s"' % output_mojom
+    yield 'public_headers = [%s\n]' % ''.join(
+        '\n  "//%s",' % include for include in sorted(self._public_includes))
+    yield 'traits_headers = [%s\n]' % ''.join(
+        '\n  "//%s",' % include
+        for include in sorted(self._traits_includes.union([os.path.normpath(
+            input_filename)])))
+    yield 'deps = [ "//ipc" ]'
+    yield 'type_mappings = [\n  %s\n]' % '\n  '.join(new_mappings)
+
+  def _format_new_mappings(self, namespace):
+    for native, mojom in self._new_custom_mappings.iteritems():
+      yield '"%s.%s=::%s",' % (namespace, mojom, native)
+
+  def format_new_types(self):
+    for native_type, typename in self._new_custom_mappings.iteritems():
+      if native_type in self._enums:
+        yield '[Native]\nenum %s;\n' % typename
+      else:
+        yield '[Native]\nstruct %s;\n' % typename
+
+  _BUILTINS = {
+      'bool': 'bool',
+      'int': 'int32',
+      'unsigned': 'uint32',
+      'char': 'uint8',
+      'unsigned char': 'uint8',
+      'short': 'int16',
+      'unsigned short': 'uint16',
+      'int8_t': 'int8',
+      'int16_t': 'int16',
+      'int32_t': 'int32',
+      'int64_t': 'int64',
+      'uint8_t': 'uint8',
+      'uint16_t': 'uint16',
+      'uint32_t': 'uint32',
+      'uint64_t': 'uint64',
+      'float': 'float',
+      'double': 'double',
+      'std::string': 'string',
+      'base::string16': 'string',
+      'base::FilePath::StringType': 'string',
+      'base::SharedMemoryHandle': 'handle<shared_memory>',
+      'IPC::PlatformFileForTransit': 'handle',
+      'base::FileDescriptor': 'handle',
+  }
+
+  def lookup_type(self, typename):
+    try:
+      return self._BUILTINS[typename]
+    except KeyError:
+      pass
+
+    vector_match = _VECTOR_PATTERN.search(typename)
+    if vector_match:
+      return 'array<%s>' % self.lookup_type(vector_match.groups()[1].strip())
+    map_match = _MAP_PATTERN.search(typename)
+    if map_match:
+      return 'map<%s, %s>' % tuple(self.lookup_type(t.strip())
+                                   for t in map_match.groups())
+    try:
+      result = self._custom_mappings[typename]['name']
+      mojom = self._custom_mappings[typename].get('mojom', None)
+      if mojom:
+        self._imports.add(mojom)
+      return result
+    except KeyError:
+      pass
+
+    match = _NAMESPACE_PATTERN.match(typename)
+    if match:
+      namespace, name = match.groups()
+    else:
+      namespace = ''
+      name = typename
+    namespace = namespace.replace('::', '.')
+    cpp_name = name
+    name = name.replace('::', '')
+
+    if name.endswith('Params'):
+      try:
+        _, name = name.rsplit('Msg_')
+      except ValueError:
+        try:
+          _, name = name.split('_', 1)
+        except ValueError:
+          pass
+
+    if namespace.endswith('.mojom'):
+      generated_mojom_name = '%s.%s' % (namespace, name)
+    elif not namespace:
+      generated_mojom_name = 'mojom.%s' % name
+    else:
+      generated_mojom_name = '%s.mojom.%s' % (namespace, name)
+
+    self._new_custom_mappings[typename] = name
+    self._add_includes(namespace, cpp_name, typename)
+    generated_mojom_name = name
+    self._custom_mappings[typename] = {'name': generated_mojom_name}
+    return generated_mojom_name
+
+  def _add_includes(self, namespace, name, fullname):
+    name_components = name.split('::')
+    is_enum = False
+    for i in xrange(len(name_components)):
+      subname = '::'.join(name_components[i:])
+      extra_names = name_components[:i] + [subname]
+      patterns = [r'\(struct\|class\|enum\)[A-Z_ ]* %s {' % s
+                  for s in extra_names]
+      if namespace:
+        patterns.extend(r'namespace %s' % namespace_component
+                        for namespace_component in namespace.split('.'))
+      includes = _git_multigrep(patterns, '*.h')
+      if includes:
+        if _git_grep(r'enum[A-Z_ ]* %s {' % subname, includes):
+          self._enums.add(fullname)
+          is_enum = True
+        logging.info('%s => public_headers = %s', fullname, includes)
+        self._public_includes.update(includes)
+        break
+
+    if is_enum:
+      patterns = ['IPC_ENUM_TRAITS[A-Z_]*(%s' % fullname]
+    else:
+      patterns = [r'\(IPC_STRUCT_TRAITS_BEGIN(\|ParamTraits<\)%s' % fullname]
+    includes = _git_multigrep(
+        patterns,
+        ['*messages.h', '*struct_traits.h', 'ipc/ipc_message_utils.h'])
+    if includes:
+      logging.info('%s => traits_headers = %s', fullname, includes)
+      self._traits_includes.update(includes)
+
+  def format_imports(self):
+    for import_name in sorted(self._imports):
+      yield 'import "%s";' % import_name
+    if self._imports:
+      yield ''
+
+
+class Argument(object):
+
+  def __init__(self, typename, name):
+    self.typename = typename.strip()
+    self.name = name.strip().replace('\n', '').replace(' ', '_').lower()
+    if not self.name:
+      global _unused_arg_count
+      self.name = 'unnamed_arg%d' % _unused_arg_count
+      _unused_arg_count += 1
+
+  def format(self, typemaps):
+    return '%s %s' % (typemaps.lookup_type(self.typename), self.name)
+
+
+class Message(object):
+
+  def __init__(self, match, content):
+    self.sync = bool(match[0])
+    self.routed = match[1] == 'ROUTED'
+    self.args = []
+    self.response_args = []
+    if self.sync:
+      num_expected_args = int(match[2][:-1])
+      num_expected_response_args = int(match[3])
+    else:
+      num_expected_args = int(match[3])
+      num_expected_response_args = 0
+    body = content.split(',')
+    name = body[0].strip()
+    try:
+      self.group, self.name = name.split('Msg_')
+    except ValueError:
+      try:
+        self.group, self.name = name.split('_')
+      except ValueError:
+        self.group = 'UnnamedInterface'
+        self.name = name
+    self.group = '%s%s' % (self.group, match[1].title())
+    args = list(self.parse_args(','.join(body[1:])))
+    if len(args) != num_expected_args + num_expected_response_args:
+      raise Exception('Incorrect number of args parsed for %s' % (name))
+    self.args = args[:num_expected_args]
+    self.response_args = args[num_expected_args:]
+
+  def parse_args(self, args_str):
+    args_str = args_str.strip()
+    if not args_str:
+      return
+    looking_for_type = False
+    type_start = 0
+    comment_start = None
+    comment_end = None
+    type_end = None
+    angle_bracket_nesting = 0
+    i = 0
+    while i < len(args_str):
+      if args_str[i] == ',' and not angle_bracket_nesting:
+        looking_for_type = True
+        if type_end is None:
+          type_end = i
+      elif args_str[i:i + 2] == '/*':
+        if type_end is None:
+          type_end = i
+        comment_start = i + 2
+        comment_end = args_str.index('*/', i + 2)
+        i = comment_end + 1
+      elif args_str[i:i + 2] == '//':
+        if type_end is None:
+          type_end = i
+        comment_start = i + 2
+        comment_end = args_str.index('\n', i + 2)
+        i = comment_end
+      elif args_str[i] == '<':
+        angle_bracket_nesting += 1
+      elif args_str[i] == '>':
+        angle_bracket_nesting -= 1
+      elif looking_for_type and args_str[i].isalpha():
+        if comment_start is not None and comment_end is not None:
+          yield Argument(args_str[type_start:type_end],
+                         args_str[comment_start:comment_end])
+        else:
+          yield Argument(args_str[type_start:type_end], '')
+        type_start = i
+        type_end = None
+        comment_start = None
+        comment_end = None
+        looking_for_type = False
+      i += 1
+    if comment_start is not None and comment_end is not None:
+      yield Argument(args_str[type_start:type_end],
+                     args_str[comment_start:comment_end])
+    else:
+      yield Argument(args_str[type_start:type_end], '')
+
+  def format(self, typemaps):
+    result = '%s(%s)' % (self.name, ','.join('\n      %s' % arg.format(typemaps)
+                                             for arg in self.args))
+    if self.sync:
+      result += ' => (%s)' % (',\n'.join('\n      %s' % arg.format(typemaps)
+                                         for arg in self.response_args))
+      result = '[Sync]\n  %s' % result
+    return '%s;' % result
+
+
+class Generator(object):
+
+  def __init__(self, input_name, output_namespace):
+    self._input_name = input_name
+    with open(input_name) as f:
+      self._content = f.read()
+    self._namespace = output_namespace
+    self._typemaps = Typemap(self._find_typemaps())
+    self._interface_definitions = []
+
+  def _get_messages(self):
+    for m in _MESSAGE_PATTERN.finditer(self._content):
+      i = m.end() + 1
+      while i < len(self._content):
+        if self._content[i:i + 2] == '/*':
+          i = self._content.index('*/', i + 2) + 1
+        elif self._content[i] == ')':
+          yield Message(m.groups(), self._content[m.end() + 1:i])
+          break
+        i += 1
+
+  def _extract_messages(self):
+    grouped_messages = {}
+    for m in self._get_messages():
+      grouped_messages.setdefault(m.group, []).append(m)
+    self._typemaps.load_typemaps()
+    for interface, messages in grouped_messages.iteritems():
+      self._interface_definitions.append(self._format_interface(interface,
+                                                                messages))
+
+  def count(self):
+    grouped_messages = {}
+    for m in self._get_messages():
+      grouped_messages.setdefault(m.group, []).append(m)
+    return sum(len(messages) for messages in grouped_messages.values())
+
+  def generate_mojom(self):
+    self._extract_messages()
+    if not self._interface_definitions:
+      return
+    yield """// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+"""
+    yield 'module %s;\n' % self._namespace
+    for import_statement in self._typemaps.format_imports():
+      yield import_statement
+    for typemap in self._typemaps.format_new_types():
+      yield typemap
+    for interface in self._interface_definitions:
+      yield interface
+      yield ''
+
+  def generate_typemap(self, output_mojom, input_filename):
+    return '\n'.join(self._typemaps.generate_typemap(
+        output_mojom, input_filename, self._namespace)).strip()
+
+  @staticmethod
+  def _find_typemaps():
+    return subprocess.check_output(
+        ['git', 'ls-files', '*.typemap']).strip().split('\n')
+
+  def _format_interface(self, name, messages):
+    return 'interface %s {\n  %s\n};' % (name,
+                                         '\n  '.join(m.format(self._typemaps)
+                                                     for m in messages))
+
+
+def parse_args():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('input', help='input messages.h file')
+  parser.add_argument(
+      '--output_namespace',
+      default='mojom',
+      help='the mojom module name to use in the generated mojom file '
+      '(default: %(default)s)')
+  parser.add_argument('--output_mojom', help='output mojom path')
+  parser.add_argument('--output_typemap', help='output typemap path')
+  parser.add_argument(
+      '--count',
+      action='store_true',
+      default=False,
+      help='count the number of messages in the input instead of generating '
+      'a mojom file')
+  parser.add_argument('-v',
+                      '--verbose',
+                      action='store_true',
+                      help='enable logging')
+  parser.add_argument('-vv', action='store_true', help='enable debug logging')
+  return parser.parse_args()
+
+
+def main():
+  args = parse_args()
+  if args.vv:
+    logging.basicConfig(level=logging.DEBUG)
+  elif args.verbose:
+    logging.basicConfig(level=logging.INFO)
+  generator = Generator(args.input, args.output_namespace)
+  if args.count:
+    count = generator.count()
+    if count:
+      print '%d %s' % (generator.count(), args.input)
+    return
+  mojom = '\n'.join(generator.generate_mojom()).strip()
+  if not mojom:
+    return
+  typemap = generator.generate_typemap(args.output_mojom, args.input)
+
+  if args.output_mojom:
+    with open(args.output_mojom, 'w') as f:
+      f.write(mojom)
+  else:
+    print mojom
+  if typemap:
+    if args.output_typemap:
+      with open(args.output_typemap, 'w') as f:
+        f.write(typemap)
+    else:
+      print typemap
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/mojo/public/tools/gn/zip.py b/mojo/public/tools/gn/zip.py
new file mode 100755
index 0000000..0d4960f
--- /dev/null
+++ b/mojo/public/tools/gn/zip.py
@@ -0,0 +1,81 @@
+#!/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.
+
+# TODO(brettw) bug 582594: merge this with build/android/gn/zip.py and update
+# callers to use the existing template rather than invoking this directly.
+
+"""Archives a set of files.
+"""
+
+import optparse
+import os
+import sys
+import zipfile
+
+sys.path.append(os.path.join(os.path.dirname(__file__),
+                             os.pardir, os.pardir, os.pardir, os.pardir,
+                             "build"))
+import gn_helpers
+
+def DoZip(inputs, link_inputs, zip_inputs, output, base_dir):
+  files = []
+  with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as outfile:
+    for f in inputs:
+      file_name = os.path.relpath(f, base_dir)
+      files.append(file_name)
+      outfile.write(f, file_name)
+    for f in link_inputs:
+      realf = os.path.realpath(f)  # Resolve symlinks.
+      file_name = os.path.relpath(realf, base_dir)
+      files.append(file_name)
+      outfile.write(realf, file_name)
+    for zf_name in zip_inputs:
+      with zipfile.ZipFile(zf_name, 'r') as zf:
+        for f in zf.namelist():
+          if f not in files:
+            files.append(f)
+            with zf.open(f) as zff:
+              outfile.writestr(f, zff.read())
+
+
+def main():
+  parser = optparse.OptionParser()
+
+  parser.add_option('--inputs',
+      help='GN format list of files to archive.')
+  parser.add_option('--link-inputs',
+      help='GN-format list of files to archive. Symbolic links are resolved.')
+  parser.add_option('--zip-inputs',
+      help='GN-format list of zip files to re-archive.')
+  parser.add_option('--output', help='Path to output archive.')
+  parser.add_option('--base-dir',
+                    help='If provided, the paths in the archive will be '
+                    'relative to this directory', default='.')
+
+  options, _ = parser.parse_args()
+
+  inputs = []
+  if (options.inputs):
+    parser = gn_helpers.GNValueParser(options.inputs)
+    inputs = parser.ParseList()
+
+  link_inputs = []
+  if options.link_inputs:
+    parser = gn_helpers.GNValueParser(options.link_inputs)
+    link_inputs = parser.ParseList()
+
+  zip_inputs = []
+  if options.zip_inputs:
+    parser = gn_helpers.GNValueParser(options.zip_inputs)
+    zip_inputs = parser.ParseList()
+
+  output = options.output
+  base_dir = options.base_dir
+
+  DoZip(inputs, link_inputs, zip_inputs, output, base_dir)
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/mojo/public/tools/manifest/manifest_collator.py b/mojo/public/tools/manifest/manifest_collator.py
new file mode 100755
index 0000000..9a6d0e9
--- /dev/null
+++ b/mojo/public/tools/manifest/manifest_collator.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium 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 collator for Mojo Application Manifests """
+
+import argparse
+import json
+import os
+import shutil
+import sys
+import urlparse
+
+eater_relative = '../../../../../tools/json_comment_eater'
+eater_relative = os.path.join(os.path.abspath(__file__), eater_relative)
+sys.path.insert(0, os.path.normpath(eater_relative))
+try:
+  import json_comment_eater
+finally:
+  sys.path.pop(0)
+
+def ParseJSONFile(filename):
+  with open(filename) as json_file:
+    try:
+      return json.loads(json_comment_eater.Nom(json_file.read()))
+    except ValueError:
+      print "%s is not a valid JSON document" % filename
+      return None
+
+def MergeDicts(left, right):
+  for k, v in right.iteritems():
+    if k not in left:
+      left[k] = v
+    else:
+      if isinstance(v, dict):
+        assert isinstance(left[k], dict)
+        MergeDicts(left[k], v)
+      elif isinstance(v, list):
+        assert isinstance(left[k], list)
+        left[k].extend(v)
+      else:
+        raise "Refusing to merge conflicting non-collection values."
+  return left
+
+
+def MergeBaseManifest(parent, base):
+  MergeDicts(parent["capabilities"], base["capabilities"])
+
+  if "applications" in base:
+    if "applications" not in parent:
+      parent["applications"] = []
+    parent["applications"].extend(base["applications"])
+
+  if "process-group" in base:
+    parent["process-group"] = base["process-group"]
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="Collate Mojo application manifests.")
+  parser.add_argument("--parent")
+  parser.add_argument("--output")
+  parser.add_argument("--application-name")
+  parser.add_argument("--base-manifest", default=None)
+  args, children = parser.parse_known_args()
+
+  parent = ParseJSONFile(args.parent)
+  if parent == None:
+    return 1
+
+  if args.base_manifest:
+    base = ParseJSONFile(args.base_manifest)
+    if base == None:
+      return 1
+    MergeBaseManifest(parent, base)
+
+  app_path = parent['name'].split(':')[1]
+  if app_path.startswith('//'):
+    raise ValueError("Application name path component '%s' must not start " \
+                     "with //" % app_path)
+
+  if args.application_name != app_path:
+    raise ValueError("Application name '%s' specified in build file does not " \
+                     "match application name '%s' specified in manifest." %
+                     (args.application_name, app_path))
+
+  applications = []
+  for child in children:
+    application = ParseJSONFile(child)
+    if application == None:
+      return 1
+    applications.append(application)
+
+  if len(applications) > 0:
+    parent['applications'] = applications
+
+  with open(args.output, 'w') as output_file:
+    json.dump(parent, output_file)
+
+  return 0
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/mojo/public/tools/prepend.py b/mojo/public/tools/prepend.py
new file mode 100755
index 0000000..de70a82
--- /dev/null
+++ b/mojo/public/tools/prepend.py
@@ -0,0 +1,37 @@
+#!/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.
+
+"""
+Prepends a given file with a given line. This can be used to add a shebang line
+to a generated file.
+"""
+
+import optparse
+import os
+import shutil
+import sys
+
+
+def main():
+  parser = optparse.OptionParser()
+  parser.add_option('--input', help='The file to prepend the line to.')
+  parser.add_option('--line', help='The line to be prepended.')
+  parser.add_option('--output', help='The output file.')
+
+  options, _ = parser.parse_args()
+  input_path = options.input
+  output_path = options.output
+  line = options.line
+
+  # Warning - this reads all of the input file into memory.
+  with open(output_path, 'w') as output_file:
+    output_file.write(line + '\n')
+    with open(input_path, 'r') as input_file:
+      shutil.copyfileobj(input_file, output_file)
+
+
+if __name__ == '__main__':
+  sys.exit(main())