Revert "libmojo: Uprev the library to r456626 from Chromium"

This reverts commit 8ac9103e05b66812c25348943383f9365d1ce3e0.

Reason for revert: Broke the mac_sdk
Exempt-From-Owner-Approval: Fixing mac_sdk

Change-Id: I0b74d1abaa66933a93fd6f82ff018e8948c1204e
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index 070e2d1..2dac654 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -13,6 +13,9 @@
   ]
 
   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" ]
   }
 
@@ -20,7 +23,7 @@
     deps += [ "//mojo/android" ]
   }
 
-  deps += [ "//services/service_manager:all" ]
+  deps += [ "//services/shell:all" ]
 }
 
 group("tests") {
@@ -28,14 +31,17 @@
   deps = [
     "//ipc:ipc_tests",
     "//mojo/common:mojo_common_unittests",
-    "//mojo/edk/js/tests",
+    "//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/service_manager/public/cpp/tests:mojo_public_application_unittests",
-    "//services/service_manager/tests",
+    "//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
index 49d7fd3..a659200 100644
--- a/mojo/DEPS
+++ b/mojo/DEPS
@@ -3,5 +3,5 @@
   "+build",
   "+testing",
 
-  "+services/service_manager",
+  "+services/shell",
 ]
diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn
index 1a8cdbd..813701c 100644
--- a/mojo/android/BUILD.gn
+++ b/mojo/android/BUILD.gn
@@ -29,7 +29,6 @@
   sources = [
     "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/WatcherImpl.java",
   ]
 
   jni_package = "mojo"
@@ -41,15 +40,12 @@
     "system/base_run_loop.h",
     "system/core_impl.cc",
     "system/core_impl.h",
-    "system/watcher_impl.cc",
-    "system/watcher_impl.h",
   ]
 
   deps = [
     ":system_java_jni_headers",
     "//base",
-    "//mojo/public/c/system",
-    "//mojo/public/cpp/system",
+    "//mojo/message_pump",
   ]
 }
 
@@ -63,12 +59,11 @@
     "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",
-    "system/src/org/chromium/mojo/system/impl/WatcherImpl.java",
   ]
 
   deps = [
     "//base:base_java",
-    "//mojo/public/java:system_java",
+    "//mojo/public/java:system",
   ]
 }
 
@@ -95,7 +90,6 @@
     "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",
-    "javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java",
   ]
 
   deps = [
@@ -105,13 +99,8 @@
     "//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_java",
-    "//mojo/public/java:system_java",
-    "//third_party/android_support_test_runner:runner_java",
-  ]
-
-  data = [
-    "//mojo/public/interfaces/bindings/tests/data/validation/",
+    "//mojo/public/java:bindings",
+    "//mojo/public/java:system",
   ]
 }
 
@@ -131,9 +120,10 @@
     ":libsystem_java",
     ":system_java_jni_headers",
     "//base",
-    "//base/test:test_support",
+    "//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",
   ]
@@ -146,10 +136,10 @@
     ":system_java",
     "//base:base_java",
     "//mojo/public/interfaces/bindings/tests:test_interfaces",
-    "//mojo/public/java:bindings_java",
-    "//third_party/android_support_test_runner:runner_java",
+    "//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/javatests/init_library.cc b/mojo/android/javatests/init_library.cc
index 9e1a593..d2d9423 100644
--- a/mojo/android/javatests/init_library.cc
+++ b/mojo/android/javatests/init_library.cc
@@ -6,33 +6,27 @@
 #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/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/android/system/watcher_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},
-    {"WatcherImpl", mojo::android::RegisterWatcherImpl},
+  { "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));
+      RegisterNativeMethods(env, kMojoRegisteredMethods,
+                            arraysize(kMojoRegisteredMethods));
 }
 
-bool NativeInit() {
-  if (!base::android::OnJNIOnLoadInit())
-    return false;
-
+bool Init() {
   mojo::edk::Init();
   return true;
 }
@@ -40,11 +34,13 @@
 }  // namespace
 
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  base::android::InitVM(vm);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  if (!base::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) ||
-      !NativeInit()) {
+  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
index fc59009..6d88985 100644
--- a/mojo/android/javatests/mojo_test_case.cc
+++ b/mojo/android/javatests/mojo_test_case.cc
@@ -16,13 +16,12 @@
 #include "base/test/test_support_android.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "jni/MojoTestCase_jni.h"
-
-using base::android::JavaParamRef;
+#include "mojo/message_pump/message_pump_mojo.h"
 
 namespace {
 
 struct TestEnvironment {
-  TestEnvironment() {}
+  TestEnvironment() : message_loop(mojo::common::MessagePumpMojo::Create()) {}
 
   base::ShadowingAtExitManager at_exit;
   base::MessageLoop message_loop;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
index f4d7ab7..c7348ca 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo;
 
+import android.content.Context;
 import android.test.InstrumentationTestCase;
 
 import org.chromium.base.ContextUtils;
@@ -25,9 +26,9 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        ContextUtils.initApplicationContext(
-                getInstrumentation().getTargetContext().getApplicationContext());
-        LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized();
+        Context appContext = getInstrumentation().getTargetContext().getApplicationContext();
+        ContextUtils.initApplicationContext(appContext);
+        LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(appContext);
         nativeInit();
         mTestEnvironmentPointer = nativeSetupTestEnvironment();
     }
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
index 38bd348..a46a157 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import junit.framework.TestCase;
 
@@ -34,8 +34,8 @@
                     BindingsHelper.utf8StringSizeInBytes(s));
         }
         assertEquals(1, BindingsHelper.utf8StringSizeInBytes("\0"));
-        String s = new StringBuilder().appendCodePoint(0x0).appendCodePoint(0x80)
-                .appendCodePoint(0x800).appendCodePoint(0x10000).toString();
+        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);
     }
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
index d280c77..6886023 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import junit.framework.TestCase;
 
@@ -21,15 +21,12 @@
 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.bindings.test.mojom.test_structs.Rect;
 import org.chromium.mojo.system.DataPipe.ConsumerHandle;
 import org.chromium.mojo.system.DataPipe.ProducerHandle;
 import org.chromium.mojo.system.MessagePipeHandle;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Testing generated classes and associated features.
@@ -84,15 +81,6 @@
         return foo;
     }
 
-    private static Rect createRect(int x, int y, int width, int height) {
-        Rect rect = new Rect();
-        rect.x = x;
-        rect.y = y;
-        rect.width = width;
-        rect.height = height;
-        return rect;
-    }
-
     private static <T> void checkConstantField(
             Field field, Class<T> expectedClass, T value) throws IllegalAccessException {
         assertEquals(expectedClass, field.getType());
@@ -221,21 +209,4 @@
         assertNotNull(emptyStruct);
     }
 
-    // In testing maps we want to make sure that the key used when inserting an
-    // item the key used when looking it up again are different objects. Java
-    // has default implementations of equals and hashCode that use reference
-    // equality and hashing, respectively, and that's not what we want for our
-    // mojom values.
-    @SmallTest
-    public void testHashMapStructKey() {
-        Map<Rect, Integer> map = new HashMap<>();
-        map.put(createRect(1, 2, 3, 4), 123);
-
-        Rect key = createRect(1, 2, 3, 4);
-        assertNotNull(map.get(key));
-        assertEquals(123, map.get(key).intValue());
-
-        map.remove(key);
-        assertTrue(map.isEmpty());
-    }
 }
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
index eea92ab..f0c0f7c 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.mojo.MojoTestCase;
 import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStruct;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
index 497be65..dfb8e21 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
index 15f9f1f..9c26875 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.mojo.MojoTestCase;
 import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
index cabe230..56f4b40 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.mojo.MojoTestCase;
 import org.chromium.mojo.system.impl.CoreImpl;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
index 8cdd4ab..c101675 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.mojo.MojoTestCase;
 import org.chromium.mojo.bindings.Callbacks.Callback1;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
index d8bd3e8..f96b1e3 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.mojo.MojoTestCase;
 import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
index b2e6ac8..8af8be1 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
index 51dbd22..f2edb01 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.mojo.MojoTestCase;
 import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiver;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
index 5affb8f..0b632a9 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.mojo.MojoTestCase;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
index 2c17e3a..a7e213c 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import junit.framework.TestCase;
 
@@ -17,8 +17,6 @@
 import org.chromium.mojo.bindings.test.mojom.mojo.Struct6;
 import org.chromium.mojo.bindings.test.mojom.mojo.StructOfNullables;
 
-import java.nio.ByteBuffer;
-
 /**
  * Tests for the serialization logic of the generated structs, using structs defined in
  * mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom .
@@ -130,46 +128,4 @@
         assertNull(struct.str);
         struct.serialize(null);
     }
-
-    /**
-     * Verifies that a struct can be serialized to and deserialized from a ByteBuffer.
-     */
-    @SmallTest
-    public void testByteBufferSerialization() {
-        Struct1 input = new Struct1();
-        input.i = 0x7F;
-
-        ByteBuffer buf = input.serialize();
-
-        byte[] expected_raw_bytes = {16, 0, 0, 0, 0, 0, 0, 0, 0x7F, 0, 0, 0, 0, 0, 0, 0};
-        ByteBuffer expected_buf = ByteBuffer.wrap(expected_raw_bytes);
-        assertEquals(expected_buf, buf);
-
-        Struct1 output = Struct1.deserialize(buf);
-        assertEquals(0x7F, output.i);
-    }
-
-    /**
-     * Verifies that a struct with handles cannot be serialized to a ByteBuffer.
-     */
-    @SmallTest
-    public void testByteBufferSerializationWithHandles() {
-        StructOfNullables struct = new StructOfNullables();
-        assertFalse(struct.hdl.isValid());
-        assertNull(struct.struct1);
-        assertNull(struct.str);
-
-        // It is okay to serialize invalid handles.
-        struct.serialize();
-
-        struct.hdl = new HandleMock();
-
-        try {
-            struct.serialize();
-            fail("Serializing a struct with handles to a ByteBuffer should have thrown an "
-                    + "exception.");
-        } catch (UnsupportedOperationException ex) {
-            // Expected.
-        }
-    }
 }
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
index 8424618..e5c4549 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.mojo.HandleMock;
@@ -13,7 +13,6 @@
 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 org.chromium.mojo.system.impl.CoreImpl;
 
 import java.io.File;
 import java.io.FileFilter;
@@ -190,10 +189,8 @@
      */
     @SmallTest
     public void testConformance() throws FileNotFoundException {
-        runTest("conformance_",
-                ConformanceTestInterface.MANAGER.buildStub(CoreImpl.getInstance(),
-                        ConformanceTestInterface.MANAGER.buildProxy(
-                                CoreImpl.getInstance(), new SinkMessageReceiver())));
+        runTest("conformance_", ConformanceTestInterface.MANAGER.buildStub(null,
+                ConformanceTestInterface.MANAGER.buildProxy(null, new SinkMessageReceiver())));
     }
 
     /**
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
index 623abc3..14259ef 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.mojo.MojoTestCase;
 
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
index 77a9bda..16c16f2 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
@@ -4,9 +4,12 @@
 
 package org.chromium.mojo.system.impl;
 
-import android.support.test.filters.SmallTest;
+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;
@@ -581,6 +584,266 @@
         }
     }
 
+    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.
      */
diff --git a/mojo/android/javatests/validation_test_util.cc b/mojo/android/javatests/validation_test_util.cc
index 75f79b3..fb4d140 100644
--- a/mojo/android/javatests/validation_test_util.cc
+++ b/mojo/android/javatests/validation_test_util.cc
@@ -14,9 +14,6 @@
 #include "jni/ValidationTestUtil_jni.h"
 #include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
 
-using base::android::JavaParamRef;
-using base::android::ScopedJavaLocalRef;
-
 namespace mojo {
 namespace android {
 
@@ -37,7 +34,8 @@
           input, &data, &num_handles, &error_message)) {
     ScopedJavaLocalRef<jstring> j_error_message =
         base::android::ConvertUTF8ToJavaString(env, error_message);
-    return Java_ValidationTestUtil_buildData(env, nullptr, 0, j_error_message);
+    return Java_ValidationTestUtil_buildData(env, NULL, 0,
+                                             j_error_message.obj());
   }
   void* data_ptr = &data[0];
   if (!data_ptr) {
@@ -46,8 +44,7 @@
   }
   jobject byte_buffer =
       env->NewDirectByteBuffer(data_ptr, data.size());
-  return Java_ValidationTestUtil_buildData(env, byte_buffer, num_handles,
-                                           nullptr);
+  return Java_ValidationTestUtil_buildData(env, byte_buffer, num_handles, NULL);
 }
 
 }  // namespace android
diff --git a/mojo/android/system/base_run_loop.cc b/mojo/android/system/base_run_loop.cc
index 22511f3..e48d2f0 100644
--- a/mojo/android/system/base_run_loop.cc
+++ b/mojo/android/system/base_run_loop.cc
@@ -10,31 +10,30 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_registrar.h"
 #include "base/bind.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 "jni/BaseRunLoop_jni.h"
-
-using base::android::JavaParamRef;
+#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;
+  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) {
-  base::RunLoop().Run();
+                const JavaParamRef<jobject>& jcaller,
+                jlong runLoopID) {
+  reinterpret_cast<base::MessageLoop*>(runLoopID)->Run();
 }
 
 static void RunUntilIdle(JNIEnv* env,
-                         const JavaParamRef<jobject>& jcaller) {
-  base::RunLoop().RunUntilIdle();
+                         const JavaParamRef<jobject>& jcaller,
+                         jlong runLoopID) {
+  reinterpret_cast<base::MessageLoop*>(runLoopID)->RunUntilIdle();
 }
 
 static void Quit(JNIEnv* env,
@@ -46,7 +45,7 @@
 static void RunJavaRunnable(
     const base::android::ScopedJavaGlobalRef<jobject>& runnable_ref) {
   Java_BaseRunLoop_runRunnable(base::android::AttachCurrentThread(),
-                               runnable_ref);
+                               runnable_ref.obj());
 }
 
 static void PostDelayedTask(JNIEnv* env,
@@ -59,10 +58,9 @@
   // use it across threads. |RunJavaRunnable| will acquire a new JNIEnv before
   // running the Runnable.
   runnable_ref.Reset(env, runnable);
-  reinterpret_cast<base::MessageLoop*>(runLoopID)
-      ->task_runner()
-      ->PostDelayedTask(FROM_HERE, base::Bind(&RunJavaRunnable, runnable_ref),
-                        base::TimeDelta::FromMicroseconds(delay));
+  reinterpret_cast<base::MessageLoop*>(runLoopID)->PostDelayedTask(
+      FROM_HERE, base::Bind(&RunJavaRunnable, runnable_ref),
+      base::TimeDelta::FromMicroseconds(delay));
 }
 
 static void DeleteMessageLoop(JNIEnv* env,
@@ -80,3 +78,4 @@
 }  // namespace android
 }  // namespace mojo
 
+
diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc
index 5cbd754..526c050 100644
--- a/mojo/android/system/core_impl.cc
+++ b/mojo/android/system/core_impl.cc
@@ -7,20 +7,56 @@
 #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 {
 
-using base::android::JavaParamRef;
-using base::android::ScopedJavaLocalRef;
-
 static jlong GetTimeTicksNow(JNIEnv* env,
                              const JavaParamRef<jobject>& jcaller) {
   return MojoGetTimeTicksNow();
@@ -329,6 +365,51 @@
   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,
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
index 3db6670..dfe92ef 100644
--- a/mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java
@@ -27,13 +27,13 @@
     @Override
     public void run() {
         assert mRunLoopID != 0 : "The run loop cannot run once closed";
-        nativeRun();
+        nativeRun(mRunLoopID);
     }
 
     @Override
     public void runUntilIdle() {
         assert mRunLoopID != 0 : "The run loop cannot run once closed";
-        nativeRunUntilIdle();
+        nativeRunUntilIdle(mRunLoopID);
     }
 
     @Override
@@ -66,8 +66,8 @@
     }
 
     private native long nativeCreateBaseRunLoop();
-    private native void nativeRun();
-    private native void nativeRunUntilIdle();
+    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
index 8330586..a20ea0a 100644
--- a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
@@ -7,6 +7,7 @@
 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;
@@ -22,7 +23,6 @@
 import org.chromium.mojo.system.SharedBufferHandle.DuplicateOptions;
 import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
 import org.chromium.mojo.system.UntypedHandle;
-import org.chromium.mojo.system.Watcher;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -35,7 +35,7 @@
  */
 @JNINamespace("mojo::android")
 @MainDex
-public class CoreImpl implements Core {
+public class CoreImpl implements Core, AsyncWaiter {
     /**
      * Discard flag for the |MojoReadData| operation.
      */
@@ -216,11 +216,11 @@
     }
 
     /**
-     * @see Core#getWatcher()
+     * @see Core#getDefaultAsyncWaiter()
      */
     @Override
-    public Watcher getWatcher() {
-        return new WatcherImpl();
+    public AsyncWaiter getDefaultAsyncWaiter() {
+        return this;
     }
 
     /**
@@ -251,6 +251,15 @@
         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);
     }
@@ -488,6 +497,59 @@
         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);
@@ -567,5 +629,10 @@
 
     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/WatcherImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/WatcherImpl.java
deleted file mode 100644
index 094ad90..0000000
--- a/mojo/android/system/src/org/chromium/mojo/system/impl/WatcherImpl.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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.
-
-package org.chromium.mojo.system.impl;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.mojo.system.Core;
-import org.chromium.mojo.system.Handle;
-import org.chromium.mojo.system.MojoResult;
-import org.chromium.mojo.system.Watcher;
-
-@JNINamespace("mojo::android")
-class WatcherImpl implements Watcher {
-    private long mImplPtr = nativeCreateWatcher();
-    private Callback mCallback;
-
-    @Override
-    public int start(Handle handle, Core.HandleSignals signals, Callback callback) {
-        if (mImplPtr == 0) {
-            return MojoResult.INVALID_ARGUMENT;
-        }
-        if (!(handle instanceof HandleBase)) {
-            return MojoResult.INVALID_ARGUMENT;
-        }
-        int result =
-                nativeStart(mImplPtr, ((HandleBase) handle).getMojoHandle(), signals.getFlags());
-        if (result == MojoResult.OK) mCallback = callback;
-        return result;
-    }
-
-    @Override
-    public void cancel() {
-        if (mImplPtr == 0) {
-            return;
-        }
-        mCallback = null;
-        nativeCancel(mImplPtr);
-    }
-
-    @Override
-    public void destroy() {
-        if (mImplPtr == 0) {
-            return;
-        }
-        nativeDelete(mImplPtr);
-        mImplPtr = 0;
-    }
-
-    @CalledByNative
-    private void onHandleReady(int result) {
-        mCallback.onResult(result);
-    }
-
-    private native long nativeCreateWatcher();
-
-    private native int nativeStart(long implPtr, int mojoHandle, int flags);
-
-    private native void nativeCancel(long implPtr);
-
-    private native void nativeDelete(long implPtr);
-}
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn
index 9e74e58..57c85d5 100644
--- a/mojo/common/BUILD.gn
+++ b/mojo/common/BUILD.gn
@@ -12,27 +12,28 @@
   ]
 }
 
+# GYP version: mojo/mojo_base.gyp:mojo_common_custom_types
 mojom("common_custom_types") {
   sources = [
-    "file.mojom",
-    "file_path.mojom",
+    "common_custom_types.mojom",
     "string16.mojom",
-    "text_direction.mojom",
-    "time.mojom",
-    "unguessable_token.mojom",
-    "values.mojom",
-    "version.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" ]
@@ -44,21 +45,21 @@
   ]
 }
 
+# GYP version: mojo/mojo_base.gyp:mojo_test_common_custom_types
 mojom("test_common_custom_types") {
   sources = [
     "test_common_custom_types.mojom",
-    "traits_test_service.mojom",
   ]
   public_deps = [
     ":common_custom_types",
   ]
 }
 
+# GYP version: mojo/mojo_base.gyp:mojo_common_unittests
 test("mojo_common_unittests") {
   deps = [
     ":common",
     ":common_custom_types",
-    ":struct_traits",
     ":test_common_custom_types",
     "//base",
     "//base:message_loop_tests",
@@ -73,21 +74,16 @@
 
   sources = [
     "common_custom_types_unittest.cc",
-    "struct_traits_unittest.cc",
+    "common_type_converters_unittest.cc",
   ]
 }
 
-source_set("struct_traits") {
-  sources = [
-    "common_custom_types_struct_traits.cc",
-    "common_custom_types_struct_traits.h",
-  ]
+test("mojo_common_perftests") {
   deps = [
-    ":common_custom_types_shared_cpp_sources",
-    "//base:base",
-    "//mojo/public/cpp/system",
-  ]
-  public_deps = [
-    "//base:i18n",
+    ":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
index e8ac428..588c68d 100644
--- a/mojo/common/DEPS
+++ b/mojo/common/DEPS
@@ -1,6 +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/time.mojom b/mojo/common/common_custom_types.mojom
similarity index 67%
rename from mojo/common/time.mojom
rename to mojo/common/common_custom_types.mojom
index 43dfcc8..aa87106 100644
--- a/mojo/common/time.mojom
+++ b/mojo/common/common_custom_types.mojom
@@ -5,11 +5,19 @@
 module mojo.common.mojom;
 
 [Native]
+struct FilePath;
+
+[Native]
+struct ListValue;
+
+[Native]
+struct DictionaryValue;
+
+[Native]
 struct Time;
 
-struct TimeDelta {
-  int64 microseconds;
-};
+[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..0b63f38
--- /dev/null
+++ b/mojo/common/common_custom_types.typemap
@@ -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.
+
+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.String16=base::string16",
+  "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_struct_traits.cc b/mojo/common/common_custom_types_struct_traits.cc
index 6289504..3f9f7d9 100644
--- a/mojo/common/common_custom_types_struct_traits.cc
+++ b/mojo/common/common_custom_types_struct_traits.cc
@@ -1,108 +1,18 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All 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_custom_types_struct_traits.h"
 
-#include "mojo/public/cpp/system/platform_handle.h"
-
 namespace mojo {
 
 // static
-bool StructTraits<common::mojom::String16DataView, base::string16>::Read(
-    common::mojom::String16DataView data,
-    base::string16* out) {
-  ArrayDataView<uint16_t> view;
-  data.GetDataDataView(&view);
+bool StructTraits<common::mojom::String16, base::string16>::Read(
+    common::mojom::String16DataView data, base::string16* out) {
+  std::vector<uint16_t> view;
+  data.ReadData(&view);
   out->assign(reinterpret_cast<const base::char16*>(view.data()), view.size());
   return true;
 }
 
-// static
-const std::vector<uint32_t>&
-StructTraits<common::mojom::VersionDataView, base::Version>::components(
-    const base::Version& version) {
-  return version.components();
-}
-
-// static
-bool StructTraits<common::mojom::VersionDataView, base::Version>::Read(
-    common::mojom::VersionDataView data,
-    base::Version* out) {
-  std::vector<uint32_t> components;
-  if (!data.ReadComponents(&components))
-    return false;
-
-  *out = base::Version(base::Version(std::move(components)));
-  return out->IsValid();
-}
-
-// static
-bool StructTraits<
-    common::mojom::UnguessableTokenDataView,
-    base::UnguessableToken>::Read(common::mojom::UnguessableTokenDataView data,
-                                  base::UnguessableToken* out) {
-  uint64_t high = data.high();
-  uint64_t low = data.low();
-
-  // Receiving a zeroed UnguessableToken is a security issue.
-  if (high == 0 && low == 0)
-    return false;
-
-  *out = base::UnguessableToken::Deserialize(high, low);
-  return true;
-}
-
-mojo::ScopedHandle StructTraits<common::mojom::FileDataView, base::File>::fd(
-    base::File& file) {
-  DCHECK(file.IsValid());
-  return mojo::WrapPlatformFile(file.TakePlatformFile());
-}
-
-bool StructTraits<common::mojom::FileDataView, base::File>::Read(
-    common::mojom::FileDataView data,
-    base::File* file) {
-  base::PlatformFile platform_handle = base::kInvalidPlatformFile;
-  if (mojo::UnwrapPlatformFile(data.TakeFd(), &platform_handle) !=
-      MOJO_RESULT_OK) {
-    return false;
-  }
-  *file = base::File(platform_handle);
-  return true;
-}
-
-// static
-common::mojom::TextDirection
-EnumTraits<common::mojom::TextDirection, base::i18n::TextDirection>::ToMojom(
-    base::i18n::TextDirection text_direction) {
-  switch (text_direction) {
-    case base::i18n::UNKNOWN_DIRECTION:
-      return common::mojom::TextDirection::UNKNOWN_DIRECTION;
-    case base::i18n::RIGHT_TO_LEFT:
-      return common::mojom::TextDirection::RIGHT_TO_LEFT;
-    case base::i18n::LEFT_TO_RIGHT:
-      return common::mojom::TextDirection::LEFT_TO_RIGHT;
-  }
-  NOTREACHED();
-  return common::mojom::TextDirection::UNKNOWN_DIRECTION;
-}
-
-// static
-bool EnumTraits<common::mojom::TextDirection, base::i18n::TextDirection>::
-    FromMojom(common::mojom::TextDirection input,
-              base::i18n::TextDirection* out) {
-  switch (input) {
-    case common::mojom::TextDirection::UNKNOWN_DIRECTION:
-      *out = base::i18n::UNKNOWN_DIRECTION;
-      return true;
-    case common::mojom::TextDirection::RIGHT_TO_LEFT:
-      *out = base::i18n::RIGHT_TO_LEFT;
-      return true;
-    case common::mojom::TextDirection::LEFT_TO_RIGHT:
-      *out = base::i18n::LEFT_TO_RIGHT;
-      return true;
-  }
-  return false;
-}
-
-}  // namespace mojo
+}  // mojo
diff --git a/mojo/common/common_custom_types_struct_traits.h b/mojo/common/common_custom_types_struct_traits.h
index b20c795..22afdb6 100644
--- a/mojo/common/common_custom_types_struct_traits.h
+++ b/mojo/common/common_custom_types_struct_traits.h
@@ -1,98 +1,27 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All 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_CUSTOM_TYPES_STRUCT_TRAITS_H_
 #define MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_
 
-#include "base/files/file.h"
-#include "base/i18n/rtl.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/unguessable_token.h"
-#include "base/version.h"
-#include "mojo/common/file.mojom-shared.h"
-#include "mojo/common/mojo_common_export.h"
-#include "mojo/common/string16.mojom-shared.h"
-#include "mojo/common/text_direction.mojom-shared.h"
-#include "mojo/common/time.mojom-shared.h"
-#include "mojo/common/unguessable_token.mojom-shared.h"
-#include "mojo/common/version.mojom-shared.h"
+#include <stdint.h>
+#include <vector>
+
+#include "mojo/common/string16.mojom.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace mojo {
 
 template <>
-struct StructTraits<common::mojom::String16DataView, base::string16> {
-  static ConstCArray<uint16_t> data(const base::string16& str) {
-    return ConstCArray<uint16_t>(str.size(),
-                                 reinterpret_cast<const uint16_t*>(str.data()));
+struct StructTraits<common::mojom::String16, base::string16> {
+  static std::vector<uint16_t> data(const base::string16& str) {
+    const uint16_t* base = str.data();
+    return std::vector<uint16_t>(base, base + str.size());
   }
-
-  static bool Read(common::mojom::String16DataView data, base::string16* out);
+  static bool Read(common::mojom::String16DataView data, base::string16* output);
 };
 
-template <>
-struct StructTraits<common::mojom::VersionDataView, base::Version> {
-  static bool IsNull(const base::Version& version) {
-    return !version.IsValid();
-  }
-  static void SetToNull(base::Version* out) {
-    *out = base::Version(std::string());
-  }
-  static const std::vector<uint32_t>& components(const base::Version& version);
-  static bool Read(common::mojom::VersionDataView data, base::Version* out);
-};
-
-// If base::UnguessableToken is no longer 128 bits, the logic below and the
-// mojom::UnguessableToken type should be updated.
-static_assert(sizeof(base::UnguessableToken) == 2 * sizeof(uint64_t),
-              "base::UnguessableToken should be of size 2 * sizeof(uint64_t).");
-
-template <>
-struct StructTraits<common::mojom::UnguessableTokenDataView,
-                    base::UnguessableToken> {
-  static uint64_t high(const base::UnguessableToken& token) {
-    return token.GetHighForSerialization();
-  }
-
-  static uint64_t low(const base::UnguessableToken& token) {
-    return token.GetLowForSerialization();
-  }
-
-  static bool Read(common::mojom::UnguessableTokenDataView data,
-                   base::UnguessableToken* out);
-};
-
-template <>
-struct StructTraits<common::mojom::TimeDeltaDataView, base::TimeDelta> {
-  static int64_t microseconds(const base::TimeDelta& delta) {
-    return delta.InMicroseconds();
-  }
-
-  static bool Read(common::mojom::TimeDeltaDataView data,
-                   base::TimeDelta* delta) {
-    *delta = base::TimeDelta::FromMicroseconds(data.microseconds());
-    return true;
-  }
-};
-
-template <>
-struct StructTraits<common::mojom::FileDataView, base::File> {
-  static bool IsNull(const base::File& file) { return !file.IsValid(); }
-
-  static void SetToNull(base::File* file) { *file = base::File(); }
-
-  static mojo::ScopedHandle fd(base::File& file);
-  static bool Read(common::mojom::FileDataView data, base::File* file);
-};
-
-template <>
-struct EnumTraits<common::mojom::TextDirection, base::i18n::TextDirection> {
-  static common::mojom::TextDirection ToMojom(
-      base::i18n::TextDirection text_direction);
-  static bool FromMojom(common::mojom::TextDirection input,
-                        base::i18n::TextDirection* out);
-};
-
-}  // namespace mojo
+}  // mojo
 
 #endif  // MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_
diff --git a/mojo/common/common_custom_types_unittest.cc b/mojo/common/common_custom_types_unittest.cc
index e3571d9..fe6bb5d 100644
--- a/mojo/common/common_custom_types_unittest.cc
+++ b/mojo/common/common_custom_types_unittest.cc
@@ -3,13 +3,10 @@
 // found in the LICENSE file.
 
 #include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
-#include "base/numerics/safe_math.h"
 #include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.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"
@@ -26,38 +23,33 @@
   }
 };
 
-template <typename T>
-struct PassTraits {
-  using Type = const T&;
+template <>
+struct BounceTestTraits<base::DictionaryValue> {
+  static void ExpectEquality(const base::DictionaryValue& a,
+                             const base::DictionaryValue& b) {
+    EXPECT_TRUE(a.Equals(&b));
+  }
 };
 
 template <>
-struct PassTraits<base::Time> {
-  using Type = base::Time;
-};
-
-template <>
-struct PassTraits<base::TimeDelta> {
-  using Type = base::TimeDelta;
-};
-
-template <>
-struct PassTraits<base::TimeTicks> {
-  using Type = base::TimeTicks;
+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,
-                      typename PassTraits<T>::Type value) {
+                      const T& value) {
   BounceTestTraits<T>::ExpectEquality(*expected_value, value);
   closure.Run();
 }
 
 template <typename T>
-base::Callback<void(typename PassTraits<T>::Type)> ExpectResponse(
-    T* expected_value,
-    const base::Closure& closure) {
+base::Callback<void(const T&)> ExpectResponse(T* expected_value,
+                                              const base::Closure& closure) {
   return base::Bind(&DoExpectResponse<T>, expected_value, closure);
 }
 
@@ -76,38 +68,24 @@
   mojo::Binding<TestFilePath> binding_;
 };
 
-class TestUnguessableTokenImpl : public TestUnguessableToken {
- public:
-  explicit TestUnguessableTokenImpl(TestUnguessableTokenRequest request)
-      : binding_(this, std::move(request)) {}
-
-  // TestUnguessableToken implementation:
-  void BounceNonce(const base::UnguessableToken& in,
-                   const BounceNonceCallback& callback) override {
-    callback.Run(in);
-  }
-
- private:
-  mojo::Binding<TestUnguessableToken> binding_;
-};
-
 class TestTimeImpl : public TestTime {
  public:
   explicit TestTimeImpl(TestTimeRequest request)
       : binding_(this, std::move(request)) {}
 
   // TestTime implementation:
-  void BounceTime(base::Time in, const BounceTimeCallback& callback) override {
+  void BounceTime(const base::Time& in,
+                  const BounceTimeCallback& callback) override {
     callback.Run(in);
   }
 
-  void BounceTimeDelta(base::TimeDelta in,
-                       const BounceTimeDeltaCallback& callback) override {
+  void BounceTimeDelta(const base::TimeDelta& in,
+                  const BounceTimeDeltaCallback& callback) override {
     callback.Run(in);
   }
 
-  void BounceTimeTicks(base::TimeTicks in,
-                       const BounceTimeTicksCallback& callback) override {
+  void BounceTimeTicks(const base::TimeTicks& in,
+                  const BounceTimeTicksCallback& callback) override {
     callback.Run(in);
   }
 
@@ -122,70 +100,19 @@
 
   // TestValue implementation:
   void BounceDictionaryValue(
-      std::unique_ptr<base::DictionaryValue> in,
+      const base::DictionaryValue& in,
       const BounceDictionaryValueCallback& callback) override {
-    callback.Run(std::move(in));
+    callback.Run(in);
   }
-
-  void BounceListValue(std::unique_ptr<base::ListValue> in,
+  void BounceListValue(const base::ListValue& in,
                        const BounceListValueCallback& callback) override {
-    callback.Run(std::move(in));
-  }
-
-  void BounceValue(std::unique_ptr<base::Value> in,
-                   const BounceValueCallback& callback) override {
-    callback.Run(std::move(in));
+    callback.Run(in);
   }
 
  private:
   mojo::Binding<TestValue> binding_;
 };
 
-class TestString16Impl : public TestString16 {
- public:
-  explicit TestString16Impl(TestString16Request request)
-      : binding_(this, std::move(request)) {}
-
-  // TestString16 implementation:
-  void BounceString16(const base::string16& in,
-                      const BounceString16Callback& callback) override {
-    callback.Run(in);
-  }
-
- private:
-  mojo::Binding<TestString16> binding_;
-};
-
-class TestFileImpl : public TestFile {
- public:
-  explicit TestFileImpl(TestFileRequest request)
-      : binding_(this, std::move(request)) {}
-
-  // TestFile implementation:
-  void BounceFile(base::File in, const BounceFileCallback& callback) override {
-    callback.Run(std::move(in));
-  }
-
- private:
-  mojo::Binding<TestFile> binding_;
-};
-
-class TestTextDirectionImpl : public TestTextDirection {
- public:
-  explicit TestTextDirectionImpl(TestTextDirectionRequest request)
-      : binding_(this, std::move(request)) {}
-
-  // TestTextDirection:
-  void BounceTextDirection(
-      base::i18n::TextDirection in,
-      const BounceTextDirectionCallback& callback) override {
-    callback.Run(in);
-  }
-
- private:
-  mojo::Binding<TestTextDirection> binding_;
-};
-
 class CommonCustomTypesTest : public testing::Test {
  protected:
   CommonCustomTypesTest() {}
@@ -203,7 +130,7 @@
   base::RunLoop run_loop;
 
   TestFilePathPtr ptr;
-  TestFilePathImpl impl(MakeRequest(&ptr));
+  TestFilePathImpl impl(GetProxy(&ptr));
 
   base::FilePath dir(FILE_PATH_LITERAL("hello"));
   base::FilePath file = dir.Append(FILE_PATH_LITERAL("world"));
@@ -213,24 +140,11 @@
   run_loop.Run();
 }
 
-TEST_F(CommonCustomTypesTest, UnguessableToken) {
-  base::RunLoop run_loop;
-
-  TestUnguessableTokenPtr ptr;
-  TestUnguessableTokenImpl impl(MakeRequest(&ptr));
-
-  base::UnguessableToken token = base::UnguessableToken::Create();
-
-  ptr->BounceNonce(token, ExpectResponse(&token, run_loop.QuitClosure()));
-
-  run_loop.Run();
-}
-
 TEST_F(CommonCustomTypesTest, Time) {
   base::RunLoop run_loop;
 
   TestTimePtr ptr;
-  TestTimeImpl impl(MakeRequest(&ptr));
+  TestTimeImpl impl(GetProxy(&ptr));
 
   base::Time t = base::Time::Now();
 
@@ -243,7 +157,7 @@
   base::RunLoop run_loop;
 
   TestTimePtr ptr;
-  TestTimeImpl impl(MakeRequest(&ptr));
+  TestTimeImpl impl(GetProxy(&ptr));
 
   base::TimeDelta t = base::TimeDelta::FromDays(123);
 
@@ -256,7 +170,7 @@
   base::RunLoop run_loop;
 
   TestTimePtr ptr;
-  TestTimeImpl impl(MakeRequest(&ptr));
+  TestTimeImpl impl(GetProxy(&ptr));
 
   base::TimeTicks t = base::TimeTicks::Now();
 
@@ -267,164 +181,43 @@
 
 TEST_F(CommonCustomTypesTest, Value) {
   TestValuePtr ptr;
-  TestValueImpl impl(MakeRequest(&ptr));
+  TestValueImpl impl(GetProxy(&ptr));
 
-  std::unique_ptr<base::Value> output;
-
-  ASSERT_TRUE(ptr->BounceValue(nullptr, &output));
-  EXPECT_FALSE(output);
-
-  std::unique_ptr<base::Value> input = base::Value::CreateNullValue();
-  ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
-  EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
-  input = base::MakeUnique<base::Value>(123);
-  ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
-  EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
-  input = base::MakeUnique<base::Value>(1.23);
-  ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
-  EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
-  input = base::MakeUnique<base::Value>(false);
-  ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
-  EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
-  input = base::MakeUnique<base::Value>("test string");
-  ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
-  EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
-  input = base::BinaryValue::CreateWithCopiedBuffer("mojo", 4);
-  ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
-  EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
-  auto dict = base::MakeUnique<base::DictionaryValue>();
-  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));
-  dict->Set("null_value", base::Value::CreateNullValue());
-  dict->SetIntegerWithoutPathExpansion("non_nested.int", 10);
+  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));
+    dict.Set("list", std::move(dict_list));
+  }
+  {
+    base::RunLoop run_loop;
+    ptr->BounceDictionaryValue(
+        dict, ExpectResponse(&dict, run_loop.QuitClosure()));
+    run_loop.Run();
   }
 
-  std::unique_ptr<base::DictionaryValue> dict_output;
-  ASSERT_TRUE(ptr->BounceDictionaryValue(dict->CreateDeepCopy(), &dict_output));
-  EXPECT_TRUE(base::Value::Equals(dict.get(), dict_output.get()));
-
-  input = std::move(dict);
-  ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
-  EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
-  auto list = base::MakeUnique<base::ListValue>();
-  list->AppendString("string");
-  list->AppendDouble(42.1);
-  list->AppendBoolean(true);
-  list->Append(base::BinaryValue::CreateWithCopiedBuffer("mojo", 4));
-  list->Append(base::Value::CreateNullValue());
+  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));
+    list.Append(std::move(list_dict));
   }
-  std::unique_ptr<base::ListValue> list_output;
-  ASSERT_TRUE(ptr->BounceListValue(list->CreateDeepCopy(), &list_output));
-  EXPECT_TRUE(base::Value::Equals(list.get(), list_output.get()));
-
-  input = std::move(list);
-  ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
-  ASSERT_TRUE(base::Value::Equals(input.get(), output.get()));
-}
-
-TEST_F(CommonCustomTypesTest, String16) {
-  base::RunLoop run_loop;
-
-  TestString16Ptr ptr;
-  TestString16Impl impl(MakeRequest(&ptr));
-
-  base::string16 str16 = base::ASCIIToUTF16("hello world");
-
-  ptr->BounceString16(str16, ExpectResponse(&str16, run_loop.QuitClosure()));
-
-  run_loop.Run();
-}
-
-TEST_F(CommonCustomTypesTest, EmptyString16) {
-  base::RunLoop run_loop;
-
-  TestString16Ptr ptr;
-  TestString16Impl impl(MakeRequest(&ptr));
-
-  base::string16 str16;
-
-  ptr->BounceString16(str16, ExpectResponse(&str16, run_loop.QuitClosure()));
-
-  run_loop.Run();
-}
-
-TEST_F(CommonCustomTypesTest, File) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
-  TestFilePtr ptr;
-  TestFileImpl impl(MakeRequest(&ptr));
-
-  base::File file(
-      temp_dir.GetPath().AppendASCII("test_file.txt"),
-      base::File::FLAG_CREATE | base::File::FLAG_WRITE | base::File::FLAG_READ);
-  const base::StringPiece test_content =
-      "A test string to be stored in a test file";
-  file.WriteAtCurrentPos(
-      test_content.data(),
-      base::CheckedNumeric<int>(test_content.size()).ValueOrDie());
-
-  base::File file_out;
-  ASSERT_TRUE(ptr->BounceFile(std::move(file), &file_out));
-  std::vector<char> content(test_content.size());
-  ASSERT_TRUE(file_out.IsValid());
-  ASSERT_EQ(static_cast<int>(test_content.size()),
-            file_out.Read(
-                0, content.data(),
-                base::CheckedNumeric<int>(test_content.size()).ValueOrDie()));
-  EXPECT_EQ(test_content,
-            base::StringPiece(content.data(), test_content.size()));
-}
-
-TEST_F(CommonCustomTypesTest, InvalidFile) {
-  TestFilePtr ptr;
-  TestFileImpl impl(MakeRequest(&ptr));
-
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  // Test that |file_out| is set to an invalid file.
-  base::File file_out(
-      temp_dir.GetPath().AppendASCII("test_file.txt"),
-      base::File::FLAG_CREATE | base::File::FLAG_WRITE | base::File::FLAG_READ);
-
-  ASSERT_TRUE(ptr->BounceFile(base::File(), &file_out));
-  EXPECT_FALSE(file_out.IsValid());
-}
-
-TEST_F(CommonCustomTypesTest, TextDirection) {
-  base::i18n::TextDirection kTestDirections[] = {base::i18n::LEFT_TO_RIGHT,
-                                                 base::i18n::RIGHT_TO_LEFT,
-                                                 base::i18n::UNKNOWN_DIRECTION};
-
-  TestTextDirectionPtr ptr;
-  TestTextDirectionImpl impl(MakeRequest(&ptr));
-
-  for (size_t i = 0; i < arraysize(kTestDirections); i++) {
-    base::i18n::TextDirection direction_out;
-    ASSERT_TRUE(ptr->BounceTextDirection(kTestDirections[i], &direction_out));
-    EXPECT_EQ(kTestDirections[i], direction_out);
+  {
+    base::RunLoop run_loop;
+    ptr->BounceListValue(list, ExpectResponse(&list, run_loop.QuitClosure()));
+    run_loop.Run();
   }
 }
 
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
index 27bd893..1133e11 100644
--- a/mojo/common/data_pipe_drainer.cc
+++ b/mojo/common/data_pipe_drainer.cc
@@ -15,10 +15,7 @@
 
 DataPipeDrainer::DataPipeDrainer(Client* client,
                                  mojo::ScopedDataPipeConsumerHandle source)
-    : client_(client),
-      source_(std::move(source)),
-      handle_watcher_(FROM_HERE),
-      weak_factory_(this) {
+    : client_(client), source_(std::move(source)), weak_factory_(this) {
   DCHECK(client_);
   handle_watcher_.Start(
       source_.get(), MOJO_HANDLE_SIGNAL_READABLE,
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
index bed5e85..8540ac6 100644
--- a/mojo/common/data_pipe_utils.cc
+++ b/mojo/common/data_pipe_utils.cc
@@ -4,9 +4,16 @@
 
 #include "mojo/common/data_pipe_utils.h"
 
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
 #include <utility>
 
-#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/message_loop/message_loop.h"
+#include "base/task_runner_util.h"
 
 namespace mojo {
 namespace common {
@@ -51,7 +58,12 @@
   return num_bytes;
 }
 
-}  // namespace
+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,
@@ -95,5 +107,25 @@
   }
 }
 
+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
index a3f7c09..426912c 100644
--- a/mojo/common/data_pipe_utils.h
+++ b/mojo/common/data_pipe_utils.h
@@ -2,19 +2,41 @@
 // 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_UTILS_H_
-#define MOJO_COMMON_DATA_PIPE_UTILS_H_
+#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/data_pipe.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.
@@ -26,7 +48,13 @@
     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_COMMON_DATA_PIPE_UTILS_H_
+#endif  // MOJO_SHELL_DATA_PIPE_UTILS_H_
diff --git a/mojo/common/file.mojom b/mojo/common/file.mojom
deleted file mode 100644
index fe22473..0000000
--- a/mojo/common/file.mojom
+++ /dev/null
@@ -1,10 +0,0 @@
-// 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;
-
-// Corresponds to |base::File| in base/files/file.h
-struct File {
-  handle fd;
-};
diff --git a/mojo/common/file.typemap b/mojo/common/file.typemap
deleted file mode 100644
index 26d4941..0000000
--- a/mojo/common/file.typemap
+++ /dev/null
@@ -1,13 +0,0 @@
-# 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/file.mojom"
-public_headers = [ "//base/files/file.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
-  "//mojo/common:struct_traits",
-]
-
-type_mappings =
-    [ "mojo.common.mojom.File=base::File[move_only,nullable_is_same_type]" ]
diff --git a/mojo/common/string16.mojom b/mojo/common/string16.mojom
index 173c867..eb1ace2 100644
--- a/mojo/common/string16.mojom
+++ b/mojo/common/string16.mojom
@@ -10,3 +10,4 @@
 struct String16 {
   array<uint16> data;
 };
+
diff --git a/mojo/common/string16.typemap b/mojo/common/string16.typemap
deleted file mode 100644
index 223de29..0000000
--- a/mojo/common/string16.typemap
+++ /dev/null
@@ -1,12 +0,0 @@
-# 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/string16.mojom"
-public_headers = [ "//base/strings/string16.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
-  "//mojo/common:struct_traits",
-]
-
-type_mappings = [ "mojo.common.mojom.String16=base::string16" ]
diff --git a/mojo/common/test_common_custom_types.mojom b/mojo/common/test_common_custom_types.mojom
index 0f13680..db91a4f 100644
--- a/mojo/common/test_common_custom_types.mojom
+++ b/mojo/common/test_common_custom_types.mojom
@@ -4,24 +4,13 @@
 
 module mojo.common.test;
 
-import "mojo/common/file.mojom";
-import "mojo/common/file_path.mojom";
-import "mojo/common/string16.mojom";
-import "mojo/common/text_direction.mojom";
-import "mojo/common/time.mojom";
-import "mojo/common/unguessable_token.mojom";
-import "mojo/common/values.mojom";
+import "mojo/common/common_custom_types.mojom";
 
 interface TestFilePath {
   BounceFilePath(mojo.common.mojom.FilePath in)
       => (mojo.common.mojom.FilePath out);
 };
 
-interface TestUnguessableToken {
-  BounceNonce(mojo.common.mojom.UnguessableToken in)
-      => (mojo.common.mojom.UnguessableToken out);
-};
-
 interface TestTime {
   BounceTime(mojo.common.mojom.Time time) => (mojo.common.mojom.Time time);
   BounceTimeDelta(mojo.common.mojom.TimeDelta time_delta)
@@ -31,31 +20,8 @@
 };
 
 interface TestValue {
-  [Sync]
   BounceDictionaryValue(mojo.common.mojom.DictionaryValue in)
       => (mojo.common.mojom.DictionaryValue out);
-  [Sync]
   BounceListValue(mojo.common.mojom.ListValue in)
       => (mojo.common.mojom.ListValue out);
-  [Sync]
-  BounceValue(mojo.common.mojom.Value? in)
-      => (mojo.common.mojom.Value? out);
-};
-
-interface TestString16 {
-  [Sync]
-  BounceString16(mojo.common.mojom.String16 in)
-      => (mojo.common.mojom.String16 out);
-};
-
-interface TestFile {
-  [Sync]
-  BounceFile(mojo.common.mojom.File? in)
-      => (mojo.common.mojom.File? out);
-};
-
-interface TestTextDirection {
-  [Sync]
-  BounceTextDirection(mojo.common.mojom.TextDirection in)
-      => (mojo.common.mojom.TextDirection out);
 };
diff --git a/mojo/common/text_direction.mojom b/mojo/common/text_direction.mojom
deleted file mode 100644
index 7d65124..0000000
--- a/mojo/common/text_direction.mojom
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2017 The Chromium 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;
-
-// Corresponds to |base::i18n::TextDirection| in base/i18n/rtl.h
-enum TextDirection {	
-  UNKNOWN_DIRECTION,	
-  RIGHT_TO_LEFT,	
-  LEFT_TO_RIGHT	
-};	
diff --git a/mojo/common/text_direction.typemap b/mojo/common/text_direction.typemap
deleted file mode 100644
index 1f5be8e..0000000
--- a/mojo/common/text_direction.typemap
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright 2017 The Chromium 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/text_direction.mojom"
-public_headers = [ "//base/i18n/rtl.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
-  "//base:i18n",
-  "//mojo/common:struct_traits",
-]
-type_mappings = [ "mojo.common.mojom.TextDirection=base::i18n::TextDirection" ]
diff --git a/mojo/common/time.typemap b/mojo/common/time.typemap
deleted file mode 100644
index 99e9e3a..0000000
--- a/mojo/common/time.typemap
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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/time.mojom"
-public_headers = [ "//base/time/time.h" ]
-traits_headers = [
-  "//ipc/ipc_message_utils.h",
-  "//mojo/common/common_custom_types_struct_traits.h",
-]
-public_deps = [
-  "//ipc",
-  "//mojo/common:struct_traits",
-]
-
-type_mappings = [
-  "mojo.common.mojom.Time=base::Time[copyable_pass_by_value]",
-  "mojo.common.mojom.TimeDelta=base::TimeDelta[copyable_pass_by_value]",
-  "mojo.common.mojom.TimeTicks=base::TimeTicks[copyable_pass_by_value]",
-]
diff --git a/mojo/common/unguessable_token.mojom b/mojo/common/unguessable_token.mojom
deleted file mode 100644
index 3279717..0000000
--- a/mojo/common/unguessable_token.mojom
+++ /dev/null
@@ -1,11 +0,0 @@
-// 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;
-
-// Corresponds to |base::UnguessableToken| in base/unguessable_token.h
-struct UnguessableToken {
-  uint64 high;
-  uint64 low;
-};
diff --git a/mojo/common/unguessable_token.typemap b/mojo/common/unguessable_token.typemap
deleted file mode 100644
index ec7b194..0000000
--- a/mojo/common/unguessable_token.typemap
+++ /dev/null
@@ -1,12 +0,0 @@
-# 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/unguessable_token.mojom"
-public_headers = [ "//base/unguessable_token.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
-  "//mojo/common:struct_traits",
-]
-
-type_mappings = [ "mojo.common.mojom.UnguessableToken=base::UnguessableToken" ]
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/common/version.mojom b/mojo/common/version.mojom
deleted file mode 100644
index 6ddf6e6..0000000
--- a/mojo/common/version.mojom
+++ /dev/null
@@ -1,10 +0,0 @@
-// 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;
-
-// Corresponds to |base::Version| in base/version.h
-struct Version {
-  array<uint32> components;
-};
diff --git a/mojo/common/version.typemap b/mojo/common/version.typemap
deleted file mode 100644
index fa7fed9..0000000
--- a/mojo/common/version.typemap
+++ /dev/null
@@ -1,12 +0,0 @@
-# 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/version.mojom"
-public_headers = [ "//base/version.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
-  "//mojo/common:struct_traits",
-]
-
-type_mappings = [ "mojo.common.mojom.Version=base::Version" ]
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/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
index 8105bed..f20fd40 100644
--- a/mojo/edk/embedder/BUILD.gn
+++ b/mojo/edk/embedder/BUILD.gn
@@ -7,16 +7,13 @@
 source_set("headers") {
   sources = [
     "configuration.h",
-    "connection_params.h",
     "embedder.h",
     "embedder_internal.h",
     "named_platform_channel_pair.h",
-    "named_platform_handle.h",
-    "named_platform_handle_utils.h",
-    "pending_process_connection.h",
     "platform_channel_pair.h",
     "platform_handle.h",
     "platform_handle_utils.h",
+    "process_delegate.h",
     "scoped_platform_handle.h",
   ]
 
@@ -36,14 +33,11 @@
 
   sources = [
     "configuration.h",
-    "connection_params.cc",
-    "connection_params.h",
     "embedder.cc",
     "embedder.h",
     "embedder_internal.h",
     "entrypoints.cc",
     "entrypoints.h",
-    "pending_process_connection.cc",
     "scoped_ipc_support.cc",
     "scoped_ipc_support.h",
 
@@ -58,6 +52,7 @@
   defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
 
   public_deps = [
+    ":delegates",
     ":headers",
     ":platform",
     "//base",
@@ -82,9 +77,6 @@
   sources = [
     "named_platform_channel_pair.h",
     "named_platform_channel_pair_win.cc",
-    "named_platform_handle.h",
-    "named_platform_handle_utils.h",
-    "named_platform_handle_utils_win.cc",
     "platform_channel_pair.cc",
     "platform_channel_pair.h",
     "platform_channel_pair_posix.cc",
@@ -101,9 +93,6 @@
     "platform_shared_buffer.h",
     "scoped_platform_handle.h",
   ]
-  if (!is_nacl) {
-    sources += [ "named_platform_handle_utils_posix.cc" ]
-  }
 
   defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
 
@@ -124,6 +113,25 @@
   }
 }
 
+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
 
diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md
index 6def874..f976fcb 100644
--- a/mojo/edk/embedder/README.md
+++ b/mojo/edk/embedder/README.md
@@ -1,327 +1,13 @@
-# Mojo Embedder Development Kit (EDK)
+Mojo Embedder API
+=================
 
-The Mojo EDK is a (binary-unstable) API which enables a process to use Mojo both
-internally and for IPC to other Mojo-embedding processes.
+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).
 
-Using any of the API surface in `//mojo/edk/embedder` requires (somewhat
-confusingly) a direct dependency on the GN `//mojo/edk/system` target. Despite
-this fact, you should never reference any of the headers in `mojo/edk/system`
-directly, as everything there is considered to be an internal detail of the EDK.
-
-## Basic Initialization
-
-In order to use Mojo in a given process, it's necessary to call
-`mojo::edk::Init` exactly once:
-
-```
-#include "mojo/edk/embedder/embedder.h"
-
-int main(int argc, char** argv) {
-  mojo::edk::Init();
-
-  // Now you can create message pipes, write messages, etc
-
-  return 0;
-}
-```
-
-As it happens though, Mojo is less useful without some kind of IPC support as
-well, and that's a second initialization step.
-
-## IPC Initialization
-
-You also need to provide the system with a background TaskRunner on which it can
-watch for inbound I/O from any of the various other processes you will later
-connect to it.
-
-Here we'll just create a new background thread for IPC and let Mojo use that.
-Note that in Chromium, we use the existing "IO thread" in the browser process
-and content child processes.
-
-```
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-
-int main(int argc, char** argv) {
-  mojo::edk::Init();
-
-  base::Thread ipc_thread("ipc!");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
-
-  // As long as this object is alive, all EDK API surface relevant to IPC
-  // connections is usable and message pipes which span a process boundary will
-  // continue to function.
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  return 0;
-}
-```
-
-This process is now fully prepared to use Mojo IPC!
-
-Note that all existing process types in Chromium already perform this setup
-very early during startup.
-
-## Connecting Two Processes
-
-Now suppose you're running a process which has initialized Mojo IPC, and you
-want to launch another process which you know will also initialize Mojo IPC.
-You want to be able to connect Mojo interfaces between these two processes.
-Rejoice, because this section was written just for you.
-
-NOTE: For legacy reasons, some API terminology may refer to concepts of "parent"
-and "child" as a relationship between processes being connected by Mojo. This
-relationship is today completely orthogonal to any notion of process hierarchy
-in the OS, and so use of these APIs is not constrained by an adherence to any
-such hierarchy.
-
-Mojo requires you to bring your own OS pipe to the party, and it will do the
-rest. It also provides a convenient mechanism for creating such pipes, known as
-a `PlatformChannelPair`.
-
-You provide one end of this pipe to the EDK in the local process via
-`PendingProcessConnection` - which can also be used to create cross-process
-message pipes (see the next section) - and you're responsible for getting the
-other end into the remote process.
-
-```
-#include "base/process/process_handle.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-
-// You write this. It launches a new process, passing the pipe handle
-// encapsulated by |channel| by any means possible (e.g. on Windows or POSIX
-// you may inhert the file descriptor/HANDLE at launch and pass a commandline
-// argument to indicate its numeric value). Returns the handle of the new
-// process.
-base::ProcessHandle LaunchCoolChildProcess(
-    mojo::edk::ScopedPlatformHandle channel);
-
-int main(int argc, char** argv) {
-  mojo::edk::Init();
-
-  base::Thread ipc_thread("ipc!");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
-
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  // This is essentially always an OS pipe (domain socket pair, Windows named
-  // pipe, etc.)
-  mojo::edk::PlatformChannelPair channel;
-
-  // This is a scoper which encapsulates the intent to connect to another
-  // process. It exists because process connection is inherently asynchronous,
-  // things may go wrong, and the lifetime of any associated resources is bound
-  // by the lifetime of this object regardless of success or failure.
-  mojo::edk::PendingProcessConnection child;
-
-  base::ProcessHandle child_handle =
-      LaunchCoolChildProcess(channel.PassClientHandle());
-
-  // At this point it's safe for |child| to go out of scope and nothing will
-  // break.
-  child.Connect(child_handle, channel.PassServerHandle());
-
-  return 0;
-}
-```
-
-The launched process code uses `SetParentPipeHandle` to get connected, and might
-look something like:
-
-```
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-
-// You write this. It acquires the ScopedPlatformHandle that was passed by
-// whomever launched this process (i.e. LaunchCoolChildProcess above).
-mojo::edk::ScopedPlatformHandle GetChannelHandle();
-
-int main(int argc, char** argv) {
-  mojo::edk::Init();
-
-  base::Thread ipc_thread("ipc!");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
-
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  mojo::edk::SetParentPipeHandle(GetChannelHandle());
-
-  return 0;
-}
-```
-
-Now you have IPC initialized between two processes. For some practical examples
-of how this is done, you can dig into the various multiprocess tests in the
-`mojo_system_unittests` test suite.
-
-## Bootstrapping Cross-Process Message Pipes
-
-Having internal Mojo IPC support initialized is pretty useless if you don't have
-any message pipes spanning the process boundary. Fortunately, this is made
-trivial by the EDK: `PendingProcessConnection` has a
-`CreateMessagePipe` method which synthesizes a new solitary message pipe
-endpoint for your immediate use, while also generating a magic token string that
-can be exchanged for the other end of the pipe via
-`mojo::edk::CreateChildMessagePipe`.
-
-The token exchange can be done by the same process (which is sometimes useful),
-or by the process that is eventually connected via `Connect()` on that
-`PendingProcessConnection`. This means that you can effectively pass message
-pipes on the commandline by passing a token string.
-
-We can modify our existing sample code as follows:
-
-```
-#include "base/command_line.h"
-#include "base/process/process_handle.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "local/foo.mojom.h"  // You provide this
-
-base::ProcessHandle LaunchCoolChildProcess(
-    const base::CommandLine& command_line,
-    mojo::edk::ScopedPlatformHandle channel);
-
-int main(int argc, char** argv) {
-  mojo::edk::Init();
-
-  base::Thread ipc_thread("ipc!");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
-
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  mojo::edk::PlatformChannelPair channel;
-
-  mojo::edk::PendingProcessConnection child;
-
-  base::CommandLine command_line;  // Assume this is appropriately initialized
-
-  // Create a new message pipe with one end being retrievable in the new
-  // process. Note that it doesn't matter whether we call CreateMessagePipe()
-  // before or after Connect(), and we can create as many different pipes as
-  // we like.
-  std::string pipe_token;
-  mojo::ScopedMessagePipeHandle my_pipe = child.CreateMessagePipe(&pipe_token);
-  command_line.AppendSwitchASCII("primordial-pipe", pipe_token);
-
-  base::ProcessHandle child_handle =
-      LaunchCoolChildProcess(command_line, channel.PassClientHandle());
-
-  child.Connect(child_handle, channel.PassServerHandle());
-
-  // We can start using our end of the pipe immediately. Here we assume the
-  // other end will eventually be bound to a local::mojom::Foo implementation,
-  // so we can start making calls on that interface.
-  //
-  // Note that this could even be done before the child process is launched and
-  // it would still work as expected.
-  local::mojom::FooPtr foo;
-  foo.Bind(local::mojom::FooPtrInfo(std::move(my_pipe), 0));
-  foo->DoSomeStuff(42);
-
-  return 0;
-}
-```
-
-and for the launched process:
-
-
-```
-#include "base/command_line.h"
-#include "base/run_loop/run_loop.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "local/foo.mojom.h"  // You provide this
-
-mojo::edk::ScopedPlatformHandle GetChannelHandle();
-
-class FooImpl : local::mojom::Foo {
- public:
-  explicit FooImpl(local::mojom::FooRequest request)
-      : binding_(this, std::move(request)) {}
-  ~FooImpl() override {}
-
-  void DoSomeStuff(int32_t n) override {
-    // ...
-  }
-
- private:
-  mojo::Binding<local::mojom::Foo> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(FooImpl);
-};
-
-int main(int argc, char** argv) {
-  base::CommandLine::Init(argc, argv);
-
-  mojo::edk::Init();
-
-  base::Thread ipc_thread("ipc!");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
-
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  mojo::edk::SetParentPipeHandle(GetChannelHandle());
-
-  mojo::ScopedMessagePipeHandle my_pipe = mojo::edk::CreateChildMessagePipe(
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          "primordial-pipe"));
-
-  local::mojom::FooRequest foo_request;
-  foo_request.Bind(std::move(my_pipe));
-  FooImpl impl(std::move(foo_request));
-
-  // Run forever!
-  base::RunLoop().Run();
-
-  return 0;
-}
-```
-
-Note that the above samples assume an interface definition in
-`//local/test.mojom` which would look something like:
-
-```
-module local.mojom;
-
-interface Foo {
-  DoSomeStuff(int32 n);
-};
-```
-
-Once you've bootstrapped your process connection with a real mojom interface,
-you can avoid any further mucking around with EDK APIs or raw message pipe
-handles, as everything beyond this point - including the passing of other
-interface pipes - can be handled eloquently using public bindings APIs.
-
-See [additional Mojo documentation](
-    https://www.chromium.org/developers/design-documents/mojo) for more
-information.
+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/connection_params.cc b/mojo/edk/embedder/connection_params.cc
deleted file mode 100644
index 9b7ec54..0000000
--- a/mojo/edk/embedder/connection_params.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 The Chromium Authors. All 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/connection_params.h"
-
-#include <utility>
-
-namespace mojo {
-namespace edk {
-
-ConnectionParams::ConnectionParams(ScopedPlatformHandle channel)
-    : channel_(std::move(channel)) {}
-
-ConnectionParams::ConnectionParams(ConnectionParams&& param)
-    : channel_(std::move(param.channel_)) {}
-
-ConnectionParams& ConnectionParams::operator=(ConnectionParams&& param) {
-  channel_ = std::move(param.channel_);
-  return *this;
-}
-
-ScopedPlatformHandle ConnectionParams::TakeChannelHandle() {
-  return std::move(channel_);
-}
-
-}  // namespace edk
-}  // namespace mojo
diff --git a/mojo/edk/embedder/connection_params.h b/mojo/edk/embedder/connection_params.h
deleted file mode 100644
index 25ffdde..0000000
--- a/mojo/edk/embedder/connection_params.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 The Chromium Authors. All 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_CONNECTION_PARAMS_H_
-#define MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_
-
-#include "base/macros.h"
-#include "build/build_config.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/edk/system/system_impl_export.h"
-
-namespace mojo {
-namespace edk {
-
-class MOJO_SYSTEM_IMPL_EXPORT ConnectionParams {
- public:
-  explicit ConnectionParams(ScopedPlatformHandle channel);
-
-  ConnectionParams(ConnectionParams&& param);
-  ConnectionParams& operator=(ConnectionParams&& param);
-
-  ScopedPlatformHandle TakeChannelHandle();
-
- private:
-  ScopedPlatformHandle channel_;
-
-  DISALLOW_COPY_AND_ASSIGN(ConnectionParams);
-};
-
-}  // namespace edk
-}  // namespace mojo
-
-#endif  // MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
index 0fdda5c..c90acbd 100644
--- a/mojo/edk/embedder/embedder.cc
+++ b/mojo/edk/embedder/embedder.cc
@@ -5,7 +5,6 @@
 #include "mojo/edk/embedder/embedder.h"
 
 #include <stdint.h>
-#include <utility>
 
 #include "base/bind.h"
 #include "base/location.h"
@@ -18,8 +17,8 @@
 #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"
-#include "mojo/edk/system/node_controller.h"
 
 #if !defined(OS_NACL)
 #include "crypto/random.h"
@@ -34,6 +33,7 @@
 namespace internal {
 
 Core* g_core;
+ProcessDelegate* g_process_delegate;
 
 Core* GetCore() { return g_core; }
 
@@ -42,9 +42,30 @@
 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(ConnectionParams(std::move(pipe)));
+  internal::g_core->InitChild(std::move(pipe));
 }
 
 void SetParentPipeHandleFromCommandLine() {
@@ -55,21 +76,6 @@
   SetParentPipeHandle(std::move(platform_channel));
 }
 
-ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe) {
-  return ConnectToPeerProcess(std::move(pipe), GenerateRandomToken());
-}
-
-ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe,
-                                             const std::string& peer_token) {
-  DCHECK(pipe.is_valid());
-  DCHECK(!peer_token.empty());
-  return internal::g_core->ConnectToPeerProcess(std::move(pipe), peer_token);
-}
-
-void ClosePeerConnection(const std::string& peer_token) {
-  return internal::g_core->ClosePeerConnection(peer_token);
-}
-
 void Init() {
   MojoSystemThunks thunks = MakeSystemThunks();
   size_t expected_size = MojoEmbedderSetSystemThunks(&thunks);
@@ -78,10 +84,6 @@
   internal::g_core = new Core();
 }
 
-void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback) {
-  internal::g_core->SetDefaultProcessErrorCallback(callback);
-}
-
 MojoResult CreatePlatformHandleWrapper(
     ScopedPlatformHandle platform_handle,
     MojoHandle* platform_handle_wrapper_handle) {
@@ -113,18 +115,19 @@
       mojo_handle, shared_memory_handle, num_bytes, read_only);
 }
 
-void InitIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+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;
 }
 
-scoped_refptr<base::TaskRunner> GetIOTaskRunner() {
-  return internal::g_core->GetNodeController()->io_task_runner();
-}
-
-void ShutdownIPCSupport(const base::Closure& callback) {
+void ShutdownIPCSupport() {
+  CHECK(internal::g_process_delegate);
   CHECK(internal::g_core);
-  internal::g_core->RequestShutdown(callback);
+  internal::g_core->RequestShutdown(
+      base::Bind(&ProcessDelegate::OnShutdownComplete,
+                 base::Unretained(internal::g_process_delegate)));
 }
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
@@ -134,7 +137,14 @@
 }
 #endif
 
+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);
 }
 
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h
index 97258e5..9e83bbe 100644
--- a/mojo/edk/embedder/embedder.h
+++ b/mojo/edk/embedder/embedder.h
@@ -16,7 +16,6 @@
 #include "base/memory/shared_memory_handle.h"
 #include "base/process/process_handle.h"
 #include "base/task_runner.h"
-#include "mojo/edk/embedder/pending_process_connection.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"
@@ -28,6 +27,10 @@
 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...()|
@@ -37,43 +40,40 @@
 // Allows changing the default max message size. Must be called before Init.
 MOJO_SYSTEM_IMPL_EXPORT void SetMaxMessageSize(size_t bytes);
 
-// Should be called as early as possible in a child process with a handle to the
-// other end of a pipe provided in the parent to
-// PendingProcessConnection::Connect.
+// 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();
 
-// Called to connect to a peer process. This should be called only if there
-// is no common ancestor for the processes involved within this mojo system.
-// Both processes must call this function, each passing one end of a platform
-// channel. This returns one end of a message pipe to each process.
-MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
-ConnectToPeerProcess(ScopedPlatformHandle pipe);
-
-// Called to connect to a peer process. This should be called only if there
-// is no common ancestor for the processes involved within this mojo system.
-// Both processes must call this function, each passing one end of a platform
-// channel. This returns one end of a message pipe to each process. |peer_token|
-// may be passed to ClosePeerConnection() to close the connection.
-MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
-ConnectToPeerProcess(ScopedPlatformHandle pipe, const std::string& peer_token);
-
-// Closes a connection to a peer process created by ConnectToPeerProcess()
-// where the same |peer_token| was used.
-MOJO_SYSTEM_IMPL_EXPORT void ClosePeerConnection(const std::string& peer_token);
-
 // Must be called first, or just after setting configuration parameters, to
 // initialize the (global, singleton) system.
 MOJO_SYSTEM_IMPL_EXPORT void Init();
 
-// Sets a default callback to invoke when an internal error is reported but
-// cannot be associated with a specific child process.
-MOJO_SYSTEM_IMPL_EXPORT void SetDefaultProcessErrorCallback(
-    const ProcessErrorCallback& callback);
-
 // Basic functions -------------------------------------------------------------
 
 // The functions in this section are available once |Init()| has been called.
@@ -126,20 +126,22 @@
 //
 // This subsystem may be shut down using |ShutdownIPCSupport()|. None of the IPC
 // functions may be called after this is called.
-//
-// |io_thread_task_runner| should live at least until |ShutdownIPCSupport()|'s
-// callback has been run.
-MOJO_SYSTEM_IMPL_EXPORT void InitIPCSupport(
-    scoped_refptr<base::TaskRunner> io_thread_task_runner);
 
-// Retrieves the TaskRunner used for IPC I/O, as set by InitIPCSupport.
-MOJO_SYSTEM_IMPL_EXPORT scoped_refptr<base::TaskRunner> GetIOTaskRunner();
+// 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, |callback| is invoked on an
-// arbitrary thread.
-MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport(const base::Closure& callback);
+// 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,
@@ -148,14 +150,25 @@
     base::PortProvider* port_provider);
 #endif
 
-// Creates a message pipe from a token in a child process. This token must have
-// been acquired by a corresponding call to
-// PendingProcessConnection::CreateMessagePipe.
+// 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 various APIs that expect
-// a globally unique token string.
+// 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
diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc
index d5a87e5..127a74f 100644
--- a/mojo/edk/embedder/embedder_unittest.cc
+++ b/mojo/edk/embedder/embedder_unittest.cc
@@ -10,24 +10,16 @@
 
 #include <utility>
 
-#include "base/base_paths.h"
 #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/ptr_util.h"
 #include "base/memory/shared_memory.h"
 #include "base/message_loop/message_loop.h"
-#include "base/path_service.h"
 #include "base/process/process_handle.h"
-#include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/test_timeouts.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
-#include "mojo/edk/embedder/named_platform_handle_utils.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/embedder/test_embedder.h"
 #include "mojo/edk/system/test_utils.h"
@@ -191,12 +183,13 @@
 }
 
 TEST_F(EmbedderTest, PipeSetup) {
-  // Ensures that a pending process connection's message pipe can be claimed by
-  // the host process itself.
-  PendingProcessConnection process;
-  std::string pipe_token;
-  ScopedMessagePipeHandle parent_mp = process.CreateMessagePipe(&pipe_token);
-  ScopedMessagePipeHandle child_mp = CreateChildMessagePipe(pipe_token);
+  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);
@@ -207,11 +200,13 @@
 TEST_F(EmbedderTest, PipeSetup_LaunchDeath) {
   PlatformChannelPair pair;
 
-  PendingProcessConnection process;
-  std::string pipe_token;
-  ScopedMessagePipeHandle parent_mp = process.CreateMessagePipe(&pipe_token);
-  process.Connect(base::GetCurrentProcessHandle(),
-                  ConnectionParams(pair.PassServerHandle()));
+  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.
@@ -226,14 +221,13 @@
 TEST_F(EmbedderTest, PipeSetup_LaunchFailure) {
   PlatformChannelPair pair;
 
-  auto process = base::MakeUnique<PendingProcessConnection>();
-  std::string pipe_token;
-  ScopedMessagePipeHandle parent_mp = process->CreateMessagePipe(&pipe_token);
+  std::string child_token = GenerateRandomToken();
+  std::string pipe_token = GenerateRandomToken();
 
-  // Ensure that if a PendingProcessConnection goes away before Connect() is
-  // called, any message pipes associated with it detect peer closure.
-  process.reset();
+  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,
@@ -563,100 +557,6 @@
 
 #endif  // !defined(OS_IOS)
 
-NamedPlatformHandle GenerateChannelName() {
-#if defined(OS_POSIX)
-  base::FilePath temp_dir;
-  CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir));
-  return NamedPlatformHandle(
-      temp_dir.AppendASCII(GenerateRandomToken()).value());
-#else
-  return NamedPlatformHandle(GenerateRandomToken());
-#endif
-}
-
-void CreateClientHandleOnIoThread(const NamedPlatformHandle& named_handle,
-                                  ScopedPlatformHandle* output) {
-  *output = CreateClientHandle(named_handle);
-}
-
-TEST_F(EmbedderTest, ClosePendingPeerConnection) {
-  NamedPlatformHandle named_handle = GenerateChannelName();
-  std::string peer_token = GenerateRandomToken();
-  ScopedMessagePipeHandle server_pipe =
-      ConnectToPeerProcess(CreateServerHandle(named_handle), peer_token);
-  ClosePeerConnection(peer_token);
-  EXPECT_EQ(MOJO_RESULT_OK,
-            Wait(server_pipe.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                 MOJO_DEADLINE_INDEFINITE, nullptr));
-  base::MessageLoop message_loop;
-  base::RunLoop run_loop;
-  ScopedPlatformHandle client_handle;
-  // Closing the channel involves posting a task to the IO thread to do the
-  // work. By the time the local message pipe has been observerd as closed,
-  // that task will have been posted. Therefore, a task to create the client
-  // connection should be handled after the channel is closed.
-  GetIOTaskRunner()->PostTaskAndReply(
-      FROM_HERE,
-      base::Bind(&CreateClientHandleOnIoThread, named_handle, &client_handle),
-      run_loop.QuitClosure());
-  run_loop.Run();
-  EXPECT_FALSE(client_handle.is_valid());
-}
-
-#if !defined(OS_IOS)
-
-TEST_F(EmbedderTest, ClosePipeToConnectedPeer) {
-  set_launch_type(LaunchType::PEER);
-  auto& controller = StartClient("ClosePipeToConnectedPeerClient");
-  MojoHandle server_mp = controller.pipe();
-  // 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));
-
-  controller.ClosePeerConnection();
-
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
-
-  EXPECT_EQ(0, controller.WaitForShutdown());
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ClosePipeToConnectedPeerClient, 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!");
-
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWait(client_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                      MOJO_DEADLINE_INDEFINITE, nullptr));
-}
-
-TEST_F(EmbedderTest, ClosePipeToConnectingPeer) {
-  set_launch_type(LaunchType::PEER);
-  auto& controller = StartClient("ClosePipeToConnectingPeerClient");
-  controller.ClosePeerConnection();
-
-  MojoHandle server_mp = controller.pipe();
-
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
-
-  EXPECT_EQ(0, controller.WaitForShutdown());
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ClosePipeToConnectingPeerClient, EmbedderTest,
-                                  client_mp) {
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
-}
-
-#endif  // !defined(OS_IOS)
-
 }  // namespace
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/embedder/named_platform_channel_pair.h b/mojo/edk/embedder/named_platform_channel_pair.h
deleted file mode 100644
index 5a83ae3..0000000
--- a/mojo/edk/embedder/named_platform_channel_pair.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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_NAMED_PLATFORM_CHANNEL_PAIR_H_
-#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "build/build_config.h"
-#include "mojo/edk/embedder/named_platform_handle.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 {
-
-// This is used to create a named bidirectional pipe to connect new child
-// processes. The resulting server handle should be passed to the EDK, and the
-// child end passed as a pipe name on the command line to the child process. The
-// child process can then retrieve the pipe name from the command line and
-// resolve it into a client handle.
-class MOJO_SYSTEM_IMPL_EXPORT NamedPlatformChannelPair {
- public:
-  struct Options {
-#if defined(OS_WIN)
-    // If non-empty, a security descriptor to use when creating the pipe. If
-    // empty, a default security descriptor will be used. See
-    // kDefaultSecurityDescriptor in named_platform_handle_utils_win.cc.
-    base::string16 security_descriptor;
-#endif
-  };
-
-  NamedPlatformChannelPair(const Options& options = {});
-  ~NamedPlatformChannelPair();
-
-  // Note: It is NOT acceptable to use this handle as a generic pipe channel. It
-  // MUST be passed to PendingProcessConnection::Connect() only.
-  ScopedPlatformHandle PassServerHandle();
-
-  // To be called in the child process, after the parent process called
-  // |PrepareToPassClientHandleToChildProcess()| and launched the child (using
-  // the provided data), to create a client handle connected to the server
-  // handle (in the parent process).
-  static ScopedPlatformHandle PassClientHandleFromParentProcess(
-      const base::CommandLine& command_line);
-
-  // Prepares to pass the client channel to a new child process, to be launched
-  // using |LaunchProcess()| (from base/launch.h). Modifies |*command_line| and
-  // |*handle_passing_info| as needed.
-  // Note: For Windows, this method only works on Vista and later.
-  void PrepareToPassClientHandleToChildProcess(
-      base::CommandLine* command_line) const;
-
-  const NamedPlatformHandle& handle() const { return pipe_handle_; }
-
- private:
-  NamedPlatformHandle pipe_handle_;
-  ScopedPlatformHandle server_handle_;
-
-  DISALLOW_COPY_AND_ASSIGN(NamedPlatformChannelPair);
-};
-
-}  // namespace edk
-}  // namespace mojo
-
-#endif  // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_
diff --git a/mojo/edk/embedder/named_platform_handle.h b/mojo/edk/embedder/named_platform_handle.h
deleted file mode 100644
index 15ca656..0000000
--- a/mojo/edk/embedder/named_platform_handle.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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_NAMED_PLATFORM_HANDLE_H_
-#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
-
-#include <string>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "mojo/edk/system/system_impl_export.h"
-
-namespace mojo {
-namespace edk {
-
-#if defined(OS_POSIX)
-struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
-  NamedPlatformHandle() {}
-  explicit NamedPlatformHandle(const base::StringPiece& name)
-      : name(name.as_string()) {}
-
-  bool is_valid() const { return !name.empty(); }
-
-  std::string name;
-};
-#elif defined(OS_WIN)
-struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
-  NamedPlatformHandle() {}
-  explicit NamedPlatformHandle(const base::StringPiece& name)
-      : name(base::UTF8ToUTF16(name)) {}
-
-  explicit NamedPlatformHandle(const base::StringPiece16& name)
-      : name(name.as_string()) {}
-
-  bool is_valid() const { return !name.empty(); }
-
-  base::string16 pipe_name() const { return L"\\\\.\\pipe\\mojo." + name; }
-
-  base::string16 name;
-};
-#else
-#error "Platform not yet supported."
-#endif
-
-}  // namespace edk
-}  // namespace mojo
-
-#endif  // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
diff --git a/mojo/edk/embedder/pending_process_connection.cc b/mojo/edk/embedder/pending_process_connection.cc
deleted file mode 100644
index d6be76e..0000000
--- a/mojo/edk/embedder/pending_process_connection.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 The Chromium Authors. All 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/pending_process_connection.h"
-
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/embedder_internal.h"
-#include "mojo/edk/system/core.h"
-
-namespace mojo {
-namespace edk {
-
-PendingProcessConnection::PendingProcessConnection()
-    : process_token_(GenerateRandomToken()) {
-  DCHECK(internal::g_core);
-}
-
-PendingProcessConnection::~PendingProcessConnection() {
-  if (has_message_pipes_ && !connected_) {
-    DCHECK(internal::g_core);
-    internal::g_core->ChildLaunchFailed(process_token_);
-  }
-}
-
-ScopedMessagePipeHandle PendingProcessConnection::CreateMessagePipe(
-    std::string* token) {
-  has_message_pipes_ = true;
-  DCHECK(internal::g_core);
-  *token = GenerateRandomToken();
-  return internal::g_core->CreateParentMessagePipe(*token, process_token_);
-}
-
-void PendingProcessConnection::Connect(
-    base::ProcessHandle process,
-    ConnectionParams connection_params,
-    const ProcessErrorCallback& error_callback) {
-  // It's now safe to avoid cleanup in the destructor, as the lifetime of any
-  // associated resources is effectively bound to the |channel| passed to
-  // AddChild() below.
-  DCHECK(!connected_);
-  connected_ = true;
-
-  DCHECK(internal::g_core);
-  internal::g_core->AddChild(process, std::move(connection_params),
-                             process_token_, error_callback);
-}
-
-}  // namespace edk
-}  // namespace mojo
diff --git a/mojo/edk/embedder/pending_process_connection.h b/mojo/edk/embedder/pending_process_connection.h
deleted file mode 100644
index ca18227..0000000
--- a/mojo/edk/embedder/pending_process_connection.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2017 The Chromium Authors. All 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_PENDING_PROCESS_CONNECTION_H_
-#define MOJO_EDK_EMBEDDER_PENDING_PROCESS_CONNECTION_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/process/process_handle.h"
-#include "mojo/edk/embedder/connection_params.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 mojo {
-namespace edk {
-
-using ProcessErrorCallback = base::Callback<void(const std::string& error)>;
-
-// Represents a potential connection to an external process. Use this object
-// to make other processes reachable from this one via Mojo IPC. Typical usage
-// might look something like:
-//
-//     PendingProcessConnection connection;
-//
-//     std::string pipe_token;
-//     ScopedMessagePipeHandle pipe = connection.CreateMessagePipe(&pipe_token);
-//
-//     // New pipes to the process are fully functional and can be used right
-//     // away, even if the process doesn't exist yet.
-//     GoDoSomethingInteresting(std::move(pipe));
-//
-//     ScopedPlatformChannelPair channel;
-//
-//     // Give the pipe token to the child process via command-line.
-//     child_command_line.AppendSwitchASCII("yer-pipe", pipe_token);
-//
-//     // Magic child process launcher which gives one end of the pipe to the
-//     // new process.
-//     LaunchProcess(child_command_line, channel.PassClientHandle());
-//
-//     // Some time later...
-//     connection.Connect(new_process, channel.PassServerHandle());
-//
-// If at any point during the above process, |connection| is destroyed before
-// Connect() can be called, |pipe| will imminently behave as if its peer has
-// been closed.
-//
-// Otherwise, if the remote process in this example eventually calls:
-//
-//     mojo::edk::SetParentPipeHandle(std::move(client_channel_handle));
-//
-//     std::string token = command_line.GetSwitchValueASCII("yer-pipe");
-//     ScopedMessagePipeHandle pipe = mojo::edk::CreateChildMessagePipe(token);
-//
-// it will be connected to this process, and its |pipe| will be connected to
-// this process's |pipe|.
-//
-// If the remote process exits or otherwise closes its client channel handle
-// before calling CreateChildMessagePipe for a given message pipe token,
-// this process's end of the corresponding message pipe will imminently behave
-// as if its peer has been closed.
-//
-class MOJO_SYSTEM_IMPL_EXPORT PendingProcessConnection {
- public:
-  PendingProcessConnection();
-  ~PendingProcessConnection();
-
-  // Creates a message pipe associated with a new globally unique string value
-  // which will be placed in |*token|.
-  //
-  // The other end of the new pipe is obtainable in the remote process (or in
-  // this process, to facilitate "single-process mode" in some applications) by
-  // passing the new |*token| value to mojo::edk::CreateChildMessagePipe. It's
-  // the caller's responsibility to communicate the value of |*token| to the
-  // remote process by any means available, e.g. a command-line argument on
-  // process launch, or some other out-of-band communication channel for an
-  // existing process.
-  //
-  // NOTES: This may be called any number of times to create multiple message
-  // pipes to the same remote process. This call ALWAYS succeeds, returning
-  // a valid message pipe handle and populating |*token| with a new unique
-  // string value.
-  ScopedMessagePipeHandle CreateMessagePipe(std::string* token);
-
-  // Connects to the process. This must be called at most once, with the process
-  // handle in |process|.
-  //
-  // |connection_param| contains the platform handle of an OS pipe which can be
-  // used to communicate with the connected process. The other end of that pipe
-  // must ultimately be passed to mojo::edk::SetParentPipeHandle in the remote
-  // process, and getting that end of the pipe into the other process is the
-  // embedder's responsibility.
-  //
-  // If this method is not called by the time the PendingProcessConnection is
-  // destroyed, it's assumed that the process is unavailable (e.g. process
-  // launch failed or the process has otherwise been terminated early), and
-  // any associated resources, such as remote endpoints of messages pipes
-  // created by CreateMessagePipe above) will be cleaned up at that time.
-  void Connect(
-      base::ProcessHandle process,
-      ConnectionParams connection_params,
-      const ProcessErrorCallback& error_callback = ProcessErrorCallback());
-
- private:
-  // A GUID representing a potential new process to be connected to this one.
-  const std::string process_token_;
-
-  // Indicates whether this object has been used to create new message pipes.
-  bool has_message_pipes_ = false;
-
-  // Indicates whether Connect() has been called yet.
-  bool connected_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(PendingProcessConnection);
-};
-
-}  // namespace edk
-}  // namespace mojo
-
-#endif  // MOJO_EDK_EMBEDDER_PENDING_PROCESS_CONNECTION_H_
diff --git a/mojo/edk/embedder/platform_channel_pair.h b/mojo/edk/embedder/platform_channel_pair.h
index 9c93f76..f80de89 100644
--- a/mojo/edk/embedder/platform_channel_pair.h
+++ b/mojo/edk/embedder/platform_channel_pair.h
@@ -69,7 +69,6 @@
   // |PrepareToPassClientHandleToChildProcess()| and launched the child (using
   // the provided data), to create a client handle connected to the server
   // handle (in the parent process).
-  // TODO(jcivelli): remove the command_line param. http://crbug.com/670106
   static ScopedPlatformHandle PassClientHandleFromParentProcess(
       const base::CommandLine& command_line);
 
diff --git a/mojo/edk/embedder/platform_channel_pair_posix.cc b/mojo/edk/embedder/platform_channel_pair_posix.cc
index 4760e95..55f3ae2 100644
--- a/mojo/edk/embedder/platform_channel_pair_posix.cc
+++ b/mojo/edk/embedder/platform_channel_pair_posix.cc
@@ -35,15 +35,6 @@
 
 namespace {
 
-#if defined(OS_ANDROID)
-enum {
-  // Leave room for any other descriptors defined in content for example.
-  // TODO(jcivelli): consider changing base::GlobalDescriptors to generate a
-  //   key when setting the file descriptor (http://crbug.com/676442).
-  kAndroidClientHandleDescriptor =
-      base::GlobalDescriptors::kBaseDescriptor + 10000,
-};
-#else
 bool IsTargetDescriptorUsed(
     const base::FileHandleMappingVector& file_handle_mapping,
     int target_fd) {
@@ -53,7 +44,6 @@
   }
   return false;
 }
-#endif
 
 }  // namespace
 
@@ -102,21 +92,13 @@
 PlatformChannelPair::PassClientHandleFromParentProcessFromString(
     const std::string& value) {
   int client_fd = -1;
-#if defined(OS_ANDROID)
-  base::GlobalDescriptors::Key key = -1;
-  if (value.empty() || !base::StringToUint(value, &key)) {
-    LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
-    return ScopedPlatformHandle();
-  }
-  client_fd = base::GlobalDescriptors::GetInstance()->Get(key);
-#else
   if (value.empty() ||
       !base::StringToInt(value, &client_fd) ||
       client_fd < base::GlobalDescriptors::kBaseDescriptor) {
     LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
     return ScopedPlatformHandle();
   }
-#endif
+
   return ScopedPlatformHandle(PlatformHandle(client_fd));
 }
 
@@ -142,12 +124,6 @@
 std::string
 PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString(
       HandlePassingInformation* handle_passing_info) const {
-#if defined(OS_ANDROID)
-  int fd = client_handle_.get().handle;
-  handle_passing_info->push_back(
-      std::pair<int, int>(fd, kAndroidClientHandleDescriptor));
-  return base::UintToString(kAndroidClientHandleDescriptor);
-#else
   DCHECK(handle_passing_info);
   // This is an arbitrary sanity check. (Note that this guarantees that the loop
   // below will terminate sanely.)
@@ -165,7 +141,6 @@
   handle_passing_info->push_back(
       std::pair<int, int>(client_handle_.get().handle, target_fd));
   return base::IntToString(target_fd);
-#endif
 }
 
 }  // namespace edk
diff --git a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
index a3fd275..e05cd79 100644
--- a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
+++ b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
@@ -153,7 +153,7 @@
     for (size_t j = 1; j <= i; j++) {
       base::FilePath unused;
       base::ScopedFILE fp(
-          base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+          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(
@@ -209,7 +209,7 @@
   {
     base::FilePath unused;
     base::ScopedFILE fp(
-        base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+        base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
     ASSERT_TRUE(fp);
     ASSERT_EQ(file_contents.size(),
               fwrite(file_contents.data(), 1, file_contents.size(), fp.get()));
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.cc b/mojo/edk/embedder/platform_channel_utils_posix.cc
index 689b6ee..3171844 100644
--- a/mojo/edk/embedder/platform_channel_utils_posix.cc
+++ b/mojo/edk/embedder/platform_channel_utils_posix.cc
@@ -8,13 +8,9 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
-#include <utility>
-
-#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
 #include "build/build_config.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
 
 #if !defined(OS_NACL)
 #include <sys/uio.h>
@@ -26,55 +22,6 @@
 
 namespace mojo {
 namespace edk {
-namespace {
-
-#if !defined(OS_NACL)
-bool IsRecoverableError() {
-  return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE ||
-         errno == ENOMEM || errno == ENOBUFS;
-}
-
-bool GetPeerEuid(PlatformHandle handle, uid_t* peer_euid) {
-  DCHECK(peer_euid);
-#if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD)
-  uid_t socket_euid;
-  gid_t socket_gid;
-  if (getpeereid(handle.handle, &socket_euid, &socket_gid) < 0) {
-    PLOG(ERROR) << "getpeereid " << handle.handle;
-    return false;
-  }
-  *peer_euid = socket_euid;
-  return true;
-#else
-  struct ucred cred;
-  socklen_t cred_len = sizeof(cred);
-  if (getsockopt(handle.handle, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) <
-      0) {
-    PLOG(ERROR) << "getsockopt " << handle.handle;
-    return false;
-  }
-  if (static_cast<unsigned>(cred_len) < sizeof(cred)) {
-    NOTREACHED() << "Truncated ucred from SO_PEERCRED?";
-    return false;
-  }
-  *peer_euid = cred.uid;
-  return true;
-#endif
-}
-
-bool IsPeerAuthorized(PlatformHandle peer_handle) {
-  uid_t peer_euid;
-  if (!GetPeerEuid(peer_handle, &peer_euid))
-    return false;
-  if (peer_euid != geteuid()) {
-    DLOG(ERROR) << "Client euid is not authorised";
-    return false;
-  }
-  return true;
-}
-#endif  // !defined(OS_NACL)
-
-}  // namespace
 
 // On Linux, |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to
 // |send()|/|sendmsg()|. (There is no way of suppressing |SIGPIPE| on
@@ -246,37 +193,5 @@
   return result;
 }
 
-bool ServerAcceptConnection(PlatformHandle server_handle,
-                            ScopedPlatformHandle* connection_handle,
-                            bool check_peer_user) {
-  DCHECK(server_handle.is_valid());
-  connection_handle->reset();
-#if defined(OS_NACL)
-  NOTREACHED();
-  return false;
-#else
-  ScopedPlatformHandle accept_handle(
-      PlatformHandle(HANDLE_EINTR(accept(server_handle.handle, NULL, 0))));
-  if (!accept_handle.is_valid())
-    return IsRecoverableError();
-
-  // Verify that the IPC channel peer is running as the same user.
-  if (check_peer_user && !IsPeerAuthorized(accept_handle.get())) {
-    return true;
-  }
-
-  if (!base::SetNonBlocking(accept_handle.get().handle)) {
-    PLOG(ERROR) << "base::SetNonBlocking() failed "
-                << accept_handle.get().handle;
-    // It's safe to keep listening on |server_handle| even if the attempt to set
-    // O_NONBLOCK failed on the client fd.
-    return true;
-  }
-
-  *connection_handle = std::move(accept_handle);
-  return true;
-#endif  // defined(OS_NACL)
-}
-
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.h b/mojo/edk/embedder/platform_channel_utils_posix.h
index 23cfa92..8b24bd0 100644
--- a/mojo/edk/embedder/platform_channel_utils_posix.h
+++ b/mojo/edk/embedder/platform_channel_utils_posix.h
@@ -18,7 +18,6 @@
 
 namespace mojo {
 namespace edk {
-class ScopedPlatformHandle;
 
 // The maximum number of handles that can be sent "at once" using
 // |PlatformChannelSendmsgWithHandles()|. This must be less than the Linux
@@ -71,16 +70,6 @@
                        std::deque<PlatformHandle>* platform_handles,
                        bool block = false);
 
-// Returns false if |server_handle| encounters an unrecoverable error.
-// Returns true if it's valid to keep listening on |server_handle|. In this
-// case, it's possible that a connection wasn't successfully established; then,
-// |connection_handle| will be invalid. If |check_peer_user| is True, the
-// connection will be rejected if the peer is running as a different user.
-MOJO_SYSTEM_IMPL_EXPORT bool ServerAcceptConnection(
-    PlatformHandle server_handle,
-    ScopedPlatformHandle* connection_handle,
-    bool check_peer_user = true);
-
 }  // namespace edk
 }  // namespace mojo
 
diff --git a/mojo/edk/embedder/platform_handle.h b/mojo/edk/embedder/platform_handle.h
index 4866e75..4f76009 100644
--- a/mojo/edk/embedder/platform_handle.h
+++ b/mojo/edk/embedder/platform_handle.h
@@ -53,9 +53,6 @@
 
   int handle = -1;
 
-  // A POSIX handle may be a listen handle that can accept a connection.
-  bool needs_connection = false;
-
 #if defined(OS_MACOSX) && !defined(OS_IOS)
   mach_port_t port = MACH_PORT_NULL;
 #endif
diff --git a/mojo/edk/embedder/platform_handle_utils_posix.cc b/mojo/edk/embedder/platform_handle_utils_posix.cc
index 5604f96..fc2a0db 100644
--- a/mojo/edk/embedder/platform_handle_utils_posix.cc
+++ b/mojo/edk/embedder/platform_handle_utils_posix.cc
@@ -15,9 +15,7 @@
   DCHECK(platform_handle.is_valid());
   // Note that |dup()| returns -1 on error (which is exactly the value we use
   // for invalid |PlatformHandle| FDs).
-  PlatformHandle duped(dup(platform_handle.handle));
-  duped.needs_connection = platform_handle.needs_connection;
-  return ScopedPlatformHandle(duped);
+  return ScopedPlatformHandle(PlatformHandle(dup(platform_handle.handle)));
 }
 
 }  // namespace edk
diff --git a/mojo/edk/embedder/platform_shared_buffer.cc b/mojo/edk/embedder/platform_shared_buffer.cc
index 58af44d..53d8edc 100644
--- a/mojo/edk/embedder/platform_shared_buffer.cc
+++ b/mojo/edk/embedder/platform_shared_buffer.cc
@@ -253,28 +253,21 @@
 bool PlatformSharedBuffer::InitFromPlatformHandlePair(
     ScopedPlatformHandle rw_platform_handle,
     ScopedPlatformHandle ro_platform_handle) {
-#if defined(OS_MACOSX)
+#if defined(OS_WIN) || defined(OS_MACOSX)
   NOTREACHED();
   return false;
-#else  // defined(OS_MACOSX)
+#else
+  DCHECK(!shared_memory_);
 
-#if defined(OS_WIN)
-  base::SharedMemoryHandle handle(rw_platform_handle.release().handle,
-                                  base::GetCurrentProcId());
-  base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle,
-                                     base::GetCurrentProcId());
-#else  // defined(OS_WIN)
   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);
-#endif  // defined(OS_WIN)
-
-  DCHECK(!shared_memory_);
-  shared_memory_.reset(new base::SharedMemory(handle, false));
   ro_shared_memory_.reset(new base::SharedMemory(ro_handle, true));
-  return true;
 
-#endif  // defined(OS_MACOSX)
+  return true;
+#endif
 }
 
 void PlatformSharedBuffer::InitFromSharedMemoryHandle(
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/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
index 41850d7..b21cdbc 100644
--- a/mojo/edk/js/tests/BUILD.gn
+++ b/mojo/edk/js/tests/BUILD.gn
@@ -2,34 +2,21 @@
 # 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")
+import("../../../../mojo/public/tools/bindings/mojom.gni")
 
-# 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") {
+source_set("js_to_cpp_tests") {
   testonly = true
-  deps = [
-    ":mojo_js_integration_tests",
-    ":mojo_js_unittests",
-  ]
-}
 
-test("mojo_js_integration_tests") {
   deps = [
     ":js_to_cpp_bindings",
     "//gin:gin_test",
     "//mojo/common",
     "//mojo/edk/js",
-    "//mojo/edk/test:run_all_unittests",
+    "//mojo/edk/test:test_support",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
-    "//mojo/public/js:bindings",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces",
+    "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
   ]
 
   sources = [
@@ -37,7 +24,9 @@
   ]
 
   data = [
+    "connection_tests.js",
     "js_to_cpp_tests.js",
+    "sample_service_tests.js",
   ]
 
   configs += [ "//v8:external_startup_data" ]
@@ -48,22 +37,3 @@
     "js_to_cpp.mojom",
   ]
 }
-
-test("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_unittests.cc",
-  ]
-}
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_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc
index e5e6bd1..6d3426d 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.cc
+++ b/mojo/edk/js/tests/js_to_cpp_tests.cc
@@ -15,15 +15,14 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.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/bindings/lib/validation_errors.h"
 #include "mojo/public/cpp/system/core.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -105,11 +104,12 @@
   args->double_val = kExpectedDoubleVal;
   args->double_inf = kExpectedDoubleInf;
   args->double_nan = kExpectedDoubleNan;
-  args->name.emplace("coming");
-  args->string_array.emplace(3);
-  (*args->string_array)[0] = "one";
-  (*args->string_array)[1] = "two";
-  (*args->string_array)[2] = "three";
+  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;
 }
 
@@ -128,10 +128,10 @@
   EXPECT_EQ(kExpectedDoubleVal, arg->double_val);
   EXPECT_EQ(kExpectedDoubleInf, arg->double_inf);
   EXPECT_NAN(arg->double_nan);
-  EXPECT_EQ(std::string("coming"), *arg->name);
-  EXPECT_EQ(std::string("one"), (*arg->string_array)[0]);
-  EXPECT_EQ(std::string("two"), (*arg->string_array)[1]);
-  EXPECT_EQ(std::string("three"), (*arg->string_array)[2]);
+  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());
 }
@@ -146,23 +146,18 @@
 // 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 std::string& arg) {
+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 CheckCorruptedString(const base::Optional<std::string>& arg) {
-  if (!arg)
+void CheckCorruptedStringArray(const Array<String>& string_array) {
+  if (string_array.is_null())
     return;
-  CheckCorruptedString(*arg);
-}
-
-void CheckCorruptedStringArray(
-    const base::Optional<std::vector<std::string>>& string_array) {
-  if (!string_array)
-    return;
-  for (size_t i = 0; i < string_array->size(); ++i)
-    CheckCorruptedString((*string_array)[i]);
+  for (size_t i = 0; i < string_array.size(); ++i)
+    CheckCorruptedString(string_array[i]);
 }
 
 void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) {
@@ -302,7 +297,7 @@
     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);
+    EXPECT_EQ(std::string("going"), special_arg->name.To<std::string>());
     CheckSampleEchoArgsList(list->next);
   }
 
@@ -388,11 +383,11 @@
     cpp_side->set_run_loop(&run_loop_);
 
     js_to_cpp::JsSidePtr js_side;
-    auto js_side_proxy = MakeRequest(&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(MakeRequest(&cpp_side_ptr));
+    cpp_side->Bind(GetProxy(&cpp_side_ptr));
 
     js_side->SetCppSide(std::move(cpp_side_ptr));
 
@@ -404,7 +399,7 @@
     gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
                                    gin::IsolateHolder::kStableV8Extras,
                                    gin::ArrayBufferAllocator::SharedInstance());
-    gin::IsolateHolder instance(base::ThreadTaskRunnerHandle::Get());
+    gin::IsolateHolder instance;
     MojoRunnerDelegate delegate;
     gin::ShellRunner runner(&delegate, instance.isolate());
     delegate.Start(&runner, js_side_proxy.PassMessagePipe().release().value(),
@@ -434,18 +429,12 @@
 }
 
 TEST_F(JsToCppTest, BitFlip) {
-  // These tests generate a lot of expected validation errors. Suppress logging.
-  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
-
   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) {
-  // These tests generate a lot of expected validation errors. Suppress logging.
-  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
-
   BackPointerCppSideConnection cpp_side_connection;
   RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
   EXPECT_TRUE(cpp_side_connection.DidSucceed());
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js
index 6b69fca..ddecc4b 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.js
+++ b/mojo/edk/js/tests/js_to_cpp_tests.js
@@ -6,9 +6,10 @@
   '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, connector, core) {
+], function (console, jsToCpp, bindings, connection, connector, core) {
   var retainedJsSide;
   var retainedJsSideStub;
   var sampleData;
@@ -21,9 +22,11 @@
   };
 
   function JsSideConnection() {
-    this.binding = new bindings.Binding(jsToCpp.JsSide, this);
   }
 
+  JsSideConnection.prototype =
+      Object.create(jsToCpp.JsSide.stubClass.prototype);
+
   JsSideConnection.prototype.setCppSide = function(cppSide) {
     this.cppSide_ = cppSide;
     this.cppSide_.startTest();
@@ -217,7 +220,9 @@
     for (i = 0; i < sampleMessage.length; ++i) {
       sampleMessage[i] = 255 - i;
     }
+    retainedJsSideStub =
+        connection.bindHandleToStub(jsSideRequestHandle, jsToCpp.JsSide);
     retainedJsSide = new JsSideConnection;
-    retainedJsSide.binding.bind(jsSideRequestHandle);
+    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/system/BUILD.gn b/mojo/edk/system/BUILD.gn
index b0acf23..84f1f30 100644
--- a/mojo/edk/system/BUILD.gn
+++ b/mojo/edk/system/BUILD.gn
@@ -20,10 +20,9 @@
     "awakable_list.cc",
     "awakable_list.h",
     "broker.h",
-    "broker_host.cc",
     "broker_host.h",
+    "broker_host_posix.cc",
     "broker_posix.cc",
-    "broker_win.cc",
     "channel.cc",
     "channel.h",
     "channel_posix.cc",
@@ -76,6 +75,7 @@
 
   public_deps = [
     "//mojo/edk/embedder",
+    "//mojo/edk/embedder:delegates",
     "//mojo/edk/embedder:platform",
     "//mojo/edk/system/ports",
     "//mojo/public/c/system",
@@ -104,7 +104,7 @@
 
   if (is_nacl && !is_nacl_nonsfi) {
     sources -= [
-      "broker_host.cc",
+      "broker_host_posix.cc",
       "broker_posix.cc",
       "channel_posix.cc",
     ]
@@ -154,7 +154,6 @@
 test("mojo_system_unittests") {
   sources = [
     "awakable_list_unittest.cc",
-    "channel_unittest.cc",
     "core_test_base.cc",
     "core_test_base.h",
     "core_unittest.cc",
@@ -187,7 +186,6 @@
     "//mojo/edk/system/ports:tests",
     "//mojo/edk/test:run_all_unittests",
     "//mojo/edk/test:test_support",
-    "//testing/gmock",
     "//testing/gtest",
   ]
 
diff --git a/mojo/edk/system/broker_host.cc b/mojo/edk/system/broker_host.cc
deleted file mode 100644
index 6096034..0000000
--- a/mojo/edk/system/broker_host.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-// 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 <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/named_platform_channel_pair.h"
-#include "mojo/edk/embedder/named_platform_handle.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 {
-
-BrokerHost::BrokerHost(base::ProcessHandle client_process,
-                       ScopedPlatformHandle platform_handle)
-#if defined(OS_WIN)
-    : client_process_(client_process)
-#endif
-{
-  CHECK(platform_handle.is_valid());
-
-  base::MessageLoop::current()->AddDestructionObserver(this);
-
-  channel_ = Channel::Create(this, ConnectionParams(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();
-}
-
-bool BrokerHost::PrepareHandlesForClient(PlatformHandleVector* handles) {
-#if defined(OS_WIN)
-  if (!Channel::Message::RewriteHandles(
-      base::GetCurrentProcessHandle(), client_process_, handles)) {
-    // NOTE: We only log an error here. We do not signal a logical error or
-    // prevent any message from being sent. The client should handle unexpected
-    // invalid handles appropriately.
-    DLOG(ERROR) << "Failed to rewrite one or more handles to broker client.";
-    return false;
-  }
-#endif
-  return true;
-}
-
-bool BrokerHost::SendChannel(ScopedPlatformHandle handle) {
-  CHECK(handle.is_valid());
-  CHECK(channel_);
-
-#if defined(OS_WIN)
-  InitData* data;
-  Channel::MessagePtr message =
-      CreateBrokerMessage(BrokerMessageType::INIT, 1, 0, &data);
-  data->pipe_name_length = 0;
-#else
-  Channel::MessagePtr message =
-      CreateBrokerMessage(BrokerMessageType::INIT, 1, nullptr);
-#endif
-  ScopedPlatformHandleVectorPtr handles;
-  handles.reset(new PlatformHandleVector(1));
-  handles->at(0) = handle.release();
-
-  // This may legitimately fail on Windows if the client process is in another
-  // session, e.g., is an elevated process.
-  if (!PrepareHandlesForClient(handles.get()))
-    return false;
-
-   message->SetHandles(std::move(handles));
-  channel_->Write(std::move(message));
-  return true;
-}
-
-#if defined(OS_WIN)
-
-void BrokerHost::SendNamedChannel(const base::StringPiece16& pipe_name) {
-  InitData* data;
-  base::char16* name_data;
-  Channel::MessagePtr message = CreateBrokerMessage(
-      BrokerMessageType::INIT, 0, sizeof(*name_data) * pipe_name.length(),
-      &data, reinterpret_cast<void**>(&name_data));
-  data->pipe_name_length = static_cast<uint32_t>(pipe_name.length());
-  std::copy(pipe_name.begin(), pipe_name.end(), name_data);
-  channel_->Write(std::move(message));
-}
-
-#endif  // defined(OS_WIN)
-
-void BrokerHost::OnBufferRequest(uint32_t num_bytes) {
-  scoped_refptr<PlatformSharedBuffer> read_only_buffer;
-  scoped_refptr<PlatformSharedBuffer> buffer =
-      PlatformSharedBuffer::Create(num_bytes);
-  if (buffer)
-    read_only_buffer = buffer->CreateReadOnlyDuplicate();
-  if (!read_only_buffer)
-    buffer = nullptr;
-
-  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();
-    PrepareHandlesForClient(handles.get());
-    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);
-      }
-      break;
-
-    default:
-      LOG(ERROR) << "Unexpected broker message type: " << header->type;
-      break;
-  }
-}
-
-void BrokerHost::OnChannelError() { delete this; }
-
-void BrokerHost::WillDestroyCurrentMessageLoop() { delete this; }
-
-}  // namespace edk
-}  // namespace mojo
diff --git a/mojo/edk/system/broker_host.h b/mojo/edk/system/broker_host.h
index a7995d2..b8f68c4 100644
--- a/mojo/edk/system/broker_host.h
+++ b/mojo/edk/system/broker_host.h
@@ -5,13 +5,8 @@
 #ifndef MOJO_EDK_SYSTEM_BROKER_HOST_H_
 #define MOJO_EDK_SYSTEM_BROKER_HOST_H_
 
-#include <stdint.h>
-
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
-#include "base/process/process_handle.h"
-#include "base/strings/string_piece.h"
-#include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/edk/system/channel.h"
 
@@ -23,21 +18,14 @@
 class BrokerHost : public Channel::Delegate,
                    public base::MessageLoop::DestructionObserver {
  public:
-  BrokerHost(base::ProcessHandle client_process, ScopedPlatformHandle handle);
+  explicit BrokerHost(ScopedPlatformHandle platform_handle);
 
   // Send |handle| to the child, to be used to establish a NodeChannel to us.
-  bool SendChannel(ScopedPlatformHandle handle);
-
-#if defined(OS_WIN)
-  // Sends a named channel to the child. Like above, but for named pipes.
-  void SendNamedChannel(const base::StringPiece16& pipe_name);
-#endif
+  void SendChannel(ScopedPlatformHandle handle);
 
  private:
   ~BrokerHost() override;
 
-  bool PrepareHandlesForClient(PlatformHandleVector* handles);
-
   // Channel::Delegate:
   void OnChannelMessage(const void* payload,
                         size_t payload_size,
@@ -47,11 +35,7 @@
   // base::MessageLoop::DestructionObserver:
   void WillDestroyCurrentMessageLoop() override;
 
-  void OnBufferRequest(uint32_t num_bytes);
-
-#if defined(OS_WIN)
-  base::ProcessHandle client_process_;
-#endif
+  void OnBufferRequest(size_t num_bytes);
 
   scoped_refptr<Channel> channel_;
 
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
index 0f0dd9d..a67be15 100644
--- a/mojo/edk/system/broker_messages.h
+++ b/mojo/edk/system/broker_messages.h
@@ -23,41 +23,26 @@
   uint32_t padding;
 };
 
-static_assert(IsAlignedForChannelMessage(sizeof(BrokerMessageHeader)),
+static_assert(sizeof(BrokerMessageHeader) % kChannelMessageAlignment == 0,
               "Invalid header size.");
 
 struct BufferRequestData {
   uint32_t size;
 };
 
-#if defined(OS_WIN)
-struct InitData {
-  // NOTE: InitData in the payload is followed by string16 data with exactly
-  // |pipe_name_length| wide characters (i.e., |pipe_name_length|*2 bytes.)
-  // This applies to Windows only.
-  uint32_t pipe_name_length;
-};
-#endif
-
 #pragma pack(pop)
 
 template <typename T>
-inline Channel::MessagePtr CreateBrokerMessage(
-    BrokerMessageType type,
-    size_t num_handles,
-    size_t extra_data_size,
-    T** out_message_data,
-    void** out_extra_data = nullptr) {
-  const size_t message_size = sizeof(BrokerMessageHeader) +
-      sizeof(**out_message_data) + extra_data_size;
+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);
-  if (out_extra_data)
-    *out_extra_data = *out_message_data + 1;
   return message;
 }
 
diff --git a/mojo/edk/system/broker_posix.cc b/mojo/edk/system/broker_posix.cc
index 8742f70..62b37b3 100644
--- a/mojo/edk/system/broker_posix.cc
+++ b/mojo/edk/system/broker_posix.cc
@@ -38,7 +38,7 @@
     PLOG(ERROR) << "Recvmsg error";
     error = true;
   } else if (static_cast<size_t>(read_result) != message->data_num_bytes()) {
-    LOG(ERROR) << "Invalid node channel message";
+    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";
@@ -94,7 +94,7 @@
 
   BufferRequestData* buffer_request;
   Channel::MessagePtr out_message = CreateBrokerMessage(
-      BrokerMessageType::BUFFER_REQUEST, 0, 0, &buffer_request);
+      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());
diff --git a/mojo/edk/system/channel.cc b/mojo/edk/system/channel.cc
index 8a44d36..56509b5 100644
--- a/mojo/edk/system/channel.cc
+++ b/mojo/edk/system/channel.cc
@@ -4,7 +4,6 @@
 
 #include "mojo/edk/system/channel.h"
 
-#include <stddef.h>
 #include <string.h>
 
 #include <algorithm>
@@ -27,47 +26,27 @@
 
 namespace {
 
-static_assert(
-    IsAlignedForChannelMessage(sizeof(Channel::Message::LegacyHeader)),
-    "Invalid LegacyHeader size.");
+static_assert(sizeof(Channel::Message::Header) % kChannelMessageAlignment == 0,
+    "Invalid Header size.");
 
-static_assert(IsAlignedForChannelMessage(sizeof(Channel::Message::Header)),
-              "Invalid Header size.");
-
-static_assert(sizeof(Channel::Message::LegacyHeader) == 8,
-              "LegacyHeader must be 8 bytes on ChromeOS and Android");
-
-static_assert(offsetof(Channel::Message::LegacyHeader, num_bytes) ==
-                  offsetof(Channel::Message::Header, num_bytes),
-              "num_bytes should be at the same offset in both Header structs.");
-static_assert(offsetof(Channel::Message::LegacyHeader, message_type) ==
-                  offsetof(Channel::Message::Header, message_type),
-              "message_type should be at the same offset in both Header "
-              "structs.");
+#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 = 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)
-#if defined(MOJO_EDK_LEGACY_PROTOCOL)
-    : Message(payload_size, max_handles, MessageType::NORMAL_LEGACY) {
-}
-#else
-    : Message(payload_size, max_handles, MessageType::NORMAL) {
-}
-#endif
-
 Channel::Message::Message(size_t payload_size,
                           size_t max_handles,
-                          MessageType message_type)
+                          Header::MessageType message_type)
     : max_handles_(max_handles) {
   DCHECK_LE(max_handles_, kMaxAttachedHandles);
 
-  const bool is_legacy_message = (message_type == MessageType::NORMAL_LEGACY);
   size_t extra_header_size = 0;
 #if defined(OS_WIN)
   // On Windows we serialize HANDLEs into the extra header space.
@@ -83,16 +62,16 @@
   }
 #endif
   // Pad extra header data to be aliged to |kChannelMessageAlignment| bytes.
-  if (!IsAlignedForChannelMessage(extra_header_size)) {
+  if (extra_header_size % kChannelMessageAlignment) {
     extra_header_size += kChannelMessageAlignment -
                          (extra_header_size % kChannelMessageAlignment);
   }
-  DCHECK(IsAlignedForChannelMessage(extra_header_size));
-  const size_t header_size =
-      is_legacy_message ? sizeof(LegacyHeader) : sizeof(Header);
-  DCHECK(extra_header_size == 0 || !is_legacy_message);
+  DCHECK_EQ(0u, extra_header_size % kChannelMessageAlignment);
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+  DCHECK_EQ(0u, extra_header_size);
+#endif
 
-  size_ = header_size + extra_header_size + payload_size;
+  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
@@ -100,21 +79,21 @@
   // 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, header_size + extra_header_size);
+  memset(data_, 0, sizeof(Header) + extra_header_size);
+  header_ = reinterpret_cast<Header*>(data_);
 
   DCHECK_LE(size_, std::numeric_limits<uint32_t>::max());
-  legacy_header()->num_bytes = static_cast<uint32_t>(size_);
+  header_->num_bytes = static_cast<uint32_t>(size_);
 
-  DCHECK_LE(header_size + extra_header_size,
+  DCHECK_LE(sizeof(Header) + extra_header_size,
             std::numeric_limits<uint16_t>::max());
-  legacy_header()->message_type = message_type;
-
-  if (is_legacy_message) {
-    legacy_header()->num_handles = static_cast<uint16_t>(max_handles);
-  } else {
-    header()->num_header_bytes =
-        static_cast<uint16_t>(header_size + extra_header_size);
-  }
+  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)
@@ -142,93 +121,79 @@
 // static
 Channel::MessagePtr Channel::Message::Deserialize(const void* data,
                                                   size_t data_num_bytes) {
-  if (data_num_bytes < sizeof(LegacyHeader))
+  if (data_num_bytes < sizeof(Header))
     return nullptr;
 
-  const LegacyHeader* legacy_header =
-      reinterpret_cast<const LegacyHeader*>(data);
-  if (legacy_header->num_bytes != data_num_bytes) {
-    DLOG(ERROR) << "Decoding invalid message: " << legacy_header->num_bytes
+  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;
   }
 
-  const Header* header = nullptr;
-  if (legacy_header->message_type == MessageType::NORMAL)
-    header = reinterpret_cast<const Header*>(data);
-
-  uint32_t extra_header_size = 0;
-  size_t payload_size = 0;
-  const char* payload = nullptr;
-  if (!header) {
-    payload_size = data_num_bytes - sizeof(LegacyHeader);
-    payload = static_cast<const char*>(data) + sizeof(LegacyHeader);
-  } 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;
-    }
-    extra_header_size = header->num_header_bytes - sizeof(Header);
-    payload_size = data_num_bytes - header->num_header_bytes;
-    payload = static_cast<const char*>(data) + header->num_header_bytes;
+#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 > 0 &&
-      extra_header_size < sizeof(MachPortsExtraHeader)) {
+  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 == 0
-          ? 0
-          : (extra_header_size - sizeof(MachPortsExtraHeader)) /
-                sizeof(MachPortsEntry);
+  uint32_t max_handles = (extra_header_size - sizeof(MachPortsExtraHeader)) /
+      sizeof(MachPortsEntry);
 #else
   const uint32_t max_handles = 0;
 #endif  // defined(OS_WIN)
 
-  const uint16_t num_handles =
-      header ? header->num_handles : legacy_header->num_handles;
-  if (num_handles > max_handles || max_handles > kMaxAttachedHandles) {
-    DLOG(ERROR) << "Decoding invalid message: " << num_handles << " > "
-                << max_handles;
+  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, legacy_header->message_type));
+  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 (header) {
-    DCHECK_EQ(message->extra_header_size(), extra_header_size);
-    DCHECK_EQ(message->header()->num_header_bytes, header->num_header_bytes);
+#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());
-    }
-    message->header()->num_handles = header->num_handles;
-  } else {
-    message->legacy_header()->num_handles = legacy_header->num_handles;
+  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(num_handles));
-  for (size_t i = 0; i < num_handles; i++) {
-    (*handles)[i].handle =
-        base::win::Uint32ToHandle(message->handles_[i].handle);
+  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
@@ -236,46 +201,12 @@
   return message;
 }
 
-const void* Channel::Message::extra_header() const {
-  DCHECK(!is_legacy_message());
-  return data_ + sizeof(Header);
-}
-
-void* Channel::Message::mutable_extra_header() {
-  DCHECK(!is_legacy_message());
-  return data_ + sizeof(Header);
-}
-
-size_t Channel::Message::extra_header_size() const {
-  return header()->num_header_bytes - sizeof(Header);
-}
-
-void* Channel::Message::mutable_payload() {
-  if (is_legacy_message())
-    return static_cast<void*>(legacy_header() + 1);
-  return data_ + header()->num_header_bytes;
-}
-
-const void* Channel::Message::payload() const {
-  if (is_legacy_message())
-    return static_cast<const void*>(legacy_header() + 1);
-  return data_ + header()->num_header_bytes;
-}
-
 size_t Channel::Message::payload_size() const {
-  if (is_legacy_message())
-    return legacy_header()->num_bytes - sizeof(LegacyHeader);
-  return size_ - header()->num_header_bytes;
-}
-
-size_t Channel::Message::num_handles() const {
-  return is_legacy_message() ? legacy_header()->num_handles
-                             : header()->num_handles;
-}
-
-bool Channel::Message::has_handles() const {
-  return (is_legacy_message() ? legacy_header()->num_handles
-                              : header()->num_handles) > 0;
+#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)
@@ -293,44 +224,31 @@
 }
 #endif
 
-bool Channel::Message::is_legacy_message() const {
-  return legacy_header()->message_type == MessageType::NORMAL_LEGACY;
-}
-
-Channel::Message::LegacyHeader* Channel::Message::legacy_header() const {
-  return reinterpret_cast<LegacyHeader*>(data_);
-}
-
-Channel::Message::Header* Channel::Message::header() const {
-  DCHECK(!is_legacy_message());
-  return reinterpret_cast<Header*>(data_);
-}
-
 void Channel::Message::SetHandles(ScopedPlatformHandleVectorPtr new_handles) {
-  if (is_legacy_message()) {
-    // Old semantics for ChromeOS and Android
-    if (legacy_header()->num_handles == 0) {
-      CHECK(!new_handles || new_handles->size() == 0);
-      return;
-    }
-    CHECK(new_handles && new_handles->size() == legacy_header()->num_handles);
-    std::swap(handle_vector_, 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());
+  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;
@@ -362,12 +280,12 @@
     }
     mach_ports_header_->num_ports = 0;
   }
-#endif
-  if (is_legacy_message())
-    legacy_header()->num_handles = 0;
-  else
-    header()->num_handles = 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() {
@@ -573,71 +491,58 @@
 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::LegacyHeader)) {
+  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 (!IsAlignedForChannelMessage(
-            reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()))) {
+    if (reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()) %
+        kChannelMessageAlignment != 0)
       read_buffer_->Realign();
-    }
 
-    // We have at least enough data available for a LegacyHeader.
-    const Message::LegacyHeader* legacy_header =
-        reinterpret_cast<const Message::LegacyHeader*>(
-            read_buffer_->occupied_bytes());
-
-    if (legacy_header->num_bytes < sizeof(Message::LegacyHeader) ||
-        legacy_header->num_bytes > kMaxChannelMessageSize) {
-      LOG(ERROR) << "Invalid message size: " << legacy_header->num_bytes;
+    // 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() < legacy_header->num_bytes) {
+    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 =
-          legacy_header->num_bytes - read_buffer_->num_occupied_bytes();
+          header->num_bytes - read_buffer_->num_occupied_bytes();
       return true;
     }
 
-    const Message::Header* header = nullptr;
-    if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY) {
-      header = reinterpret_cast<const Message::Header*>(legacy_header);
-    }
-
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
     size_t extra_header_size = 0;
     const void* extra_header = nullptr;
-    size_t payload_size = 0;
-    void* payload = nullptr;
-    if (header) {
-      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;
-      }
-      extra_header_size = header->num_header_bytes - sizeof(Message::Header);
-      extra_header = extra_header_size ? header + 1 : nullptr;
-      payload_size = header->num_bytes - header->num_header_bytes;
-      payload = payload_size
-                    ? reinterpret_cast<Message::Header*>(
-                          const_cast<char*>(read_buffer_->occupied_bytes()) +
-                          header->num_header_bytes)
-                    : nullptr;
-    } else {
-      payload_size = legacy_header->num_bytes - sizeof(Message::LegacyHeader);
-      payload = payload_size
-                    ? const_cast<Message::LegacyHeader*>(&legacy_header[1])
-                    : 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)
 
-    const uint16_t num_handles =
-        header ? header->num_handles : legacy_header->num_handles;
     ScopedPlatformHandleVectorPtr handles;
-    if (num_handles > 0) {
-      if (!GetReadPlatformHandles(num_handles, extra_header, extra_header_size,
-                                  &handles)) {
+    if (header->num_handles > 0) {
+      if (!GetReadPlatformHandles(header->num_handles, extra_header,
+                                 extra_header_size, &handles)) {
         return false;
       }
 
@@ -648,9 +553,8 @@
     }
 
     // We've got a complete message! Dispatch it and try another.
-    if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY &&
-        legacy_header->message_type != Message::MessageType::NORMAL) {
-      if (!OnControlMessage(legacy_header->message_type, payload, payload_size,
+    if (header->message_type != Message::Header::MessageType::NORMAL) {
+      if (!OnControlMessage(header->message_type, payload, payload_size,
                             std::move(handles))) {
         return false;
       }
@@ -660,7 +564,7 @@
       did_dispatch_message = true;
     }
 
-    read_buffer_->Discard(legacy_header->num_bytes);
+    read_buffer_->Discard(header->num_bytes);
   }
 
   *next_read_size_hint = did_dispatch_message ? 0 : kReadBufferSize;
@@ -672,7 +576,7 @@
     delegate_->OnChannelError();
 }
 
-bool Channel::OnControlMessage(Message::MessageType message_type,
+bool Channel::OnControlMessage(Message::Header::MessageType message_type,
                                const void* payload,
                                size_t payload_size,
                                ScopedPlatformHandleVectorPtr handles) {
diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h
index 33a510c..aa6d70c 100644
--- a/mojo/edk/system/channel.h
+++ b/mojo/edk/system/channel.h
@@ -10,7 +10,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/process/process_handle.h"
 #include "base/task_runner.h"
-#include "mojo/edk/embedder/connection_params.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 
@@ -19,70 +18,53 @@
 
 const size_t kChannelMessageAlignment = 8;
 
-constexpr bool IsAlignedForChannelMessage(size_t n) {
-  return n % kChannelMessageAlignment == 0;
-}
-
 // 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 MOJO_SYSTEM_IMPL_EXPORT Channel
-    : public base::RefCountedThreadSafe<Channel> {
+class Channel : public base::RefCountedThreadSafe<Channel> {
  public:
   struct Message;
 
   using MessagePtr = std::unique_ptr<Message>;
 
   // A message to be written to a channel.
-  struct MOJO_SYSTEM_IMPL_EXPORT Message {
-    enum class MessageType : uint16_t {
-      // An old format normal message, that uses the LegacyHeader.
-      // Only used on Android and ChromeOS.
-      // TODO(jcivelli): remove legacy support when Arc++ has updated to Mojo
-      //                 with normal versioned messages. crbug.com/695645
-      NORMAL_LEGACY = 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
-      // A normal message that uses Header and can contain extra header values.
-      NORMAL,
-    };
-
+  struct Message {
 #pragma pack(push, 1)
-    // Old message wire format for ChromeOS and Android, used by NORMAL_LEGACY
-    // messages.
-    struct LegacyHeader {
+    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;
-    };
-
-    // Header used by NORMAL messages.
-    // To preserve backward compatibility with LegacyHeader, the num_bytes and
-    // message_type field must be at the same offset as in LegacyHeader.
-    struct Header {
-      // Message size in bytes, including the header.
-      uint32_t num_bytes;
-
+#else
       // Total size of header, including extra header data (i.e. HANDLEs on
       // windows).
       uint16_t num_header_bytes;
 
-      MessageType message_type;
-
       // 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)
@@ -103,7 +85,7 @@
       // Actual number of Mach ports encoded in the extra header.
       uint16_t num_ports;
 
-      // Array of encoded Mach ports. If |num_ports| > 0, |entries[0]| through
+      // Array of encoded Mach ports. If |num_ports| > 0, |entires[0]| through
       // to |entries[num_ports-1]| inclusive are valid.
       MachPortsEntry entries[0];
     };
@@ -122,8 +104,10 @@
 
     // 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);
-    Message(size_t payload_size, size_t max_handles, MessageType message_type);
+    Message(size_t payload_size,
+            size_t max_handles,
+            Header::MessageType message_type = Header::MessageType::NORMAL);
+
     ~Message();
 
     // Constructs a Message from serialized message data.
@@ -132,24 +116,30 @@
     const void* data() const { return data_; }
     size_t data_num_bytes() const { return size_; }
 
-    const void* extra_header() const;
-    void* mutable_extra_header();
-    size_t extra_header_size() const;
-
-    void* mutable_payload();
-    const void* payload() const;
+#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);
+    }
 
-    size_t num_handles() const;
-    bool has_handles() const;
+    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
 
-    bool is_legacy_message() const;
-    LegacyHeader* legacy_header() const;
-    Header* header() const;
-
     // Note: SetHandles() and TakeHandles() invalidate any previous value of
     // handles().
     void SetHandles(ScopedPlatformHandleVectorPtr new_handles);
@@ -171,12 +161,11 @@
                                PlatformHandleVector* handles);
 #endif
 
-    void SetVersionForTest(uint16_t version_number);
-
    private:
-    size_t size_ = 0;
-    size_t max_handles_ = 0;
-    char* data_ = nullptr;
+    size_t size_;
+    size_t max_handles_;
+    char* data_;
+    Header* header_;
 
     ScopedPlatformHandleVectorPtr handle_vector_;
 
@@ -214,7 +203,7 @@
   // |delegate| is destroyed.
   static scoped_refptr<Channel> Create(
       Delegate* delegate,
-      ConnectionParams connection_params,
+      ScopedPlatformHandle platform_handle,
       scoped_refptr<base::TaskRunner> io_task_runner);
 
   // Request that the channel be shut down. This should always be called before
@@ -281,7 +270,7 @@
 
   // Handles a received control message. Returns |true| if the message is
   // accepted, or |false| otherwise.
-  virtual bool OnControlMessage(Message::MessageType message_type,
+  virtual bool OnControlMessage(Message::Header::MessageType message_type,
                                 const void* payload,
                                 size_t payload_size,
                                 ScopedPlatformHandleVectorPtr handles);
diff --git a/mojo/edk/system/channel_posix.cc b/mojo/edk/system/channel_posix.cc
index 8b4ca7f..16a9304 100644
--- a/mojo/edk/system/channel_posix.cc
+++ b/mojo/edk/system/channel_posix.cc
@@ -88,18 +88,17 @@
                      public base::MessageLoopForIO::Watcher {
  public:
   ChannelPosix(Delegate* delegate,
-               ConnectionParams connection_params,
+               ScopedPlatformHandle handle,
                scoped_refptr<base::TaskRunner> io_task_runner)
       : Channel(delegate),
         self_(this),
-        handle_(connection_params.TakeChannelHandle()),
+        handle_(std::move(handle)),
         io_task_runner_(io_task_runner)
 #if defined(OS_MACOSX)
         ,
         handles_to_close_(new PlatformHandleVector)
 #endif
   {
-    CHECK(handle_.is_valid());
   }
 
   void Start() override {
@@ -211,22 +210,12 @@
   void StartOnIOThread() {
     DCHECK(!read_watcher_);
     DCHECK(!write_watcher_);
-    read_watcher_.reset(
-        new base::MessageLoopForIO::FileDescriptorWatcher(FROM_HERE));
+    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);
-    if (handle_.get().needs_connection) {
-      base::MessageLoopForIO::current()->WatchFileDescriptor(
-          handle_.get().handle, false /* persistent */,
-          base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this);
-    } else {
-      write_watcher_.reset(
-          new base::MessageLoopForIO::FileDescriptorWatcher(FROM_HERE));
-      base::MessageLoopForIO::current()->WatchFileDescriptor(
-          handle_.get().handle, true /* persistent */,
-          base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this);
-      base::AutoLock lock(write_lock_);
-      FlushOutgoingMessagesNoLock();
-    }
   }
 
   void WaitForWriteOnIOThread() {
@@ -276,24 +265,6 @@
   // base::MessageLoopForIO::Watcher:
   void OnFileCanReadWithoutBlocking(int fd) override {
     CHECK_EQ(fd, handle_.get().handle);
-    if (handle_.get().needs_connection) {
-#if !defined(OS_NACL)
-      read_watcher_.reset();
-      base::MessageLoop::current()->RemoveDestructionObserver(this);
-
-      ScopedPlatformHandle accept_fd;
-      ServerAcceptConnection(handle_.get(), &accept_fd);
-      if (!accept_fd.is_valid()) {
-        OnError();
-        return;
-      }
-      handle_ = std::move(accept_fd);
-      StartOnIOThread();
-#else
-      NOTREACHED();
-#endif
-      return;
-    }
 
     bool read_error = false;
     size_t next_read_size = 0;
@@ -350,10 +321,6 @@
   // 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) {
-    if (handle_.get().needs_connection) {
-      outgoing_messages_.emplace_front(std::move(message_view));
-      return true;
-    }
     size_t bytes_written = 0;
     do {
       message_view.advance_data_offset(bytes_written);
@@ -388,7 +355,7 @@
           }
           MessagePtr fds_message(
               new Channel::Message(sizeof(fds[0]) * fds.size(), 0,
-                                   Message::MessageType::HANDLES_SENT));
+                                   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);
@@ -403,26 +370,8 @@
       }
 
       if (result < 0) {
-        if (errno != EAGAIN && errno != EWOULDBLOCK
-#if defined(OS_MACOSX)
-            // On OS X if sendmsg() is trying to send fds between processes and
-            // there isn't enough room in the output buffer to send the fd
-            // structure over atomically then EMSGSIZE is returned.
-            //
-            // EMSGSIZE presents a problem since the system APIs can only call
-            // us when there's room in the socket buffer and not when there is
-            // "enough" room.
-            //
-            // The current behavior is to return to the event loop when EMSGSIZE
-            // is received and hopefull service another FD.  This is however
-            // still technically a busy wait since the event loop will call us
-            // right back until the receiver has read enough data to allow
-            // passing the FD over atomically.
-            && errno != EMSGSIZE
-#endif
-            ) {
+        if (errno != EAGAIN && errno != EWOULDBLOCK)
           return false;
-        }
         message_view.SetHandles(std::move(handles));
         outgoing_messages_.emplace_front(std::move(message_view));
         WaitForWriteOnIOThreadNoLock();
@@ -463,22 +412,22 @@
   }
 
 #if defined(OS_MACOSX)
-  bool OnControlMessage(Message::MessageType message_type,
+  bool OnControlMessage(Message::Header::MessageType message_type,
                         const void* payload,
                         size_t payload_size,
                         ScopedPlatformHandleVectorPtr handles) override {
     switch (message_type) {
-      case Message::MessageType::HANDLES_SENT: {
+      case Message::Header::MessageType::HANDLES_SENT: {
         if (payload_size == 0)
           break;
         MessagePtr message(new Channel::Message(
-            payload_size, 0, Message::MessageType::HANDLES_SENT_ACK));
+            payload_size, 0, Message::Header::MessageType::HANDLES_SENT_ACK));
         memcpy(message->mutable_payload(), payload, payload_size);
         Write(std::move(message));
         return true;
       }
 
-      case Message::MessageType::HANDLES_SENT_ACK: {
+      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;
@@ -562,10 +511,9 @@
 // static
 scoped_refptr<Channel> Channel::Create(
     Delegate* delegate,
-    ConnectionParams connection_params,
+    ScopedPlatformHandle platform_handle,
     scoped_refptr<base::TaskRunner> io_task_runner) {
-  return new ChannelPosix(delegate, std::move(connection_params),
-                          io_task_runner);
+  return new ChannelPosix(delegate, std::move(platform_handle), io_task_runner);
 }
 
 }  // namespace edk
diff --git a/mojo/edk/system/channel_win.cc b/mojo/edk/system/channel_win.cc
index c15df16..c989344 100644
--- a/mojo/edk/system/channel_win.cc
+++ b/mojo/edk/system/channel_win.cc
@@ -19,7 +19,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/synchronization/lock.h"
 #include "base/task_runner.h"
-#include "base/win/win_util.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 
 namespace mojo {
@@ -138,8 +137,8 @@
     const HandleEntry* extra_header_handles =
         reinterpret_cast<const HandleEntry*>(extra_header);
     for (size_t i = 0; i < num_handles; i++) {
-      (*handles)->at(i).handle =
-          base::win::Uint32ToHandle(extra_header_handles[i].handle);
+      (*handles)->at(i).handle = reinterpret_cast<HANDLE>(
+          static_cast<uintptr_t>(extra_header_handles[i].handle));
     }
     return true;
   }
@@ -350,10 +349,9 @@
 // static
 scoped_refptr<Channel> Channel::Create(
     Delegate* delegate,
-    ConnectionParams connection_params,
+    ScopedPlatformHandle platform_handle,
     scoped_refptr<base::TaskRunner> io_task_runner) {
-  return new ChannelWin(delegate, connection_params.TakeChannelHandle(),
-                        io_task_runner);
+  return new ChannelWin(delegate, std::move(platform_handle), io_task_runner);
 }
 
 }  // namespace edk
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index 1e0bf4e..ada3753 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -44,8 +44,8 @@
 // This is an unnecessarily large limit that is relatively easy to enforce.
 const uint32_t kMaxHandlesPerMessage = 1024 * 1024;
 
-// TODO(rockot): 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.
+// 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,
@@ -166,17 +166,13 @@
   return handles_.GetDispatcher(handle);
 }
 
-void Core::SetDefaultProcessErrorCallback(
-    const ProcessErrorCallback& callback) {
-  default_process_error_callback_ = callback;
-}
-
 void Core::AddChild(base::ProcessHandle process_handle,
-                    ConnectionParams connection_params,
+                    ScopedPlatformHandle platform_handle,
                     const std::string& child_token,
                     const ProcessErrorCallback& process_error_callback) {
   GetNodeController()->ConnectToChild(process_handle,
-                                      std::move(connection_params), child_token,
+                                      std::move(platform_handle),
+                                      child_token,
                                       process_error_callback);
 }
 
@@ -185,26 +181,8 @@
   GetNodeController()->CloseChildPorts(child_token);
 }
 
-ScopedMessagePipeHandle Core::ConnectToPeerProcess(
-    ScopedPlatformHandle pipe_handle,
-    const std::string& peer_token) {
-  RequestContext request_context;
-  ports::PortRef port0, port1;
-  GetNodeController()->node()->CreatePortPair(&port0, &port1);
-  MojoHandle handle = AddDispatcher(new MessagePipeDispatcher(
-      GetNodeController(), port0, kUnknownPipeIdForDebug, 0));
-  ConnectionParams connection_params(std::move(pipe_handle));
-  GetNodeController()->ConnectToPeer(std::move(connection_params), port1,
-                                     peer_token);
-  return ScopedMessagePipeHandle(MessagePipeHandle(handle));
-}
-
-void Core::ClosePeerConnection(const std::string& peer_token) {
-  GetNodeController()->ClosePeerConnection(peer_token);
-}
-
-void Core::InitChild(ConnectionParams connection_params) {
-  GetNodeController()->ConnectToParent(std::move(connection_params));
+void Core::InitChild(ScopedPlatformHandle platform_handle) {
+  GetNodeController()->ConnectToParent(std::move(platform_handle));
 }
 
 void Core::SetMachPortProvider(base::PortProvider* port_provider) {
@@ -332,7 +310,15 @@
 }
 
 void Core::RequestShutdown(const base::Closure& callback) {
-  GetNodeController()->RequestShutdown(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::CreateParentMessagePipe(
@@ -768,8 +754,6 @@
       reinterpret_cast<MessageForTransit*>(message)->ports_message();
   if (ports_message.source_node() == ports::kInvalidNodeName) {
     DVLOG(1) << "Received invalid message from unknown node.";
-    if (!default_process_error_callback_.is_null())
-      default_process_error_callback_.Run(std::string(error, error_num_bytes));
     return MOJO_RESULT_OK;
   }
 
@@ -790,12 +774,12 @@
   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(rockot): Use Configuration to get default data pipe capacity.
+  // 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(rockot): Broker through the parent when necessary.
+  // TODO: Broker through the parent when necessary.
   scoped_refptr<PlatformSharedBuffer> ring_buffer =
       GetNodeController()->CreateSharedBuffer(
           create_options.capacity_num_bytes);
@@ -1101,7 +1085,7 @@
                                   const MojoHandleSignals* signals,
                                   uint32_t num_handles,
                                   MojoDeadline deadline,
-                                  uint32_t* result_index,
+                                  uint32_t *result_index,
                                   HandleSignalsState* signals_states) {
   CHECK(handles);
   CHECK(signals);
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index 1e20a87..64298f9 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -6,7 +6,6 @@
 #define MOJO_EDK_SYSTEM_CORE_H_
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "base/callback.h"
@@ -40,7 +39,7 @@
 // are thread-safe.
 class MOJO_SYSTEM_IMPL_EXPORT Core {
  public:
-  Core();
+  explicit Core();
   virtual ~Core();
 
   // Called exactly once, shortly after construction, and before any other
@@ -52,27 +51,17 @@
 
   scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle);
 
-  void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback);
-
   // Called in the parent process any time a new child is launched.
   void AddChild(base::ProcessHandle process_handle,
-                ConnectionParams connection_params,
+                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 to connect to a peer process. This should be called only if there
-  // is no common ancestor for the processes involved within this mojo system.
-  // Both processes must call this function, each passing one end of a platform
-  // channel. This returns one end of a message pipe to each process.
-  ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe_handle,
-                                               const std::string& peer_token);
-  void ClosePeerConnection(const std::string& peer_token);
-
   // Called in a child process exactly once during early initialization.
-  void InitChild(ConnectionParams connection_params);
+  void InitChild(ScopedPlatformHandle platform_handle);
 
   // Creates a message pipe endpoint associated with |token|, which a child
   // holding the token can later locate and connect to.
@@ -273,7 +262,7 @@
                               const MojoHandleSignals* signals,
                               uint32_t num_handles,
                               MojoDeadline deadline,
-                              uint32_t* result_index,
+                              uint32_t *result_index,
                               HandleSignalsState* signals_states);
 
   // Used to pass ownership of our NodeController over to the IO thread in the
@@ -295,10 +284,6 @@
   // to access it.
   std::unique_ptr<NodeController> node_controller_;
 
-  // The default callback to invoke, if any, when a process error is reported
-  // but cannot be associated with a specific process.
-  ProcessErrorCallback default_process_error_callback_;
-
   base::Lock handles_lock_;
   HandleTable handles_;
 
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index 814ce4b..33a7068 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -846,16 +846,14 @@
       MOJO_RESULT_FAILED_PRECONDITION,
       core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
   ASSERT_EQ(0u, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   // Write.
@@ -874,10 +872,8 @@
   hss = kEmptyMojoHandleSignalsState;
   ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0,
                                          &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -998,9 +994,8 @@
   ASSERT_EQ(
       MOJO_RESULT_DEADLINE_EXCEEDED,
       core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
-  EXPECT_EQ(0u, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   // TODO(vtl): More.
@@ -1086,10 +1081,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
                          &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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,
@@ -1140,10 +1133,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
                          &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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,
@@ -1196,10 +1187,8 @@
   hss = kEmptyMojoHandleSignalsState;
   ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE,
                                          1000000000, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index c908e3a..23cb2e0 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -124,8 +124,6 @@
                                                 uint32_t* num_bytes,
                                                 MojoReadDataFlags flags) {
   base::AutoLock lock(lock_);
-  new_data_available_ = false;
-
   if (!shared_ring_buffer_ || in_transit_)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
@@ -206,7 +204,6 @@
                                                      uint32_t* buffer_num_bytes,
                                                      MojoReadDataFlags flags) {
   base::AutoLock lock(lock_);
-  new_data_available_ = false;
   if (!shared_ring_buffer_ || in_transit_)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
@@ -422,10 +419,8 @@
     base::AutoLock lock(dispatcher->lock_);
     dispatcher->read_offset_ = state->read_offset;
     dispatcher->bytes_available_ = state->bytes_available;
-    dispatcher->new_data_available_ = state->bytes_available > 0;
     dispatcher->peer_closed_ = state->flags & kFlagPeerClosed;
     dispatcher->InitializeNoLock();
-    dispatcher->UpdateSignalsStateNoLock();
   }
 
   return dispatcher;
@@ -478,25 +473,16 @@
 
   HandleSignalsState rv;
   if (shared_ring_buffer_ && bytes_available_) {
-    if (!in_two_phase_read_) {
+    if (!in_two_phase_read_)
       rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
-      if (new_data_available_)
-        rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE;
-    }
     rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
   } else if (!peer_closed_ && shared_ring_buffer_) {
     rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
   }
 
-  if (shared_ring_buffer_) {
-    if (new_data_available_ || !peer_closed_)
-      rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE;
-  }
-
   if (peer_closed_)
     rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
   rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
-
   return rv;
 }
 
@@ -540,8 +526,8 @@
   } else if (rv == ports::OK && port_status.has_messages && !in_transit_) {
     ports::ScopedMessage message;
     do {
-      int rv = node_controller_->node()->GetMessage(
-          control_port_, &message, nullptr);
+      int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr,
+                                                      &message);
       if (rv != ports::OK)
         peer_closed_ = true;
       if (message) {
@@ -576,11 +562,8 @@
     } while (message);
   }
 
-  bool has_new_data = bytes_available_ != previous_bytes_available;
-  if (has_new_data)
-    new_data_available_ = true;
-
-  if (peer_closed_ != was_peer_closed || has_new_data) {
+  if (peer_closed_ != was_peer_closed ||
+      bytes_available_ != previous_bytes_available) {
     awakable_list_.AwakeForStateChange(GetHandleSignalsStateNoLock());
   }
 }
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h
index b323c16..6a7fb1c 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -24,6 +24,7 @@
 namespace mojo {
 namespace edk {
 
+struct DataPipeControlMessage;
 class NodeController;
 
 // This is the Dispatcher implementation for the consumer handle for data
@@ -117,9 +118,6 @@
   uint32_t read_offset_ = 0;
   uint32_t bytes_available_ = 0;
 
-  // Indicates whether any new data is available since the last read attempt.
-  bool new_data_available_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher);
 };
 
diff --git a/mojo/edk/system/data_pipe_control_message.h b/mojo/edk/system/data_pipe_control_message.h
index ec84ea3..82ee594 100644
--- a/mojo/edk/system/data_pipe_control_message.h
+++ b/mojo/edk/system/data_pipe_control_message.h
@@ -17,6 +17,7 @@
 namespace edk {
 
 class NodeController;
+class PortsMessage;
 
 enum DataPipeCommand : uint32_t {
   // Signal to the consumer that new data is available.
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc
index 8c1993a..d056e7d 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -137,8 +137,9 @@
   if (*num_bytes == 0)
     return MOJO_RESULT_OK;  // Nothing to do.
 
-  if ((flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) &&
-      (*num_bytes > available_capacity_)) {
+  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;
@@ -402,7 +403,6 @@
     dispatcher->available_capacity_ = state->available_capacity;
     dispatcher->peer_closed_ = state->flags & kFlagPeerClosed;
     dispatcher->InitializeNoLock();
-    dispatcher->UpdateSignalsStateNoLock();
   }
 
   return dispatcher;
@@ -504,8 +504,8 @@
   } else if (rv == ports::OK && port_status.has_messages && !in_transit_) {
     ports::ScopedMessage message;
     do {
-      int rv = node_controller_->node()->GetMessage(
-          control_port_, &message, nullptr);
+      int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr,
+                                                      &message);
       if (rv != ports::OK)
         peer_closed_ = true;
       if (message) {
diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc
index 610aeac..526444c 100644
--- a/mojo/edk/system/data_pipe_unittest.cc
+++ b/mojo/edk/system/data_pipe_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/system/test_utils.h"
@@ -21,7 +20,6 @@
 #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/cpp/system/watcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace mojo {
@@ -42,9 +40,6 @@
 const char kMultiprocessTestData[] = "hello i'm a string that is 36 bytes";
 const int kMultiprocessMaxIter = 5;
 
-// TODO(rockot): There are many uses of ASSERT where EXPECT would be more
-// appropriate. Fix this.
-
 class DataPipeTest : public test::MojoTestBase {
  public:
   DataPipeTest() : producer_(MOJO_HANDLE_INVALID),
@@ -164,8 +159,7 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &state));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            state.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, state.satisfied_signals);
 
   elements[0] = -1;
   elements[1] = -1;
@@ -251,10 +245,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   // Query.
@@ -360,10 +352,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -487,9 +477,8 @@
   ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(0u, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(0u, hss.satisfied_signals);
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   // Write two elements.
@@ -502,10 +491,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -518,9 +505,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -536,9 +522,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -561,10 +546,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -575,10 +558,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_TRUE(hss.satisfied_signals & (MOJO_HANDLE_SIGNAL_READABLE |
-                                       MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -586,11 +567,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE |
-                MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -611,64 +589,6 @@
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 }
 
-TEST_F(DataPipeTest, ConsumerNewDataReadable) {
-  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|.
-  };
-  EXPECT_EQ(MOJO_RESULT_OK, Create(&options));
-
-  int32_t elements[2] = {123, 456};
-  uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
-  EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
-
-  // The consumer handle should appear to be readable and have new data.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
-
-  // Now try to read a minimum of 6 elements.
-  int32_t read_elements[6];
-  uint32_t num_read_bytes = sizeof(read_elements);
-  EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
-            MojoReadData(consumer_, read_elements, &num_read_bytes,
-                         MOJO_READ_DATA_FLAG_ALL_OR_NONE));
-
-  // The consumer should still appear to be readable, but not with new data.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr));
-  EXPECT_EQ(
-      MOJO_RESULT_DEADLINE_EXCEEDED,
-      MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 0, nullptr));
-
-  // Write four more elements.
-  EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
-  EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
-
-  // The consumer handle should once again appear to be readable with new data.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
-
-  // Read should succeed this time.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadData(consumer_, read_elements, &num_read_bytes,
-                         MOJO_READ_DATA_FLAG_ALL_OR_NONE));
-
-  // And once again the consumer is unreadable.
-  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
-            MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr));
-  EXPECT_EQ(
-      MOJO_RESULT_DEADLINE_EXCEEDED,
-      MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 0, nullptr));
-}
-
 // Test with two-phase APIs and also closing the producer with an active
 // consumer waiter.
 TEST_F(DataPipeTest, ConsumerWaitingTwoPhase) {
@@ -686,7 +606,7 @@
   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, false));
+  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);
@@ -699,10 +619,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -721,9 +639,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -787,8 +704,7 @@
   ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
   ASSERT_EQ(0u, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   static_cast<int32_t*>(write_ptr)[0] = 123;
@@ -807,10 +723,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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
@@ -826,10 +740,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -855,8 +767,7 @@
   ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
   ASSERT_EQ(0u, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   // End the two-phase read without reading anything.
@@ -867,8 +778,7 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 }
 
@@ -887,7 +797,7 @@
   ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
   MojoHandleSignalsState hss;
 
-  // Try writing more than the total capacity of the pipe.
+  // Try writing way too much.
   uint32_t num_bytes = 20u * sizeof(int32_t);
   int32_t buffer[100];
   Seq(0, arraysize(buffer), buffer);
@@ -913,10 +823,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE |
-                MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+  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.
@@ -924,11 +832,12 @@
   ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
   ASSERT_EQ(5u * sizeof(int32_t), num_bytes);
 
-  // Try writing more than the available capacity of the pipe, but less than the
-  // total capacity.
+  /* 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);
@@ -1004,9 +913,9 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+  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.
@@ -1069,9 +978,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_TRUE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -1233,10 +1141,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -1325,11 +1231,9 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             hss.satisfiable_signals);
 
   // Peek that data.
@@ -1386,10 +1290,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -1413,9 +1315,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -1501,10 +1402,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -1571,10 +1470,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -1614,10 +1511,8 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
                      MOJO_DEADLINE_INDEFINITE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -1655,11 +1550,9 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
                      MOJO_DEADLINE_INDEFINITE, &state));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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 |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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.
@@ -1682,11 +1575,9 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
                      MOJO_DEADLINE_INDEFINITE, &state));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             state.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             state.satisfiable_signals);
 
   int32_t read_data;
@@ -1758,8 +1649,8 @@
                                        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_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE);
-    EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+              hss.satisfiable_signals);
   }
 
   return num_bytes == 0;
@@ -2007,90 +1898,6 @@
   END_CHILD()
 }
 
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(DataPipeStatusChangeInTransitClient,
-                                  DataPipeTest, parent) {
-  // This test verifies that peer closure is detectable through various
-  // mechanisms when it races with handle transfer.
-
-  MojoHandle handles[6];
-  EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 6));
-  MojoHandle* producers = &handles[0];
-  MojoHandle* consumers = &handles[3];
-
-  // Wait on producer 0 using MojoWait.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(producers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
-
-  // Wait on consumer 0 using MojoWait.
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWait(consumers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                     MOJO_DEADLINE_INDEFINITE, nullptr));
-
-  base::MessageLoop message_loop;
-
-  // Wait on producer 1 and consumer 1 using Watchers.
-  {
-    base::RunLoop run_loop;
-    int count = 0;
-    auto callback = base::Bind(
-        [] (base::RunLoop* loop, int* count, MojoResult result) {
-          EXPECT_EQ(MOJO_RESULT_OK, result);
-          if (++*count == 2)
-            loop->Quit();
-        },
-        &run_loop, &count);
-    Watcher producer_watcher(FROM_HERE), consumer_watcher(FROM_HERE);
-    producer_watcher.Start(
-        Handle(producers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, callback);
-    consumer_watcher.Start(
-        Handle(consumers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, callback);
-    run_loop.Run();
-  }
-
-  // Wait on producer 2 by polling with MojoWriteData.
-  MojoResult result;
-  do {
-    uint32_t num_bytes = 0;
-    result = MojoWriteData(
-        producers[2], nullptr, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
-  } while (result == MOJO_RESULT_OK);
-  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
-  // Wait on consumer 2 by polling with MojoReadData.
-  do {
-    char byte;
-    uint32_t num_bytes = 1;
-    result = MojoReadData(
-        consumers[2], &byte, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
-  } while (result == MOJO_RESULT_SHOULD_WAIT);
-  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
-  for (size_t i = 0; i < 6; ++i)
-    CloseHandle(handles[i]);
-}
-
-TEST_F(DataPipeTest, StatusChangeInTransit) {
-  MojoHandle producers[6];
-  MojoHandle consumers[6];
-  for (size_t i = 0; i < 6; ++i)
-    CreateDataPipe(&producers[i], &consumers[i], 1);
-
-  RUN_CHILD_ON_PIPE(DataPipeStatusChangeInTransitClient, child)
-    MojoHandle handles[] = { producers[0], producers[1], producers[2],
-                             consumers[3], consumers[4], consumers[5] };
-
-    // Send 3 producers and 3 consumers, and let their transfer race with their
-    // peers' closure.
-    WriteMessageWithHandles(child, "o_O", handles, 6);
-
-    for (size_t i = 0; i < 3; ++i)
-      CloseHandle(consumers[i]);
-    for (size_t i = 3; i < 6; ++i)
-      CloseHandle(producers[i]);
-  END_CHILD()
-}
-
 #endif  // !defined(OS_IOS)
 
 }  // namespace
diff --git a/mojo/edk/system/handle_table.cc b/mojo/edk/system/handle_table.cc
index b570793..e41817d 100644
--- a/mojo/edk/system/handle_table.cc
+++ b/mojo/edk/system/handle_table.cc
@@ -21,8 +21,7 @@
     return MOJO_HANDLE_INVALID;
 
   MojoHandle handle = next_available_handle_++;
-  auto result =
-      handles_.insert(std::make_pair(handle, Entry(std::move(dispatcher))));
+  auto result = handles_.insert(std::make_pair(handle, Entry(dispatcher)));
   DCHECK(result.second);
 
   return handle;
@@ -67,7 +66,7 @@
   if (it->second.busy)
     return MOJO_RESULT_BUSY;
 
-  *dispatcher = std::move(it->second.dispatcher);
+  *dispatcher = it->second.dispatcher;
   handles_.erase(it);
   return MOJO_RESULT_OK;
 }
@@ -125,7 +124,7 @@
 HandleTable::Entry::Entry() {}
 
 HandleTable::Entry::Entry(scoped_refptr<Dispatcher> dispatcher)
-    : dispatcher(std::move(dispatcher)) {}
+    : dispatcher(dispatcher) {}
 
 HandleTable::Entry::Entry(const Entry& other) = default;
 
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
index f27336b..f06054d 100644
--- a/mojo/edk/system/message_pipe_dispatcher.cc
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -14,7 +14,6 @@
 #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_filter.h"
 #include "mojo/edk/system/ports_message.h"
 #include "mojo/edk/system/request_context.h"
 
@@ -60,103 +59,6 @@
   DISALLOW_COPY_AND_ASSIGN(PortObserverThunk);
 };
 
-// A MessageFilter used by ReadMessage to determine whether a message should
-// actually be consumed yet.
-class ReadMessageFilter : public ports::MessageFilter {
- public:
-  // Creates a new ReadMessageFilter which captures and potentially modifies
-  // various (unowned) local state within MessagePipeDispatcher::ReadMessage.
-  ReadMessageFilter(bool read_any_size,
-                    bool may_discard,
-                    uint32_t* num_bytes,
-                    uint32_t* num_handles,
-                    bool* no_space,
-                    bool* invalid_message)
-      : read_any_size_(read_any_size),
-        may_discard_(may_discard),
-        num_bytes_(num_bytes),
-        num_handles_(num_handles),
-        no_space_(no_space),
-        invalid_message_(invalid_message) {}
-
-  ~ReadMessageFilter() override {}
-
-  // ports::MessageFilter:
-  bool Match(const ports::Message& m) override {
-    const PortsMessage& message = static_cast<const PortsMessage&>(m);
-    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;
-  }
-
- private:
-  const bool read_any_size_;
-  const bool may_discard_;
-  uint32_t* const num_bytes_;
-  uint32_t* const num_handles_;
-  bool* const no_space_;
-  bool* const invalid_message_;
-
-  DISALLOW_COPY_AND_ASSIGN(ReadMessageFilter);
-};
-
-#if DCHECK_IS_ON()
-
-// A MessageFilter which never matches a message. Used to peek at the size of
-// the next available message on a port, for debug logging only.
-class PeekSizeMessageFilter : public ports::MessageFilter {
- public:
-  PeekSizeMessageFilter() {}
-  ~PeekSizeMessageFilter() override {}
-
-  // ports::MessageFilter:
-  bool Match(const ports::Message& message) override {
-    message_size_ = message.num_payload_bytes();
-    return false;
-  }
-
-  size_t message_size() const { return message_size_; }
-
- private:
-  size_t message_size_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(PeekSizeMessageFilter);
-};
-
-#endif  // DCHECK_IS_ON()
-
 MessagePipeDispatcher::MessagePipeDispatcher(NodeController* node_controller,
                                              const ports::PortRef& port,
                                              uint64_t pipe_id,
@@ -239,7 +141,7 @@
   size_t num_bytes = message->num_bytes();
   int rv = node_controller_->SendMessage(port_, message->TakePortsMessage());
 
-  DVLOG(4) << "Sent message on pipe " << pipe_id_ << " endpoint " << endpoint_
+  DVLOG(2) << "Sent message on pipe " << pipe_id_ << " endpoint " << endpoint_
            << " [port=" << port_.name() << "; rv=" << rv
            << "; num_bytes=" << num_bytes << "]";
 
@@ -249,6 +151,8 @@
         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;
     }
 
@@ -282,9 +186,50 @@
   // This flag exists to support both new and old API behavior.
 
   ports::ScopedMessage ports_message;
-  ReadMessageFilter filter(read_any_size, may_discard, num_bytes, num_handles,
-                           &no_space, &invalid_message);
-  int rv = node_controller_->node()->GetMessage(port_, &ports_message, &filter);
+  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;
@@ -313,6 +258,8 @@
 
     // 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;
   }
 
@@ -583,11 +530,15 @@
   if (node_controller_->node()->GetStatus(port_, &port_status) == ports::OK) {
     if (port_status.has_messages) {
       ports::ScopedMessage unused;
-      PeekSizeMessageFilter filter;
-      node_controller_->node()->GetMessage(port_, &unused, &filter);
-      DVLOG(4) << "New message detected on message pipe " << pipe_id_
+      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=" << filter.message_size() << "]";
+               << "; size=" << message_size << "]";
     }
     if (port_status.peer_closed) {
       DVLOG(2) << "Peer closure detected on message pipe " << pipe_id_
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index 6743222..fddd0fd 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -21,6 +21,7 @@
 namespace edk {
 
 class NodeController;
+class PortsMessage;
 
 class MessagePipeDispatcher : public Dispatcher {
  public:
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc
index 8f48950..fcfaeca 100644
--- a/mojo/edk/system/message_pipe_unittest.cc
+++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -706,17 +706,6 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
 }
 
-TEST_F(MessagePipeTest, ClosePipesStressTest) {
-  // Stress test to exercise https://crbug.com/665869.
-  const size_t kNumPipes = 100000;
-  for (size_t i = 0; i < kNumPipes; ++i) {
-    MojoHandle a, b;
-    CreateMessagePipe(&a, &b);
-    MojoClose(a);
-    MojoClose(b);
-  }
-}
-
 }  // 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
index 498980c..6e2c6f1 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -18,8 +18,6 @@
 #include "base/files/scoped_file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
 #include "base/strings/string_split.h"
 #include "build/build_config.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
@@ -31,7 +29,6 @@
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/functions.h"
 #include "mojo/public/c/system/types.h"
-#include "mojo/public/cpp/system/watcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 
@@ -71,16 +68,6 @@
   };
 };
 
-class MultiprocessMessagePipeTestWithPeerSupport
-    : public MultiprocessMessagePipeTest,
-      public testing::WithParamInterface<test::MojoTestBase::LaunchType> {
- protected:
-  void SetUp() override {
-    test::MojoTestBase::SetUp();
-    set_launch_type(GetParam());
-  }
-};
-
 // 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,
@@ -129,7 +116,7 @@
   return rv;
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, Basic) {
+TEST_F(MultiprocessMessagePipeTest, Basic) {
   RUN_CHILD_ON_PIPE(EchoEcho, h)
     std::string hello("hello");
     ASSERT_EQ(MOJO_RESULT_OK,
@@ -165,7 +152,7 @@
   END_CHILD_AND_EXPECT_EXIT_CODE(1 % 100);
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, QueueMessages) {
+TEST_F(MultiprocessMessagePipeTest, QueueMessages) {
   static const size_t kNumMessages = 1001;
   RUN_CHILD_ON_PIPE(EchoEcho, h)
     for (size_t i = 0; i < kNumMessages; i++) {
@@ -428,7 +415,7 @@
     for (size_t i = 0; i < pipe_count; ++i) {
       base::FilePath unused;
       base::ScopedFILE fp(
-          CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+          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());
@@ -443,8 +430,7 @@
     }
 
     char message[128];
-    snprintf(message, sizeof(message), "hello %d",
-             static_cast<int>(pipe_count));
+    sprintf(message, "hello %d", static_cast<int>(pipe_count));
     ASSERT_EQ(MOJO_RESULT_OK,
               MojoWriteMessage(h, message,
                                static_cast<uint32_t>(strlen(message)),
@@ -466,9 +452,9 @@
 #if !defined(OS_ANDROID)
 INSTANTIATE_TEST_CASE_P(PipeCount,
                         MultiprocessMessagePipeTestWithPipeCount,
-                        // TODO(rockot): Re-enable the 140-pipe case when
-                        // ChannelPosix has support for sending lots of handles.
-                        testing::Values(1u, 128u /*, 140u*/));
+                        // 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) {
@@ -523,7 +509,7 @@
   return 0;
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipePassing) {
+TEST_F(MultiprocessMessagePipeTest, MessagePipePassing) {
   RUN_CHILD_ON_PIPE(CheckMessagePipe, h)
     MojoCreateSharedBufferOptions options;
     options.struct_size = sizeof(options);
@@ -565,7 +551,7 @@
   END_CHILD()
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipeTwoPassing) {
+TEST_F(MultiprocessMessagePipeTest, MessagePipeTwoPassing) {
   RUN_CHILD_ON_PIPE(CheckMessagePipe, h)
     MojoHandle mp1, mp2;
     ASSERT_EQ(MOJO_RESULT_OK,
@@ -695,9 +681,11 @@
   END_CHILD();
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, CreateMessagePipe) {
+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'));
 
@@ -705,7 +693,7 @@
   CloseHandle(p1);
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PassMessagePipeLocal) {
+TEST_F(MultiprocessMessagePipeTest, PassMessagePipeLocal) {
   MojoHandle p0, p1;
   CreateMessagePipe(&p0, &p1);
   VerifyTransmission(p0, p1, "testing testing");
@@ -745,7 +733,7 @@
   return 0;
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MultiprocessChannelPipe) {
+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");
@@ -770,8 +758,7 @@
   return 0;
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
-       PassMessagePipeCrossProcess) {
+TEST_F(MultiprocessMessagePipeTest, PassMessagePipeCrossProcess) {
   MojoHandle p0, p1;
   CreateMessagePipe(&p0, &p1);
   RUN_CHILD_ON_PIPE(EchoServiceClient, h)
@@ -828,8 +815,7 @@
   return 0;
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
-       PassMoarMessagePipesCrossProcess) {
+TEST_F(MultiprocessMessagePipeTest, PassMoarMessagePipesCrossProcess) {
   MojoHandle echo_factory_proxy, echo_factory_request;
   CreateMessagePipe(&echo_factory_proxy, &echo_factory_request);
 
@@ -874,8 +860,7 @@
   CloseHandle(echo_proxy_c);
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
-       ChannelPipesWithMultipleChildren) {
+TEST_F(MultiprocessMessagePipeTest, ChannelPipesWithMultipleChildren) {
   RUN_CHILD_ON_PIPE(ChannelEchoClient, a)
     RUN_CHILD_ON_PIPE(ChannelEchoClient, b)
       VerifyEcho(a, "hello child 0");
@@ -905,7 +890,7 @@
   EXPECT_EQ("quit", ReadMessage(h));
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PingPongPipe) {
+TEST_F(MultiprocessMessagePipeTest, PingPongPipe) {
   MojoHandle p0, p1;
   CreateMessagePipe(&p0, &p1);
 
@@ -998,7 +983,7 @@
     }
   }
 
-  for (auto& pipe : named_pipes)
+  for (auto& pipe: named_pipes)
     CloseHandle(pipe.second);
 
   return 0;
@@ -1017,8 +1002,8 @@
       b.SendHandle("y", p1);
 
       // Make sure they can talk.
-      a.Send("say:x:hello");
-      b.Send("hear:y:hello");
+      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!");
@@ -1111,12 +1096,11 @@
   MojoHandle p;
   EXPECT_EQ("foo", ReadMessageWithHandles(h, &p, 1));
 
-  auto result = MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                         MOJO_DEADLINE_INDEFINITE, nullptr);
-  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+                                     MOJO_DEADLINE_INDEFINITE, nullptr));
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendPipeThenClosePeer) {
+TEST_F(MultiprocessMessagePipeTest, SendPipeThenClosePeer) {
   RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeer, h)
     MojoHandle a, b;
     CreateMessagePipe(&a, &b);
@@ -1192,7 +1176,8 @@
   END_CHILD()
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendClosePeerSend) {
+
+TEST_F(MultiprocessMessagePipeTest, SendClosePeerSend) {
   MojoHandle a, b;
   CreateMessagePipe(&a, &b);
 
@@ -1235,7 +1220,7 @@
   EXPECT_EQ("quit", ReadMessage(h));
 }
 
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, WriteCloseSendPeer) {
+TEST_F(MultiprocessMessagePipeTest, WriteCloseSendPeer) {
   MojoHandle pipe[2];
   CreateMessagePipe(&pipe[0], &pipe[1]);
 
@@ -1258,61 +1243,41 @@
   END_CHILD()
 }
 
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MessagePipeStatusChangeInTransitClient,
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(BootstrapMessagePipeAsyncClient,
                                   MultiprocessMessagePipeTest, parent) {
-  // This test verifies that peer closure is detectable through various
-  // mechanisms when it races with handle transfer.
-  MojoHandle handles[4];
-  EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 4));
+  // 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());
 
-  // Wait on handle 0 using MojoWait.
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWait(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                                     MOJO_DEADLINE_INDEFINITE, nullptr));
+  // Create a new pipe using our end of the channel.
+  ScopedMessagePipeHandle pipe = edk::CreateMessagePipe(std::move(channel));
 
-  base::MessageLoop message_loop;
-
-  // Wait on handle 1 using a Watcher.
-  {
-    base::RunLoop run_loop;
-    Watcher watcher(FROM_HERE);
-    watcher.Start(Handle(handles[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-                  base::Bind([] (base::RunLoop* loop, MojoResult result) {
-                    EXPECT_EQ(MOJO_RESULT_OK, result);
-                    loop->Quit();
-                  }, &run_loop));
-    run_loop.Run();
-  }
-
-  // Wait on handle 2 by polling with MojoReadMessage.
-  MojoResult result;
-  do {
-    result = MojoReadMessage(handles[2], nullptr, nullptr, nullptr, nullptr,
-                             MOJO_READ_MESSAGE_FLAG_NONE);
-  } while (result == MOJO_RESULT_SHOULD_WAIT);
-  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
-  // Wait on handle 3 by polling with MojoWriteMessage.
-  do {
-    result = MojoWriteMessage(handles[3], nullptr, 0, nullptr, 0,
-                              MOJO_WRITE_MESSAGE_FLAG_NONE);
-  } while (result == MOJO_RESULT_OK);
-  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
-  for (size_t i = 0; i < 4; ++i)
-    CloseHandle(handles[i]);
+  // Ensure that we can read and write on the new pipe.
+  VerifyEcho(pipe.get().value(), "goodbye");
 }
 
-TEST_F(MultiprocessMessagePipeTest, MessagePipeStatusChangeInTransit) {
-  MojoHandle local_handles[4];
-  MojoHandle sent_handles[4];
-  for (size_t i = 0; i < 4; ++i)
-    CreateMessagePipe(&local_handles[i], &sent_handles[i]);
+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);
 
-  RUN_CHILD_ON_PIPE(MessagePipeStatusChangeInTransitClient, child)
-    // Send 4 handles and let their transfer race with their peers' closure.
-    WriteMessageWithHandles(child, "o_O", sent_handles, 4);
-    for (size_t i = 0; i < 4; ++i)
-      CloseHandle(local_handles[i]);
+    // 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()
 }
 
@@ -1380,13 +1345,7 @@
   EXPECT_NE(std::string::npos, first_process_error.find(kFirstErrorMessage));
   EXPECT_NE(std::string::npos, second_process_error.find(kSecondErrorMessage));
 }
-INSTANTIATE_TEST_CASE_P(
-    ,
-    MultiprocessMessagePipeTestWithPeerSupport,
-    testing::Values(test::MojoTestBase::LaunchType::CHILD,
-                    test::MojoTestBase::LaunchType::PEER,
-                    test::MojoTestBase::LaunchType::NAMED_CHILD,
-                    test::MojoTestBase::LaunchType::NAMED_PEER));
+
 }  // namespace
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/system/node_channel.cc b/mojo/edk/system/node_channel.cc
index b0f770d..ce094c1 100644
--- a/mojo/edk/system/node_channel.cc
+++ b/mojo/edk/system/node_channel.cc
@@ -47,7 +47,6 @@
 #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
   PORTS_MESSAGE_FROM_RELAY,
 #endif
-  ACCEPT_PEER,
 };
 
 struct Header {
@@ -55,8 +54,8 @@
   uint32_t padding;
 };
 
-static_assert(IsAlignedForChannelMessage(sizeof(Header)),
-              "Invalid header size.");
+static_assert(sizeof(Header) % kChannelMessageAlignment == 0,
+    "Invalid header size.");
 
 struct AcceptChildData {
   ports::NodeName parent_name;
@@ -68,12 +67,6 @@
   ports::NodeName child_name;
 };
 
-struct AcceptPeerData {
-  ports::NodeName token;
-  ports::NodeName peer_name;
-  ports::PortName port_name;
-};
-
 // This message may include a process handle on plaforms that require it.
 struct AddBrokerClientData {
   ports::NodeName client_name;
@@ -160,14 +153,14 @@
 // static
 scoped_refptr<NodeChannel> NodeChannel::Create(
     Delegate* delegate,
-    ConnectionParams connection_params,
+    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(connection_params), io_task_runner,
+  return new NodeChannel(delegate, std::move(platform_handle), io_task_runner,
                          process_error_callback);
 #endif
 }
@@ -289,18 +282,6 @@
   WriteChannelMessage(std::move(message));
 }
 
-void NodeChannel::AcceptPeer(const ports::NodeName& sender_name,
-                             const ports::NodeName& token,
-                             const ports::PortName& port_name) {
-  AcceptPeerData* data;
-  Channel::MessagePtr message =
-      CreateMessage(MessageType::ACCEPT_PEER, sizeof(AcceptPeerData), 0, &data);
-  data->token = token;
-  data->peer_name = sender_name;
-  data->port_name = port_name;
-  WriteChannelMessage(std::move(message));
-}
-
 void NodeChannel::AddBrokerClient(const ports::NodeName& client_name,
                                   base::ProcessHandle process_handle) {
   AddBrokerClientData* data;
@@ -454,18 +435,17 @@
 #endif  // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
 
 NodeChannel::NodeChannel(Delegate* delegate,
-                         ConnectionParams connection_params,
+                         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(connection_params), io_task_runner_))
+      , channel_(
+          Channel::Create(this, std::move(platform_handle), io_task_runner_))
 #endif
-{
+      {
 }
 
 NodeChannel::~NodeChannel() {
@@ -748,16 +728,6 @@
 
 #endif  // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
 
-    case MessageType::ACCEPT_PEER: {
-      const AcceptPeerData* data;
-      if (GetMessagePayload(payload, payload_size, &data)) {
-        delegate_->OnAcceptPeer(remote_node_name_, data->token, data->peer_name,
-                                data->port_name);
-        return;
-      }
-      break;
-    }
-
     default:
       break;
   }
@@ -801,6 +771,7 @@
     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());
diff --git a/mojo/edk/system/node_channel.h b/mojo/edk/system/node_channel.h
index 95dc341..5a5fc8a 100644
--- a/mojo/edk/system/node_channel.h
+++ b/mojo/edk/system/node_channel.h
@@ -16,7 +16,6 @@
 #include "base/synchronization/lock.h"
 #include "base/task_runner.h"
 #include "build/build_config.h"
-#include "mojo/edk/embedder/connection_params.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
@@ -77,10 +76,7 @@
                                          const ports::NodeName& source_node,
                                          Channel::MessagePtr message) = 0;
 #endif
-    virtual void OnAcceptPeer(const ports::NodeName& from_node,
-                              const ports::NodeName& token,
-                              const ports::NodeName& peer_name,
-                              const ports::PortName& port_name) = 0;
+
     virtual void OnChannelError(const ports::NodeName& node,
                                 NodeChannel* channel) = 0;
 
@@ -91,7 +87,7 @@
 
   static scoped_refptr<NodeChannel> Create(
       Delegate* delegate,
-      ConnectionParams connection_params,
+      ScopedPlatformHandle platform_handle,
       scoped_refptr<base::TaskRunner> io_task_runner,
       const ProcessErrorCallback& process_error_callback);
 
@@ -128,9 +124,6 @@
                    const ports::NodeName& token);
   void AcceptParent(const ports::NodeName& token,
                     const ports::NodeName& child_name);
-  void AcceptPeer(const ports::NodeName& sender_name,
-                  const ports::NodeName& token,
-                  const ports::PortName& port_name);
   void AddBrokerClient(const ports::NodeName& client_name,
                        base::ProcessHandle process_handle);
   void BrokerClientAdded(const ports::NodeName& client_name,
@@ -168,7 +161,7 @@
       std::queue<std::pair<ports::NodeName, Channel::MessagePtr>>;
 
   NodeChannel(Delegate* delegate,
-              ConnectionParams connection_params,
+              ScopedPlatformHandle platform_handle,
               scoped_refptr<base::TaskRunner> io_task_runner,
               const ProcessErrorCallback& process_error_callback);
   ~NodeChannel() override;
diff --git a/mojo/edk/system/node_controller.cc b/mojo/edk/system/node_controller.cc
index 7bdb571..63291a5 100644
--- a/mojo/edk/system/node_controller.cc
+++ b/mojo/edk/system/node_controller.cc
@@ -18,8 +18,6 @@
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
 #include "mojo/edk/embedder/embedder_internal.h"
-#include "mojo/edk/embedder/named_platform_channel_pair.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/system/broker.h"
 #include "mojo/edk/system/broker_host.h"
@@ -60,7 +58,7 @@
   // 8k is the maximum number of file descriptors allowed in Chrome.
   UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.ConnectedPeers",
                               static_cast<int32_t>(count),
-                              1 /* min */,
+                              0 /* min */,
                               8000 /* max */,
                               50 /* bucket count */);
 }
@@ -71,7 +69,7 @@
   // 8k is the maximum number of file descriptors allowed in Chrome.
   UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.PendingChildren",
                               static_cast<int32_t>(count),
-                              1 /* min */,
+                              0 /* min */,
                               8000 /* max */,
                               50 /* bucket count */);
 }
@@ -165,7 +163,7 @@
 
 void NodeController::ConnectToChild(
     base::ProcessHandle process_handle,
-    ConnectionParams connection_params,
+    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
@@ -196,10 +194,13 @@
 #endif
 
   io_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&NodeController::ConnectToChildOnIOThread,
-                            base::Unretained(this), process_handle,
-                            base::Passed(&connection_params), node_name,
-                            process_error_callback));
+      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) {
@@ -226,19 +227,14 @@
   AcceptIncomingMessages();
 }
 
-void NodeController::ClosePeerConnection(const std::string& peer_token) {
-  io_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&NodeController::ClosePeerConnectionOnIOThread,
-                            base::Unretained(this), peer_token));
-}
-
-void NodeController::ConnectToParent(ConnectionParams connection_params) {
-#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
-  // Use the bootstrap channel for the broker and receive the node's channel
-  // synchronously as the first message from the broker.
+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(connection_params.TakeChannelHandle()));
-  ScopedPlatformHandle platform_handle = broker_->GetParentPlatformHandle();
+  broker_.reset(new Broker(std::move(platform_handle)));
+  platform_handle = broker_->GetParentPlatformHandle();
   UMA_HISTOGRAM_TIMES("Mojo.System.GetParentPlatformHandleSyncTime",
                       timer.Elapsed());
 
@@ -247,33 +243,21 @@
     // 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.";
-    CancelPendingPortMerges();
     return;
   }
-  connection_params = ConnectionParams(std::move(platform_handle));
 #endif
 
   io_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&NodeController::ConnectToParentOnIOThread,
-                 base::Unretained(this), base::Passed(&connection_params)));
+                 base::Unretained(this),
+                 base::Passed(&platform_handle)));
 }
 
-void NodeController::ConnectToPeer(ConnectionParams connection_params,
-                                   const ports::PortRef& port,
-                                   const std::string& peer_token) {
-  ports::NodeName node_name;
-  GenerateRandomName(&node_name);
-  io_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&NodeController::ConnectToPeerOnIOThread,
-                 base::Unretained(this), base::Passed(&connection_params),
-                 node_name, port, peer_token));
-}
-
-void NodeController::SetPortObserver(const ports::PortRef& port,
-                                     scoped_refptr<PortObserver> observer) {
-  node_->SetUserData(port, std::move(observer));
+void NodeController::SetPortObserver(
+    const ports::PortRef& port,
+    const scoped_refptr<PortObserver>& observer) {
+  node_->SetUserData(port, observer);
 }
 
 void NodeController::ClosePort(const ports::PortRef& port) {
@@ -361,7 +345,7 @@
 
 scoped_refptr<PlatformSharedBuffer> NodeController::CreateSharedBuffer(
     size_t num_bytes) {
-#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
+#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
@@ -392,40 +376,24 @@
 
 void NodeController::ConnectToChildOnIOThread(
     base::ProcessHandle process_handle,
-    ConnectionParams connection_params,
+    ScopedPlatformHandle platform_handle,
     ports::NodeName token,
     const ProcessErrorCallback& process_error_callback) {
   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
 
-#if !defined(OS_MACOSX) && !defined(OS_NACL)
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL)
   PlatformChannelPair node_channel;
-  ScopedPlatformHandle server_handle = node_channel.PassServerHandle();
   // BrokerHost owns itself.
-  BrokerHost* broker_host =
-      new BrokerHost(process_handle, connection_params.TakeChannelHandle());
-  bool channel_ok = broker_host->SendChannel(node_channel.PassClientHandle());
-
-#if defined(OS_WIN)
-  if (!channel_ok) {
-    // On Windows the above operation may fail if the channel is crossing a
-    // session boundary. In that case we fall back to a named pipe.
-    NamedPlatformChannelPair named_channel;
-    server_handle = named_channel.PassServerHandle();
-    broker_host->SendNamedChannel(named_channel.handle().name);
-  }
+  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
-  CHECK(channel_ok);
-#endif  // defined(OS_WIN)
-
   scoped_refptr<NodeChannel> channel =
-      NodeChannel::Create(this, ConnectionParams(std::move(server_handle)),
-                          io_task_runner_, process_error_callback);
-
-#else  // !defined(OS_MACOSX) && !defined(OS_NACL)
-  scoped_refptr<NodeChannel> channel =
-      NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
+      NodeChannel::Create(this, std::move(platform_handle), io_task_runner_,
                           process_error_callback);
-#endif  // !defined(OS_MACOSX) && !defined(OS_NACL)
+#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
@@ -443,7 +411,7 @@
 }
 
 void NodeController::ConnectToParentOnIOThread(
-    ConnectionParams connection_params) {
+    ScopedPlatformHandle platform_handle) {
   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
 
   {
@@ -454,7 +422,7 @@
     // 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(connection_params), io_task_runner_,
+        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.
@@ -466,37 +434,6 @@
   bootstrap_parent_channel_->Start();
 }
 
-void NodeController::ConnectToPeerOnIOThread(ConnectionParams connection_params,
-                                             ports::NodeName token,
-                                             ports::PortRef port,
-                                             const std::string& peer_token) {
-  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
-
-  scoped_refptr<NodeChannel> channel = NodeChannel::Create(
-      this, std::move(connection_params), io_task_runner_, {});
-  peer_connections_.insert(
-      {token, PeerConnection{channel, port, peer_token}});
-  peers_by_token_.insert({peer_token, token});
-
-  channel->SetRemoteNodeName(token);
-  channel->Start();
-
-  channel->AcceptPeer(name_, token, port.name());
-}
-
-void NodeController::ClosePeerConnectionOnIOThread(
-    const std::string& peer_token) {
-  RequestContext request_context(RequestContext::Source::SYSTEM);
-  auto peer = peers_by_token_.find(peer_token);
-  // The connection may already be closed.
-  if (peer == peers_by_token_.end())
-    return;
-
-  // |peer| may be removed so make a copy of |name|.
-  ports::NodeName name = peer->second;
-  DropPeer(name, nullptr);
-}
-
 scoped_refptr<NodeChannel> NodeController::GetPeerChannel(
     const ports::NodeName& name) {
   base::AutoLock lock(peers_lock_);
@@ -622,18 +559,16 @@
     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)
-    CancelPendingPortMerges();
+  if (is_parent) {
+    base::AutoLock lock(pending_port_merges_lock_);
+    reject_pending_merges_ = true;
 
-  auto peer = peer_connections_.find(name);
-  if (peer != peer_connections_.end()) {
-    peers_by_token_.erase(peer->second.peer_token);
-    ports_to_close.push_back(peer->second.local_port);
-    peer_connections_.erase(peer);
+    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)
@@ -717,50 +652,20 @@
 }
 
 void NodeController::AcceptIncomingMessages() {
-  // This is an impactically large value which should never be reached in
-  // practice. See the CHECK below for usage.
-  constexpr size_t kMaxAcceptedMessages = 1000000;
+  {
+    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_);
 
-  size_t num_messages_accepted = 0;
-  while (incoming_messages_flag_) {
-    // TODO: We may need to be more careful to avoid starving the rest of the
-    // thread here. Revisit this if it turns out to be a problem. One
-    // alternative would be to schedule a task to continue pumping messages
-    // after flushing once.
-
-    messages_lock_.Acquire();
-    if (incoming_messages_.empty()) {
-      messages_lock_.Release();
-      break;
+      while (!messages.empty()) {
+        node_->AcceptMessage(std::move(messages.front()));
+        messages.pop();
+      }
     }
-
-    // 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_);
-    incoming_messages_flag_.Set(false);
-    messages_lock_.Release();
-
-    num_messages_accepted += messages.size();
-    while (!messages.empty()) {
-      node_->AcceptMessage(std::move(messages.front()));
-      messages.pop();
-    }
-
-    // This is effectively a safeguard against potential bugs which might lead
-    // to runaway message cycles. If any such cycles arise, we'll start seeing
-    // crash reports from this location.
-    CHECK_LE(num_messages_accepted, kMaxAcceptedMessages);
-  }
-
-  if (num_messages_accepted >= 4) {
-    // Note: We avoid logging this histogram for the vast majority of cases.
-    // See https://crbug.com/685763 for more context.
-    UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.MessagesAcceptedPerEvent",
-                                static_cast<int32_t>(num_messages_accepted),
-                                1 /* min */,
-                                500 /* max */,
-                                50 /* bucket count */);
   }
 
   AttemptShutdownIfRequested();
@@ -807,7 +712,6 @@
     peers_.clear();
     pending_children_.clear();
     pending_peer_messages_.clear();
-    peer_connections_.clear();
   }
 
   for (const auto& peer : all_peers)
@@ -842,7 +746,6 @@
         !incoming_messages_task_posted_;
     incoming_messages_task_posted_ |= schedule_pump_task;
     incoming_messages_.emplace(std::move(message));
-    incoming_messages_flag_.Set(true);
   } else {
     SendPeerMessage(node, std::move(message));
   }
@@ -993,10 +896,9 @@
   }
 
   PlatformChannelPair broker_channel;
-  ConnectionParams connection_params(broker_channel.PassServerHandle());
-  scoped_refptr<NodeChannel> client =
-      NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
-                          ProcessErrorCallback());
+  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
@@ -1072,9 +974,8 @@
     broker = parent;
   } else {
     DCHECK(broker_channel.is_valid());
-    broker =
-        NodeChannel::Create(this, ConnectionParams(std::move(broker_channel)),
-                            io_task_runner_, ProcessErrorCallback());
+    broker = NodeChannel::Create(this, std::move(broker_channel),
+                                 io_task_runner_, ProcessErrorCallback());
     AddPeer(broker_name, broker, true /* start_channel */);
   }
 
@@ -1195,15 +1096,15 @@
   if (!channel_handle.is_valid()) {
     node_->LostConnectionToNode(name);
 
-    DVLOG(1) << "Could not be introduced to peer " << 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, ConnectionParams(std::move(channel_handle)),
-                          io_task_runner_, ProcessErrorCallback());
+      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 */);
@@ -1318,46 +1219,6 @@
 }
 #endif
 
-void NodeController::OnAcceptPeer(const ports::NodeName& from_node,
-                                  const ports::NodeName& token,
-                                  const ports::NodeName& peer_name,
-                                  const ports::PortName& port_name) {
-  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
-
-  auto it = peer_connections_.find(from_node);
-  if (it == peer_connections_.end()) {
-    DLOG(ERROR) << "Received unexpected AcceptPeer message from " << from_node;
-    DropPeer(from_node, nullptr);
-    return;
-  }
-
-  scoped_refptr<NodeChannel> channel = std::move(it->second.channel);
-  ports::PortRef local_port = it->second.local_port;
-  std::string peer_token = std::move(it->second.peer_token);
-  peer_connections_.erase(it);
-  DCHECK(channel);
-
-  // If the peer connection is a self connection (which is used in tests),
-  // drop the channel to it and skip straight to merging the ports.
-  if (name_ == peer_name) {
-    peers_by_token_.erase(peer_token);
-  } else {
-    peers_by_token_[peer_token] = peer_name;
-    peer_connections_.insert(
-        {peer_name, PeerConnection{nullptr, local_port, peer_token}});
-    DVLOG(1) << "Node " << name_ << " accepted peer " << peer_name;
-
-    AddPeer(peer_name, channel, false /* start_channel */);
-  }
-
-  // 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() < port_name) {
-    node()->MergePorts(local_port, peer_name, port_name);
-  }
-}
-
 void NodeController::OnChannelError(const ports::NodeName& from_node,
                                     NodeChannel* channel) {
   if (io_task_runner_->RunsTasksOnCurrentThread()) {
@@ -1387,21 +1248,6 @@
 }
 #endif
 
-void NodeController::CancelPendingPortMerges() {
-  std::vector<ports::PortRef> ports_to_close;
-
-  {
-    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);
-}
-
 void NodeController::DestroyOnIOThreadShutdown() {
   destroy_on_io_thread_shutdown_ = true;
 }
@@ -1415,8 +1261,7 @@
     base::AutoLock lock(shutdown_lock_);
     if (shutdown_callback_.is_null())
       return;
-    if (!node_->CanShutdownCleanly(
-          ports::Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)) {
+    if (!node_->CanShutdownCleanly(true /* allow_local_ports */)) {
       DVLOG(2) << "Unable to cleanly shut down node " << name_;
       return;
     }
@@ -1431,29 +1276,5 @@
   callback.Run();
 }
 
-NodeController::PeerConnection::PeerConnection() = default;
-
-NodeController::PeerConnection::PeerConnection(
-    const PeerConnection& other) = default;
-
-NodeController::PeerConnection::PeerConnection(
-    PeerConnection&& other) = default;
-
-NodeController::PeerConnection::PeerConnection(
-    scoped_refptr<NodeChannel> channel,
-    const ports::PortRef& local_port,
-    const std::string& peer_token)
-    : channel(std::move(channel)),
-      local_port(local_port),
-      peer_token(peer_token) {}
-
-NodeController::PeerConnection::~PeerConnection() = default;
-
-NodeController::PeerConnection& NodeController::PeerConnection::
-operator=(const PeerConnection& other) = default;
-
-NodeController::PeerConnection& NodeController::PeerConnection::
-operator=(PeerConnection&& other) = default;
-
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/system/node_controller.h b/mojo/edk/system/node_controller.h
index 46a2d61..11d5f19 100644
--- a/mojo/edk/system/node_controller.h
+++ b/mojo/edk/system/node_controller.h
@@ -73,7 +73,7 @@
 
   // Connects this node to a child node. This node will initiate a handshake.
   void ConnectToChild(base::ProcessHandle process_handle,
-                      ConnectionParams connection_params,
+                      ScopedPlatformHandle platform_handle,
                       const std::string& child_token,
                       const ProcessErrorCallback& process_error_callback);
 
@@ -81,23 +81,14 @@
   // |child_token|.
   void CloseChildPorts(const std::string& child_token);
 
-  // Close a connection to a peer associated with |peer_token|.
-  void ClosePeerConnection(const std::string& peer_token);
-
   // Connects this node to a parent node. The parent node will initiate a
   // handshake.
-  void ConnectToParent(ConnectionParams connection_params);
-
-  // Connects this node to a peer node. On success, |port| will be merged with
-  // the corresponding port in the peer node.
-  void ConnectToPeer(ConnectionParams connection_params,
-                     const ports::PortRef& port,
-                     const std::string& peer_token);
+  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,
-                       scoped_refptr<PortObserver> observer);
+                       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.
@@ -148,36 +139,12 @@
     const std::string child_token;
   };
 
-  struct PeerConnection {
-    PeerConnection();
-    PeerConnection(const PeerConnection& other);
-    PeerConnection(PeerConnection&& other);
-    PeerConnection(scoped_refptr<NodeChannel> channel,
-                   const ports::PortRef& local_port,
-                   const std::string& peer_token);
-    ~PeerConnection();
-
-    PeerConnection& operator=(const PeerConnection& other);
-    PeerConnection& operator=(PeerConnection&& other);
-
-
-    scoped_refptr<NodeChannel> channel;
-    ports::PortRef local_port;
-    std::string peer_token;
-  };
-
   void ConnectToChildOnIOThread(
       base::ProcessHandle process_handle,
-      ConnectionParams connection_params,
+      ScopedPlatformHandle platform_handle,
       ports::NodeName token,
       const ProcessErrorCallback& process_error_callback);
-  void ConnectToParentOnIOThread(ConnectionParams connection_params);
-
-  void ConnectToPeerOnIOThread(ConnectionParams connection_params,
-                               ports::NodeName token,
-                               ports::PortRef port,
-                               const std::string& peer_token);
-  void ClosePeerConnectionOnIOThread(const std::string& node_name);
+  void ConnectToParentOnIOThread(ScopedPlatformHandle platform_handle);
 
   scoped_refptr<NodeChannel> GetPeerChannel(const ports::NodeName& name);
   scoped_refptr<NodeChannel> GetParentChannel();
@@ -239,21 +206,12 @@
                                const ports::NodeName& source_node,
                                Channel::MessagePtr message) override;
 #endif
-  void OnAcceptPeer(const ports::NodeName& from_node,
-                    const ports::NodeName& token,
-                    const ports::NodeName& peer_name,
-                    const ports::PortName& port_name) override;
   void OnChannelError(const ports::NodeName& from_node,
                       NodeChannel* channel) override;
 #if defined(OS_MACOSX) && !defined(OS_IOS)
   MachPortRelay* GetMachPortRelay() override;
 #endif
 
-  // Cancels all pending port merges. These are merges which are supposed to
-  // be requested from the parent ASAP, and they may be cancelled if the
-  // connection to the parent is broken or never established.
-  void CancelPendingPortMerges();
-
   // 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.
@@ -328,8 +286,6 @@
   // Ensures that there is only one incoming messages task posted to the IO
   // thread.
   bool incoming_messages_task_posted_ = false;
-  // Flag to fast-path checking |incoming_messages_|.
-  AtomicFlag incoming_messages_flag_;
 
   // Guards |shutdown_callback_|.
   base::Lock shutdown_lock_;
@@ -347,19 +303,12 @@
   // Channels to children during handshake.
   NodeMap pending_children_;
 
-  using PeerNodeMap =
-      std::unordered_map<ports::NodeName, PeerConnection>;
-  PeerNodeMap peer_connections_;
-
-  // Maps from peer token to node name, pending or not.
-  std::unordered_map<std::string, ports::NodeName> peers_by_token_;
-
   // 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_MACOSX) && !defined(OS_NACL_SFI)
-  // Broker for sync shared buffer creation in children.
+#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
 
diff --git a/mojo/edk/system/platform_handle_dispatcher_unittest.cc b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
index 7a94262..ad6dc38 100644
--- a/mojo/edk/system/platform_handle_dispatcher_unittest.cc
+++ b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
@@ -28,7 +28,7 @@
 
   base::FilePath unused;
   base::ScopedFILE fp(
-      CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+      CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
   ASSERT_TRUE(fp);
   EXPECT_EQ(sizeof(kHelloWorld),
             fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get()));
@@ -70,7 +70,7 @@
 
   base::FilePath unused;
   base::ScopedFILE fp(
-      CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+      CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
   EXPECT_EQ(sizeof(kFooBar), fwrite(kFooBar, 1, sizeof(kFooBar), fp.get()));
 
   scoped_refptr<PlatformHandleDispatcher> dispatcher =
diff --git a/mojo/edk/system/ports/BUILD.gn b/mojo/edk/system/ports/BUILD.gn
index 37b2548..239b3a4 100644
--- a/mojo/edk/system/ports/BUILD.gn
+++ b/mojo/edk/system/ports/BUILD.gn
@@ -10,7 +10,6 @@
     "event.h",
     "message.cc",
     "message.h",
-    "message_filter.h",
     "message_queue.cc",
     "message_queue.h",
     "name.cc",
diff --git a/mojo/edk/system/ports/message_filter.h b/mojo/edk/system/ports/message_filter.h
deleted file mode 100644
index bf8fa21..0000000
--- a/mojo/edk/system/ports/message_filter.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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_FILTER_H_
-#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_
-
-namespace mojo {
-namespace edk {
-namespace ports {
-
-class Message;
-
-// An interface which can be implemented to filter port messages according to
-// arbitrary policy.
-class MessageFilter {
- public:
-  virtual ~MessageFilter() {}
-
-  // Returns true of |message| should be accepted by whomever is applying this
-  // filter. See MessageQueue::GetNextMessage(), for example.
-  virtual bool Match(const Message& message) = 0;
-};
-
-}  // namespace ports
-}  // namespace edk
-}  // namespace mojo
-
-#endif  // MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_
diff --git a/mojo/edk/system/ports/message_queue.cc b/mojo/edk/system/ports/message_queue.cc
index defb1b6..ef2e940 100644
--- a/mojo/edk/system/ports/message_queue.cc
+++ b/mojo/edk/system/ports/message_queue.cc
@@ -8,7 +8,6 @@
 
 #include "base/logging.h"
 #include "mojo/edk/system/ports/event.h"
-#include "mojo/edk/system/ports/message_filter.h"
 
 namespace mojo {
 namespace edk {
@@ -45,9 +44,10 @@
   return !heap_.empty() && GetSequenceNum(heap_[0]) == next_sequence_num_;
 }
 
-void MessageQueue::GetNextMessage(ScopedMessage* message,
-                                  MessageFilter* filter) {
-  if (!HasNextMessage() || (filter && !filter->Match(*heap_[0].get()))) {
+void MessageQueue::GetNextMessageIf(
+    std::function<bool(const Message&)> selector,
+    ScopedMessage* message) {
+  if (!HasNextMessage() || (selector && !selector(*heap_[0].get()))) {
     message->reset();
     return;
   }
diff --git a/mojo/edk/system/ports/message_queue.h b/mojo/edk/system/ports/message_queue.h
index d9a47ed..d90ac1a 100644
--- a/mojo/edk/system/ports/message_queue.h
+++ b/mojo/edk/system/ports/message_queue.h
@@ -22,8 +22,6 @@
 const uint64_t kInitialSequenceNum = 1;
 const uint64_t kInvalidSequenceNum = std::numeric_limits<uint64_t>::max();
 
-class MessageFilter;
-
 // 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
@@ -40,9 +38,9 @@
 
   bool HasNextMessage() const;
 
-  // Gives ownership of the message. If |filter| is non-null, the next message
-  // will only be retrieved if the filter successfully matches it.
-  void GetNextMessage(ScopedMessage* message, MessageFilter* filter);
+  // 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
diff --git a/mojo/edk/system/ports/name.cc b/mojo/edk/system/ports/name.cc
index ea17698..7088cbf 100644
--- a/mojo/edk/system/ports/name.cc
+++ b/mojo/edk/system/ports/name.cc
@@ -8,10 +8,6 @@
 namespace edk {
 namespace ports {
 
-extern const PortName kInvalidPortName = {0, 0};
-
-extern const NodeName kInvalidNodeName = {0, 0};
-
 std::ostream& operator<<(std::ostream& stream, const Name& name) {
   std::ios::fmtflags flags(stream.flags());
   stream << std::hex << std::uppercase << name.v1;
diff --git a/mojo/edk/system/ports/name.h b/mojo/edk/system/ports/name.h
index 72e41b9..1082719 100644
--- a/mojo/edk/system/ports/name.h
+++ b/mojo/edk/system/ports/name.h
@@ -40,14 +40,14 @@
   PortName(uint64_t v1, uint64_t v2) : Name(v1, v2) {}
 };
 
-extern const PortName kInvalidPortName;
+const PortName kInvalidPortName = {0, 0};
 
 struct NodeName : Name {
   NodeName() : Name(0, 0) {}
   NodeName(uint64_t v1, uint64_t v2) : Name(v1, v2) {}
 };
 
-extern const NodeName kInvalidNodeName;
+const NodeName kInvalidNodeName = {0, 0};
 
 }  // namespace ports
 }  // namespace edk
diff --git a/mojo/edk/system/ports/node.cc b/mojo/edk/system/ports/node.cc
index 9c6f1fd..128ecdf 100644
--- a/mojo/edk/system/ports/node.cc
+++ b/mojo/edk/system/ports/node.cc
@@ -8,7 +8,6 @@
 
 #include <utility>
 
-#include "base/atomicops.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
@@ -65,10 +64,10 @@
     DLOG(WARNING) << "Unclean shutdown for node " << name_;
 }
 
-bool Node::CanShutdownCleanly(ShutdownPolicy policy) {
+bool Node::CanShutdownCleanly(bool allow_local_ports) {
   base::AutoLock ports_lock(ports_lock_);
 
-  if (policy == ShutdownPolicy::DONT_ALLOW_LOCAL_PORTS) {
+  if (!allow_local_ports) {
 #if DCHECK_IS_ON()
     for (auto entry : ports_) {
       DVLOG(2) << "Port " << entry.first << " referencing node "
@@ -79,8 +78,6 @@
     return ports_.empty();
   }
 
-  DCHECK_EQ(policy, ShutdownPolicy::ALLOW_LOCAL_PORTS);
-
   // 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.
@@ -117,7 +114,8 @@
   PortName port_name;
   delegate_->GenerateRandomPortName(&port_name);
 
-  scoped_refptr<Port> port(new Port(kInitialSequenceNum, kInitialSequenceNum));
+  scoped_refptr<Port> port = make_scoped_refptr(new Port(kInitialSequenceNum,
+                                                         kInitialSequenceNum));
   int rv = AddPortWithName(port_name, port);
   if (rv != OK)
     return rv;
@@ -169,7 +167,7 @@
 }
 
 int Node::SetUserData(const PortRef& port_ref,
-                      scoped_refptr<UserData> user_data) {
+                      const scoped_refptr<UserData>& user_data) {
   Port* port = port_ref.port();
 
   base::AutoLock lock(port->lock);
@@ -263,12 +261,16 @@
   return OK;
 }
 
-int Node::GetMessage(const PortRef& port_ref,
-                     ScopedMessage* message,
-                     MessageFilter* filter) {
+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(4) << "GetMessage for " << port_ref.name() << "@" << name_;
+  DVLOG(2) << "GetMessageIf for " << port_ref.name() << "@" << name_;
 
   Port* port = port_ref.port();
   {
@@ -284,7 +286,7 @@
     if (!CanAcceptMoreMessages(port))
       return ERROR_PORT_PEER_CLOSED;
 
-    port->message_queue.GetNextMessage(message, filter);
+    port->message_queue.GetNextMessageIf(std::move(selector), message);
   }
 
   // Allow referenced ports to trigger PortStatusChanged calls.
@@ -424,7 +426,7 @@
     ports_buf << message->ports()[i];
   }
 
-  DVLOG(4) << "AcceptMessage " << event->sequence_num
+  DVLOG(2) << "AcceptMessage " << event->sequence_num
              << " [ports=" << ports_buf.str() << "] at "
              << port_name << "@" << name_;
 #endif
@@ -504,7 +506,7 @@
            << " pointing to "
            << port->peer_port_name << "@" << port->peer_node_name;
 
-  return BeginProxying(PortRef(port_name, std::move(port)));
+  return BeginProxying(PortRef(port_name, port));
 }
 
 int Node::OnObserveProxy(const PortName& port_name,
@@ -529,6 +531,17 @@
   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;
   }
 
@@ -619,7 +632,7 @@
     port->remove_proxy_on_last_message = true;
     port->last_sequence_num_to_receive = last_sequence_num;
   }
-  TryRemoveProxy(PortRef(port_name, std::move(port)));
+  TryRemoveProxy(PortRef(port_name, port));
   return OK;
 }
 
@@ -696,7 +709,7 @@
                          forwarded_data));
 
   if (notify_delegate) {
-    PortRef port_ref(port_name, std::move(port));
+    PortRef port_ref(port_name, port);
     delegate_->PortStatusChanged(port_ref);
   }
   return OK;
@@ -771,10 +784,11 @@
   return ERROR_PORT_STATE_UNEXPECTED;
 }
 
-int Node::AddPortWithName(const PortName& port_name, scoped_refptr<Port> port) {
+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, std::move(port))).second)
+  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_;
@@ -803,11 +817,6 @@
   if (iter == ports_.end())
     return nullptr;
 
-#if defined(OS_ANDROID) && defined(ARCH_CPU_ARM64)
-  // Workaround for https://crbug.com/665869.
-  base::subtle::MemoryBarrier();
-#endif
-
   return iter->second;
 }
 
@@ -994,10 +1003,10 @@
            << 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 GetMessage).
+  // new port finds its way to the consumer (see GetMessageIf).
   port->message_queue.set_signalable(false);
 
-  int rv = AddPortWithName(port_name, std::move(port));
+  int rv = AddPortWithName(port_name, port);
   if (rv != OK)
     return rv;
 
@@ -1078,7 +1087,7 @@
   }
 
 #if DCHECK_IS_ON()
-  DVLOG(4) << "Sending message "
+  DVLOG(2) << "Sending message "
            << GetEventData<UserEventData>(*message)->sequence_num
            << " [ports=" << ports_buf.str() << "]"
            << " from " << port_name << "@" << name_
@@ -1176,7 +1185,7 @@
 
   for (;;) {
     ScopedMessage message;
-    port->message_queue.GetNextMessage(&message, nullptr);
+    port->message_queue.GetNextMessageIf(nullptr, &message);
     if (!message)
       break;
 
diff --git a/mojo/edk/system/ports/node.h b/mojo/edk/system/ports/node.h
index 55b8d27..3aeadca 100644
--- a/mojo/edk/system/ports/node.h
+++ b/mojo/edk/system/ports/node.h
@@ -44,16 +44,10 @@
   bool peer_closed;
 };
 
-class MessageFilter;
 class NodeDelegate;
 
 class Node {
  public:
-  enum class ShutdownPolicy {
-    DONT_ALLOW_LOCAL_PORTS,
-    ALLOW_LOCAL_PORTS,
-  };
-
   // Does not take ownership of the delegate.
   Node(const NodeName& name, NodeDelegate* delegate);
   ~Node();
@@ -65,11 +59,9 @@
   // method may be called again after AcceptMessage to check if the Node is now
   // ready to be destroyed.
   //
-  // If |policy| is set to |ShutdownPolicy::ALLOW_LOCAL_PORTS|, this will return
-  // |true| even if some ports remain alive, as long as none of them are proxies
-  // to another node.
-  bool CanShutdownCleanly(
-      ShutdownPolicy policy = ShutdownPolicy::DONT_ALLOW_LOCAL_PORTS);
+  // 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);
@@ -90,7 +82,8 @@
   int CreatePortPair(PortRef* port0_ref, PortRef* port1_ref);
 
   // User data associated with the port.
-  int SetUserData(const PortRef& port_ref, scoped_refptr<UserData> user_data);
+  int SetUserData(const PortRef& port_ref,
+                  const scoped_refptr<UserData>& user_data);
   int GetUserData(const PortRef& port_ref,
                   scoped_refptr<UserData>* user_data);
 
@@ -107,15 +100,15 @@
   // 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.
-  //
-  // If |filter| is non-null, the next available message is returned only if it
-  // is matched by the filter. If the provided filter does not match the next
-  // available message, GetMessage() behaves as if there is no message
-  // available. Ownership of |filter| is not taken, and it must outlive the
-  // extent of this call.
-  int GetMessage(const PortRef& port_ref,
-                 ScopedMessage* message,
-                 MessageFilter* filter);
+  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
@@ -163,7 +156,8 @@
   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, scoped_refptr<Port> port);
+  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);
diff --git a/mojo/edk/system/ports/port_ref.cc b/mojo/edk/system/ports/port_ref.cc
index 675754d..bd59629 100644
--- a/mojo/edk/system/ports/port_ref.cc
+++ b/mojo/edk/system/ports/port_ref.cc
@@ -16,8 +16,9 @@
 PortRef::PortRef() {
 }
 
-PortRef::PortRef(const PortName& name, scoped_refptr<Port> port)
-    : name_(name), port_(std::move(port)) {}
+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_) {
diff --git a/mojo/edk/system/ports/port_ref.h b/mojo/edk/system/ports/port_ref.h
index 59036c3..9af4a8d 100644
--- a/mojo/edk/system/ports/port_ref.h
+++ b/mojo/edk/system/ports/port_ref.h
@@ -19,7 +19,7 @@
  public:
   ~PortRef();
   PortRef();
-  PortRef(const PortName& name, scoped_refptr<Port> port);
+  PortRef(const PortName& name, const scoped_refptr<Port>& port);
 
   PortRef(const PortRef& other);
   PortRef& operator=(const PortRef& other);
diff --git a/mojo/edk/system/ports/ports_unittest.cc b/mojo/edk/system/ports/ports_unittest.cc
index cb48b3e..200e72b 100644
--- a/mojo/edk/system/ports/ports_unittest.cc
+++ b/mojo/edk/system/ports/ports_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -10,18 +9,9 @@
 #include <map>
 #include <queue>
 #include <sstream>
-#include <utility>
 
-#include "base/bind.h"
-#include "base/callback.h"
 #include "base/logging.h"
-#include "base/memory/ref_counted.h"
 #include "base/rand_util.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/stringprintf.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
 #include "mojo/edk/system/ports/event.h"
 #include "mojo/edk/system/ports/node.h"
 #include "mojo/edk/system/ports/node_delegate.h"
@@ -34,8 +24,24 @@
 
 namespace {
 
-bool MessageEquals(const ScopedMessage& message, const base::StringPiece& s) {
-  return !strcmp(static_cast<const char*>(message->payload_bytes()), s.data());
+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 {
@@ -65,140 +71,132 @@
   }
 };
 
-class TestNode;
+struct Task {
+  Task(NodeName node_name, ScopedMessage message)
+      : node_name(node_name),
+        message(std::move(message)),
+        priority(base::RandUint64()) {
+  }
 
-class MessageRouter {
- public:
-  virtual ~MessageRouter() {}
-
-  virtual void GeneratePortName(PortName* name) = 0;
-  virtual void ForwardMessage(TestNode* from_node,
-                              const NodeName& node_name,
-                              ScopedMessage message) = 0;
-  virtual void BroadcastMessage(TestNode* from_node, ScopedMessage message) = 0;
+  NodeName node_name;
+  ScopedMessage message;
+  uint64_t priority;
 };
 
-class TestNode : public NodeDelegate {
+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 TestNode(uint64_t id)
-      : node_name_(id, 1),
-        node_(node_name_, this),
-        node_thread_(base::StringPrintf("Node %" PRIu64 " thread", id)),
-        messages_available_event_(
-            base::WaitableEvent::ResetPolicy::AUTOMATIC,
-            base::WaitableEvent::InitialState::NOT_SIGNALED),
-        idle_event_(
-            base::WaitableEvent::ResetPolicy::MANUAL,
-            base::WaitableEvent::InitialState::SIGNALED) {
+  explicit TestNodeDelegate(const NodeName& node_name)
+      : node_name_(node_name),
+        drop_messages_(false),
+        read_messages_(true),
+        save_messages_(false) {
   }
 
-  ~TestNode() override {
-    StopWhenIdle();
-    node_thread_.Stop();
-  }
-
-  const NodeName& name() const { return node_name_; }
-
-  // NOTE: Node is thread-safe.
-  Node& node() { return node_; }
-
-  base::WaitableEvent& idle_event() { return idle_event_; }
-
-  bool IsIdle() {
-    base::AutoLock lock(lock_);
-    return started_ && !dispatching_ &&
-        (incoming_messages_.empty() || (block_on_event_ && blocked_));
-  }
-
-  void BlockOnEvent(EventType type) {
-    base::AutoLock lock(lock_);
-    blocked_event_type_ = type;
-    block_on_event_ = true;
-  }
-
-  void Unblock() {
-    base::AutoLock lock(lock_);
-    block_on_event_ = false;
-    messages_available_event_.Signal();
-  }
-
-  void Start(MessageRouter* router) {
-    router_ = router;
-    node_thread_.Start();
-    node_thread_.task_runner()->PostTask(
-        FROM_HERE,
-        base::Bind(&TestNode::ProcessMessages, base::Unretained(this)));
-  }
-
-  void StopWhenIdle() {
-    base::AutoLock lock(lock_);
-    should_quit_ = true;
-    messages_available_event_.Signal();
-  }
-
-  void WakeUp() { messages_available_event_.Signal(); }
-
-  int SendStringMessage(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(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(const PortRef& port,
-                                const std::string& s,
-                                const PortRef& sent_port) {
-    return SendStringMessageWithPort(port, s, sent_port.name());
-  }
-
-  void set_drop_messages(bool value) {
-    base::AutoLock lock(lock_);
-    drop_messages_ = value;
-  }
-
-  void set_save_messages(bool value) {
-    base::AutoLock lock(lock_);
-    save_messages_ = value;
-  }
-
-  bool ReadMessage(const PortRef& port, ScopedMessage* message) {
-    return node_.GetMessage(port, message, nullptr) == OK && *message;
-  }
+  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) {
-    base::AutoLock lock(lock_);
     if (saved_messages_.empty()) {
       message->reset();
       return false;
     }
-    std::swap(*message, saved_messages_.front());
+    *message = std::move(saved_messages_.front());
     saved_messages_.pop();
     return true;
   }
 
-  void EnqueueMessage(ScopedMessage message) {
-    idle_event_.Reset();
-
-    // NOTE: This may be called from ForwardMessage and thus must not reenter
-    // |node_|.
-    base::AutoLock lock(lock_);
-    incoming_messages_.emplace(std::move(message));
-    messages_available_event_.Signal();
-  }
-
   void GenerateRandomPortName(PortName* port_name) override {
-    DCHECK(router_);
-    router_->GeneratePortName(port_name);
+    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 {
@@ -207,570 +205,489 @@
 
   void ForwardMessage(const NodeName& node_name,
                       ScopedMessage message) override {
-    {
-      base::AutoLock lock(lock_);
-      if (drop_messages_) {
-        DVLOG(1) << "Dropping ForwardMessage from node "
-                 << node_name_ << " to " << node_name;
-
-        base::AutoUnlock unlock(lock_);
-        ClosePortsInMessage(message.get());
-        return;
-      }
+    if (drop_messages_) {
+      DVLOG(1) << "Dropping ForwardMessage from node "
+               << node_name_ << " to " << node_name;
+      ClosePortsInMessage(GetNode(node_name), message.get());
+      return;
     }
-
-    DCHECK(router_);
     DVLOG(1) << "ForwardMessage from node "
              << node_name_ << " to " << node_name;
-    router_->ForwardMessage(this, node_name, std::move(message));
+    task_queue.push(new Task(node_name, std::move(message)));
   }
 
   void BroadcastMessage(ScopedMessage message) override {
-    router_->BroadcastMessage(this, std::move(message));
+    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 {
-    // The port may be closed, in which case we ignore the notification.
-    base::AutoLock lock(lock_);
-    if (!save_messages_)
+    DVLOG(1) << "PortStatusChanged for " << port.name() << "@" << node_name_;
+    if (!read_messages_)
       return;
-
+    Node* node = GetNode(node_name_);
     for (;;) {
       ScopedMessage message;
-      {
-        base::AutoUnlock unlock(lock_);
-        if (!ReadMessage(port, &message))
-          break;
+      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);
+        }
       }
-
-      saved_messages_.emplace(std::move(message));
-    }
-  }
-
-  void ClosePortsInMessage(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));
     }
   }
 
  private:
-  void ProcessMessages() {
-    for (;;) {
-      messages_available_event_.Wait();
-
-      base::AutoLock lock(lock_);
-
-      if (should_quit_)
-        return;
-
-      dispatching_ = true;
-      while (!incoming_messages_.empty()) {
-        if (block_on_event_ &&
-            GetEventHeader(*incoming_messages_.front())->type ==
-                blocked_event_type_) {
-          blocked_ = true;
-          // Go idle if we hit a blocked event type.
-          break;
-        } else {
-          blocked_ = false;
-        }
-        ScopedMessage message = std::move(incoming_messages_.front());
-        incoming_messages_.pop();
-
-        // NOTE: AcceptMessage() can re-enter this object to call any of the
-        // NodeDelegate interface methods.
-        base::AutoUnlock unlock(lock_);
-        node_.AcceptMessage(std::move(message));
-      }
-
-      dispatching_ = false;
-      started_ = true;
-      idle_event_.Signal();
-    };
+  void SaveMessage(ScopedMessage message) {
+    saved_messages_.emplace(std::move(message));
   }
 
-  const NodeName node_name_;
-  Node node_;
-  MessageRouter* router_ = nullptr;
-
-  base::Thread node_thread_;
-  base::WaitableEvent messages_available_event_;
-  base::WaitableEvent idle_event_;
-
-  // Guards fields below.
-  base::Lock lock_;
-  bool started_ = false;
-  bool dispatching_ = false;
-  bool should_quit_ = false;
-  bool drop_messages_ = false;
-  bool save_messages_ = false;
-  bool blocked_ = false;
-  bool block_on_event_ = false;
-  EventType blocked_event_type_;
-  std::queue<ScopedMessage> incoming_messages_;
   std::queue<ScopedMessage> saved_messages_;
+  NodeName node_name_;
+  bool drop_messages_;
+  bool read_messages_;
+  bool save_messages_;
 };
 
-class PortsTest : public testing::Test, public MessageRouter {
+class PortsTest : public testing::Test {
  public:
-  void AddNode(TestNode* node) {
-    {
-      base::AutoLock lock(lock_);
-      nodes_[node->name()] = node;
-    }
-    node->Start(this);
+  void SetUp() override {
+    DiscardPendingTasks();
+    SetNode(NodeName(0, 1), nullptr);
+    SetNode(NodeName(1, 1), nullptr);
+    SetNode(NodeName(2, 1), nullptr);
   }
-
-  void RemoveNode(TestNode* node) {
-    {
-      base::AutoLock lock(lock_);
-      nodes_.erase(node->name());
-    }
-
-    for (const auto& entry : nodes_)
-      entry.second->node().LostConnectionToNode(node->name());
-  }
-
-  // Waits until all known Nodes are idle. Message forwarding and processing
-  // is handled in such a way that idleness is a stable state: once all nodes in
-  // the system are idle, they will remain idle until the test explicitly
-  // initiates some further event (e.g. sending a message, closing a port, or
-  // removing a Node).
-  void WaitForIdle() {
-    for (;;) {
-      base::AutoLock global_lock(global_lock_);
-      bool all_nodes_idle = true;
-      for (const auto& entry : nodes_) {
-        if (!entry.second->IsIdle())
-          all_nodes_idle = false;
-        entry.second->WakeUp();
-      }
-      if (all_nodes_idle)
-        return;
-
-      // Wait for any Node to signal that it's idle.
-      base::AutoUnlock global_unlock(global_lock_);
-      std::vector<base::WaitableEvent*> events;
-      for (const auto& entry : nodes_)
-        events.push_back(&entry.second->idle_event());
-      base::WaitableEvent::WaitMany(events.data(), events.size());
-    }
-  }
-
-  void CreatePortPair(TestNode* node0,
-                      PortRef* port0,
-                      TestNode* node1,
-                      PortRef* port1) {
-    if (node0 == node1) {
-      EXPECT_EQ(OK, node0->node().CreatePortPair(port0, port1));
-    } else {
-      EXPECT_EQ(OK, node0->node().CreateUninitializedPort(port0));
-      EXPECT_EQ(OK, node1->node().CreateUninitializedPort(port1));
-      EXPECT_EQ(OK, node0->node().InitializePort(*port0, node1->name(),
-                                                 port1->name()));
-      EXPECT_EQ(OK, node1->node().InitializePort(*port1, node0->name(),
-                                                 port0->name()));
-    }
-  }
-
- private:
-  // MessageRouter:
-  void GeneratePortName(PortName* name) override {
-    base::AutoLock lock(lock_);
-    name->v1 = next_port_id_++;
-    name->v2 = 0;
-  }
-
-  void ForwardMessage(TestNode* from_node,
-                      const NodeName& node_name,
-                      ScopedMessage message) override {
-    base::AutoLock global_lock(global_lock_);
-    base::AutoLock lock(lock_);
-    // Drop messages from nodes that have been removed.
-    if (nodes_.find(from_node->name()) == nodes_.end()) {
-      from_node->ClosePortsInMessage(message.get());
-      return;
-    }
-
-    auto it = nodes_.find(node_name);
-    if (it == nodes_.end()) {
-      DVLOG(1) << "Node not found: " << node_name;
-      return;
-    }
-
-    it->second->EnqueueMessage(std::move(message));
-  }
-
-  void BroadcastMessage(TestNode* from_node, ScopedMessage message) override {
-    base::AutoLock global_lock(global_lock_);
-    base::AutoLock lock(lock_);
-
-    // Drop messages from nodes that have been removed.
-    if (nodes_.find(from_node->name()) == nodes_.end())
-      return;
-
-    for (const auto& entry : nodes_) {
-      TestNode* node = entry.second;
-      // Broadcast doesn't deliver to the local node.
-      if (node == from_node)
-        continue;
-
-      // NOTE: We only need to support broadcast of events. Events 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->EnqueueMessage(std::move(new_message));
-    }
-  }
-
-  base::MessageLoop message_loop_;
-
-  // Acquired before any operation which makes a Node busy, and before testing
-  // if all nodes are idle.
-  base::Lock global_lock_;
-
-  base::Lock lock_;
-  uint64_t next_port_id_ = 1;
-  std::map<NodeName, TestNode*> nodes_;
 };
 
 }  // namespace
 
 TEST_F(PortsTest, Basic1) {
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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;
-  CreatePortPair(&node0, &x0, &node1, &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.node().CreatePortPair(&a0, &a1));
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", a1));
-  EXPECT_EQ(OK, node0.node().ClosePort(a0));
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", a1));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(x0));
-  EXPECT_EQ(OK, node1.node().ClosePort(x1));
+  EXPECT_EQ(OK, node0.ClosePort(a0));
 
-  WaitForIdle();
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
 
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, Basic2) {
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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;
-  CreatePortPair(&node0, &x0, &node1, &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.node().CreatePortPair(&b0, &b1));
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", b1));
-  EXPECT_EQ(OK, node0.SendStringMessage(b0, "hello again"));
+  EXPECT_EQ(OK, node0.CreatePortPair(&b0, &b1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", b1));
+  EXPECT_EQ(OK, SendStringMessage(&node0, b0, "hello again"));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(b0));
+  // This may cause a SendMessage(b1) failure.
+  EXPECT_EQ(OK, node0.ClosePort(b0));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(x0));
-  EXPECT_EQ(OK, node1.node().ClosePort(x1));
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, Basic3) {
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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;
-  CreatePortPair(&node0, &x0, &node1, &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.node().CreatePortPair(&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"));
 
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", a1));
-  EXPECT_EQ(OK, node0.SendStringMessage(a0, "hello again"));
-
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a0));
+  // Transfer a0 as well.
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "foo", a0));
 
   PortRef b0, b1;
-  EXPECT_EQ(OK, node0.node().CreatePortPair(&b0, &b1));
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "bar", b1));
-  EXPECT_EQ(OK, node0.SendStringMessage(b0, "baz"));
+  EXPECT_EQ(OK, node0.CreatePortPair(&b0, &b1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "bar", b1));
+  EXPECT_EQ(OK, SendStringMessage(&node0, b0, "baz"));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(b0));
+  // This may cause a SendMessage(b1) failure.
+  EXPECT_EQ(OK, node0.ClosePort(b0));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(x0));
-  EXPECT_EQ(OK, node1.node().ClosePort(x1));
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, LostConnectionToNode1) {
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
 
-  TestNode node1(1);
-  AddNode(&node1);
-  node1.set_drop_messages(true);
+  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;
-  CreatePortPair(&node0, &x0, &node1, &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 to node1 and simulate a lost connection to node1.
+  // 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.node().CreatePortPair(&a0, &a1));
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a1));
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "foo", a1));
 
-  WaitForIdle();
+  PumpTasks();
 
-  RemoveNode(&node1);
+  EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_EQ(OK, node0.node().ClosePort(a0));
-  EXPECT_EQ(OK, node0.node().ClosePort(x0));
-  EXPECT_EQ(OK, node1.node().ClosePort(x1));
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, LostConnectionToNode2) {
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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;
-  CreatePortPair(&node0, &x0, &node1, &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.node().CreatePortPair(&a0, &a1));
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "take a1", a1));
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "take a1", a1));
 
-  WaitForIdle();
+  PumpTasks();
 
-  node1.set_drop_messages(true);
+  node1_delegate.set_drop_messages(true);
 
-  RemoveNode(&node1);
+  EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
 
-  WaitForIdle();
+  PumpTasks();
 
-  // a0 should have eventually detected peer closure after node loss.
   ScopedMessage message;
-  EXPECT_EQ(ERROR_PORT_PEER_CLOSED,
-            node0.node().GetMessage(a0, &message, nullptr));
+  EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.GetMessage(a0, &message));
   EXPECT_FALSE(message);
 
-  EXPECT_EQ(OK, node0.node().ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(a0));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(x0));
+  EXPECT_EQ(OK, node0.ClosePort(x0));
 
-  EXPECT_EQ(OK, node1.node().GetMessage(x1, &message, nullptr));
+  EXPECT_EQ(OK, node1.GetMessage(x1, &message));
   EXPECT_TRUE(message);
-  node1.ClosePortsInMessage(message.get());
+  ClosePortsInMessage(&node1, message.get());
 
-  EXPECT_EQ(OK, node1.node().ClosePort(x1));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  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.
 
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
 
-  TestNode node2(2);
-  AddNode(&node2);
+  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;
-  CreatePortPair(&node0, &A, &node1, &B);
-  CreatePortPair(&node1, &C, &node2, &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.node().CreatePortPair(&E, &F));
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, ".", F));
+  EXPECT_EQ(OK, node0.CreatePortPair(&E, &F));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, ".", F));
 
-  WaitForIdle();
+  PumpTasks();
 
   ScopedMessage message;
-  ASSERT_TRUE(node1.ReadMessage(B, &message));
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
   ASSERT_EQ(1u, message->num_ports());
 
-  EXPECT_EQ(OK, node1.node().GetPort(message->ports()[0], &F));
+  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.
 
-  // Make sure node2 stops processing events when it encounters an ObserveProxy.
-  node2.BlockOnEvent(EventType::kObserveProxy);
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node1, C, ".", F));
 
-  EXPECT_EQ(OK, node1.SendStringMessageWithPort(C, ".", F));
-  WaitForIdle();
+  node_map[2] = nullptr;
+  EXPECT_EQ(OK, node1.LostConnectionToNode(node2_name));
 
-  // Simulate node 1 and 2 disconnecting.
-  EXPECT_EQ(OK, node1.node().LostConnectionToNode(node2.name()));
-
-  // Let node2 continue processing events and wait for everyone to go idle.
-  node2.Unblock();
-  WaitForIdle();
+  PumpTasks();
 
   // Port F should be gone.
-  EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.node().GetPort(F.name(), &F));
+  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.node().GetStatus(E, &status));
+  EXPECT_EQ(OK, node0.GetStatus(E, &status));
   EXPECT_TRUE(status.peer_closed);
 
-  EXPECT_EQ(OK, node0.node().ClosePort(A));
-  EXPECT_EQ(OK, node1.node().ClosePort(B));
-  EXPECT_EQ(OK, node1.node().ClosePort(C));
-  EXPECT_EQ(OK, node0.node().ClosePort(E));
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(B));
+  EXPECT_EQ(OK, node1.ClosePort(C));
+  EXPECT_EQ(OK, node0.ClosePort(E));
 
-  WaitForIdle();
-
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  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.
 
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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;
-  CreatePortPair(&node0, &A, &node1, &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.node().CreatePortPair(&C, &D));
+  EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, ".", D));
 
-  // Send D but block node0 on an ObserveProxy event.
-  node0.BlockOnEvent(EventType::kObserveProxy);
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, ".", D));
-
-  // node0 won't collapse the proxy but node1 will receive the message before
-  // going idle.
-  WaitForIdle();
+  // Pump tasks until the start of port collapse for port D, which should become
+  // a proxy.
+  PumpUntilTask(EventType::kObserveProxy);
 
   ScopedMessage message;
-  ASSERT_TRUE(node1.ReadMessage(B, &message));
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
   ASSERT_EQ(1u, message->num_ports());
+
   PortRef E;
-  EXPECT_EQ(OK, node1.node().GetPort(message->ports()[0], &E));
+  EXPECT_EQ(OK, node1.GetPort(message->ports()[0], &E));
 
-  RemoveNode(&node1);
-
-  node0.Unblock();
-  WaitForIdle();
+  EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
+  PumpTasks();
 
   // Port C should have detected peer closure.
   PortStatus status;
-  EXPECT_EQ(OK, node0.node().GetStatus(C, &status));
+  EXPECT_EQ(OK, node0.GetStatus(C, &status));
   EXPECT_TRUE(status.peer_closed);
 
-  EXPECT_EQ(OK, node0.node().ClosePort(A));
-  EXPECT_EQ(OK, node1.node().ClosePort(B));
-  EXPECT_EQ(OK, node0.node().ClosePort(C));
-  EXPECT_EQ(OK, node1.node().ClosePort(E));
+  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.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, GetMessage1) {
-  TestNode node(0);
-  AddNode(&node);
+  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, node.node().CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
 
   ScopedMessage message;
-  EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr));
+  EXPECT_EQ(OK, node0.GetMessage(a0, &message));
   EXPECT_FALSE(message);
 
-  EXPECT_EQ(OK, node.node().ClosePort(a1));
+  EXPECT_EQ(OK, node0.ClosePort(a1));
 
-  WaitForIdle();
-
-  EXPECT_EQ(ERROR_PORT_PEER_CLOSED,
-            node.node().GetMessage(a0, &message, nullptr));
+  EXPECT_EQ(OK, node0.GetMessage(a0, &message));
   EXPECT_FALSE(message);
 
-  EXPECT_EQ(OK, node.node().ClosePort(a0));
+  PumpTasks();
 
-  WaitForIdle();
+  EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.GetMessage(a0, &message));
+  EXPECT_FALSE(message);
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, GetMessage2) {
-  TestNode node(0);
-  AddNode(&node);
+  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, node.node().CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
 
-  EXPECT_EQ(OK, node.SendStringMessage(a1, "1"));
+  EXPECT_EQ(OK, SendStringMessage(&node0, a1, "1"));
 
   ScopedMessage message;
-  EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr));
+  EXPECT_EQ(OK, node0.GetMessage(a0, &message));
 
   ASSERT_TRUE(message);
-  EXPECT_TRUE(MessageEquals(message, "1"));
+  EXPECT_EQ(0, strcmp("1", ToString(message)));
 
-  EXPECT_EQ(OK, node.node().ClosePort(a0));
-  EXPECT_EQ(OK, node.node().ClosePort(a1));
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(a1));
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, GetMessage3) {
-  TestNode node(0);
-  AddNode(&node);
+  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, node.node().CreatePortPair(&a0, &a1));
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
 
   const char* kStrings[] = {
     "1",
@@ -779,305 +696,371 @@
   };
 
   for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i)
-    EXPECT_EQ(OK, node.SendStringMessage(a1, kStrings[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, node.node().GetMessage(a0, &message, nullptr));
+    EXPECT_EQ(OK, node0.GetMessage(a0, &message));
     ASSERT_TRUE(message);
-    EXPECT_TRUE(MessageEquals(message, kStrings[i]));
+    EXPECT_EQ(0, strcmp(kStrings[i], ToString(message)));
+    DVLOG(1) << "got " << kStrings[i];
   }
 
-  EXPECT_EQ(OK, node.node().ClosePort(a0));
-  EXPECT_EQ(OK, node.node().ClosePort(a1));
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(a1));
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, Delegation1) {
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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;
-  CreatePortPair(&node0, &x0, &node1, &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.node().CreatePortPair(&a0, &a1));
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "a1", a1));
-  WaitForIdle();
+  EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "a1", a1));
+
+  PumpTasks();
 
   ScopedMessage message;
-  ASSERT_TRUE(node1.ReadMessage(x1, &message));
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+
   ASSERT_EQ(1u, message->num_ports());
-  EXPECT_TRUE(MessageEquals(message, "a1"));
 
   // This is "a1" from the point of view of node1.
   PortName a2_name = message->ports()[0];
-  EXPECT_EQ(OK, node1.SendStringMessageWithPort(x1, "a2", a2_name));
-  EXPECT_EQ(OK, node0.SendStringMessage(a0, "hello"));
 
-  WaitForIdle();
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node1, x1, "a2", a2_name));
 
-  ASSERT_TRUE(node0.ReadMessage(x0, &message));
+  PumpTasks();
+
+  EXPECT_EQ(OK, SendStringMessage(&node0, a0, "hello"));
+
+  PumpTasks();
+
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+
   ASSERT_EQ(1u, message->num_ports());
-  EXPECT_TRUE(MessageEquals(message, "a2"));
 
   // This is "a2" from the point of view of node1.
   PortName a3_name = message->ports()[0];
 
   PortRef a3;
-  EXPECT_EQ(OK, node0.node().GetPort(a3_name, &a3));
+  EXPECT_EQ(OK, node0.GetPort(a3_name, &a3));
 
-  ASSERT_TRUE(node0.ReadMessage(a3, &message));
+  EXPECT_EQ(0, strcmp("a2", ToString(message)));
+
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+
   EXPECT_EQ(0u, message->num_ports());
-  EXPECT_TRUE(MessageEquals(message, "hello"));
+  EXPECT_EQ(0, strcmp("hello", ToString(message)));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(a0));
-  EXPECT_EQ(OK, node0.node().ClosePort(a3));
+  EXPECT_EQ(OK, node0.ClosePort(a0));
+  EXPECT_EQ(OK, node0.ClosePort(a3));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(x0));
-  EXPECT_EQ(OK, node1.node().ClosePort(x1));
+  EXPECT_EQ(OK, node0.ClosePort(x0));
+  EXPECT_EQ(OK, node1.ClosePort(x1));
 
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, Delegation2) {
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  SetNode(node0_name, &node0);
 
-  TestNode node1(1);
-  AddNode(&node1);
+  NodeName node1_name(1, 1);
+  TestNodeDelegate node1_delegate(node1_name);
+  Node node1(node1_name, &node1_delegate);
+  node_map[1] = &node1;
 
-  for (int i = 0; i < 100; ++i) {
+  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;
-    CreatePortPair(&node0, &A, &node1, &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.node().CreatePortPair(&C, &D));
+    EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
 
     PortRef E, F;
-    EXPECT_EQ(OK, node0.node().CreatePortPair(&E, &F));
-
-    node1.set_save_messages(true);
+    EXPECT_EQ(OK, node0.CreatePortPair(&E, &F));
 
     // Pass D over A to B.
-    EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, "1", D));
+    EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "1", D));
 
     // Pass F over C to D.
-    EXPECT_EQ(OK, node0.SendStringMessageWithPort(C, "1", F));
+    EXPECT_EQ(OK, SendStringMessageWithPort(&node0, C, "1", F));
 
     // This message should find its way to node1.
-    EXPECT_EQ(OK, node0.SendStringMessage(E, "hello"));
+    EXPECT_EQ(OK, SendStringMessage(&node0, E, "hello"));
 
-    WaitForIdle();
+    PumpTasks();
 
-    EXPECT_EQ(OK, node0.node().ClosePort(C));
-    EXPECT_EQ(OK, node0.node().ClosePort(E));
+    EXPECT_EQ(OK, node0.ClosePort(C));
+    EXPECT_EQ(OK, node0.ClosePort(E));
 
-    EXPECT_EQ(OK, node0.node().ClosePort(A));
-    EXPECT_EQ(OK, node1.node().ClosePort(B));
+    EXPECT_EQ(OK, node0.ClosePort(A));
+    EXPECT_EQ(OK, node1.ClosePort(B));
 
-    bool got_hello = false;
-    ScopedMessage message;
-    while (node1.GetSavedMessage(&message)) {
-      node1.ClosePortsInMessage(message.get());
-      if (MessageEquals(message, "hello")) {
-        got_hello = true;
+    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;
       }
     }
 
-    EXPECT_TRUE(got_hello);
-
-    WaitForIdle();  // Because closing ports may have generated tasks.
+    PumpTasks();  // Because ClosePort may have generated tasks.
   }
 
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, SendUninitialized) {
-  TestNode node(0);
-  AddNode(&node);
+  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, node.node().CreateUninitializedPort(&x0));
-  EXPECT_EQ(ERROR_PORT_STATE_UNEXPECTED, node.SendStringMessage(x0, "oops"));
-  EXPECT_EQ(OK, node.node().ClosePort(x0));
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  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) {
-  TestNode node(0);
-  AddNode(&node);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  node.set_save_messages(true);
+  node0_delegate.set_save_messages(true);
 
   PortRef A, B;
-  EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
 
   // Try to send A over itself.
 
   EXPECT_EQ(ERROR_PORT_CANNOT_SEND_SELF,
-            node.SendStringMessageWithPort(A, "oops", A));
+            SendStringMessageWithPort(&node0, A, "oops", A));
 
   // Try to send B over A.
 
   EXPECT_EQ(ERROR_PORT_CANNOT_SEND_PEER,
-            node.SendStringMessageWithPort(A, "nope", B));
+            SendStringMessageWithPort(&node0, A, "nope", B));
 
   // B should be closed immediately.
-  EXPECT_EQ(ERROR_PORT_UNKNOWN, node.node().GetPort(B.name(), &B));
+  EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.GetPort(B.name(), &B));
 
-  WaitForIdle();
+  PumpTasks();
 
   // There should have been no messages accepted.
   ScopedMessage message;
-  EXPECT_FALSE(node.GetSavedMessage(&message));
+  EXPECT_FALSE(node0_delegate.GetSavedMessage(&message));
 
-  EXPECT_EQ(OK, node.node().ClosePort(A));
+  EXPECT_EQ(OK, node0.ClosePort(A));
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, DontLeakUnreceivedPorts) {
-  TestNode node(0);
-  AddNode(&node);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  PortRef A, B, C, D;
-  EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node.node().CreatePortPair(&C, &D));
+  node0_delegate.set_read_messages(false);
 
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(A, "foo", D));
+  PortRef A, B;
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
 
-  EXPECT_EQ(OK, node.node().ClosePort(C));
-  EXPECT_EQ(OK, node.node().ClosePort(A));
-  EXPECT_EQ(OK, node.node().ClosePort(B));
+  PortRef C, D;
+  EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
 
-  WaitForIdle();
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "foo", D));
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  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) {
-  TestNode node(0);
-  AddNode(&node);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  PortRef A, B, C, D;
-  EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node.node().CreatePortPair(&C, &D));
+  node0_delegate.set_save_messages(true);
 
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(A, "foo", D));
+  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(node.ReadMessage(B, &message));
+  EXPECT_TRUE(node0_delegate.GetSavedMessage(&message));
   ASSERT_EQ(1u, message->num_ports());
-  EXPECT_TRUE(MessageEquals(message, "foo"));
+
   PortRef E;
-  ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E));
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
 
-  EXPECT_TRUE(
-      node.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
+  EXPECT_TRUE(node0.CanShutdownCleanly(true));
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(
-      node.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
-  EXPECT_FALSE(node.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(true));
+  EXPECT_FALSE(node0.CanShutdownCleanly(false));
 
-  EXPECT_EQ(OK, node.node().ClosePort(A));
-  EXPECT_EQ(OK, node.node().ClosePort(B));
-  EXPECT_EQ(OK, node.node().ClosePort(C));
-  EXPECT_EQ(OK, node.node().ClosePort(E));
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node0.ClosePort(B));
+  EXPECT_EQ(OK, node0.ClosePort(C));
+  EXPECT_EQ(OK, node0.ClosePort(E));
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, ProxyCollapse1) {
-  TestNode node(0);
-  AddNode(&node);
+  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, node.node().CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
 
   PortRef X, Y;
-  EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y));
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
 
   ScopedMessage message;
 
   // Send B and receive it as C.
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B));
-  ASSERT_TRUE(node.ReadMessage(Y, &message));
+  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, node.node().GetPort(message->ports()[0], &C));
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
 
   // Send C and receive it as D.
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", C));
-  ASSERT_TRUE(node.ReadMessage(Y, &message));
+  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, node.node().GetPort(message->ports()[0], &D));
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D));
 
   // Send D and receive it as E.
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", D));
-  ASSERT_TRUE(node.ReadMessage(Y, &message));
+  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, node.node().GetPort(message->ports()[0], &E));
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
 
-  EXPECT_EQ(OK, node.node().ClosePort(X));
-  EXPECT_EQ(OK, node.node().ClosePort(Y));
+  EXPECT_EQ(OK, node0.ClosePort(X));
+  EXPECT_EQ(OK, node0.ClosePort(Y));
 
-  EXPECT_EQ(OK, node.node().ClosePort(A));
-  EXPECT_EQ(OK, node.node().ClosePort(E));
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node0.ClosePort(E));
 
-  // The node should not idle until all proxies are collapsed.
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, ProxyCollapse2) {
-  TestNode node(0);
-  AddNode(&node);
+  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, node.node().CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
 
   PortRef X, Y;
-  EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y));
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
 
   ScopedMessage message;
 
-  // Send B and A to create proxies in each direction.
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B));
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", A));
+  // 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));
 
-  EXPECT_EQ(OK, node.node().ClosePort(X));
-  EXPECT_EQ(OK, node.node().ClosePort(Y));
+  // 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. The sent ports will be closed
-  // eventually as a result of Y's closure.
+  // Ensure that the proxies can collapse.
 
-  WaitForIdle();
+  EXPECT_EQ(OK, node0.ClosePort(X));
+  EXPECT_EQ(OK, node0.ClosePort(Y));
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_EQ(OK, node0.ClosePort(C));
+  EXPECT_EQ(OK, node0.ClosePort(D));
+
+  PumpTasks();
+
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, SendWithClosedPeer) {
@@ -1085,47 +1068,56 @@
   // closed, the newly created port will be aware of that peer closure, and the
   // proxy will eventually collapse.
 
-  TestNode node(0);
-  AddNode(&node);
+  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, node.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node.SendStringMessage(A, "hey"));
-  EXPECT_EQ(OK, node.node().ClosePort(A));
+  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, node.node().CreatePortPair(&X, &Y));
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B));
+  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(node.ReadMessage(Y, &message));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
   ASSERT_EQ(1u, message->num_ports());
+
   PortRef C;
-  ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C));
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
 
-  EXPECT_EQ(OK, node.node().ClosePort(X));
-  EXPECT_EQ(OK, node.node().ClosePort(Y));
+  PumpTasks();
 
-  WaitForIdle();
+  // C should receive the message originally sent to B, and it should also be
+  // aware of A's closure.
 
-  // C should have received the message originally sent to B, and it should also
-  // be aware of A's closure.
-
-  ASSERT_TRUE(node.ReadMessage(C, &message));
-  EXPECT_TRUE(MessageEquals(message, "hey"));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
 
   PortStatus status;
-  EXPECT_EQ(OK, node.node().GetStatus(C, &status));
+  EXPECT_EQ(OK, node0.GetStatus(C, &status));
   EXPECT_FALSE(status.receiving_messages);
   EXPECT_FALSE(status.has_messages);
   EXPECT_TRUE(status.peer_closed);
 
-  node.node().ClosePort(C);
+  node0.ClosePort(C);
 
-  WaitForIdle();
-
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, SendWithClosedPeerSent) {
@@ -1134,342 +1126,391 @@
   // eventually notified of the closure, and the dead-end proxies will
   // eventually be removed.
 
-  TestNode node(0);
-  AddNode(&node);
+  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, node.node().CreatePortPair(&X, &Y));
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
 
   PortRef A, B;
-  EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
 
   ScopedMessage message;
 
   // Send A as new port C.
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", A));
-
-  ASSERT_TRUE(node.ReadMessage(Y, &message));
+  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, node.node().GetPort(message->ports()[0], &C));
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
 
   // Send C as new port D.
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", C));
-
-  ASSERT_TRUE(node.ReadMessage(Y, &message));
+  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, node.node().GetPort(message->ports()[0], &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, node.SendStringMessage(D, "hey"));
-  EXPECT_EQ(OK, node.node().ClosePort(D));
+  EXPECT_EQ(OK, SendStringMessage(&node0, D, "hey"));
+  EXPECT_EQ(OK, node0.ClosePort(D));
+
+  PumpTasks();
 
   // Now send B as new port E.
 
-  EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B));
-  EXPECT_EQ(OK, node.node().ClosePort(X));
+  node0_delegate.set_read_messages(true);
+  EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
 
-  ASSERT_TRUE(node.ReadMessage(Y, &message));
+  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, node.node().GetPort(message->ports()[0], &E));
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
 
-  EXPECT_EQ(OK, node.node().ClosePort(Y));
-
-  WaitForIdle();
+  PumpTasks();
 
   // E should receive the message originally sent to B, and it should also be
   // aware of D's closure.
 
-  ASSERT_TRUE(node.ReadMessage(E, &message));
-  EXPECT_TRUE(MessageEquals(message, "hey"));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
 
   PortStatus status;
-  EXPECT_EQ(OK, node.node().GetStatus(E, &status));
+  EXPECT_EQ(OK, node0.GetStatus(E, &status));
   EXPECT_FALSE(status.receiving_messages);
   EXPECT_FALSE(status.has_messages);
   EXPECT_TRUE(status.peer_closed);
 
-  EXPECT_EQ(OK, node.node().ClosePort(E));
+  node0.ClosePort(E);
 
-  WaitForIdle();
+  PumpTasks();
 
-  EXPECT_TRUE(node.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, MergePorts) {
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node1.node().CreatePortPair(&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, node0.SendStringMessage(A, "hey"));
+  EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey"));
+
+  PumpTasks();
 
   // Initiate a merge between B and C.
-  EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
 
-  WaitForIdle();
+  PumpTasks();
 
-  // Expect all proxies to be gone once idle.
-  EXPECT_TRUE(
-      node0.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
-  EXPECT_TRUE(
-      node1.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
+  // 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.ReadMessage(D, &message));
-  EXPECT_TRUE(MessageEquals(message, "hey"));
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(A));
-  EXPECT_EQ(OK, node1.node().ClosePort(D));
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(D));
 
   // No more ports should be open.
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  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.
 
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node1.node().CreatePortPair(&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, node0.SendStringMessage(A, "hey"));
+  EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey"));
+
+  PumpTasks();
 
   // Close A.
-  EXPECT_EQ(OK, node0.node().ClosePort(A));
+  EXPECT_EQ(OK, node0.ClosePort(A));
 
   // Initiate a merge between B and C.
-  EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
 
-  WaitForIdle();
+  PumpTasks();
 
-  // Expect all proxies to be gone once idle. node0 should have no ports since
-  // A was explicitly closed.
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(
-      node1.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
+  // 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.ReadMessage(D, &message));
-  EXPECT_TRUE(MessageEquals(message, "hey"));
+  ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
 
-  EXPECT_EQ(OK, node1.node().ClosePort(D));
+  EXPECT_EQ(OK, node1.ClosePort(D));
 
   // No more ports should be open.
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  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.
 
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D));
+  EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+  EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
 
-  // Write a message on D and close it.
-  EXPECT_EQ(OK, node0.SendStringMessage(D, "hey"));
-  EXPECT_EQ(OK, node1.node().ClosePort(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.node().MergePorts(B, node1.name(), C.name()));
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
 
-  WaitForIdle();
+  PumpTasks();
 
-  // Expect all proxies to be gone once idle. node1 should have no ports since
-  // D was explicitly closed.
-  EXPECT_TRUE(
-      node0.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  // 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.ReadMessage(A, &message));
-  EXPECT_TRUE(MessageEquals(message, "hey"));
+  ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+  EXPECT_EQ(0, strcmp("hey", ToString(message)));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(A));
+  EXPECT_EQ(OK, node0.ClosePort(A));
 
   // No more ports should be open.
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  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.
 
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node1.node().CreatePortPair(&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.node().ClosePort(A));
-  EXPECT_EQ(OK, node1.node().ClosePort(D));
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(D));
 
-  WaitForIdle();
+  PumpTasks();
 
   // Initiate a merge between B and C.
-  EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
 
-  WaitForIdle();
+  PumpTasks();
 
   // Expect everything to have gone away.
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 TEST_F(PortsTest, MergePortsWithMovedPeers) {
-  // This tests that ports can be merged successfully even if their peers are
-  // moved around.
+  // This tests that no ports can be merged successfully even if their peers
+  // are moved around.
 
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node1.node().CreatePortPair(&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.node().CreatePortPair(&X, &Y));
+  EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
 
   ScopedMessage message;
 
   // Move A to new port E.
-  EXPECT_EQ(OK, node0.SendStringMessageWithPort(X, "foo", A));
-  ASSERT_TRUE(node0.ReadMessage(Y, &message));
+  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.node().GetPort(message->ports()[0], &E));
+  ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
 
-  EXPECT_EQ(OK, node0.node().ClosePort(X));
-  EXPECT_EQ(OK, node0.node().ClosePort(Y));
+  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, node0.SendStringMessage(E, "hey"));
-  EXPECT_EQ(OK, node1.SendStringMessage(D, "hi"));
+  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.node().MergePorts(B, node1.name(), C.name()));
+  EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
 
-  WaitForIdle();
+  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.ReadMessage(E, &message));
-  EXPECT_TRUE(MessageEquals(message, "hi"));
-  ASSERT_TRUE(node1.ReadMessage(D, &message));
-  EXPECT_TRUE(MessageEquals(message, "hey"));
+  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.node().ClosePort(E));
-  EXPECT_EQ(OK, node1.node().ClosePort(D));
+  EXPECT_EQ(OK, node0.ClosePort(E));
+  EXPECT_EQ(OK, node1.ClosePort(D));
 
-  WaitForIdle();
+  PumpTasks();
 
   // Expect everything to have gone away.
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  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.
 
-  TestNode node0(0);
-  AddNode(&node0);
+  NodeName node0_name(0, 1);
+  TestNodeDelegate node0_delegate(node0_name);
+  Node node0(node0_name, &node0_delegate);
+  node_map[0] = &node0;
 
-  TestNode node1(1);
-  AddNode(&node1);
+  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.node().CreatePortPair(&A, &B));
-  EXPECT_EQ(OK, node1.node().CreatePortPair(&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.node().CreatePortPair(&X, &Y));
-
-  // Block the merge from proceeding until we can do something stupid with port
-  // C. This avoids the test logic racing with async merge logic.
-  node1.BlockOnEvent(EventType::kMergePort);
-
-  // Initiate the merge between B and C.
-  EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
-
-  // Move C to a new port E. This is not a sane use of Node's public API but
-  // is still hypothetically possible. It allows us to force a merge failure
-  // because C will be in an invalid state by the term the merge is processed.
-  // As a result, B should be closed.
-  EXPECT_EQ(OK, node1.SendStringMessageWithPort(X, "foo", C));
-
-  node1.Unblock();
-
-  ASSERT_TRUE(node1.ReadMessage(Y, &message));
+  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.node().GetPort(message->ports()[0], &E));
+  ASSERT_EQ(OK, node1.GetPort(message->ports()[0], &E));
+  EXPECT_EQ(OK, node1.ClosePort(X));
+  EXPECT_EQ(OK, node1.ClosePort(Y));
 
-  EXPECT_EQ(OK, node1.node().ClosePort(X));
-  EXPECT_EQ(OK, node1.node().ClosePort(Y));
+  // C goes away as a result of normal proxy removal.
+  PumpTasks();
 
-  WaitForIdle();
+  EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.GetPort(C.name(), &C));
 
-  // C goes away as a result of normal proxy removal. B should have been closed
-  // cleanly by the failed MergePorts.
-  EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.node().GetPort(C.name(), &C));
-  EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.node().GetPort(B.name(), &B));
+  // 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.node().ClosePort(A));
-  EXPECT_EQ(OK, node1.node().ClosePort(D));
-  EXPECT_EQ(OK, node1.node().ClosePort(E));
+  EXPECT_EQ(OK, node0.ClosePort(A));
+  EXPECT_EQ(OK, node1.ClosePort(D));
+  EXPECT_EQ(OK, node1.ClosePort(E));
 
-  WaitForIdle();
+  PumpTasks();
 
   // Expect everything to have gone away.
-  EXPECT_TRUE(node0.node().CanShutdownCleanly());
-  EXPECT_TRUE(node1.node().CanShutdownCleanly());
+  EXPECT_TRUE(node0.CanShutdownCleanly(false));
+  EXPECT_TRUE(node1.CanShutdownCleanly(false));
 }
 
 }  // namespace test
diff --git a/mojo/edk/system/request_context.cc b/mojo/edk/system/request_context.cc
index 6370ab1..276e39f 100644
--- a/mojo/edk/system/request_context.cc
+++ b/mojo/edk/system/request_context.cc
@@ -77,12 +77,12 @@
     const HandleSignalsState& state) {
   DCHECK(IsCurrent());
   watch_notify_finalizers_->push_back(
-      WatchNotifyFinalizer(std::move(watcher), result, state));
+      WatchNotifyFinalizer(watcher, result, state));
 }
 
 void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher) {
   DCHECK(IsCurrent());
-  watch_cancel_finalizers_->push_back(std::move(watcher));
+  watch_cancel_finalizers_->push_back(watcher);
 }
 
 bool RequestContext::IsCurrent() const {
@@ -93,7 +93,8 @@
     scoped_refptr<Watcher> watcher,
     MojoResult result,
     const HandleSignalsState& state)
-    : watcher(std::move(watcher)), result(result), state(state) {}
+    : watcher(watcher), result(result), state(state) {
+}
 
 RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
     const WatchNotifyFinalizer& other) = default;
diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h
index 6015595..1648dd2 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.h
+++ b/mojo/edk/system/shared_buffer_dispatcher.h
@@ -21,6 +21,7 @@
 
 namespace edk {
 class NodeController;
+class PlatformSupport;
 
 class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher final : public Dispatcher {
  public:
diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn
index a15456a..ebacc64 100644
--- a/mojo/edk/test/BUILD.gn
+++ b/mojo/edk/test/BUILD.gn
@@ -9,6 +9,8 @@
   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",
diff --git a/mojo/edk/test/mojo_test_base.cc b/mojo/edk/test/mojo_test_base.cc
index f1032d7..5d77a11 100644
--- a/mojo/edk/test/mojo_test_base.cc
+++ b/mojo/edk/test/mojo_test_base.cc
@@ -44,15 +44,14 @@
 MojoTestBase::ClientController& MojoTestBase::StartClient(
     const std::string& client_name) {
   clients_.push_back(base::MakeUnique<ClientController>(
-      client_name, this, process_error_callback_, launch_type_));
+      client_name, this, process_error_callback_));
   return *clients_.back();
 }
 
 MojoTestBase::ClientController::ClientController(
     const std::string& client_name,
     MojoTestBase* test,
-    const ProcessErrorCallback& process_error_callback,
-    LaunchType launch_type) {
+    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
@@ -64,7 +63,7 @@
   base::AutoLock lock(g_mach_broker->GetLock());
 #endif
   helper_.set_process_error_callback(process_error_callback);
-  pipe_ = helper_.StartChild(client_name, launch_type);
+  pipe_ = helper_.StartChild(client_name);
 #if defined(OS_MACOSX)
   g_mach_broker->AddPlaceholderForPid(helper_.test_child().Handle());
 #endif
@@ -76,12 +75,6 @@
       << "Test clients should be waited on explicitly with WaitForShutdown().";
 }
 
-void MojoTestBase::ClientController::ClosePeerConnection() {
-#if !defined(OS_IOS)
-  helper_.ClosePeerConnection();
-#endif
-}
-
 int MojoTestBase::ClientController::WaitForShutdown() {
   was_shutdown_ = true;
 #if !defined(OS_IOS)
diff --git a/mojo/edk/test/mojo_test_base.h b/mojo/edk/test/mojo_test_base.h
index fa5b64c..4f55145 100644
--- a/mojo/edk/test/mojo_test_base.h
+++ b/mojo/edk/test/mojo_test_base.h
@@ -29,8 +29,6 @@
   MojoTestBase();
   ~MojoTestBase() override;
 
-  using LaunchType = MultiprocessTestHelper::LaunchType;
-
  protected:
   using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>;
 
@@ -38,13 +36,11 @@
    public:
     ClientController(const std::string& client_name,
                      MojoTestBase* test,
-                     const ProcessErrorCallback& process_error_callback,
-                     LaunchType launch_type);
+                     const ProcessErrorCallback& process_error_callback_);
     ~ClientController();
 
     MojoHandle pipe() const { return pipe_.get().value(); }
 
-    void ClosePeerConnection();
     int WaitForShutdown();
 
    private:
@@ -152,8 +148,6 @@
   // Reads data from a data pipe.
   static std::string ReadData(MojoHandle consumer, size_t size);
 
-  void set_launch_type(LaunchType launch_type) { launch_type_ = launch_type; }
-
  private:
   friend class ClientController;
 
@@ -161,8 +155,6 @@
 
   ProcessErrorCallback process_error_callback_;
 
-  LaunchType launch_type_ = LaunchType::CHILD;
-
   DISALLOW_COPY_AND_ASSIGN(MojoTestBase);
 };
 
diff --git a/mojo/edk/test/multiprocess_test_helper.cc b/mojo/edk/test/multiprocess_test_helper.cc
index de6e2d9..82a82c8 100644
--- a/mojo/edk/test/multiprocess_test_helper.cc
+++ b/mojo/edk/test/multiprocess_test_helper.cc
@@ -8,14 +8,12 @@
 #include <set>
 #include <utility>
 
-#include "base/base_paths.h"
 #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/path_service.h"
 #include "base/process/kill.h"
 #include "base/process/process_handle.h"
 #include "base/run_loop.h"
@@ -24,9 +22,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
-#include "mojo/edk/embedder/named_platform_handle_utils.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -43,13 +38,11 @@
 namespace {
 
 const char kMojoPrimordialPipeToken[] = "mojo-primordial-pipe-token";
-const char kMojoNamedPipeName[] = "mojo-named-pipe-name";
 
-template <typename Func>
-int RunClientFunction(Func handler) {
-  CHECK(MultiprocessTestHelper::primordial_pipe.is_valid());
-  ScopedMessagePipeHandle pipe =
-      std::move(MultiprocessTestHelper::primordial_pipe);
+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());
 }
 
@@ -62,17 +55,15 @@
 }
 
 ScopedMessagePipeHandle MultiprocessTestHelper::StartChild(
-    const std::string& test_child_name,
-    LaunchType launch_type) {
-  return StartChildWithExtraSwitch(test_child_name, std::string(),
-                                   std::string(), launch_type);
+    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,
-    LaunchType launch_type) {
+    const std::string& switch_value) {
   CHECK(!test_child_name.empty());
   CHECK(!test_child_.IsValid());
 
@@ -97,23 +88,12 @@
   }
 
   PlatformChannelPair channel;
-  NamedPlatformHandle named_pipe;
   HandlePassingInformation handle_passing_info;
-  if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) {
-    channel.PrepareToPassClientHandleToChildProcess(&command_line,
-                                                    &handle_passing_info);
-  } else if (launch_type == LaunchType::NAMED_CHILD ||
-             launch_type == LaunchType::NAMED_PEER) {
-#if defined(OS_POSIX)
-    base::FilePath temp_dir;
-    CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir));
-    named_pipe = NamedPlatformHandle(
-        temp_dir.AppendASCII(GenerateRandomToken()).value());
-#else
-    named_pipe = NamedPlatformHandle(GenerateRandomToken());
-#endif
-    command_line.AppendSwitchNative(kMojoNamedPipeName, named_pipe.name);
-  }
+  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));
@@ -136,44 +116,18 @@
 #error "Not supported yet."
 #endif
 
-  // NOTE: In the case of named pipes, it's important that the server handle be
-  // created before the child process is launched; otherwise the server binding
-  // the pipe path can race with child's connection to the pipe.
-  ScopedPlatformHandle server_handle;
-  if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) {
-    server_handle = channel.PassServerHandle();
-  } else if (launch_type == LaunchType::NAMED_CHILD ||
-             launch_type == LaunchType::NAMED_PEER) {
-    server_handle = CreateServerHandle(named_pipe);
-  }
-
-  PendingProcessConnection process;
-  ScopedMessagePipeHandle pipe;
-  if (launch_type == LaunchType::CHILD ||
-      launch_type == LaunchType::NAMED_CHILD) {
-    std::string pipe_token;
-    pipe = process.CreateMessagePipe(&pipe_token);
-    command_line.AppendSwitchASCII(kMojoPrimordialPipeToken, pipe_token);
-  } else if (launch_type == LaunchType::PEER ||
-             launch_type == LaunchType::NAMED_PEER) {
-    peer_token_ = mojo::edk::GenerateRandomToken();
-    pipe = ConnectToPeerProcess(std::move(server_handle), peer_token_);
-  }
+  std::string child_token = mojo::edk::GenerateRandomToken();
+  ScopedMessagePipeHandle pipe = CreateParentMessagePipe(pipe_token,
+                                                         child_token);
 
   test_child_ =
       base::SpawnMultiProcessTestChild(test_child_main, command_line, options);
-  if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER)
-    channel.ChildProcessLaunched();
+  channel.ChildProcessLaunched();
 
-  if (launch_type == LaunchType::CHILD ||
-      launch_type == LaunchType::NAMED_CHILD) {
-    DCHECK(server_handle.is_valid());
-    process.Connect(test_child_.Handle(),
-                    ConnectionParams(std::move(server_handle)),
-                    process_error_callback_);
-  }
-
+  ChildProcessLaunched(test_child_.Handle(), channel.PassServerHandle(),
+                       child_token, process_error_callback_);
   CHECK(test_child_.IsValid());
+
   return pipe;
 }
 
@@ -181,18 +135,18 @@
   CHECK(test_child_.IsValid());
 
   int rv = -1;
-  WaitForMultiprocessTestChildExit(test_child_, TestTimeouts::action_timeout(),
-                                   &rv);
+#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;
 }
 
-void MultiprocessTestHelper::ClosePeerConnection() {
-  DCHECK(!peer_token_.empty());
-  ::mojo::edk::ClosePeerConnection(peer_token_);
-  peer_token_.clear();
-}
-
 bool MultiprocessTestHelper::WaitForChildTestShutdown() {
   return WaitForChildShutdown() == 0;
 }
@@ -201,33 +155,17 @@
 void MultiprocessTestHelper::ChildSetup() {
   CHECK(base::CommandLine::InitializedForCurrentProcess());
 
-  std::string primordial_pipe_token =
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          kMojoPrimordialPipeToken);
-  NamedPlatformHandle named_pipe(
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
-          kMojoNamedPipeName));
-  if (!primordial_pipe_token.empty()) {
-    primordial_pipe = CreateChildMessagePipe(primordial_pipe_token);
+  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"));
+  CHECK(base::MachPortBroker::ChildSendTaskPortToParent("mojo_test"));
 #endif
-    if (named_pipe.is_valid()) {
-      SetParentPipeHandle(CreateClientHandle(named_pipe));
-    } else {
-      SetParentPipeHandle(
-          PlatformChannelPair::PassClientHandleFromParentProcess(
-              *base::CommandLine::ForCurrentProcess()));
-    }
-  } else {
-    if (named_pipe.is_valid()) {
-      primordial_pipe = ConnectToPeerProcess(CreateClientHandle(named_pipe));
-    } else {
-      primordial_pipe = ConnectToPeerProcess(
-          PlatformChannelPair::PassClientHandleFromParentProcess(
-              *base::CommandLine::ForCurrentProcess()));
-    }
-  }
+
+  SetParentPipeHandle(
+      PlatformChannelPair::PassClientHandleFromParentProcess(
+          *base::CommandLine::ForCurrentProcess()));
 }
 
 // static
@@ -249,7 +187,7 @@
 }
 
 // static
-mojo::ScopedMessagePipeHandle MultiprocessTestHelper::primordial_pipe;
+std::string MultiprocessTestHelper::primordial_pipe_token;
 
 }  // namespace test
 }  // namespace edk
diff --git a/mojo/edk/test/multiprocess_test_helper.h b/mojo/edk/test/multiprocess_test_helper.h
index dd9bd6a..203eb11 100644
--- a/mojo/edk/test/multiprocess_test_helper.h
+++ b/mojo/edk/test/multiprocess_test_helper.h
@@ -19,6 +19,7 @@
 namespace mojo {
 
 namespace edk {
+class PlatformChannelPair;
 
 namespace test {
 
@@ -26,31 +27,13 @@
  public:
   using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>;
 
-  enum class LaunchType {
-    // Launch the child process as a child in the mojo system.
-    CHILD,
-
-    // Launch the child process as an unrelated peer process in the mojo system.
-    PEER,
-
-    // Launch the child process as a child in the mojo system, using a named
-    // pipe.
-    NAMED_CHILD,
-
-    // Launch the child process as an unrelated peer process in the mojo
-    // system, using a named pipe.
-    NAMED_PEER,
-  };
-
   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,
-      LaunchType launch_type = LaunchType::CHILD);
+  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
@@ -58,15 +41,12 @@
   ScopedMessagePipeHandle StartChildWithExtraSwitch(
       const std::string& test_child_name,
       const std::string& switch_string,
-      const std::string& switch_value,
-      LaunchType launch_type);
+      const std::string& switch_value);
 
   void set_process_error_callback(const ProcessErrorCallback& callback) {
     process_error_callback_ = callback;
   }
 
-  void ClosePeerConnection();
-
   // 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
@@ -89,7 +69,7 @@
   static int RunClientTestMain(const base::Callback<void(MojoHandle)>& main);
 
   // For use (and only valid) in the child process:
-  static mojo::ScopedMessagePipeHandle primordial_pipe;
+  static std::string primordial_pipe_token;
 
  private:
   // Valid after |StartChild()| and before |WaitForChildShutdown()|.
@@ -97,8 +77,6 @@
 
   ProcessErrorCallback process_error_callback_;
 
-  std::string peer_token_;
-
   DISALLOW_COPY_AND_ASSIGN(MultiprocessTestHelper);
 };
 
diff --git a/mojo/edk/test/run_all_perftests.cc b/mojo/edk/test/run_all_perftests.cc
index 3ce3b47..50c41d7 100644
--- a/mojo/edk/test/run_all_perftests.cc
+++ b/mojo/edk/test/run_all_perftests.cc
@@ -7,19 +7,24 @@
 #include "base/test/perf_test_suite.h"
 #include "base/test/test_io_thread.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.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);
-  mojo::edk::ScopedIPCSupport ipc_support(
-      test_io_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
+  // 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
index a057825..05aed91 100644
--- a/mojo/edk/test/run_all_unittests.cc
+++ b/mojo/edk/test/run_all_unittests.cc
@@ -11,8 +11,8 @@
 #include "base/test/test_io_thread.h"
 #include "base/test/test_suite.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.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"
@@ -26,6 +26,10 @@
   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.
@@ -39,10 +43,11 @@
 
   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());
 
-  mojo::edk::ScopedIPCSupport ipc_support(
-      test_io_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
   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/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/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
index 3baf667..9442479 100644
--- a/mojo/public/BUILD.gn
+++ b/mojo/public/BUILD.gn
@@ -13,8 +13,8 @@
 
   if (is_android) {
     deps += [
-      "java:bindings_java",
-      "java:system_java",
+      "java:bindings",
+      "java:system",
     ]
   }
 }
@@ -23,6 +23,7 @@
   deps = [
     "c/system",
     "cpp/bindings",
+    "interfaces/bindings",
     "js",
   ]
 }
diff --git a/mojo/public/README.md b/mojo/public/README.md
index dd91742..a31a8a8 100644
--- a/mojo/public/README.md
+++ b/mojo/public/README.md
@@ -31,7 +31,7 @@
 --------
 
 The platform/ subdirectory contains any build-time requirements (e.g., static
-libraries) that may be needed to produce a Service library for certain
+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
diff --git a/mojo/public/c/system/BUILD.gn b/mojo/public/c/system/BUILD.gn
index c3b3d5f..3ec12f6 100644
--- a/mojo/public/c/system/BUILD.gn
+++ b/mojo/public/c/system/BUILD.gn
@@ -25,7 +25,7 @@
 
 # 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 //services/service_manager/public/cpp/service.gni.
+# mojo_native_application template in //mojo/public/mojo_application.gni.
 source_set("set_thunks_for_app") {
   sources = [
     "set_thunks_for_app.cc",
diff --git a/mojo/public/c/system/core.h b/mojo/public/c/system/core.h
index 77d452b..3775e90 100644
--- a/mojo/public/c/system/core.h
+++ b/mojo/public/c/system/core.h
@@ -13,6 +13,7 @@
 #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"
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/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
index 8a54380..ef44a87 100644
--- a/mojo/public/c/system/tests/core_unittest.cc
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -192,8 +192,7 @@
             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 |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
             state.satisfiable_signals);
 
   // The producer |hp| should be writable.
@@ -230,10 +229,8 @@
                                          &result_index, states));
 
   EXPECT_EQ(0u, result_index);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            states[0].satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+  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|.
diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c
index fa3caa5..a01e14b 100644
--- a/mojo/public/c/system/tests/core_unittest_pure_c.c
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -21,7 +21,7 @@
 #define FAILURE(message) \
   __FILE__ "(" STRINGIFY2(__LINE__) "): Failure: " message
 
-// Makeshift gtest.
+// Poor man's gtest.
 #define EXPECT_EQ(a, b)                                                  \
   do {                                                                   \
     if ((a) != (b))                                                      \
diff --git a/mojo/public/c/system/types.h b/mojo/public/c/system/types.h
index 7e02eeb..3482d4e 100644
--- a/mojo/public/c/system/types.h
+++ b/mojo/public/c/system/types.h
@@ -149,11 +149,6 @@
 //   |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.
-//   |MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE| - Can read data from a data pipe
-//       consumer handle (implying MOJO_HANDLE_SIGNAL_READABLE is also set),
-//       AND there is some nonzero quantity of new data available on the pipe
-//       since the last |MojoReadData()| or |MojoBeginReadData()| call on the
-//       handle.
 
 typedef uint32_t MojoHandleSignals;
 
@@ -162,13 +157,11 @@
 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;
-const MojoHandleSignals MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE = 1 << 3;
 #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)
-#define MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE ((MojoHandleSignals)1 << 3);
 #endif
 
 // |MojoHandleSignalsState|: Returned by wait functions to indicate the
diff --git a/mojo/public/c/test_support/BUILD.gn b/mojo/public/c/test_support/BUILD.gn
index e2abd58..75d9c13 100644
--- a/mojo/public/c/test_support/BUILD.gn
+++ b/mojo/public/c/test_support/BUILD.gn
@@ -2,6 +2,7 @@
 # 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"
 
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 5c41384..3ec9824 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -2,31 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings"
-
-component("bindings") {
+static_library("bindings") {
   sources = [
-    # Normally, targets should depend on the source_sets generated by mojom
-    # targets. However, the generated source_sets use portions of the bindings
-    # library. In order to avoid linker warnings about locally-defined imports
-    # in Windows components build, this target depends on the generated C++
-    # files directly so that the EXPORT macro defintions match.
-    "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared-internal.h",
-    "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared.cc",
-    "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared.h",
-    "$interfaces_bindings_gen_dir/interface_control_messages.mojom.cc",
-    "$interfaces_bindings_gen_dir/interface_control_messages.mojom.h",
-    "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared-internal.h",
-    "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared.cc",
-    "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared.h",
-    "$interfaces_bindings_gen_dir/pipe_control_messages.mojom.cc",
-    "$interfaces_bindings_gen_dir/pipe_control_messages.mojom.h",
-    "array_data_view.h",
+    "array.h",
     "array_traits.h",
     "array_traits_carray.h",
+    "array_traits_standard.h",
     "array_traits_stl.h",
     "associated_binding.h",
-    "associated_binding_set.h",
     "associated_group.h",
     "associated_group_controller.h",
     "associated_interface_ptr.h",
@@ -34,13 +17,8 @@
     "associated_interface_request.h",
     "binding.h",
     "binding_set.h",
-    "bindings_export.h",
-    "clone_traits.h",
-    "connection_error_callback.h",
     "connector.h",
-    "disconnect_reason.h",
-    "filter_chain.h",
-    "interface_data_view.h",
+    "enum_traits.h",
     "interface_endpoint_client.h",
     "interface_endpoint_controller.h",
     "interface_id.h",
@@ -51,35 +29,34 @@
     "lib/array_internal.cc",
     "lib/array_internal.h",
     "lib/array_serialization.h",
-    "lib/associated_binding.cc",
     "lib/associated_group.cc",
     "lib/associated_group_controller.cc",
     "lib/associated_interface_ptr_state.h",
-    "lib/binding_state.cc",
     "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/equals_traits.h",
     "lib/filter_chain.cc",
+    "lib/filter_chain.h",
     "lib/fixed_buffer.cc",
     "lib/fixed_buffer.h",
     "lib/handle_interface_serialization.h",
-    "lib/hash_util.h",
     "lib/interface_endpoint_client.cc",
     "lib/interface_ptr_state.h",
     "lib/map_data_internal.h",
     "lib/map_serialization.h",
-    "lib/may_auto_lock.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",
@@ -91,8 +68,11 @@
     "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",
@@ -114,35 +94,32 @@
     "lib/validation_util.cc",
     "lib/validation_util.h",
     "map.h",
-    "map_data_view.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",
-    "native_struct_data_view.h",
+    "no_interface.h",
     "pipe_control_message_handler.h",
     "pipe_control_message_handler_delegate.h",
     "pipe_control_message_proxy.h",
-    "raw_ptr_impl_ref_traits.h",
     "scoped_interface_endpoint_handle.h",
-    "string_data_view.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_associated_binding.h",
     "strong_binding.h",
-    "strong_binding_set.h",
     "struct_ptr.h",
     "sync_call_restrictions.h",
     "sync_handle_registry.h",
     "sync_handle_watcher.h",
-    "thread_safe_interface_ptr.h",
     "type_converter.h",
-    "union_traits.h",
-    "unique_ptr_impl_ref_traits.h",
   ]
 
   public_deps = [
@@ -154,16 +131,12 @@
 
   deps = [
     "//base",
-    "//mojo/public/interfaces/bindings:bindings__generator",
-    "//mojo/public/interfaces/bindings:bindings_shared__generator",
+    "//mojo/public/interfaces/bindings:bindings_cpp_sources",
   ]
-
-  defines = [ "MOJO_CPP_BINDINGS_IMPLEMENTATION" ]
 }
 
 source_set("struct_traits") {
   sources = [
-    "enum_traits.h",
     "struct_traits.h",
   ]
 }
@@ -172,13 +145,16 @@
   # 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_hash_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 = [
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_data_view.h b/mojo/public/cpp/bindings/array_data_view.h
deleted file mode 100644
index d02a884..0000000
--- a/mojo/public/cpp/bindings/array_data_view.h
+++ /dev/null
@@ -1,244 +0,0 @@
-// 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_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_DATA_VIEW_H_
-
-#include <type_traits>
-
-#include "mojo/public/cpp/bindings/lib/array_internal.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"
-
-namespace mojo {
-namespace internal {
-
-template <typename T, typename EnableType = void>
-class ArrayDataViewImpl;
-
-template <typename T>
-class ArrayDataViewImpl<
-    T,
-    typename std::enable_if<
-        BelongsTo<T, MojomTypeCategory::POD>::value>::type> {
- public:
-  using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
-  ArrayDataViewImpl(Data_* data, SerializationContext* context)
-      : data_(data), context_(context) {}
-
-  T operator[](size_t index) const { return data_->at(index); }
-
-  const T* data() const { return data_->storage(); }
-
- protected:
-  Data_* data_;
-  SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
-    T,
-    typename std::enable_if<
-        BelongsTo<T, MojomTypeCategory::BOOLEAN>::value>::type> {
- public:
-  using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
-  ArrayDataViewImpl(Data_* data, SerializationContext* context)
-      : data_(data), context_(context) {}
-
-  bool operator[](size_t index) const { return data_->at(index); }
-
- protected:
-  Data_* data_;
-  SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
-    T,
-    typename std::enable_if<
-        BelongsTo<T, MojomTypeCategory::ENUM>::value>::type> {
- public:
-  static_assert(sizeof(T) == sizeof(int32_t), "Unexpected enum size");
-
-  using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
-  ArrayDataViewImpl(Data_* data, SerializationContext* context)
-      : data_(data), context_(context) {}
-
-  T operator[](size_t index) const { return static_cast<T>(data_->at(index)); }
-
-  const T* data() const { return reinterpret_cast<const T*>(data_->storage()); }
-
-  template <typename U>
-  bool Read(size_t index, U* output) {
-    return Deserialize<T>(data_->at(index), output);
-  }
-
- protected:
-  Data_* data_;
-  SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
-    T,
-    typename std::enable_if<
-        BelongsTo<T,
-                  MojomTypeCategory::ASSOCIATED_INTERFACE |
-                      MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST |
-                      MojomTypeCategory::INTERFACE |
-                      MojomTypeCategory::INTERFACE_REQUEST>::value>::type> {
- public:
-  using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
-  ArrayDataViewImpl(Data_* data, SerializationContext* context)
-      : data_(data), context_(context) {}
-
-  template <typename U>
-  U Take(size_t index) {
-    U result;
-    bool ret = Deserialize<T>(&data_->at(index), &result, context_);
-    DCHECK(ret);
-    return result;
-  }
-
- protected:
-  Data_* data_;
-  SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
-    T,
-    typename std::enable_if<
-        BelongsTo<T, MojomTypeCategory::HANDLE>::value>::type> {
- public:
-  using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
-  ArrayDataViewImpl(Data_* data, SerializationContext* context)
-      : data_(data), context_(context) {}
-
-  T Take(size_t index) {
-    T result;
-    bool ret = Deserialize<T>(&data_->at(index), &result, context_);
-    DCHECK(ret);
-    return result;
-  }
-
- protected:
-  Data_* data_;
-  SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<T,
-                        typename std::enable_if<BelongsTo<
-                            T,
-                            MojomTypeCategory::ARRAY | MojomTypeCategory::MAP |
-                                MojomTypeCategory::STRING |
-                                MojomTypeCategory::STRUCT>::value>::type> {
- public:
-  using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
-  ArrayDataViewImpl(Data_* data, SerializationContext* context)
-      : data_(data), context_(context) {}
-
-  void GetDataView(size_t index, T* output) {
-    *output = T(data_->at(index).Get(), context_);
-  }
-
-  template <typename U>
-  bool Read(size_t index, U* output) {
-    return Deserialize<T>(data_->at(index).Get(), output, context_);
-  }
-
- protected:
-  Data_* data_;
-  SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
-    T,
-    typename std::enable_if<
-        BelongsTo<T, MojomTypeCategory::UNION>::value>::type> {
- public:
-  using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
-  ArrayDataViewImpl(Data_* data, SerializationContext* context)
-      : data_(data), context_(context) {}
-
-  void GetDataView(size_t index, T* output) {
-    *output = T(&data_->at(index), context_);
-  }
-
-  template <typename U>
-  bool Read(size_t index, U* output) {
-    return Deserialize<T>(&data_->at(index), output, context_);
-  }
-
- protected:
-  Data_* data_;
-  SerializationContext* context_;
-};
-
-}  // namespace internal
-
-template <typename K, typename V>
-class MapDataView;
-
-template <typename T>
-class ArrayDataView : public internal::ArrayDataViewImpl<T> {
- public:
-  using Element = T;
-  using Data_ = typename internal::ArrayDataViewImpl<T>::Data_;
-
-  ArrayDataView() : internal::ArrayDataViewImpl<T>(nullptr, nullptr) {}
-
-  ArrayDataView(Data_* data, internal::SerializationContext* context)
-      : internal::ArrayDataViewImpl<T>(data, context) {}
-
-  bool is_null() const { return !this->data_; }
-
-  size_t size() const { return this->data_->size(); }
-
-  // Methods to access elements are different for different element types. They
-  // are inherited from internal::ArrayDataViewImpl:
-
-  // POD types except boolean and enums:
-  //   T operator[](size_t index) const;
-  //   const T* data() const;
-
-  // Boolean:
-  //   bool operator[](size_t index) const;
-
-  // Enums:
-  //   T operator[](size_t index) const;
-  //   const T* data() const;
-  //   template <typename U>
-  //   bool Read(size_t index, U* output);
-
-  // Handles:
-  //   T Take(size_t index);
-
-  // Interfaces:
-  //   template <typename U>
-  //   U Take(size_t index);
-
-  // Object types:
-  //   void GetDataView(size_t index, T* output);
-  //   template <typename U>
-  //   bool Read(size_t index, U* output);
-
- private:
-  template <typename K, typename V>
-  friend class MapDataView;
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/array_traits.h b/mojo/public/cpp/bindings/array_traits.h
index 594b2e0..366573d 100644
--- a/mojo/public/cpp/bindings/array_traits.h
+++ b/mojo/public/cpp/bindings/array_traits.h
@@ -47,9 +47,7 @@
 //     static void AdvanceIterator(Iterator& iterator);
 //
 //     // Returns a reference to the value at the current position of
-//     // |iterator|. Optionally, the ConstIterator version of GetValue can
-//     // return by value instead of by reference if it makes sense for the
-//     // type.
+//     // |iterator|.
 //     static const T& GetValue(ConstIterator& iterator);
 //     static T& GetValue(Iterator& iterator);
 //
diff --git a/mojo/public/cpp/bindings/array_traits_carray.h b/mojo/public/cpp/bindings/array_traits_carray.h
index 3ff694b..ffcf9d5 100644
--- a/mojo/public/cpp/bindings/array_traits_carray.h
+++ b/mojo/public/cpp/bindings/array_traits_carray.h
@@ -20,14 +20,6 @@
 };
 
 template <typename T>
-struct ConstCArray {
-  ConstCArray() : size(0), data(nullptr) {}
-  ConstCArray(size_t size, const T* data) : size(size), data(data) {}
-  size_t size;
-  const T* data;
-};
-
-template <typename T>
 struct ArrayTraits<CArray<T>> {
   using Element = T;
 
@@ -56,21 +48,6 @@
   }
 };
 
-template <typename T>
-struct ArrayTraits<ConstCArray<T>> {
-  using Element = T;
-
-  static bool IsNull(const ConstCArray<T>& input) { return !input.data; }
-
-  static size_t GetSize(const ConstCArray<T>& input) { return input.size; }
-
-  static const T* GetData(const ConstCArray<T>& input) { return input.data; }
-
-  static const T& GetAt(const ConstCArray<T>& input, size_t index) {
-    return input.data[index];
-  }
-};
-
 }  // 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
index dec47bf..9054a92 100644
--- a/mojo/public/cpp/bindings/array_traits_stl.h
+++ b/mojo/public/cpp/bindings/array_traits_stl.h
@@ -5,8 +5,6 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_
 
-#include <map>
-#include <set>
 #include <vector>
 
 #include "mojo/public/cpp/bindings/array_traits.h"
@@ -44,17 +42,13 @@
     return input[index];
   }
 
-  static inline bool Resize(std::vector<T>& input, size_t size) {
-    // Instead of calling std::vector<T>::resize() directly, this is a hack to
-    // make compilers happy. Some compilers (e.g., Mac, Android, Linux MSan)
-    // 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.
-    //
-    // The "inline" keyword of this method matters. Without it, we have observed
-    // significant perf regression with some tests on Mac. crbug.com/631415
+  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);
     }
@@ -63,65 +57,6 @@
   }
 };
 
-// This ArrayTraits specialization is used only for serialization.
-template <typename T>
-struct ArrayTraits<std::set<T>> {
-  using Element = T;
-  using ConstIterator = typename std::set<T>::const_iterator;
-
-  static bool IsNull(const std::set<T>& input) {
-    // std::set<> is always converted to non-null mojom array.
-    return false;
-  }
-
-  static size_t GetSize(const std::set<T>& input) { return input.size(); }
-
-  static ConstIterator GetBegin(const std::set<T>& input) {
-    return input.begin();
-  }
-  static void AdvanceIterator(ConstIterator& iterator) {
-    ++iterator;
-  }
-  static const T& GetValue(ConstIterator& iterator) {
-    return *iterator;
-  }
-};
-
-template <typename K, typename V>
-struct MapValuesArrayView {
-  explicit MapValuesArrayView(const std::map<K, V>& map) : map(map) {}
-  const std::map<K, V>& map;
-};
-
-// Convenience function to create a MapValuesArrayView<> that infers the
-// template arguments from its argument type.
-template <typename K, typename V>
-MapValuesArrayView<K, V> MapValuesToArray(const std::map<K, V>& map) {
-  return MapValuesArrayView<K, V>(map);
-}
-
-// This ArrayTraits specialization is used only for serialization and converts
-// a map<K, V> into an array<V>, discarding the keys.
-template <typename K, typename V>
-struct ArrayTraits<MapValuesArrayView<K, V>> {
-  using Element = V;
-  using ConstIterator = typename std::map<K, V>::const_iterator;
-
-  static bool IsNull(const MapValuesArrayView<K, V>& input) {
-    // std::map<> is always converted to non-null mojom array.
-    return false;
-  }
-
-  static size_t GetSize(const MapValuesArrayView<K, V>& input) {
-    return input.map.size();
-  }
-  static ConstIterator GetBegin(const MapValuesArrayView<K, V>& input) {
-    return input.map.begin();
-  }
-  static void AdvanceIterator(ConstIterator& iterator) { ++iterator; }
-  static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
-};
-
 }  // 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/associated_binding.h b/mojo/public/cpp/bindings/associated_binding.h
index 5941166..1da5009 100644
--- a/mojo/public/cpp/bindings/associated_binding.h
+++ b/mojo/public/cpp/bindings/associated_binding.h
@@ -6,78 +6,23 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_BINDING_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/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_interface_ptr_info.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/bindings_export.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
-#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 
 namespace mojo {
 
-class MessageReceiver;
-
-// Base class used to factor out code in AssociatedBinding<T> expansions, in
-// particular for Bind().
-class MOJO_CPP_BINDINGS_EXPORT AssociatedBindingBase {
- public:
-  AssociatedBindingBase();
-  ~AssociatedBindingBase();
-
-  // Adds a message filter to be notified of each incoming message before
-  // dispatch. If a filter returns |false| from Accept(), the message is not
-  // dispatched and the pipe is closed. Filters cannot be removed.
-  void AddFilter(std::unique_ptr<MessageReceiver> filter);
-
-  // Closes the associated interface. Puts this object into a state where it can
-  // be rebound.
-  void Close();
-
-  // Similar to the method above, but also specifies a disconnect reason.
-  void CloseWithReason(uint32_t custom_reason, const std::string& description);
-
-  // 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);
-
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler);
-
-  // Indicates whether the associated binding has been completed.
-  bool is_bound() const { return !!endpoint_client_; }
-
-  // Sends a message on the underlying message pipe and runs the current
-  // message loop until its response is received. This can be used in tests to
-  // verify that no message was sent on a message pipe in response to some
-  // stimulus.
-  void FlushForTesting();
-
- protected:
-  void BindImpl(ScopedInterfaceEndpointHandle handle,
-                MessageReceiverWithResponderStatus* receiver,
-                std::unique_ptr<MessageReceiver> payload_validator,
-                bool expect_sync_requests,
-                scoped_refptr<base::SingleThreadTaskRunner> runner,
-                uint32_t interface_version);
-
-  std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
-};
-
 // Represents the implementation side of an associated interface. It is similar
 // to Binding, except that it doesn't own a message pipe handle.
 //
@@ -88,48 +33,53 @@
 // 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,
-          typename ImplRefTraits = RawPtrImplRefTraits<Interface>>
-class AssociatedBinding : public AssociatedBindingBase {
+template <typename Interface>
+class AssociatedBinding {
  public:
-  using ImplPointerType = typename ImplRefTraits::PointerType;
-
   // 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(ImplPointerType impl) { stub_.set_sink(impl); }
+  explicit AssociatedBinding(Interface* impl) : impl_(impl) {
+    stub_.set_sink(impl_);
+  }
 
   // Constructs a completed associated binding of |impl|. The output |ptr_info|
-  // should be sent by another interface. |impl| must outlive this object.
-  AssociatedBinding(ImplPointerType impl,
+  // 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(std::move(impl)) {
-    Bind(ptr_info, std::move(runner));
+      : AssociatedBinding(impl) {
+    Bind(ptr_info, associated_group, std::move(runner));
   }
 
   // Constructs a completed associated binding of |impl|. |impl| must outlive
   // the binding.
-  AssociatedBinding(ImplPointerType impl,
+  AssociatedBinding(Interface* impl,
                     AssociatedInterfaceRequest<Interface> request,
                     scoped_refptr<base::SingleThreadTaskRunner> runner =
                         base::ThreadTaskRunnerHandle::Get())
-      : AssociatedBinding(std::move(impl)) {
+      : 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 sent by another
-  // interface.
+  // 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()) {
-    auto request = MakeRequest(ptr_info);
-    ptr_info->set_version(Interface::Version_);
+    AssociatedInterfaceRequest<Interface> request;
+    associated_group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_PTR,
+                                                ptr_info, &request);
     Bind(std::move(request), std::move(runner));
   }
 
@@ -137,10 +87,35 @@
   void Bind(AssociatedInterfaceRequest<Interface> request,
             scoped_refptr<base::SingleThreadTaskRunner> runner =
                 base::ThreadTaskRunnerHandle::Get()) {
-    BindImpl(request.PassHandle(), &stub_,
-             base::WrapUnique(new typename Interface::RequestValidator_()),
-             Interface::HasSyncMethods_, std::move(runner),
-             Interface::Version_);
+    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
@@ -153,15 +128,44 @@
     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 ImplRefTraits::GetRawPointer(&stub_.sink()); }
+  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:
-  typename Interface::template Stub_<ImplRefTraits> stub_;
+  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);
 };
diff --git a/mojo/public/cpp/bindings/associated_group.h b/mojo/public/cpp/bindings/associated_group.h
index 14e78ec..836c0d6 100644
--- a/mojo/public/cpp/bindings/associated_group.h
+++ b/mojo/public/cpp/bindings/associated_group.h
@@ -5,9 +5,11 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_
 
-#include "base/callback.h"
+#include <utility>
+
 #include "base/memory/ref_counted.h"
-#include "mojo/public/cpp/bindings/bindings_export.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 {
@@ -15,34 +17,71 @@
 class AssociatedGroupController;
 
 // AssociatedGroup refers to all the interface endpoints running at one end of a
-// message pipe.
+// message pipe. It is used to create associated interfaces for that message
+// pipe.
 // It is thread safe and cheap to make copies.
-class MOJO_CPP_BINDINGS_EXPORT AssociatedGroup {
+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();
-
-  explicit AssociatedGroup(scoped_refptr<AssociatedGroupController> controller);
-
-  explicit AssociatedGroup(const ScopedInterfaceEndpointHandle& handle);
-
   AssociatedGroup(const AssociatedGroup& other);
 
   ~AssociatedGroup();
 
   AssociatedGroup& operator=(const AssociatedGroup& other);
 
-  // The return value of this getter if this object is initialized with a
-  // ScopedInterfaceEndpointHandle:
-  //   - If the handle is invalid, the return value will always be null.
-  //   - If the handle is valid and non-pending, the return value will be
-  //     non-null and remain unchanged even if the handle is later reset.
-  //   - If the handle is pending asssociation, the return value will initially
-  //     be null, change to non-null when/if the handle is associated, and
-  //     remain unchanged ever since.
-  AssociatedGroupController* GetController();
+  // |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:
-  base::Callback<AssociatedGroupController*()> controller_getter_;
+  friend class AssociatedGroupController;
+
+  void CreateEndpointHandlePair(
+      ScopedInterfaceEndpointHandle* local_endpoint,
+      ScopedInterfaceEndpointHandle* remote_endpoint);
+
   scoped_refptr<AssociatedGroupController> controller_;
 };
 
diff --git a/mojo/public/cpp/bindings/associated_group_controller.h b/mojo/public/cpp/bindings/associated_group_controller.h
index d33c277..0ab8253 100644
--- a/mojo/public/cpp/bindings/associated_group_controller.h
+++ b/mojo/public/cpp/bindings/associated_group_controller.h
@@ -9,47 +9,41 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/optional.h"
+#include "base/memory/ref_counted_delete_on_message_loop.h"
 #include "base/single_thread_task_runner.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.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,
-// which corresponds to one end of a message pipe.
-class MOJO_CPP_BINDINGS_EXPORT AssociatedGroupController
-    : public base::RefCountedThreadSafe<AssociatedGroupController> {
+// An internal interface used to manage endpoints within an associated group.
+class AssociatedGroupController :
+    public base::RefCountedDeleteOnMessageLoop<AssociatedGroupController> {
  public:
-  // Associates an interface with this AssociatedGroupController's message pipe.
-  // It takes ownership of |handle_to_send| and returns an interface ID that
-  // could be sent by any endpoints within the same associated group.
-  // If |handle_to_send| is not in pending association state, it returns
-  // kInvalidInterfaceId. Otherwise, the peer handle of |handle_to_send| joins
-  // the associated group and is no longer pending.
-  virtual InterfaceId AssociateInterface(
-      ScopedInterfaceEndpointHandle handle_to_send) = 0;
+  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
-  // joins this associated group.
+  // 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.
-  //
-  // On failure, the method returns an invalid handle. Usually that is because
-  // the ID has already been used to create a handle.
   virtual ScopedInterfaceEndpointHandle CreateLocalEndpointHandle(
       InterfaceId id) = 0;
 
   // Closes an interface endpoint handle.
-  virtual void CloseEndpointHandle(
-      InterfaceId id,
-      const base::Optional<DisconnectReason>& reason) = 0;
+  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
@@ -69,23 +63,21 @@
   // and notifies all interfaces running on this pipe.
   virtual void RaiseError() = 0;
 
+  std::unique_ptr<AssociatedGroup> CreateAssociatedGroup();
+
  protected:
-  friend class base::RefCountedThreadSafe<AssociatedGroupController>;
+  friend class base::RefCountedDeleteOnMessageLoop<AssociatedGroupController>;
+  friend class base::DeleteHelper<AssociatedGroupController>;
 
-  // Creates a new ScopedInterfaceEndpointHandle within this associated group.
+  // Creates a new ScopedInterfaceEndpointHandle associated with this
+  // controller.
   ScopedInterfaceEndpointHandle CreateScopedInterfaceEndpointHandle(
-      InterfaceId id);
-
-  // Notifies that the interface represented by |handle_to_send| and its peer
-  // has been associated with this AssociatedGroupController's message pipe, and
-  // |handle_to_send|'s peer has joined this associated group. (Note: it is the
-  // peer who has joined the associated group; |handle_to_send| will be sent to
-  // the remote side.)
-  // Returns false if |handle_to_send|'s peer has closed.
-  bool NotifyAssociation(ScopedInterfaceEndpointHandle* handle_to_send,
-                         InterfaceId id);
+      InterfaceId id,
+      bool is_local);
 
   virtual ~AssociatedGroupController();
+
+  DISALLOW_COPY_AND_ASSIGN(AssociatedGroupController);
 };
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr.h b/mojo/public/cpp/bindings/associated_interface_ptr.h
index 8e66f4e..10494ce 100644
--- a/mojo/public/cpp/bindings/associated_interface_ptr.h
+++ b/mojo/public/cpp/bindings/associated_interface_ptr.h
@@ -6,8 +6,6 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
 
 #include <stdint.h>
-
-#include <string>
 #include <utility>
 
 #include "base/callback.h"
@@ -16,12 +14,10 @@
 #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/connection_error_callback.h"
 #include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h"
-#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
-#include "mojo/public/cpp/system/message_pipe.h"
 
 namespace mojo {
 
@@ -30,9 +26,6 @@
 template <typename Interface>
 class AssociatedInterfacePtr {
  public:
-  using InterfaceType = Interface;
-  using PtrInfoType = AssociatedInterfacePtrInfo<Interface>;
-
   // Constructs an unbound AssociatedInterfacePtr.
   AssociatedInterfacePtr() {}
   AssociatedInterfacePtr(decltype(nullptr)) {}
@@ -65,16 +58,20 @@
   // multiple task runners to a single thread for the purposes of task
   // scheduling.
   //
-  // NOTE: The corresponding AssociatedInterfaceRequest must be sent over
-  // another interface before using this object to make calls. Please see the
-  // comments of MakeRequest(AssociatedInterfacePtr<Interface>*) for more
-  // details.
+  // 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();
 
-    if (info.is_valid())
+    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));
   }
 
@@ -89,6 +86,9 @@
   // 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.
@@ -107,12 +107,6 @@
     internal_state_.RequireVersion(version);
   }
 
-  // Sends a message on the underlying message pipe and runs the current
-  // message loop until its response is received. This can be used in tests to
-  // verify that no message was sent on a message pipe in response to some
-  // stimulus.
-  void FlushForTesting() { internal_state_.FlushForTesting(); }
-
   // Closes the associated interface (if any) and returns the pointer to the
   // unbound state.
   void reset() {
@@ -120,13 +114,6 @@
     internal_state_.Swap(&doomed);
   }
 
-  // Similar to the method above, but also specifies a disconnect reason.
-  void ResetWithReason(uint32_t custom_reason, const std::string& description) {
-    if (internal_state_.is_bound())
-      internal_state_.CloseWithReason(custom_reason, description);
-    reset();
-  }
-
   // 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(); }
@@ -139,11 +126,6 @@
     internal_state_.set_connection_error_handler(error_handler);
   }
 
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    internal_state_.set_connection_error_with_reason_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.
@@ -159,6 +141,12 @@
     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_;
@@ -191,95 +179,30 @@
   DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtr);
 };
 
-// Creates an associated interface. The returned request is supposed to be sent
-// over another interface (either associated or non-associated).
+// 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| must NOT be used to make calls before the request is sent.
-// Violating that will lead to crash. 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.
+// 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> MakeRequest(
+AssociatedInterfaceRequest<Interface> GetProxy(
     AssociatedInterfacePtr<Interface>* ptr,
+    AssociatedGroup* group,
     scoped_refptr<base::SingleThreadTaskRunner> runner =
         base::ThreadTaskRunnerHandle::Get()) {
+  AssociatedInterfaceRequest<Interface> request;
   AssociatedInterfacePtrInfo<Interface> ptr_info;
-  auto request = MakeRequest(&ptr_info);
+  group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_REQUEST,
+                                   &ptr_info, &request);
+
   ptr->Bind(std::move(ptr_info), std::move(runner));
   return request;
 }
 
-// Creates an associated interface. One of the two endpoints is supposed to be
-// sent over another interface (either associated or non-associated); while the
-// other is used locally.
-//
-// NOTE: If |ptr_info| is used locally and bound to an AssociatedInterfacePtr,
-// the interface pointer must NOT be used to make calls before the request is
-// sent. Please see NOTE of the previous function for more details.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> MakeRequest(
-    AssociatedInterfacePtrInfo<Interface>* ptr_info) {
-  ScopedInterfaceEndpointHandle handle0;
-  ScopedInterfaceEndpointHandle handle1;
-  ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&handle0,
-                                                              &handle1);
-
-  ptr_info->set_handle(std::move(handle0));
-  ptr_info->set_version(0);
-
-  AssociatedInterfaceRequest<Interface> request;
-  request.Bind(std::move(handle1));
-  return request;
-}
-
-// Like |GetProxy|, but the interface is never associated with any other
-// interface. The returned request can be bound directly to the corresponding
-// associated interface implementation, without first passing it through a
-// message pipe endpoint.
-//
-// This function has two main uses:
-//
-//  * In testing, where the returned request is bound to e.g. a mock and there
-//    are no other interfaces involved.
-//
-//  * When discarding messages sent on an interface, which can be done by
-//    discarding the returned request.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> GetIsolatedProxy(
-    AssociatedInterfacePtr<Interface>* ptr) {
-  MessagePipe pipe;
-  scoped_refptr<internal::MultiplexRouter> router0 =
-      new internal::MultiplexRouter(std::move(pipe.handle0),
-                                    internal::MultiplexRouter::MULTI_INTERFACE,
-                                    false, base::ThreadTaskRunnerHandle::Get());
-  scoped_refptr<internal::MultiplexRouter> router1 =
-      new internal::MultiplexRouter(std::move(pipe.handle1),
-                                    internal::MultiplexRouter::MULTI_INTERFACE,
-                                    true, base::ThreadTaskRunnerHandle::Get());
-
-  ScopedInterfaceEndpointHandle endpoint0, endpoint1;
-  ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&endpoint0,
-                                                              &endpoint1);
-  InterfaceId id = router1->AssociateInterface(std::move(endpoint0));
-  endpoint0 = router0->CreateLocalEndpointHandle(id);
-
-  ptr->Bind(AssociatedInterfacePtrInfo<Interface>(std::move(endpoint0),
-                                                  Interface::Version_));
-
-  AssociatedInterfaceRequest<Interface> request;
-  request.Bind(std::move(endpoint1));
-  return request;
-}
-
-// Creates an associated interface proxy in its own AssociatedGroup.
-// TODO(yzshen): Rename GetIsolatedProxy() to MakeIsolatedRequest(), and change
-// all callsites of this function to directly use that.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> MakeRequestForTesting(
-    AssociatedInterfacePtr<Interface>* ptr) {
-  return GetIsolatedProxy(ptr);
-}
-
 }  // 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
index 3c6ca54..bfb3297 100644
--- a/mojo/public/cpp/bindings/associated_interface_ptr_info.h
+++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
@@ -20,7 +20,6 @@
 class AssociatedInterfacePtrInfo {
  public:
   AssociatedInterfacePtrInfo() : version_(0u) {}
-  AssociatedInterfacePtrInfo(std::nullptr_t) : version_(0u) {}
 
   AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other)
       : handle_(std::move(other.handle_)), version_(other.version_) {
diff --git a/mojo/public/cpp/bindings/associated_interface_request.h b/mojo/public/cpp/bindings/associated_interface_request.h
index c37636c..30fcd16 100644
--- a/mojo/public/cpp/bindings/associated_interface_request.h
+++ b/mojo/public/cpp/bindings/associated_interface_request.h
@@ -5,7 +5,6 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
 
-#include <string>
 #include <utility>
 
 #include "base/macros.h"
@@ -65,10 +64,6 @@
     return !is_pending() && !other.is_pending();
   }
 
-  void ResetWithReason(uint32_t custom_reason, const std::string& description) {
-    handle_.ResetWithReason(custom_reason, description);
-  }
-
  private:
   ScopedInterfaceEndpointHandle handle_;
 
diff --git a/mojo/public/cpp/bindings/binding.h b/mojo/public/cpp/bindings/binding.h
index 1da331b..8c9ee2f 100644
--- a/mojo/public/cpp/bindings/binding.h
+++ b/mojo/public/cpp/bindings/binding.h
@@ -5,7 +5,6 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
 
-#include <string>
 #include <utility>
 
 #include "base/callback_forward.h"
@@ -13,17 +12,15 @@
 #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/connection_error_callback.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/bindings/raw_ptr_impl_ref_traits.h"
 #include "mojo/public/cpp/system/core.h"
 
 namespace mojo {
 
-class MessageReceiver;
+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
@@ -66,24 +63,21 @@
 // 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,
-          typename ImplRefTraits = RawPtrImplRefTraits<Interface>>
+template <typename Interface>
 class Binding {
  public:
-  using ImplPointerType = typename ImplRefTraits::PointerType;
-
   // 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(ImplPointerType impl) : internal_state_(std::move(impl)) {}
+  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(ImplPointerType impl,
+  Binding(Interface* impl,
           ScopedMessagePipeHandle handle,
           scoped_refptr<base::SingleThreadTaskRunner> runner =
               base::ThreadTaskRunnerHandle::Get())
-      : Binding(std::move(impl)) {
+      : Binding(impl) {
     Bind(std::move(handle), std::move(runner));
   }
 
@@ -92,22 +86,22 @@
   // 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(ImplPointerType impl,
+  Binding(Interface* impl,
           InterfacePtr<Interface>* ptr,
           scoped_refptr<base::SingleThreadTaskRunner> runner =
               base::ThreadTaskRunnerHandle::Get())
-      : Binding(std::move(impl)) {
+      : 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(ImplPointerType impl,
+  Binding(Interface* impl,
           InterfaceRequest<Interface> request,
           scoped_refptr<base::SingleThreadTaskRunner> runner =
               base::ThreadTaskRunnerHandle::Get())
-      : Binding(std::move(impl)) {
+      : Binding(impl) {
     Bind(request.PassMessagePipe(), std::move(runner));
   }
 
@@ -158,14 +152,6 @@
     Bind(request.PassMessagePipe(), std::move(runner));
   }
 
-  // Adds a message filter to be notified of each incoming message before
-  // dispatch. If a filter returns |false| from Accept(), the message is not
-  // dispatched and the pipe is closed. Filters cannot be removed.
-  void AddFilter(std::unique_ptr<MessageReceiver> filter) {
-    DCHECK(is_bound());
-    internal_state_.AddFilter(std::move(filter));
-  }
-
   // Whether there are any associated interfaces running on the pipe currently.
   bool HasAssociatedInterfaces() const {
     return internal_state_.HasAssociatedInterfaces();
@@ -203,11 +189,6 @@
   // state where it can be rebound to a new pipe.
   void Close() { internal_state_.Close(); }
 
-  // Similar to the method above, but also specifies a disconnect reason.
-  void CloseWithReason(uint32_t custom_reason, const std::string& description) {
-    internal_state_.CloseWithReason(custom_reason, description);
-  }
-
   // 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
@@ -236,12 +217,6 @@
     internal_state_.set_connection_error_handler(error_handler);
   }
 
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    DCHECK(is_bound());
-    internal_state_.set_connection_error_with_reason_handler(error_handler);
-  }
-
   // Returns the interface implementation that was previously specified. Caller
   // does not take ownership.
   Interface* impl() { return internal_state_.impl(); }
@@ -256,17 +231,20 @@
   // transferred to the caller.
   MessagePipeHandle handle() const { return internal_state_.handle(); }
 
-  // Sends a no-op message on the underlying message pipe and runs the current
-  // message loop until its response is received. This can be used in tests to
-  // verify that no message was sent on a message pipe in response to some
-  // stimulus.
-  void FlushForTesting() { internal_state_.FlushForTesting(); }
+  // 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, ImplRefTraits> internal_state_;
+  internal::BindingState<Interface, Interface::PassesAssociatedKinds_>
+      internal_state_;
 
   DISALLOW_COPY_AND_ASSIGN(Binding);
 };
diff --git a/mojo/public/cpp/bindings/binding_set.h b/mojo/public/cpp/bindings/binding_set.h
index 919f9c0..b1baca6 100644
--- a/mojo/public/cpp/bindings/binding_set.h
+++ b/mojo/public/cpp/bindings/binding_set.h
@@ -5,263 +5,111 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
 
-#include <string>
+#include <algorithm>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/message.h"
 
 namespace mojo {
 
-template <typename BindingType>
-struct BindingSetTraits;
-
-template <typename Interface, typename ImplRefTraits>
-struct BindingSetTraits<Binding<Interface, ImplRefTraits>> {
-  using ProxyType = InterfacePtr<Interface>;
-  using RequestType = InterfaceRequest<Interface>;
-  using BindingType = Binding<Interface, ImplRefTraits>;
-  using ImplPointerType = typename BindingType::ImplPointerType;
-
-  static RequestType MakeRequest(ProxyType* proxy) {
-    return mojo::MakeRequest(proxy);
-  }
-};
-
-using BindingId = size_t;
-
-template <typename ContextType>
-struct BindingSetContextTraits {
-  using Type = ContextType;
-
-  static constexpr bool SupportsContext() { return true; }
-};
-
-template <>
-struct BindingSetContextTraits<void> {
-  // NOTE: This choice of Type only matters insofar as it affects the size of
-  // the |context_| field of a BindingSetBase::Entry with void context. The
-  // context value is never used in this case.
-  using Type = bool;
-
-  static constexpr bool SupportsContext() { return false; }
-};
-
-// Generic definition used for BindingSet and AssociatedBindingSet to own a
-// collection of bindings which point to the same implementation.
-//
-// If |ContextType| is non-void, then every added binding must include a context
-// value of that type, and |dispatch_context()| will return that value during
-// the extent of any message dispatch targeting that specific binding.
-template <typename Interface, typename BindingType, typename ContextType>
-class BindingSetBase {
+// 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:
-  using ContextTraits = BindingSetContextTraits<ContextType>;
-  using Context = typename ContextTraits::Type;
-  using PreDispatchCallback = base::Callback<void(const Context&)>;
-  using Traits = BindingSetTraits<BindingType>;
-  using ProxyType = typename Traits::ProxyType;
-  using RequestType = typename Traits::RequestType;
-  using ImplPointerType = typename Traits::ImplPointerType;
-
-  BindingSetBase() {}
+  BindingSet() {}
+  ~BindingSet() { CloseAllBindings(); }
 
   void set_connection_error_handler(const base::Closure& error_handler) {
     error_handler_ = error_handler;
-    error_with_reason_handler_.Reset();
   }
 
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    error_with_reason_handler_ = error_handler;
-    error_handler_.Reset();
+  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());
   }
 
-  // Sets a callback to be invoked immediately before dispatching any message or
-  // error received by any of the bindings in the set. This may only be used
-  // with a non-void |ContextType|.
-  void set_pre_dispatch_handler(const PreDispatchCallback& handler) {
-    static_assert(ContextTraits::SupportsContext(),
-                  "Pre-dispatch handler usage requires non-void context type.");
-    pre_dispatch_handler_ = handler;
+  // 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;
   }
 
-  // Adds a new binding to the set which binds |request| to |impl| with no
-  // additional context.
-  BindingId AddBinding(ImplPointerType impl, RequestType request) {
-    static_assert(!ContextTraits::SupportsContext(),
-                  "Context value required for non-void context type.");
-    return AddBindingImpl(std::move(impl), std::move(request), false);
+  void CloseAllBindings() {
+    for (const auto& it : bindings_) {
+      if (it) {
+        it->Close();
+        delete it.get();
+      }
+    }
+    bindings_.clear();
   }
 
-  // Adds a new binding associated with |context|.
-  BindingId AddBinding(ImplPointerType impl,
-                       RequestType request,
-                       Context context) {
-    static_assert(ContextTraits::SupportsContext(),
-                  "Context value unsupported for void context type.");
-    return AddBindingImpl(std::move(impl), std::move(request),
-                          std::move(context));
-  }
-
-  // Removes a binding from the set. Note that this is safe to call even if the
-  // binding corresponding to |id| has already been removed.
-  //
-  // Returns |true| if the binding was removed and |false| if it didn't exist.
-  bool RemoveBinding(BindingId id) {
-    auto it = bindings_.find(id);
-    if (it == bindings_.end())
-      return false;
-    bindings_.erase(it);
-    return true;
-  }
-
-  // Returns a proxy bound to one end of a pipe whose other end is bound to
-  // |this|. If |id_storage| is not null, |*id_storage| will be set to the ID
-  // of the added binding.
-  ProxyType CreateInterfacePtrAndBind(ImplPointerType impl,
-                                      BindingId* id_storage = nullptr) {
-    ProxyType proxy;
-    BindingId id = AddBinding(std::move(impl), Traits::MakeRequest(&proxy));
-    if (id_storage)
-      *id_storage = id;
-    return proxy;
-  }
-
-  void CloseAllBindings() { bindings_.clear(); }
-
   bool empty() const { return bindings_.empty(); }
 
-  // Implementations may call this when processing a dispatched message or
-  // error. During the extent of message or error dispatch, this will return the
-  // context associated with the specific binding which received the message or
-  // error. Use AddBinding() to associated a context with a specific binding.
-  const Context& dispatch_context() const {
-    static_assert(ContextTraits::SupportsContext(),
-                  "dispatch_context() requires non-void context type.");
-    DCHECK(dispatch_context_);
-    return *dispatch_context_;
-  }
-
-  void FlushForTesting() {
-    for (auto& binding : bindings_)
-      binding.second->FlushForTesting();
-  }
-
  private:
-  friend class Entry;
-
-  class Entry {
+  class Element {
    public:
-    Entry(ImplPointerType impl,
-          RequestType request,
-          BindingSetBase* binding_set,
-          BindingId binding_id,
-          Context context)
-        : binding_(std::move(impl), std::move(request)),
-          binding_set_(binding_set),
-          binding_id_(binding_id),
-          context_(std::move(context)) {
-      if (ContextTraits::SupportsContext())
-        binding_.AddFilter(base::MakeUnique<DispatchFilter>(this));
-      binding_.set_connection_error_with_reason_handler(
-          base::Bind(&Entry::OnConnectionError, base::Unretained(this)));
+    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)));
     }
 
-    void FlushForTesting() { binding_.FlushForTesting(); }
+    ~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:
-    class DispatchFilter : public MessageReceiver {
-     public:
-      explicit DispatchFilter(Entry* entry) : entry_(entry) {}
-      ~DispatchFilter() override {}
+    Binding<Interface> binding_;
+    base::Closure error_handler_;
+    base::WeakPtrFactory<Element> weak_ptr_factory_;
 
-     private:
-      // MessageReceiver:
-      bool Accept(Message* message) override {
-        entry_->WillDispatch();
-        return true;
-      }
-
-      Entry* entry_;
-
-      DISALLOW_COPY_AND_ASSIGN(DispatchFilter);
-    };
-
-    void WillDispatch() {
-      DCHECK(ContextTraits::SupportsContext());
-      binding_set_->SetDispatchContext(&context_);
-    }
-
-    void OnConnectionError(uint32_t custom_reason,
-                           const std::string& description) {
-      if (ContextTraits::SupportsContext())
-        WillDispatch();
-      binding_set_->OnConnectionError(binding_id_, custom_reason, description);
-    }
-
-    BindingType binding_;
-    BindingSetBase* const binding_set_;
-    const BindingId binding_id_;
-    Context const context_;
-
-    DISALLOW_COPY_AND_ASSIGN(Entry);
+    DISALLOW_COPY_AND_ASSIGN(Element);
   };
 
-  void SetDispatchContext(const Context* context) {
-    DCHECK(ContextTraits::SupportsContext());
-    dispatch_context_ = context;
-    if (!pre_dispatch_handler_.is_null())
-      pre_dispatch_handler_.Run(*context);
-  }
-
-  BindingId AddBindingImpl(ImplPointerType impl,
-                           RequestType request,
-                           Context context) {
-    BindingId id = next_binding_id_++;
-    DCHECK_GE(next_binding_id_, 0u);
-    auto entry = base::MakeUnique<Entry>(std::move(impl), std::move(request),
-                                         this, id, std::move(context));
-    bindings_.insert(std::make_pair(id, std::move(entry)));
-    return id;
-  }
-
-  void OnConnectionError(BindingId id,
-                         uint32_t custom_reason,
-                         const std::string& description) {
-    auto it = bindings_.find(id);
-    DCHECK(it != bindings_.end());
-
-    // We keep the Entry alive throughout error dispatch.
-    std::unique_ptr<Entry> entry = std::move(it->second);
-    bindings_.erase(it);
+  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();
-    else if (!error_with_reason_handler_.is_null())
-      error_with_reason_handler_.Run(custom_reason, description);
   }
 
   base::Closure error_handler_;
-  ConnectionErrorWithReasonCallback error_with_reason_handler_;
-  PreDispatchCallback pre_dispatch_handler_;
-  BindingId next_binding_id_ = 0;
-  std::map<BindingId, std::unique_ptr<Entry>> bindings_;
-  const Context* dispatch_context_ = nullptr;
+  std::vector<base::WeakPtr<Element>> bindings_;
 
-  DISALLOW_COPY_AND_ASSIGN(BindingSetBase);
+  DISALLOW_COPY_AND_ASSIGN(BindingSet);
 };
 
-template <typename Interface, typename ContextType = void>
-using BindingSet = BindingSetBase<Interface, Binding<Interface>, ContextType>;
-
 }  // namespace mojo
 
 #endif  // MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
diff --git a/mojo/public/cpp/bindings/bindings_export.h b/mojo/public/cpp/bindings/bindings_export.h
deleted file mode 100644
index 9fd7a27..0000000
--- a/mojo/public/cpp/bindings/bindings_export.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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_BINDINGS_EXPORT_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_BINDINGS_EXPORT_H_
-
-#if defined(COMPONENT_BUILD)
-
-#if defined(WIN32)
-
-#if defined(MOJO_CPP_BINDINGS_IMPLEMENTATION)
-#define MOJO_CPP_BINDINGS_EXPORT __declspec(dllexport)
-#else
-#define MOJO_CPP_BINDINGS_EXPORT __declspec(dllimport)
-#endif
-
-#else  // !defined(WIN32)
-
-#if defined(MOJO_CPP_BINDINGS_IMPLEMENTATION)
-#define MOJO_CPP_BINDINGS_EXPORT __attribute((visibility("default")))
-#else
-#define MOJO_CPP_BINDINGS_EXPORT
-#endif
-
-#endif  // defined(WIN32)
-
-#else  // !defined(COMPONENT_BUILD)
-
-#define MOJO_CPP_BINDINGS_EXPORT
-
-#endif  // defined(COMPONENT_BUILD)
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_BINDINGS_EXPORT_H_
diff --git a/mojo/public/cpp/bindings/clone_traits.h b/mojo/public/cpp/bindings/clone_traits.h
deleted file mode 100644
index 203ab34..0000000
--- a/mojo/public/cpp/bindings/clone_traits.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2017 The Chromium Authors. All 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_CLONE_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_CLONE_TRAITS_H_
-
-#include <type_traits>
-#include <unordered_map>
-#include <vector>
-
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/lib/template_util.h"
-
-namespace mojo {
-
-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:
-  internal::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>(mojo::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(mojo::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(mojo::Clone(element.first),
-                                   mojo::Clone(element.second)));
-    }
-    return result;
-  }
-};
-
-template <typename T>
-T Clone(const T& input) {
-  return CloneTraits<T>::Clone(input);
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_CLONE_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/connection_error_callback.h b/mojo/public/cpp/bindings/connection_error_callback.h
deleted file mode 100644
index 306e99e..0000000
--- a/mojo/public/cpp/bindings/connection_error_callback.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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_CONNECTION_ERROR_CALLBACK_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_ERROR_CALLBACK_H_
-
-#include "base/callback.h"
-
-namespace mojo {
-
-// This callback type accepts user-defined disconnect reason and description. If
-// the other side specifies a reason on closing the connection, it will be
-// passed to the error handler.
-using ConnectionErrorWithReasonCallback =
-    base::Callback<void(uint32_t /* custom_reason */,
-                        const std::string& /* description */)>;
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_ERROR_CALLBACK_H_
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index 01e9236..d14ad17 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -8,13 +8,10 @@
 #include <memory>
 
 #include "base/callback.h"
-#include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/sync_handle_watcher.h"
 #include "mojo/public/cpp/system/core.h"
@@ -36,8 +33,7 @@
 //   - 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 MOJO_CPP_BINDINGS_EXPORT Connector
-    : NON_EXPORTED_BASE(public MessageReceiver) {
+class Connector : public MessageReceiver {
  public:
   enum ConnectorConfig {
     // Connector::Accept() is only called from a single thread.
@@ -142,7 +138,6 @@
 
   // Whether currently the control flow is inside the sync handle watcher
   // callback.
-  // It always returns false after CloseMessagePipe()/PassMessagePipe().
   bool during_sync_handle_watcher_callback() const {
     return sync_handle_watcher_callback_count_ > 0;
   }
@@ -151,10 +146,6 @@
     return task_runner_.get();
   }
 
-  // Sets the tag used by the heap profiler.
-  // |tag| must be a const string literal.
-  void SetWatcherHeapProfilerTag(const char* tag);
-
  private:
   // Callback of mojo::Watcher.
   void OnWatcherHandleReady(MojoResult result);
@@ -164,8 +155,7 @@
 
   void WaitToReadMore();
 
-  // Returns false if it is impossible to receive more messages in the future.
-  // |this| may have been destroyed in that case.
+  // Returns false if |this| was destroyed during message dispatch.
   WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result);
 
   // |this| can be destroyed during message dispatch.
@@ -185,40 +175,31 @@
   base::Closure connection_error_handler_;
 
   ScopedMessagePipeHandle message_pipe_;
-  MessageReceiver* incoming_receiver_ = nullptr;
+  MessageReceiver* incoming_receiver_;
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  std::unique_ptr<Watcher> handle_watcher_;
+  Watcher handle_watcher_;
 
-  bool error_ = false;
-  bool drop_writes_ = false;
-  bool enforce_errors_from_incoming_receiver_ = true;
+  bool error_;
+  bool drop_writes_;
+  bool enforce_errors_from_incoming_receiver_;
 
-  bool paused_ = false;
+  bool paused_;
 
   // If sending messages is allowed from multiple threads, |lock_| is used to
   // protect modifications to |message_pipe_| and |drop_writes_|.
-  base::Optional<base::Lock> lock_;
+  std::unique_ptr<base::Lock> lock_;
 
   std::unique_ptr<SyncHandleWatcher> sync_watcher_;
-  bool allow_woken_up_by_others_ = false;
+  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_ = 0;
+  size_t sync_handle_watcher_callback_count_;
 
   base::ThreadChecker thread_checker_;
 
-  base::Lock connected_lock_;
-  bool connected_ = true;
-
-  // The tag used to track heap allocations that originated from a Watcher
-  // notification.
-  const char* heap_profiler_tag_ = nullptr;
-
   // 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.
-  // NOTE: This weak pointer is invalidated when the message pipe is closed or
-  // transferred (i.e., when |connected_| is set to false).
   base::WeakPtr<Connector> weak_self_;
   base::WeakPtrFactory<Connector> weak_factory_;
 
diff --git a/mojo/public/cpp/bindings/disconnect_reason.h b/mojo/public/cpp/bindings/disconnect_reason.h
deleted file mode 100644
index c04e8ad..0000000
--- a/mojo/public/cpp/bindings/disconnect_reason.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 The Chromium Authors. All 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_DISCONNECT_REASON_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_DISCONNECT_REASON_H_
-
-#include <stdint.h>
-
-#include <string>
-
-namespace mojo {
-
-struct DisconnectReason {
- public:
-  DisconnectReason(uint32_t in_custom_reason, const std::string& in_description)
-      : custom_reason(in_custom_reason), description(in_description) {}
-
-  uint32_t custom_reason;
-  std::string description;
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_DISCONNECT_REASON_H_
diff --git a/mojo/public/cpp/bindings/filter_chain.h b/mojo/public/cpp/bindings/filter_chain.h
deleted file mode 100644
index 1262f39..0000000
--- a/mojo/public/cpp/bindings/filter_chain.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2014 The Chromium Authors. All 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_FILTER_CHAIN_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_FILTER_CHAIN_H_
-
-#include <utility>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/message.h"
-
-namespace mojo {
-
-class MOJO_CPP_BINDINGS_EXPORT FilterChain
-    : NON_EXPORTED_BASE(public MessageReceiver) {
- 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() override;
-
-  template <typename FilterType, typename... Args>
-  inline void Append(Args&&... args);
-
-  void Append(std::unique_ptr<MessageReceiver> filter);
-
-  // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
-  // this object is alive.
-  void SetSink(MessageReceiver* sink);
-
-  // MessageReceiver:
-  bool Accept(Message* message) override;
-
- private:
-  std::vector<std::unique_ptr<MessageReceiver>> filters_;
-
-  MessageReceiver* sink_;
-
-  DISALLOW_COPY_AND_ASSIGN(FilterChain);
-};
-
-template <typename FilterType, typename... Args>
-inline void FilterChain::Append(Args&&... args) {
-  Append(base::MakeUnique<FilterType>(std::forward<Args>(args)...));
-}
-
-template <>
-inline void FilterChain::Append<PassThroughFilter>() {
-}
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_FILTER_CHAIN_H_
diff --git a/mojo/public/cpp/bindings/interface_data_view.h b/mojo/public/cpp/bindings/interface_data_view.h
deleted file mode 100644
index ef12254..0000000
--- a/mojo/public/cpp/bindings/interface_data_view.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_DATA_VIEW_H_
-
-namespace mojo {
-
-// They are used for type identification purpose only.
-template <typename Interface>
-class AssociatedInterfacePtrInfoDataView {};
-
-template <typename Interface>
-class AssociatedInterfaceRequestDataView {};
-
-template <typename Interface>
-class InterfacePtrDataView {};
-
-template <typename Interface>
-class InterfaceRequestDataView {};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h
index 0aea756..9dc40a2 100644
--- a/mojo/public/cpp/bindings/interface_endpoint_client.h
+++ b/mojo/public/cpp/bindings/interface_endpoint_client.h
@@ -11,42 +11,34 @@
 #include <memory>
 
 #include "base/callback.h"
-#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
-#include "mojo/public/cpp/bindings/filter_chain.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/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 MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient
-    : NON_EXPORTED_BASE(public MessageReceiverWithResponder) {
+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<MessageReceiver> payload_validator,
+                          std::unique_ptr<MessageFilter> payload_validator,
                           bool expect_sync_requests,
-                          scoped_refptr<base::SingleThreadTaskRunner> runner,
-                          uint32_t interface_version);
+                          scoped_refptr<base::SingleThreadTaskRunner> runner);
   ~InterfaceEndpointClient() override;
 
   // Sets the error handler to receive notifications when an error is
@@ -54,14 +46,6 @@
   void set_connection_error_handler(const base::Closure& error_handler) {
     DCHECK(thread_checker_.CalledOnValidThread());
     error_handler_ = error_handler;
-    error_with_reason_handler_.Reset();
-  }
-
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    error_with_reason_handler_ = error_handler;
-    error_handler_.Reset();
   }
 
   // Returns true if an error was encountered.
@@ -76,11 +60,11 @@
     return !async_responders_.empty() || !sync_responses_.empty();
   }
 
+  AssociatedGroupController* group_controller() const {
+    return handle_.group_controller();
+  }
   AssociatedGroup* associated_group();
-
-  // Adds a MessageReceiver which can filter a message after validation but
-  // before dispatch.
-  void AddFilter(std::unique_ptr<MessageReceiver> filter);
+  uint32_t interface_id() const;
 
   // After this call the object is in an invalid state and shouldn't be reused.
   ScopedInterfaceEndpointHandle PassHandle();
@@ -89,11 +73,7 @@
   // and notifies all interfaces running on this pipe.
   void RaiseError();
 
-  void CloseWithReason(uint32_t custom_reason, const std::string& description);
-
   // MessageReceiverWithResponder implementation:
-  // They must only be called when the handle is not in pending association
-  // state.
   bool Accept(Message* message) override;
   bool AcceptWithResponder(Message* message,
                            MessageReceiver* responder) override;
@@ -103,14 +83,7 @@
 
   // NOTE: |message| must have passed message header validation.
   bool HandleIncomingMessage(Message* message);
-  void NotifyError(const base::Optional<DisconnectReason>& reason);
-
-  // The following methods send interface control messages.
-  // They must only be called when the handle is not in pending association
-  // state.
-  void QueryVersion(const base::Callback<void(uint32_t)>& callback);
-  void RequireVersion(uint32_t version);
-  void FlushForTesting();
+  void NotifyError();
 
  private:
   // Maps from the id of a response to the MessageReceiver that handles the
@@ -123,7 +96,7 @@
     explicit SyncResponseInfo(bool* in_response_received);
     ~SyncResponseInfo();
 
-    Message response;
+    std::unique_ptr<Message> response;
 
     // Points to a stack-allocated variable.
     bool* response_received;
@@ -150,37 +123,26 @@
     DISALLOW_COPY_AND_ASSIGN(HandleIncomingMessageThunk);
   };
 
-  void InitControllerIfNecessary();
-
-  void OnAssociationEvent(
-      ScopedInterfaceEndpointHandle::AssociationEvent event);
-
   bool HandleValidatedMessage(Message* message);
 
-  const bool expect_sync_requests_ = false;
-
   ScopedInterfaceEndpointHandle handle_;
   std::unique_ptr<AssociatedGroup> associated_group_;
-  InterfaceEndpointController* controller_ = nullptr;
+  InterfaceEndpointController* controller_;
 
-  MessageReceiverWithResponderStatus* const incoming_receiver_ = nullptr;
+  MessageReceiverWithResponderStatus* const incoming_receiver_;
+  std::unique_ptr<MessageFilter> payload_validator_;
   HandleIncomingMessageThunk thunk_;
-  FilterChain filters_;
 
   AsyncResponderMap async_responders_;
   SyncResponseMap sync_responses_;
 
-  uint64_t next_request_id_ = 1;
+  uint64_t next_request_id_;
 
   base::Closure error_handler_;
-  ConnectionErrorWithReasonCallback error_with_reason_handler_;
-  bool encountered_error_ = false;
+  bool encountered_error_;
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
-  internal::ControlMessageProxy control_message_proxy_;
-  internal::ControlMessageHandler control_message_handler_;
-
   base::ThreadChecker thread_checker_;
 
   base::WeakPtrFactory<InterfaceEndpointClient> weak_ptr_factory_;
diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h
index e88be74..edcb9bf 100644
--- a/mojo/public/cpp/bindings/interface_ptr.h
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -6,8 +6,6 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
 
 #include <stdint.h>
-
-#include <string>
 #include <utility>
 
 #include "base/callback_forward.h"
@@ -16,12 +14,13 @@
 #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/connection_error_callback.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
@@ -38,9 +37,6 @@
 template <typename Interface>
 class InterfacePtr {
  public:
-  using InterfaceType = Interface;
-  using PtrInfoType = InterfacePtrInfo<Interface>;
-
   // Constructs an unbound InterfacePtr.
   InterfacePtr() {}
   InterfacePtr(decltype(nullptr)) {}
@@ -118,12 +114,6 @@
     internal_state_.RequireVersion(version);
   }
 
-  // Sends a no-op message on the underlying message pipe and runs the current
-  // message loop until its response is received. This can be used in tests to
-  // verify that no message was sent on a message pipe in response to some
-  // stimulus.
-  void FlushForTesting() { internal_state_.FlushForTesting(); }
-
   // Closes the bound message pipe (if any) and returns the pointer to the
   // unbound state.
   void reset() {
@@ -131,13 +121,6 @@
     internal_state_.Swap(&doomed);
   }
 
-  // Similar to the method above, but also specifies a disconnect reason.
-  void ResetWithReason(uint32_t custom_reason, const std::string& description) {
-    if (internal_state_.is_bound())
-      internal_state_.CloseWithReason(custom_reason, description);
-    reset();
-  }
-
   // Whether there are any associated interfaces running on the pipe currently.
   bool HasAssociatedInterfaces() const {
     return internal_state_.HasAssociatedInterfaces();
@@ -157,11 +140,6 @@
     internal_state_.set_connection_error_handler(error_handler);
   }
 
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    internal_state_.set_connection_error_with_reason_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).
@@ -184,6 +162,14 @@
     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;
@@ -194,7 +180,8 @@
   }
 
   // DO NOT USE. Exposed only for internal use and for testing.
-  internal::InterfacePtrState<Interface>* internal_state() {
+  internal::InterfacePtrState<Interface, Interface::PassesAssociatedKinds_>*
+  internal_state() {
     return &internal_state_;
   }
 
@@ -202,7 +189,9 @@
   // implicitly convertible to a real bool (which is dangerous).
  private:
   // TODO(dcheng): Use an explicit conversion operator.
-  typedef internal::InterfacePtrState<Interface> InterfacePtr::*Testable;
+  typedef internal::InterfacePtrState<Interface,
+                                      Interface::PassesAssociatedKinds_>
+      InterfacePtr::*Testable;
 
  public:
   operator Testable() const {
@@ -218,7 +207,8 @@
   template <typename T>
   bool operator!=(const InterfacePtr<T>& other) const = delete;
 
-  typedef internal::InterfacePtrState<Interface> State;
+  typedef internal::InterfacePtrState<Interface,
+                                      Interface::PassesAssociatedKinds_> State;
   mutable State internal_state_;
 
   DISALLOW_COPY_AND_ASSIGN(InterfacePtr);
diff --git a/mojo/public/cpp/bindings/interface_ptr_set.h b/mojo/public/cpp/bindings/interface_ptr_set.h
index 09a2682..d4b2046 100644
--- a/mojo/public/cpp/bindings/interface_ptr_set.h
+++ b/mojo/public/cpp/bindings/interface_ptr_set.h
@@ -16,10 +16,6 @@
 namespace mojo {
 namespace internal {
 
-// TODO(blundell): This class should be rewritten to be structured
-// similarly to BindingSet if possible, with PtrSet owning its
-// Elements and those Elements calling back into PtrSet on connection
-// error.
 template <typename Interface, template <typename> class Ptr>
 class PtrSet {
  public:
@@ -59,13 +55,7 @@
 
     ~Element() {}
 
-    void Close() {
-      ptr_.reset();
-
-      // Resetting the interface ptr means that it won't call this object back
-      // on connection error anymore, so this object must delete itself now.
-      DeleteElement(this);
-    }
+    void Close() { ptr_.reset(); }
 
     Interface* get() { return ptr_.get(); }
 
diff --git a/mojo/public/cpp/bindings/interface_request.h b/mojo/public/cpp/bindings/interface_request.h
index 29d8836..fc23aec 100644
--- a/mojo/public/cpp/bindings/interface_request.h
+++ b/mojo/public/cpp/bindings/interface_request.h
@@ -5,17 +5,12 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
 
-#include <string>
 #include <utility>
 
 #include "base/macros.h"
-#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/pipe_control_message_proxy.h"
-#include "mojo/public/cpp/system/message_pipe.h"
 
 namespace mojo {
 
@@ -33,19 +28,6 @@
   InterfaceRequest() {}
   InterfaceRequest(decltype(nullptr)) {}
 
-  // Creates a new message pipe over which Interface is to be served, binding
-  // the specified InterfacePtr to one end of the message pipe and this
-  // InterfaceRequest to the other. For example usage, see comments on
-  // MakeRequest(InterfacePtr*) below.
-  explicit InterfaceRequest(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));
-    Bind(std::move(pipe.handle1));
-  }
-
   // Takes the message pipe from another InterfaceRequest.
   InterfaceRequest(InterfaceRequest&& other) {
     handle_ = std::move(other.handle_);
@@ -82,20 +64,6 @@
     return !is_pending() && !other.is_pending();
   }
 
-  void ResetWithReason(uint32_t custom_reason, const std::string& description) {
-    if (!handle_.is_valid())
-      return;
-
-    Message message =
-        PipeControlMessageProxy::ConstructPeerEndpointClosedMessage(
-            kMasterInterfaceId, DisconnectReason(custom_reason, description));
-    MojoResult result = WriteMessageNew(
-        handle_.get(), message.TakeMojoMessage(), MOJO_WRITE_MESSAGE_FLAG_NONE);
-    DCHECK_EQ(MOJO_RESULT_OK, result);
-
-    handle_.reset();
-  }
-
  private:
   ScopedMessagePipeHandle handle_;
 
@@ -135,9 +103,9 @@
 //
 //   DatabasePtr database = ...;  // Connect to database.
 //   TablePtr table;
-//   database->OpenTable(MakeRequest(&table));
+//   database->OpenTable(GetProxy(&table));
 //
-// Upon return from MakeRequest, |table| is ready to have methods called on it.
+// Upon return from GetProxy, |table| is ready to have methods called on it.
 //
 // Example #2: Registering a local implementation with a remote service.
 // =====================================================================
@@ -151,16 +119,19 @@
 //
 //   CollectorPtr collector = ...;  // Connect to Collector.
 //   SourcePtr source;
-//   InterfaceRequest<Source> source_request(&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> MakeRequest(
+InterfaceRequest<Interface> GetProxy(
     InterfacePtr<Interface>* ptr,
     scoped_refptr<base::SingleThreadTaskRunner> runner =
         base::ThreadTaskRunnerHandle::Get()) {
-  return InterfaceRequest<Interface>(ptr, runner);
+  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.
diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h
index eecfcfb..ba6d16e 100644
--- a/mojo/public/cpp/bindings/lib/array_internal.h
+++ b/mojo/public/cpp/bindings/lib/array_internal.h
@@ -13,7 +13,6 @@
 
 #include "base/logging.h"
 #include "mojo/public/c/system/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.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"
@@ -29,13 +28,13 @@
 template <typename K, typename V>
 class Map_Data;
 
-MOJO_CPP_BINDINGS_EXPORT std::string
-MakeMessageWithArrayIndex(const char* message, size_t size, size_t index);
+std::string MakeMessageWithArrayIndex(const char* message,
+                                      size_t size,
+                                      size_t index);
 
-MOJO_CPP_BINDINGS_EXPORT std::string MakeMessageWithExpectedArraySize(
-    const char* message,
-    size_t size,
-    size_t expected_size);
+std::string MakeMessageWithExpectedArraySize(const char* message,
+                                             size_t size,
+                                             size_t expected_size);
 
 template <typename T>
 struct ArrayDataTraits {
@@ -68,7 +67,7 @@
 struct ArrayDataTraits<bool> {
   // Helper class to emulate a reference to a bool, used for direct element
   // access.
-  class MOJO_CPP_BINDINGS_EXPORT BitRef {
+  class BitRef {
    public:
     ~BitRef();
     BitRef& operator=(bool value);
@@ -110,7 +109,7 @@
 //
 // TODO(yzshen): Validation code should be organzied in a way similar to
 // Serializer<>, or merged into it. It should be templatized with the mojo
-// data view type instead of the data type, that way we can use MojomTypeTraits
+// 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>
@@ -263,7 +262,7 @@
       T,
       IsUnionDataType<T>::value,
       std::is_same<T, AssociatedInterface_Data>::value ||
-          std::is_same<T, AssociatedEndpointHandle_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;
diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h
index d2f8ecf..5db27a5 100644
--- a/mojo/public/cpp/bindings/lib/array_serialization.h
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -14,11 +14,12 @@
 #include <vector>
 
 #include "base/logging.h"
-#include "mojo/public/cpp/bindings/array_data_view.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 {
@@ -45,7 +46,7 @@
   using GetNextResult =
       decltype(Traits::GetValue(std::declval<IteratorType&>()));
   GetNextResult GetNext() {
-    GetNextResult value = Traits::GetValue(iter_);
+    auto& value = Traits::GetValue(iter_);
     Traits::AdvanceIterator(iter_);
     return value;
   }
@@ -286,19 +287,13 @@
   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) {
-    size_t element_count = input->GetSize();
-    if (BelongsTo<Element,
-                  MojomTypeCategory::ASSOCIATED_INTERFACE |
-                      MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST>::value) {
-      for (size_t i = 0; i < element_count; ++i) {
-        typename UserTypeIterator::GetNextResult next = input->GetNext();
-        size_t size = PrepareToSerialize<Element>(next, context);
-        DCHECK_EQ(size, 0u);
-      }
-    }
-    return sizeof(Data) + Align(element_count * sizeof(typename Data::Element));
+    return sizeof(Data) +
+           Align(input->GetSize() * sizeof(typename Data::Element));
   }
 
   static void SerializeElements(UserTypeIterator* input,
@@ -311,8 +306,7 @@
 
     size_t size = input->GetSize();
     for (size_t i = 0; i < size; ++i) {
-      typename UserTypeIterator::GetNextResult next = input->GetNext();
-      Serialize<Element>(next, &output->at(i), context);
+      Serialize<Element>(input->GetNext(), &output->at(i), context);
 
       static const ValidationError kError =
           BelongsTo<Element,
@@ -367,10 +361,8 @@
                                   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) {
-      typename UserTypeIterator::GetNextResult next = input->GetNext();
-      size += PrepareToSerialize<Element>(next, context);
-    }
+    for (size_t i = 0; i < element_count; ++i)
+      size += PrepareToSerialize<Element>(input->GetNext(), context);
     return size;
   }
 
@@ -382,8 +374,7 @@
     size_t size = input->GetSize();
     for (size_t i = 0; i < size; ++i) {
       DataElementPtr data_ptr;
-      typename UserTypeIterator::GetNextResult next = input->GetNext();
-      SerializeCaller<Element>::Run(next, buf, &data_ptr,
+      SerializeCaller<Element>::Run(input->GetNext(), buf, &data_ptr,
                                     validate_params->element_validate_params,
                                     context);
       output->at(i).Set(data_ptr);
@@ -453,6 +444,10 @@
   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();
@@ -460,8 +455,7 @@
     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.
-      typename UserTypeIterator::GetNextResult next = input->GetNext();
-      size += PrepareToSerialize<Element>(next, false, context);
+      size += PrepareToSerialize<Element>(input->GetNext(), false, context);
     }
     return size;
   }
@@ -474,8 +468,7 @@
     size_t size = input->GetSize();
     for (size_t i = 0; i < size; ++i) {
       typename Data::Element* result = output->storage() + i;
-      typename UserTypeIterator::GetNextResult next = input->GetNext();
-      Serialize<Element>(next, buf, &result, true, context);
+      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,
@@ -499,13 +492,13 @@
 };
 
 template <typename Element, typename MaybeConstUserType>
-struct Serializer<ArrayDataView<Element>, MaybeConstUserType> {
+struct Serializer<Array<Element>, MaybeConstUserType> {
   using UserType = typename std::remove_const<MaybeConstUserType>::type;
   using Traits = ArrayTraits<UserType>;
-  using Impl = ArraySerializer<ArrayDataView<Element>,
+  using Impl = ArraySerializer<Array<Element>,
                                MaybeConstUserType,
                                ArrayIterator<Traits, MaybeConstUserType>>;
-  using Data = typename MojomTypeTraits<ArrayDataView<Element>>::Data;
+  using Data = typename MojomTypeTraits<Array<Element>>::Data;
 
   static size_t PrepareToSerialize(MaybeConstUserType& input,
                                    SerializationContext* context) {
diff --git a/mojo/public/cpp/bindings/lib/associated_group.cc b/mojo/public/cpp/bindings/lib/associated_group.cc
index 3e95eeb..a9c53b5 100644
--- a/mojo/public/cpp/bindings/lib/associated_group.cc
+++ b/mojo/public/cpp/bindings/lib/associated_group.cc
@@ -8,27 +8,28 @@
 
 namespace mojo {
 
-AssociatedGroup::AssociatedGroup() = default;
+AssociatedGroup::AssociatedGroup() {}
 
-AssociatedGroup::AssociatedGroup(
-    scoped_refptr<AssociatedGroupController> controller)
-    : controller_(std::move(controller)) {}
+AssociatedGroup::AssociatedGroup(const AssociatedGroup& other)
+    : controller_(other.controller_) {}
 
-AssociatedGroup::AssociatedGroup(const ScopedInterfaceEndpointHandle& handle)
-    : controller_getter_(handle.CreateGroupControllerGetter()) {}
+AssociatedGroup::~AssociatedGroup() {}
 
-AssociatedGroup::AssociatedGroup(const AssociatedGroup& other) = default;
+AssociatedGroup& AssociatedGroup::operator=(const AssociatedGroup& other) {
+  if (this == &other)
+    return *this;
 
-AssociatedGroup::~AssociatedGroup() = default;
+  controller_ = other.controller_;
+  return *this;
+}
 
-AssociatedGroup& AssociatedGroup::operator=(const AssociatedGroup& other) =
-    default;
+void AssociatedGroup::CreateEndpointHandlePair(
+    ScopedInterfaceEndpointHandle* local_endpoint,
+    ScopedInterfaceEndpointHandle* remote_endpoint) {
+  if (!controller_)
+    return;
 
-AssociatedGroupController* AssociatedGroup::GetController() {
-  if (controller_)
-    return controller_.get();
-
-  return controller_getter_.Run();
+  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
index f4a9aa2..42db9b3 100644
--- a/mojo/public/cpp/bindings/lib/associated_group_controller.cc
+++ b/mojo/public/cpp/bindings/lib/associated_group_controller.cc
@@ -8,17 +8,25 @@
 
 namespace mojo {
 
+AssociatedGroupController::AssociatedGroupController(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : base::RefCountedDeleteOnMessageLoop<AssociatedGroupController>(
+          task_runner) {}
+
 AssociatedGroupController::~AssociatedGroupController() {}
 
-ScopedInterfaceEndpointHandle
-AssociatedGroupController::CreateScopedInterfaceEndpointHandle(InterfaceId id) {
-  return ScopedInterfaceEndpointHandle(id, this);
+std::unique_ptr<AssociatedGroup>
+AssociatedGroupController::CreateAssociatedGroup() {
+  std::unique_ptr<AssociatedGroup> group(new AssociatedGroup);
+  group->controller_ = this;
+  return group;
 }
 
-bool AssociatedGroupController::NotifyAssociation(
-    ScopedInterfaceEndpointHandle* handle_to_send,
-    InterfaceId id) {
-  return handle_to_send->NotifyAssociation(id, this);
+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
index 72f7960..c7f74fb 100644
--- a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
@@ -9,7 +9,6 @@
 
 #include <algorithm>  // For |std::swap()|.
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -19,10 +18,11 @@
 #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/connection_error_callback.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"
 
@@ -46,12 +46,18 @@
 
   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) {
-    // It is safe to capture |this| because the callback won't be run after this
-    // object goes away.
-    endpoint_client_->QueryVersion(
-        base::Bind(&AssociatedInterfacePtrState::OnQueryVersion,
-                   base::Unretained(this), 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) {
@@ -59,13 +65,9 @@
       return;
 
     version_ = version;
-    endpoint_client_->RequireVersion(version);
-  }
-
-  void FlushForTesting() { endpoint_client_->FlushForTesting(); }
-
-  void CloseWithReason(uint32_t custom_reason, const std::string& description) {
-    endpoint_client_->CloseWithReason(custom_reason, description);
+    // 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) {
@@ -83,13 +85,13 @@
     DCHECK(info.is_valid());
 
     version_ = info.version();
-    // The version is only queried from the client so the value passed here
-    // will not be used.
     endpoint_client_.reset(new InterfaceEndpointClient(
         info.PassHandle(), nullptr,
         base::WrapUnique(new typename Interface::ResponseValidator_()), false,
-        std::move(runner), 0u));
+        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
@@ -112,12 +114,6 @@
     endpoint_client_->set_connection_error_handler(error_handler);
   }
 
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    DCHECK(endpoint_client_);
-    endpoint_client_->set_connection_error_with_reason_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();
@@ -127,13 +123,6 @@
     return endpoint_client_ ? endpoint_client_->associated_group() : nullptr;
   }
 
-  void ForwardMessage(Message message) { endpoint_client_->Accept(&message); }
-
-  void ForwardMessageWithResponder(Message message,
-                                   std::unique_ptr<MessageReceiver> responder) {
-    endpoint_client_->AcceptWithResponder(&message, responder.release());
-  }
-
  private:
   using Proxy = typename Interface::Proxy_;
 
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h
index 0b0dbee..c8d3e83 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.h
+++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -6,7 +6,6 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_
 
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -16,15 +15,15 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/filter_chain.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"
@@ -32,34 +31,76 @@
 namespace mojo {
 namespace internal {
 
-class MOJO_CPP_BINDINGS_EXPORT BindingStateBase {
+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:
-  BindingStateBase();
-  ~BindingStateBase();
+  explicit BindingState(Interface* impl) : impl_(impl) {
+    stub_.set_sink(impl_);
+  }
 
-  void AddFilter(std::unique_ptr<MessageReceiver> filter);
+  ~BindingState() { Close(); }
 
-  bool HasAssociatedInterfaces() const;
+  void Bind(ScopedMessagePipeHandle handle,
+            scoped_refptr<base::SingleThreadTaskRunner> runner) {
+    DCHECK(!router_);
+    internal::FilterChain filters;
+    filters.Append<MessageHeaderValidator>(Interface::Name_);
+    filters.Append<typename Interface::RequestValidator_>();
 
-  void PauseIncomingMethodCallProcessing();
-  void ResumeIncomingMethodCallProcessing();
+    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);
+      MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) {
+    DCHECK(router_);
+    return router_->WaitForIncomingMessage(deadline);
+  }
 
-  void Close();
-  void CloseWithReason(uint32_t custom_reason, const std::string& description);
+  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());
-    endpoint_client_->set_connection_error_handler(error_handler);
+    connection_error_handler_ = error_handler;
   }
 
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    DCHECK(is_bound());
-    endpoint_client_->set_connection_error_with_reason_handler(error_handler);
-  }
+  Interface* impl() { return impl_; }
 
   bool is_bound() const { return !!router_; }
 
@@ -68,42 +109,90 @@
     return router_->handle();
   }
 
-  void FlushForTesting();
+  AssociatedGroup* associated_group() { return nullptr; }
 
-  void EnableTestingMode();
+  void EnableTestingMode() {
+    DCHECK(is_bound());
+    router_->EnableTestingMode();
+  }
 
- protected:
-  void BindInternal(ScopedMessagePipeHandle handle,
-                    scoped_refptr<base::SingleThreadTaskRunner> runner,
-                    const char* interface_name,
-                    std::unique_ptr<MessageReceiver> request_validator,
-                    bool passes_associated_kinds,
-                    bool has_sync_methods,
-                    MessageReceiverWithResponderStatus* stub,
-                    uint32_t interface_version);
+ private:
+  void DestroyRouter() {
+    router_->set_connection_error_handler(base::Closure());
+    delete router_;
+    router_ = nullptr;
+    connection_error_handler_.Reset();
+  }
 
-  scoped_refptr<internal::MultiplexRouter> router_;
-  std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
+  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);
 };
 
-template <typename Interface, typename ImplRefTraits>
-class BindingState : public BindingStateBase {
+// 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:
-  using ImplPointerType = typename ImplRefTraits::PointerType;
-
-  explicit BindingState(ImplPointerType impl) {
-    stub_.set_sink(std::move(impl));
+  explicit BindingState(Interface* impl) : impl_(impl) {
+    stub_.set_sink(impl_);
   }
 
   ~BindingState() { Close(); }
 
   void Bind(ScopedMessagePipeHandle handle,
             scoped_refptr<base::SingleThreadTaskRunner> runner) {
-    BindingStateBase::BindInternal(
-        std::move(handle), runner, Interface::Name_,
-        base::MakeUnique<typename Interface::RequestValidator_>(),
-        Interface::PassesAssociatedKinds_, Interface::HasSyncMethods_, &stub_,
-        Interface::Version_);
+    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() {
@@ -111,13 +200,45 @@
     InterfaceRequest<Interface> request =
         MakeRequest<Interface>(router_->PassMessagePipe());
     router_ = nullptr;
+    connection_error_handler_.Reset();
     return request;
   }
 
-  Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); }
+  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:
-  typename Interface::template Stub_<ImplRefTraits> stub_;
+  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);
 };
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
index 631daec..b37d872 100644
--- a/mojo/public/cpp/bindings/lib/bindings_internal.h
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -17,26 +17,30 @@
 namespace mojo {
 
 template <typename T>
-class ArrayDataView;
+class Array;
 
 template <typename T>
-class AssociatedInterfacePtrInfoDataView;
+class AssociatedInterfacePtrInfo;
 
 template <typename T>
-class AssociatedInterfaceRequestDataView;
+class AssociatedInterfaceRequest;
 
 template <typename T>
-class InterfacePtrDataView;
+class InterfacePtr;
 
 template <typename T>
-class InterfaceRequestDataView;
+class InterfaceRequest;
 
 template <typename K, typename V>
-class MapDataView;
+class Map;
 
-class NativeStructDataView;
+class String;
 
-class StringDataView;
+template <typename T>
+class StructPtr;
+
+template <typename T>
+class InlinedStructPtr;
 
 namespace internal {
 
@@ -54,17 +58,12 @@
 template <typename K, typename V>
 class Map_Data;
 
-class NativeStruct_Data;
-
 using String_Data = Array_Data<char>;
 
-inline size_t Align(size_t size) {
-  return (size + 7) & ~0x7;
-}
+size_t Align(size_t size);
+char* AlignPointer(char* ptr);
 
-inline bool IsAligned(const void* ptr) {
-  return !(reinterpret_cast<uintptr_t>(ptr) & 0x7);
-}
+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
@@ -74,19 +73,7 @@
 //
 // A null pointer is encoded as an offset value of 0.
 //
-inline 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);
-}
-
+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)
@@ -124,8 +111,6 @@
 };
 static_assert(sizeof(Pointer<char>) == 8, "Bad_sizeof(Pointer)");
 
-using GenericPointer = Pointer<void>;
-
 struct Handle_Data {
   Handle_Data() = default;
   explicit Handle_Data(uint32_t value) : value(value) {}
@@ -142,24 +127,19 @@
 };
 static_assert(sizeof(Interface_Data) == 8, "Bad_sizeof(Interface_Data)");
 
-struct AssociatedEndpointHandle_Data {
-  AssociatedEndpointHandle_Data() = default;
-  explicit AssociatedEndpointHandle_Data(uint32_t value) : value(value) {}
-
-  bool is_valid() const { return value != kEncodedInvalidHandleValue; }
-
-  uint32_t value;
-};
-static_assert(sizeof(AssociatedEndpointHandle_Data) == 4,
-              "Bad_sizeof(AssociatedEndpointHandle_Data)");
-
 struct AssociatedInterface_Data {
-  AssociatedEndpointHandle_Data handle;
+  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>
@@ -223,7 +203,7 @@
 };
 
 template <typename T>
-struct MojomTypeTraits<ArrayDataView<T>, false> {
+struct MojomTypeTraits<Array<T>, false> {
   using Data = Array_Data<typename MojomTypeTraits<T>::DataAsArrayElement>;
   using DataAsArrayElement = Pointer<Data>;
 
@@ -231,7 +211,7 @@
 };
 
 template <typename T>
-struct MojomTypeTraits<AssociatedInterfacePtrInfoDataView<T>, false> {
+struct MojomTypeTraits<AssociatedInterfacePtrInfo<T>, false> {
   using Data = AssociatedInterface_Data;
   using DataAsArrayElement = Data;
 
@@ -240,8 +220,8 @@
 };
 
 template <typename T>
-struct MojomTypeTraits<AssociatedInterfaceRequestDataView<T>, false> {
-  using Data = AssociatedEndpointHandle_Data;
+struct MojomTypeTraits<AssociatedInterfaceRequest<T>, false> {
+  using Data = AssociatedInterfaceRequest_Data;
   using DataAsArrayElement = Data;
 
   static const MojomTypeCategory category =
@@ -273,7 +253,7 @@
 };
 
 template <typename T>
-struct MojomTypeTraits<InterfacePtrDataView<T>, false> {
+struct MojomTypeTraits<InterfacePtr<T>, false> {
   using Data = Interface_Data;
   using DataAsArrayElement = Data;
 
@@ -281,7 +261,7 @@
 };
 
 template <typename T>
-struct MojomTypeTraits<InterfaceRequestDataView<T>, false> {
+struct MojomTypeTraits<InterfaceRequest<T>, false> {
   using Data = Handle_Data;
   using DataAsArrayElement = Data;
 
@@ -290,7 +270,7 @@
 };
 
 template <typename K, typename V>
-struct MojomTypeTraits<MapDataView<K, V>, false> {
+struct MojomTypeTraits<Map<K, V>, false> {
   using Data = Map_Data<typename MojomTypeTraits<K>::DataAsArrayElement,
                         typename MojomTypeTraits<V>::DataAsArrayElement>;
   using DataAsArrayElement = Pointer<Data>;
@@ -299,21 +279,39 @@
 };
 
 template <>
-struct MojomTypeTraits<NativeStructDataView, false> {
-  using Data = internal::NativeStruct_Data;
-  using DataAsArrayElement = Pointer<Data>;
-
-  static const MojomTypeCategory category = MojomTypeCategory::STRUCT;
-};
-
-template <>
-struct MojomTypeTraits<StringDataView, false> {
+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 =
diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h
index 213a445..c3b570e 100644
--- a/mojo/public/cpp/bindings/lib/buffer.h
+++ b/mojo/public/cpp/bindings/lib/buffer.h
@@ -7,61 +7,15 @@
 
 #include <stddef.h>
 
-#include "base/logging.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-
 namespace mojo {
 namespace internal {
 
-// Buffer provides an interface to allocate memory blocks which are 8-byte
-// aligned and zero-initialized. It doesn't own the underlying memory. Users
-// must ensure that the memory stays valid while using the allocated blocks from
-// Buffer.
+// 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:
-  Buffer() {}
-
-  // The memory must have been zero-initialized. |data| must be 8-byte
-  // aligned.
-  void Initialize(void* data, size_t size) {
-    DCHECK(IsAligned(data));
-
-    data_ = data;
-    size_ = size;
-    cursor_ = reinterpret_cast<uintptr_t>(data);
-    data_end_ = cursor_ + size;
-  }
-
-  size_t size() const { return size_; }
-
-  void* data() const { return data_; }
-
-  // Allocates |num_bytes| from the buffer and returns a pointer to the start of
-  // the allocated block.
-  // The resulting address is 8-byte aligned, and the content of the memory is
-  // zero-filled.
-  void* Allocate(size_t num_bytes) {
-    num_bytes = Align(num_bytes);
-    uintptr_t result = cursor_;
-    cursor_ += num_bytes;
-    if (cursor_ > data_end_ || cursor_ < result) {
-      NOTREACHED();
-      cursor_ -= num_bytes;
-      return nullptr;
-    }
-
-    return reinterpret_cast<void*>(result);
-  }
-
- private:
-  void* data_ = nullptr;
-  size_t size_ = 0;
-
-  uintptr_t cursor_ = 0;
-  uintptr_t data_end_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(Buffer);
+  virtual ~Buffer() {}
+  virtual void* Allocate(size_t num_bytes) = 0;
 };
 
 }  // namespace internal
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
index 4426def..1bb38f0 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -12,20 +12,52 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
-#include "mojo/public/cpp/bindings/lib/may_auto_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) {
-  if (config == MULTI_THREADED_SEND)
-    lock_.emplace();
-
   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.
@@ -33,34 +65,25 @@
 }
 
 Connector::~Connector() {
-  {
-    // Allow for quick destruction on any thread if the pipe is already closed.
-    base::AutoLock lock(connected_lock_);
-    if (!connected_)
-      return;
-  }
-
   DCHECK(thread_checker_.CalledOnValidThread());
+
   CancelWait();
 }
 
 void Connector::CloseMessagePipe() {
-  // Throw away the returned message pipe.
-  PassMessagePipe();
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  CancelWait();
+  MayAutoLock locker(lock_.get());
+  message_pipe_.reset();
 }
 
 ScopedMessagePipeHandle Connector::PassMessagePipe() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   CancelWait();
-  internal::MayAutoLock locker(&lock_);
-  ScopedMessagePipeHandle message_pipe = std::move(message_pipe_);
-  weak_factory_.InvalidateWeakPtrs();
-  sync_handle_watcher_callback_count_ = 0;
-
-  base::AutoLock lock(connected_lock_);
-  connected_ = false;
-  return message_pipe;
+  MayAutoLock locker(lock_.get());
+  return std::move(message_pipe_);
 }
 
 void Connector::RaiseError() {
@@ -120,7 +143,7 @@
   if (error_)
     return false;
 
-  internal::MayAutoLock locker(&lock_);
+  MayAutoLock locker(lock_.get());
 
   if (!message_pipe_.is_valid() || drop_writes_)
     return true;
@@ -181,13 +204,6 @@
   return sync_watcher_->SyncWatch(should_stop);
 }
 
-void Connector::SetWatcherHeapProfilerTag(const char* tag) {
-  heap_profiler_tag_ = tag;
-  if (handle_watcher_) {
-    handle_watcher_->set_heap_profiler_tag(tag);
-  }
-}
-
 void Connector::OnWatcherHandleReady(MojoResult result) {
   OnHandleReadyInternal(result);
 }
@@ -198,10 +214,8 @@
   sync_handle_watcher_callback_count_++;
   OnHandleReadyInternal(result);
   // At this point, this object might have been deleted.
-  if (weak_self) {
-    DCHECK_LT(0u, sync_handle_watcher_callback_count_);
+  if (weak_self)
     sync_handle_watcher_callback_count_--;
-  }
 }
 
 void Connector::OnHandleReadyInternal(MojoResult result) {
@@ -217,14 +231,12 @@
 
 void Connector::WaitToReadMore() {
   CHECK(!paused_);
-  DCHECK(!handle_watcher_);
+  DCHECK(!handle_watcher_.IsWatching());
 
-  handle_watcher_.reset(new Watcher(FROM_HERE, task_runner_));
-  if (heap_profiler_tag_)
-    handle_watcher_->set_heap_profiler_tag(heap_profiler_tag_);
-  MojoResult rv = handle_watcher_->Start(
+  MojoResult rv = handle_watcher_.Start(
       message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
-      base::Bind(&Connector::OnWatcherHandleReady, base::Unretained(this)));
+      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
@@ -245,8 +257,8 @@
 
   bool receiver_result = false;
 
-  // Detect if |this| was destroyed or the message pipe was closed/transferred
-  // during message dispatch.
+  // 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;
@@ -280,11 +292,9 @@
   while (!error_) {
     MojoResult rv;
 
-    if (!ReadSingleMessage(&rv)) {
-      // Return immediately without touching any members. |this| may have been
-      // destroyed.
+    // Return immediately if |this| was destroyed. Do not touch any members!
+    if (!ReadSingleMessage(&rv))
       return;
-    }
 
     if (paused_)
       return;
@@ -295,7 +305,7 @@
 }
 
 void Connector::CancelWait() {
-  handle_watcher_.reset();
+  handle_watcher_.Cancel();
   sync_watcher_.reset();
 }
 
@@ -315,7 +325,7 @@
 
   if (force_pipe_reset) {
     CancelWait();
-    internal::MayAutoLock locker(&lock_);
+    MayAutoLock locker(lock_.get());
     message_pipe_.reset();
     MessagePipe dummy_pipe;
     message_pipe_ = std::move(dummy_pipe.handle0);
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.cc b/mojo/public/cpp/bindings/lib/control_message_handler.cc
index c90aada..9f44e88 100644
--- a/mojo/public/cpp/bindings/lib/control_message_handler.cc
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc
@@ -11,53 +11,15 @@
 #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_util.h"
 #include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
 
 namespace mojo {
 namespace internal {
-namespace {
-
-bool ValidateControlRequestWithResponse(Message* message) {
-  ValidationContext validation_context(message->payload(),
-                                       message->payload_num_bytes(), 0, 0,
-                                       message, "ControlRequestValidator");
-  if (!ValidateMessageIsRequestExpectingResponse(message, &validation_context))
-    return false;
-
-  switch (message->header()->name) {
-    case interface_control::kRunMessageId:
-      return ValidateMessagePayload<
-          interface_control::internal::RunMessageParams_Data>(
-          message, &validation_context);
-  }
-  return false;
-}
-
-bool ValidateControlRequestWithoutResponse(Message* message) {
-  ValidationContext validation_context(message->payload(),
-                                       message->payload_num_bytes(), 0, 0,
-                                       message, "ControlRequestValidator");
-  if (!ValidateMessageIsRequestWithoutResponse(message, &validation_context))
-    return false;
-
-  switch (message->header()->name) {
-    case interface_control::kRunOrClosePipeMessageId:
-      return ValidateMessageIsRequestWithoutResponse(message,
-                                                     &validation_context) &&
-             ValidateMessagePayload<
-                 interface_control::internal::RunOrClosePipeMessageParams_Data>(
-                 message, &validation_context);
-  }
-  return false;
-}
-
-}  // namespace
 
 // static
 bool ControlMessageHandler::IsControlMessage(const Message* message) {
-  return message->header()->name == interface_control::kRunMessageId ||
-         message->header()->name == interface_control::kRunOrClosePipeMessageId;
+  return message->header()->name == kRunMessageId ||
+         message->header()->name == kRunOrClosePipeMessageId;
 }
 
 ControlMessageHandler::ControlMessageHandler(uint32_t interface_version)
@@ -68,10 +30,7 @@
 }
 
 bool ControlMessageHandler::Accept(Message* message) {
-  if (!ValidateControlRequestWithoutResponse(message))
-    return false;
-
-  if (message->header()->name == interface_control::kRunOrClosePipeMessageId)
+  if (message->header()->name == kRunOrClosePipeMessageId)
     return RunOrClosePipe(message);
 
   NOTREACHED();
@@ -81,10 +40,7 @@
 bool ControlMessageHandler::AcceptWithResponder(
     Message* message,
     MessageReceiverWithStatus* responder) {
-  if (!ValidateControlRequestWithResponse(message))
-    return false;
-
-  if (message->header()->name == interface_control::kRunMessageId)
+  if (message->header()->name == kRunMessageId)
     return Run(message, responder);
 
   NOTREACHED();
@@ -93,37 +49,20 @@
 
 bool ControlMessageHandler::Run(Message* message,
                                 MessageReceiverWithStatus* responder) {
-  interface_control::internal::RunMessageParams_Data* params =
-      reinterpret_cast<interface_control::internal::RunMessageParams_Data*>(
-          message->mutable_payload());
-  interface_control::RunMessageParamsPtr params_ptr;
-  Deserialize<interface_control::RunMessageParamsDataView>(params, &params_ptr,
-                                                           &context_);
-  auto& input = *params_ptr->input;
-  interface_control::RunOutputPtr output = interface_control::RunOutput::New();
-  if (input.is_query_version()) {
-    output->set_query_version_result(
-        interface_control::QueryVersionResult::New());
-    output->get_query_version_result()->version = interface_version_;
-  } else if (input.is_flush_for_testing()) {
-    output.reset();
-  } else {
-    output.reset();
-  }
+  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_;
 
-  auto response_params_ptr = interface_control::RunResponseMessageParams::New();
-  response_params_ptr->output = std::move(output);
-  size_t size =
-      PrepareToSerialize<interface_control::RunResponseMessageParamsDataView>(
-          response_params_ptr, &context_);
-  MessageBuilder builder(interface_control::kRunMessageId,
-                         Message::kFlagIsResponse, size, 0);
-  builder.message()->set_request_id(message->request_id());
+  size_t size = PrepareToSerialize<RunResponseMessageParamsPtr>(
+      response_params_ptr, &context_);
+  ResponseMessageBuilder builder(kRunMessageId, size, message->request_id());
 
-  interface_control::internal::RunResponseMessageParams_Data* response_params =
-      nullptr;
-  Serialize<interface_control::RunResponseMessageParamsDataView>(
-      response_params_ptr, builder.buffer(), &response_params, &context_);
+  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;
@@ -132,18 +71,13 @@
 }
 
 bool ControlMessageHandler::RunOrClosePipe(Message* message) {
-  interface_control::internal::RunOrClosePipeMessageParams_Data* params =
-      reinterpret_cast<
-          interface_control::internal::RunOrClosePipeMessageParams_Data*>(
+  RunOrClosePipeMessageParams_Data* params =
+      reinterpret_cast<RunOrClosePipeMessageParams_Data*>(
           message->mutable_payload());
-  interface_control::RunOrClosePipeMessageParamsPtr params_ptr;
-  Deserialize<interface_control::RunOrClosePipeMessageParamsDataView>(
-      params, &params_ptr, &context_);
-  auto& input = *params_ptr->input;
-  if (input.is_require_version())
-    return interface_version_ >= input.get_require_version()->version;
+  RunOrClosePipeMessageParamsPtr params_ptr;
+  Deserialize<RunOrClosePipeMessageParamsPtr>(params, &params_ptr, &context_);
 
-  return false;
+  return interface_version_ >= params_ptr->require_version->version;
 }
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.h b/mojo/public/cpp/bindings/lib/control_message_handler.h
index 3c385e4..13b5aa6 100644
--- a/mojo/public/cpp/bindings/lib/control_message_handler.h
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.h
@@ -7,9 +7,7 @@
 
 #include <stdint.h>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/serialization_context.h"
 #include "mojo/public/cpp/bindings/message.h"
 
@@ -17,8 +15,7 @@
 namespace internal {
 
 // Handlers for request messages defined in interface_control_messages.mojom.
-class MOJO_CPP_BINDINGS_EXPORT ControlMessageHandler
-    : NON_EXPORTED_BASE(public MessageReceiverWithResponderStatus) {
+class ControlMessageHandler : public MessageReceiverWithResponderStatus {
  public:
   static bool IsControlMessage(const Message* message);
 
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
index 23de991..7af409d 100644
--- a/mojo/public/cpp/bindings/lib/control_message_proxy.cc
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
@@ -9,12 +9,9 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/macros.h"
-#include "base/run_loop.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_util.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
 
@@ -23,28 +20,11 @@
 
 namespace {
 
-bool ValidateControlResponse(Message* message) {
-  ValidationContext validation_context(message->payload(),
-                                       message->payload_num_bytes(), 0, 0,
-                                       message, "ControlResponseValidator");
-  if (!ValidateMessageIsResponse(message, &validation_context))
-    return false;
-
-  switch (message->header()->name) {
-    case interface_control::kRunMessageId:
-      return ValidateMessagePayload<
-          interface_control::internal::RunResponseMessageParams_Data>(
-          message, &validation_context);
-  }
-  return false;
-}
-
-using RunCallback =
-    base::Callback<void(interface_control::RunResponseMessageParamsPtr)>;
+using RunCallback = base::Callback<void(QueryVersionResultPtr)>;
 
 class RunResponseForwardToCallback : public MessageReceiver {
  public:
-  explicit RunResponseForwardToCallback(const RunCallback& callback)
+  RunResponseForwardToCallback(const RunCallback& callback)
       : callback_(callback) {}
   bool Accept(Message* message) override;
 
@@ -54,83 +34,59 @@
 };
 
 bool RunResponseForwardToCallback::Accept(Message* message) {
-  if (!ValidateControlResponse(message))
-    return false;
-
-  interface_control::internal::RunResponseMessageParams_Data* params =
-      reinterpret_cast<
-          interface_control::internal::RunResponseMessageParams_Data*>(
+  RunResponseMessageParams_Data* params =
+      reinterpret_cast<RunResponseMessageParams_Data*>(
           message->mutable_payload());
-  interface_control::RunResponseMessageParamsPtr params_ptr;
+  RunResponseMessageParamsPtr params_ptr;
   SerializationContext context;
-  Deserialize<interface_control::RunResponseMessageParamsDataView>(
-      params, &params_ptr, &context);
+  Deserialize<RunResponseMessageParamsPtr>(params, &params_ptr, &context);
 
-  callback_.Run(std::move(params_ptr));
+  callback_.Run(std::move(params_ptr->query_version_result));
   return true;
 }
 
 void SendRunMessage(MessageReceiverWithResponder* receiver,
-                    interface_control::RunInputPtr input_ptr,
-                    const RunCallback& callback) {
-  SerializationContext context;
+                    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);
 
-  auto params_ptr = interface_control::RunMessageParams::New();
-  params_ptr->input = std::move(input_ptr);
-  size_t size = PrepareToSerialize<interface_control::RunMessageParamsDataView>(
-      params_ptr, &context);
-  MessageBuilder builder(interface_control::kRunMessageId,
-                         Message::kFlagExpectsResponse, size, 0);
+  size_t size = PrepareToSerialize<RunMessageParamsPtr>(params_ptr, context);
+  RequestMessageBuilder builder(kRunMessageId, size);
 
-  interface_control::internal::RunMessageParams_Data* params = nullptr;
-  Serialize<interface_control::RunMessageParamsDataView>(
-      params_ptr, builder.buffer(), &params, &context);
+  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;
 }
 
-Message ConstructRunOrClosePipeMessage(
-    interface_control::RunOrClosePipeInputPtr input_ptr) {
-  SerializationContext context;
+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);
 
-  auto params_ptr = interface_control::RunOrClosePipeMessageParams::New();
-  params_ptr->input = std::move(input_ptr);
+  size_t size =
+      PrepareToSerialize<RunOrClosePipeMessageParamsPtr>(params_ptr, context);
+  MessageBuilder builder(kRunOrClosePipeMessageId, size);
 
-  size_t size = PrepareToSerialize<
-      interface_control::RunOrClosePipeMessageParamsDataView>(params_ptr,
-                                                              &context);
-  MessageBuilder builder(interface_control::kRunOrClosePipeMessageId, 0, size,
-                         0);
-
-  interface_control::internal::RunOrClosePipeMessageParams_Data* params =
-      nullptr;
-  Serialize<interface_control::RunOrClosePipeMessageParamsDataView>(
-      params_ptr, builder.buffer(), &params, &context);
-  return std::move(*builder.message());
-}
-
-void SendRunOrClosePipeMessage(
-    MessageReceiverWithResponder* receiver,
-    interface_control::RunOrClosePipeInputPtr input_ptr) {
-  Message message(ConstructRunOrClosePipeMessage(std::move(input_ptr)));
-
-  bool ok = receiver->Accept(&message);
+  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,
-    interface_control::RunResponseMessageParamsPtr run_response) {
-  uint32_t version = 0u;
-  if (run_response->output && run_response->output->is_query_version_result())
-    version = run_response->output->get_query_version_result()->version;
-  callback.Run(version);
-}
-
-void RunClosure(const base::Closure& callback,
-                interface_control::RunResponseMessageParamsPtr run_response) {
-  callback.Run();
+void RunVersionCallback(const base::Callback<void(uint32_t)>& callback,
+                        QueryVersionResultPtr query_version_result) {
+  callback.Run(query_version_result->version);
 }
 
 }  // namespace
@@ -139,49 +95,16 @@
     : receiver_(receiver) {
 }
 
-ControlMessageProxy::~ControlMessageProxy() = default;
-
 void ControlMessageProxy::QueryVersion(
     const base::Callback<void(uint32_t)>& callback) {
-  auto input_ptr = interface_control::RunInput::New();
-  input_ptr->set_query_version(interface_control::QueryVersion::New());
-  SendRunMessage(receiver_, std::move(input_ptr),
-                 base::Bind(&RunVersionCallback, callback));
+  SendRunMessage(receiver_, QueryVersion::New(),
+                 base::Bind(&RunVersionCallback, callback), &context_);
 }
 
 void ControlMessageProxy::RequireVersion(uint32_t version) {
-  auto require_version = interface_control::RequireVersion::New();
+  RequireVersionPtr require_version(RequireVersion::New());
   require_version->version = version;
-  auto input_ptr = interface_control::RunOrClosePipeInput::New();
-  input_ptr->set_require_version(std::move(require_version));
-  SendRunOrClosePipeMessage(receiver_, std::move(input_ptr));
-}
-
-void ControlMessageProxy::FlushForTesting() {
-  if (encountered_error_)
-    return;
-
-  auto input_ptr = interface_control::RunInput::New();
-  input_ptr->set_flush_for_testing(interface_control::FlushForTesting::New());
-  base::RunLoop run_loop;
-  run_loop_quit_closure_ = run_loop.QuitClosure();
-  SendRunMessage(
-      receiver_, std::move(input_ptr),
-      base::Bind(&RunClosure,
-                 base::Bind(&ControlMessageProxy::RunFlushForTestingClosure,
-                            base::Unretained(this))));
-  run_loop.Run();
-}
-
-void ControlMessageProxy::RunFlushForTestingClosure() {
-  DCHECK(!run_loop_quit_closure_.is_null());
-  base::ResetAndReturn(&run_loop_quit_closure_).Run();
-}
-
-void ControlMessageProxy::OnConnectionError() {
-  encountered_error_ = true;
-  if (!run_loop_quit_closure_.is_null())
-    RunFlushForTestingClosure();
+  SendRunOrClosePipeMessage(receiver_, std::move(require_version), &context_);
 }
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.h b/mojo/public/cpp/bindings/lib/control_message_proxy.h
index 2f9314e..5ec6ddc 100644
--- a/mojo/public/cpp/bindings/lib/control_message_proxy.h
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.h
@@ -7,9 +7,8 @@
 
 #include <stdint.h>
 
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/serialization_context.h"
 
 namespace mojo {
@@ -19,26 +18,18 @@
 namespace internal {
 
 // Proxy for request messages defined in interface_control_messages.mojom.
-class MOJO_CPP_BINDINGS_EXPORT ControlMessageProxy {
+class ControlMessageProxy {
  public:
   // Doesn't take ownership of |receiver|. It must outlive this object.
   explicit ControlMessageProxy(MessageReceiverWithResponder* receiver);
-  ~ControlMessageProxy();
 
   void QueryVersion(const base::Callback<void(uint32_t)>& callback);
   void RequireVersion(uint32_t version);
 
-  void FlushForTesting();
-  void OnConnectionError();
-
- private:
-  void RunFlushForTestingClosure();
-
+ protected:
   // Not owned.
   MessageReceiverWithResponder* receiver_;
-  bool encountered_error_ = false;
-
-  base::Closure run_loop_quit_closure_;
+  SerializationContext context_;
 
   DISALLOW_COPY_AND_ASSIGN(ControlMessageProxy);
 };
diff --git a/mojo/public/cpp/bindings/lib/equals_traits.h b/mojo/public/cpp/bindings/lib/equals_traits.h
deleted file mode 100644
index 53c7dce..0000000
--- a/mojo/public/cpp/bindings/lib/equals_traits.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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_EQUALS_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_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 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_EQUALS_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/lib/filter_chain.cc b/mojo/public/cpp/bindings/lib/filter_chain.cc
index 5d919fe..899bac1 100644
--- a/mojo/public/cpp/bindings/lib/filter_chain.cc
+++ b/mojo/public/cpp/bindings/lib/filter_chain.cc
@@ -2,13 +2,14 @@
 // 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/filter_chain.h"
+#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) {
 }
@@ -25,23 +26,24 @@
 }
 
 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);
 }
 
-bool FilterChain::Accept(Message* message) {
+MessageReceiver* FilterChain::GetHead() {
   DCHECK(sink_);
-  for (auto& filter : filters_)
-    if (!filter->Accept(message))
-      return false;
-  return sink_->Accept(message);
+  return filters_.empty() ? sink_ : filters_.front();
 }
 
-void FilterChain::Append(std::unique_ptr<MessageReceiver> filter) {
-  filters_.emplace_back(std::move(filter));
-}
-
+}  // 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
index 725a193..50b8a21 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -4,25 +4,56 @@
 
 #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);
+  size_ = internal::Align(size);
   // Use calloc here to ensure all message memory is zero'd out.
-  void* ptr = calloc(size, 1);
-  Initialize(ptr, size);
+  ptr_ = static_cast<char*>(calloc(size_, 1));
 }
 
 FixedBufferForTesting::~FixedBufferForTesting() {
-  free(data());
+  free(ptr_);
 }
 
 void* FixedBufferForTesting::Leak() {
-  void* ptr = data();
-  Initialize(nullptr, 0);
+  char* ptr = ptr_;
+  ptr_ = nullptr;
+  cursor_ = 0;
+  size_ = 0;
   return ptr;
 }
 
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
index 070b0c8..9a5704b 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.h
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -7,21 +7,62 @@
 
 #include <stddef.h>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/buffer.h"
 
 namespace mojo {
 namespace internal {
 
-// FixedBufferForTesting owns its buffer. The Leak method may be used to steal
-// the underlying memory.
-class MOJO_CPP_BINDINGS_EXPORT FixedBufferForTesting
-    : NON_EXPORTED_BASE(public Buffer) {
+// 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();
+  ~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
diff --git a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
index 14ed21f..344c2ca 100644
--- a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
+++ b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
@@ -5,12 +5,9 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_HANDLE_INTERFACE_SERIALIZATION_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_HANDLE_INTERFACE_SERIALIZATION_H_
 
-#include <type_traits>
-
 #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_data_view.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"
@@ -21,98 +18,52 @@
 namespace mojo {
 namespace internal {
 
-template <typename Base, typename T>
-struct Serializer<AssociatedInterfacePtrInfoDataView<Base>,
+template <typename T>
+struct Serializer<AssociatedInterfacePtrInfo<T>,
                   AssociatedInterfacePtrInfo<T>> {
-  static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
-  static size_t PrepareToSerialize(const AssociatedInterfacePtrInfo<T>& input,
-                                   SerializationContext* context) {
-    if (input.handle().is_valid())
-      context->associated_endpoint_count++;
-    return 0;
-  }
-
   static void Serialize(AssociatedInterfacePtrInfo<T>& input,
                         AssociatedInterface_Data* output,
                         SerializationContext* context) {
-    DCHECK(!input.handle().is_valid() || input.handle().pending_association());
-    if (input.handle().is_valid()) {
-      // Set to the index of the element pushed to the back of the vector.
-      output->handle.value =
-          static_cast<uint32_t>(context->associated_endpoint_handles.size());
-      context->associated_endpoint_handles.push_back(input.PassHandle());
-    } else {
-      output->handle.value = kEncodedInvalidHandleValue;
-    }
+    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) {
-    if (input->handle.is_valid()) {
-      DCHECK_LT(input->handle.value,
-                context->associated_endpoint_handles.size());
-      output->set_handle(
-          std::move(context->associated_endpoint_handles[input->handle.value]));
-    } else {
-      output->set_handle(ScopedInterfaceEndpointHandle());
-    }
+    output->set_handle(context->group_controller->CreateLocalEndpointHandle(
+        FetchAndReset(&input->interface_id)));
     output->set_version(input->version);
     return true;
   }
 };
 
-template <typename Base, typename T>
-struct Serializer<AssociatedInterfaceRequestDataView<Base>,
+template <typename T>
+struct Serializer<AssociatedInterfaceRequest<T>,
                   AssociatedInterfaceRequest<T>> {
-  static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
-  static size_t PrepareToSerialize(const AssociatedInterfaceRequest<T>& input,
-                                   SerializationContext* context) {
-    if (input.handle().is_valid())
-      context->associated_endpoint_count++;
-    return 0;
-  }
-
   static void Serialize(AssociatedInterfaceRequest<T>& input,
-                        AssociatedEndpointHandle_Data* output,
+                        AssociatedInterfaceRequest_Data* output,
                         SerializationContext* context) {
-    DCHECK(!input.handle().is_valid() || input.handle().pending_association());
-    if (input.handle().is_valid()) {
-      // Set to the index of the element pushed to the back of the vector.
-      output->value =
-          static_cast<uint32_t>(context->associated_endpoint_handles.size());
-      context->associated_endpoint_handles.push_back(input.PassHandle());
-    } else {
-      output->value = kEncodedInvalidHandleValue;
-    }
+    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(AssociatedEndpointHandle_Data* input,
+  static bool Deserialize(AssociatedInterfaceRequest_Data* input,
                           AssociatedInterfaceRequest<T>* output,
                           SerializationContext* context) {
-    if (input->is_valid()) {
-      DCHECK_LT(input->value, context->associated_endpoint_handles.size());
-      output->Bind(
-          std::move(context->associated_endpoint_handles[input->value]));
-    } else {
-      output->Bind(ScopedInterfaceEndpointHandle());
-    }
+    output->Bind(context->group_controller->CreateLocalEndpointHandle(
+        FetchAndReset(&input->interface_id)));
     return true;
   }
 };
 
-template <typename Base, typename T>
-struct Serializer<InterfacePtrDataView<Base>, InterfacePtr<T>> {
-  static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
-  static size_t PrepareToSerialize(const InterfacePtr<T>& input,
-                                   SerializationContext* context) {
-    return 0;
-  }
-
+template <typename T>
+struct Serializer<InterfacePtr<T>, InterfacePtr<T>> {
   static void Serialize(InterfacePtr<T>& input,
                         Interface_Data* output,
                         SerializationContext* context) {
@@ -131,15 +82,8 @@
   }
 };
 
-template <typename Base, typename T>
-struct Serializer<InterfaceRequestDataView<Base>, InterfaceRequest<T>> {
-  static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
-  static size_t PrepareToSerialize(const InterfaceRequest<T>& input,
-                                   SerializationContext* context) {
-    return 0;
-  }
-
+template <typename T>
+struct Serializer<InterfaceRequest<T>, InterfaceRequest<T>> {
   static void Serialize(InterfaceRequest<T>& input,
                         Handle_Data* output,
                         SerializationContext* context) {
@@ -156,11 +100,6 @@
 
 template <typename T>
 struct Serializer<ScopedHandleBase<T>, ScopedHandleBase<T>> {
-  static size_t PrepareToSerialize(const ScopedHandleBase<T>& input,
-                                   SerializationContext* context) {
-    return 0;
-  }
-
   static void Serialize(ScopedHandleBase<T>& input,
                         Handle_Data* output,
                         SerializationContext* context) {
diff --git a/mojo/public/cpp/bindings/lib/hash_util.h b/mojo/public/cpp/bindings/lib/hash_util.h
deleted file mode 100644
index 93280d6..0000000
--- a/mojo/public/cpp/bindings/lib/hash_util.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// 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_HASH_UTIL_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_
-
-#include <cstring>
-#include <functional>
-#include <type_traits>
-#include <vector>
-
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/lib/template_util.h"
-
-namespace mojo {
-namespace internal {
-
-template <typename T>
-size_t HashCombine(size_t seed, const T& value) {
-  // Based on proposal in:
-  // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf
-  return seed ^ (std::hash<T>()(value) + (seed << 6) + (seed >> 2));
-}
-
-template <typename T>
-struct HasHashMethod {
-  template <typename U>
-  static char Test(decltype(&U::Hash));
-  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_hash_method = HasHashMethod<T>::value>
-struct HashTraits;
-
-template <typename T>
-size_t Hash(size_t seed, const T& value);
-
-template <typename T>
-struct HashTraits<T, true> {
-  static size_t Hash(size_t seed, const T& value) { return value.Hash(seed); }
-};
-
-template <typename T>
-struct HashTraits<T, false> {
-  static size_t Hash(size_t seed, const T& value) {
-    return HashCombine(seed, value);
-  }
-};
-
-template <typename T>
-struct HashTraits<std::vector<T>, false> {
-  static size_t Hash(size_t seed, const std::vector<T>& value) {
-    for (const auto& element : value) {
-      seed = HashCombine(seed, element);
-    }
-    return seed;
-  }
-};
-
-template <typename T>
-struct HashTraits<base::Optional<std::vector<T>>, false> {
-  static size_t Hash(size_t seed, const base::Optional<std::vector<T>>& value) {
-    if (!value)
-      return HashCombine(seed, 0);
-
-    return Hash(seed, *value);
-  }
-};
-
-template <typename T>
-size_t Hash(size_t seed, const T& value) {
-  return HashTraits<T>::Hash(seed, value);
-}
-
-}  // namespace internal
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
index 3eca5a1..e1f388a 100644
--- a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
+++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/location.h"
-#include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
@@ -18,7 +17,6 @@
 #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/lib/validation_util.h"
 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
 
 namespace mojo {
@@ -47,7 +45,7 @@
         task_runner_(std::move(runner)) {}
   ~ResponderThunk() override {
     if (!accept_was_invoked_) {
-      // The Service handled a message that was expecting a response
+      // 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
@@ -134,47 +132,47 @@
 InterfaceEndpointClient::InterfaceEndpointClient(
     ScopedInterfaceEndpointHandle handle,
     MessageReceiverWithResponderStatus* receiver,
-    std::unique_ptr<MessageReceiver> payload_validator,
+    std::unique_ptr<MessageFilter> payload_validator,
     bool expect_sync_requests,
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    uint32_t interface_version)
-    : expect_sync_requests_(expect_sync_requests),
-      handle_(std::move(handle)),
+    scoped_refptr<base::SingleThreadTaskRunner> runner)
+    : handle_(std::move(handle)),
       incoming_receiver_(receiver),
+      payload_validator_(std::move(payload_validator)),
       thunk_(this),
-      filters_(&thunk_),
+      next_request_id_(1),
+      encountered_error_(false),
       task_runner_(std::move(runner)),
-      control_message_proxy_(this),
-      control_message_handler_(interface_version),
       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.
-  if (payload_validator)
-    filters_.Append(std::move(payload_validator));
+  payload_validator_->set_sink(&thunk_);
 
-  if (handle_.pending_association()) {
-    handle_.SetAssociationEventHandler(base::Bind(
-        &InterfaceEndpointClient::OnAssociationEvent, base::Unretained(this)));
-  } else {
-    InitControllerIfNecessary();
-  }
+  controller_ = handle_.group_controller()->AttachEndpointClient(
+      handle_, this, task_runner_);
+  if (expect_sync_requests)
+    controller_->AllowWokenUpBySyncWatchOnSameThread();
 }
 
 InterfaceEndpointClient::~InterfaceEndpointClient() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (controller_)
-    handle_.group_controller()->DetachEndpointClient(handle_);
+  handle_.group_controller()->DetachEndpointClient(handle_);
 }
 
 AssociatedGroup* InterfaceEndpointClient::associated_group() {
   if (!associated_group_)
-    associated_group_ = base::MakeUnique<AssociatedGroup>(handle_);
+    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());
@@ -182,73 +180,38 @@
   if (!handle_.is_valid())
     return ScopedInterfaceEndpointHandle();
 
-  handle_.SetAssociationEventHandler(
-      ScopedInterfaceEndpointHandle::AssociationEventCallback());
-
-  if (controller_) {
-    controller_ = nullptr;
-    handle_.group_controller()->DetachEndpointClient(handle_);
-  }
+  controller_ = nullptr;
+  handle_.group_controller()->DetachEndpointClient(handle_);
 
   return std::move(handle_);
 }
 
-void InterfaceEndpointClient::AddFilter(
-    std::unique_ptr<MessageReceiver> filter) {
-  filters_.Append(std::move(filter));
-}
-
 void InterfaceEndpointClient::RaiseError() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (!handle_.pending_association())
-    handle_.group_controller()->RaiseError();
-}
-
-void InterfaceEndpointClient::CloseWithReason(uint32_t custom_reason,
-                                              const std::string& description) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  auto handle = PassHandle();
-  handle.ResetWithReason(custom_reason, description);
+  handle_.group_controller()->RaiseError();
 }
 
 bool InterfaceEndpointClient::Accept(Message* message) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(controller_);
   DCHECK(!message->has_flag(Message::kFlagExpectsResponse));
-  DCHECK(!handle_.pending_association());
-
-  // This has to been done even if connection error has occurred. For example,
-  // the message contains a pending associated request. The user may try to use
-  // the corresponding associated interface pointer after sending this message.
-  // That associated interface pointer has to join an associated group in order
-  // to work properly.
-  if (!message->associated_endpoint_handles()->empty())
-    message->SerializeAssociatedEndpointHandles(handle_.group_controller());
 
   if (encountered_error_)
     return false;
 
-  InitControllerIfNecessary();
-
   return controller_->SendMessage(message);
 }
 
 bool InterfaceEndpointClient::AcceptWithResponder(Message* message,
                                                   MessageReceiver* responder) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(controller_);
   DCHECK(message->has_flag(Message::kFlagExpectsResponse));
-  DCHECK(!handle_.pending_association());
-
-  // Please see comments in Accept().
-  if (!message->associated_endpoint_handles()->empty())
-    message->SerializeAssociatedEndpointHandles(handle_.group_controller());
 
   if (encountered_error_)
     return false;
 
-  InitControllerIfNecessary();
-
   // 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)
@@ -271,18 +234,20 @@
   bool response_received = false;
   std::unique_ptr<MessageReceiver> sync_responder(responder);
   sync_responses_.insert(std::make_pair(
-      request_id, base::MakeUnique<SyncResponseInfo>(&response_received)));
+      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(base::ContainsKey(sync_responses_, request_id));
+    DCHECK(ContainsKey(sync_responses_, request_id));
     auto iter = sync_responses_.find(request_id);
     DCHECK_EQ(&response_received, iter->second->response_received);
-    if (response_received)
-      ignore_result(sync_responder->Accept(&iter->second->response));
+    if (response_received) {
+      std::unique_ptr<Message> response = std::move(iter->second->response);
+      ignore_result(sync_responder->Accept(response.get()));
+    }
     sync_responses_.erase(iter);
   }
 
@@ -292,97 +257,30 @@
 
 bool InterfaceEndpointClient::HandleIncomingMessage(Message* message) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return filters_.Accept(message);
+
+  return payload_validator_->Accept(message);
 }
 
-void InterfaceEndpointClient::NotifyError(
-    const base::Optional<DisconnectReason>& reason) {
+void InterfaceEndpointClient::NotifyError() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (encountered_error_)
     return;
   encountered_error_ = true;
-
-  // Response callbacks may hold on to resource, and there's no need to keep
-  // them alive any longer. Note that it's allowed that a pending response
-  // callback may own this endpoint, so we simply move the responders onto the
-  // stack here and let them be destroyed when the stack unwinds.
-  AsyncResponderMap responders = std::move(async_responders_);
-
-  control_message_proxy_.OnConnectionError();
-
-  if (!error_handler_.is_null()) {
-    base::Closure error_handler = std::move(error_handler_);
-    error_handler.Run();
-  } else if (!error_with_reason_handler_.is_null()) {
-    ConnectionErrorWithReasonCallback error_with_reason_handler =
-        std::move(error_with_reason_handler_);
-    if (reason) {
-      error_with_reason_handler.Run(reason->custom_reason, reason->description);
-    } else {
-      error_with_reason_handler.Run(0, std::string());
-    }
-  }
-}
-
-void InterfaceEndpointClient::QueryVersion(
-    const base::Callback<void(uint32_t)>& callback) {
-  control_message_proxy_.QueryVersion(callback);
-}
-
-void InterfaceEndpointClient::RequireVersion(uint32_t version) {
-  control_message_proxy_.RequireVersion(version);
-}
-
-void InterfaceEndpointClient::FlushForTesting() {
-  control_message_proxy_.FlushForTesting();
-}
-
-void InterfaceEndpointClient::InitControllerIfNecessary() {
-  if (controller_ || handle_.pending_association())
-    return;
-
-  controller_ = handle_.group_controller()->AttachEndpointClient(handle_, this,
-                                                                 task_runner_);
-  if (expect_sync_requests_)
-    controller_->AllowWokenUpBySyncWatchOnSameThread();
-}
-
-void InterfaceEndpointClient::OnAssociationEvent(
-    ScopedInterfaceEndpointHandle::AssociationEvent event) {
-  if (event == ScopedInterfaceEndpointHandle::ASSOCIATED) {
-    InitControllerIfNecessary();
-  } else if (event ==
-             ScopedInterfaceEndpointHandle::PEER_CLOSED_BEFORE_ASSOCIATION) {
-    task_runner_->PostTask(FROM_HERE,
-                           base::Bind(&InterfaceEndpointClient::NotifyError,
-                                      weak_ptr_factory_.GetWeakPtr(),
-                                      handle_.disconnect_reason()));
-  }
+  if (!error_handler_.is_null())
+    error_handler_.Run();
 }
 
 bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) {
   DCHECK_EQ(handle_.id(), message->interface_id());
 
-  if (encountered_error_) {
-    // This message is received after error has been encountered. For associated
-    // interfaces, this means the remote side sends a
-    // PeerAssociatedEndpointClosed event but continues to send more messages
-    // for the same interface. Close the pipe because this shouldn't happen.
-    DVLOG(1) << "A message is received for an interface after it has been "
-             << "disconnected. Closing the pipe.";
-    return false;
-  }
-
   if (message->has_flag(Message::kFlagExpectsResponse)) {
+    if (!incoming_receiver_)
+      return false;
+
     MessageReceiverWithStatus* responder =
         new ResponderThunk(weak_ptr_factory_.GetWeakPtr(), task_runner_);
-    bool ok = false;
-    if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
-      ok = control_message_handler_.AcceptWithResponder(message, responder);
-    } else {
-      ok = incoming_receiver_->AcceptWithResponder(message, responder);
-    }
+    bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
     if (!ok)
       delete responder;
     return ok;
@@ -393,7 +291,8 @@
       auto it = sync_responses_.find(request_id);
       if (it == sync_responses_.end())
         return false;
-      it->second->response = std::move(*message);
+      it->second->response.reset(new Message());
+      message->MoveTo(it->second->response.get());
       *it->second->response_received = true;
       return true;
     }
@@ -405,8 +304,8 @@
     async_responders_.erase(it);
     return responder->Accept(message);
   } else {
-    if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
-      return control_message_handler_.Accept(message);
+    if (!incoming_receiver_)
+      return false;
 
     return incoming_receiver_->Accept(message);
   }
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
index 8f5b4ff..584933e 100644
--- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -9,7 +9,6 @@
 
 #include <algorithm>  // For |std::swap()|.
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -20,20 +19,174 @@
 #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/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/filter_chain.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 {
+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) {}
 
@@ -56,10 +209,13 @@
   void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
     ConfigureProxyIfNecessary();
 
-    // It is safe to capture |this| because the callback won't be run after this
-    // object goes away.
-    endpoint_client_->QueryVersion(base::Bind(
-        &InterfacePtrState::OnQueryVersion, base::Unretained(this), 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(&InterfacePtrState::OnQueryVersion, base::Unretained(this),
+                   callback));
   }
 
   void RequireVersion(uint32_t version) {
@@ -69,17 +225,9 @@
       return;
 
     version_ = version;
-    endpoint_client_->RequireVersion(version);
-  }
-
-  void FlushForTesting() {
-    ConfigureProxyIfNecessary();
-    endpoint_client_->FlushForTesting();
-  }
-
-  void CloseWithReason(uint32_t custom_reason, const std::string& description) {
-    ConfigureProxyIfNecessary();
-    endpoint_client_->CloseWithReason(custom_reason, description);
+    // 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) {
@@ -132,14 +280,6 @@
     endpoint_client_->set_connection_error_handler(error_handler);
   }
 
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    ConfigureProxyIfNecessary();
-
-    DCHECK(endpoint_client_);
-    endpoint_client_->set_connection_error_with_reason_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();
@@ -155,17 +295,6 @@
     router_->EnableTestingMode();
   }
 
-  void ForwardMessage(Message message) {
-    ConfigureProxyIfNecessary();
-    endpoint_client_->Accept(&message);
-  }
-
-  void ForwardMessageWithResponder(Message message,
-                                   std::unique_ptr<MessageReceiver> responder) {
-    ConfigureProxyIfNecessary();
-    endpoint_client_->AcceptWithResponder(&message, responder.release());
-  }
-
  private:
   using Proxy = typename Interface::Proxy_;
 
@@ -180,22 +309,15 @@
     if (!handle_.is_valid())
       return;
 
-    MultiplexRouter::Config config =
-        Interface::PassesAssociatedKinds_
-            ? MultiplexRouter::MULTI_INTERFACE
-            : (Interface::HasSyncMethods_
-                   ? MultiplexRouter::SINGLE_INTERFACE_WITH_SYNC_METHODS
-                   : MultiplexRouter::SINGLE_INTERFACE);
-    router_ = new MultiplexRouter(std::move(handle_), config, true, runner_);
+    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_),
-        // The version is only queried from the client so the value passed here
-        // will not be used.
-        0u));
+        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,
diff --git a/mojo/public/cpp/bindings/lib/map_serialization.h b/mojo/public/cpp/bindings/lib/map_serialization.h
index 718a763..c28b835 100644
--- a/mojo/public/cpp/bindings/lib/map_serialization.h
+++ b/mojo/public/cpp/bindings/lib/map_serialization.h
@@ -8,11 +8,11 @@
 #include <type_traits>
 #include <vector>
 
-#include "mojo/public/cpp/bindings/array_data_view.h"
+#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_data_view.h"
+#include "mojo/public/cpp/bindings/map.h"
 
 namespace mojo {
 namespace internal {
@@ -46,15 +46,12 @@
  public:
   using Base = MapReaderBase<MaybeConstUserType>;
   using Traits = typename Base::Traits;
-  using MaybeConstIterator = typename Base::MaybeConstIterator;
 
   explicit MapKeyReader(MaybeConstUserType& input) : Base(input) {}
   ~MapKeyReader() {}
 
-  using GetNextResult =
-      decltype(Traits::GetKey(std::declval<MaybeConstIterator&>()));
-  GetNextResult GetNext() {
-    GetNextResult key = Traits::GetKey(this->iter_);
+  const typename Traits::Key& GetNext() {
+    const typename Traits::Key& key = Traits::GetKey(this->iter_);
     Traits::AdvanceIterator(this->iter_);
     return key;
   }
@@ -81,17 +78,17 @@
 };
 
 template <typename Key, typename Value, typename MaybeConstUserType>
-struct Serializer<MapDataView<Key, Value>, 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<MapDataView<Key, Value>>::Data;
-  using KeyArraySerializer = ArraySerializer<ArrayDataView<Key>,
+  using Data = typename MojomTypeTraits<Map<Key, Value>>::Data;
+  using KeyArraySerializer = ArraySerializer<Array<Key>,
                                              std::vector<UserKey>,
                                              MapKeyReader<MaybeConstUserType>>;
   using ValueArraySerializer =
-      ArraySerializer<ArrayDataView<Value>,
+      ArraySerializer<Array<Value>,
                       std::vector<UserValue>,
                       MapValueReader<MaybeConstUserType>>;
 
@@ -125,8 +122,8 @@
 
     auto result = Data::New(buf);
     if (result) {
-      auto keys_ptr = MojomTypeTraits<ArrayDataView<Key>>::Data::New(
-          Traits::GetSize(input), buf);
+      auto keys_ptr =
+          MojomTypeTraits<Array<Key>>::Data::New(Traits::GetSize(input), buf);
       if (keys_ptr) {
         MapKeyReader<MaybeConstUserType> key_reader(input);
         KeyArraySerializer::SerializeElements(
@@ -135,8 +132,8 @@
         result->keys.Set(keys_ptr);
       }
 
-      auto values_ptr = MojomTypeTraits<ArrayDataView<Value>>::Data::New(
-          Traits::GetSize(input), buf);
+      auto values_ptr =
+          MojomTypeTraits<Array<Value>>::Data::New(Traits::GetSize(input), buf);
       if (values_ptr) {
         MapValueReader<MaybeConstUserType> value_reader(input);
         ValueArraySerializer::SerializeElements(
diff --git a/mojo/public/cpp/bindings/lib/may_auto_lock.h b/mojo/public/cpp/bindings/lib/may_auto_lock.h
deleted file mode 100644
index 06091fe..0000000
--- a/mojo/public/cpp/bindings/lib/may_auto_lock.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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_MAY_AUTO_LOCK_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_
-
-#include "base/macros.h"
-#include "base/optional.h"
-#include "base/synchronization/lock.h"
-
-namespace mojo {
-namespace internal {
-
-// Similar to base::AutoLock, except that it does nothing if |lock| passed into
-// the constructor is null.
-class MayAutoLock {
- public:
-  explicit MayAutoLock(base::Optional<base::Lock>* lock)
-      : lock_(lock->has_value() ? &lock->value() : nullptr) {
-    if (lock_)
-      lock_->Acquire();
-  }
-
-  ~MayAutoLock() {
-    if (lock_) {
-      lock_->AssertAcquired();
-      lock_->Release();
-    }
-  }
-
- private:
-  base::Lock* lock_;
-  DISALLOW_COPY_AND_ASSIGN(MayAutoLock);
-};
-
-// Similar to base::AutoUnlock, except that it does nothing if |lock| passed
-// into the constructor is null.
-class MayAutoUnlock {
- public:
-  explicit MayAutoUnlock(base::Optional<base::Lock>* lock)
-      : lock_(lock->has_value() ? &lock->value() : nullptr) {
-    if (lock_) {
-      lock_->AssertAcquired();
-      lock_->Release();
-    }
-  }
-
-  ~MayAutoUnlock() {
-    if (lock_)
-      lock_->Acquire();
-  }
-
- private:
-  base::Lock* lock_;
-  DISALLOW_COPY_AND_ASSIGN(MayAutoUnlock);
-};
-
-}  // namespace internal
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index e5f3808..939e064 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -11,58 +11,18 @@
 #include <algorithm>
 #include <utility>
 
-#include "base/bind.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
-#include "base/threading/thread_local.h"
-#include "mojo/public/cpp/bindings/associated_group_controller.h"
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
 
 namespace mojo {
 
-namespace {
-
-base::LazyInstance<base::ThreadLocalPointer<internal::MessageDispatchContext>>::
-    DestructorAtExit g_tls_message_dispatch_context = LAZY_INSTANCE_INITIALIZER;
-
-base::LazyInstance<base::ThreadLocalPointer<SyncMessageResponseContext>>::
-    DestructorAtExit g_tls_sync_response_context = LAZY_INSTANCE_INITIALIZER;
-
-void DoNotifyBadMessage(Message message, const std::string& error) {
-  message.NotifyBadMessage(error);
-}
-
-}  // namespace
-
 Message::Message() {
 }
 
-Message::Message(Message&& other)
-    : buffer_(std::move(other.buffer_)),
-      handles_(std::move(other.handles_)),
-      associated_endpoint_handles_(
-          std::move(other.associated_endpoint_handles_)) {}
-
 Message::~Message() {
   CloseHandles();
 }
 
-Message& Message::operator=(Message&& other) {
-  Reset();
-  std::swap(other.buffer_, buffer_);
-  std::swap(other.handles_, handles_);
-  std::swap(other.associated_endpoint_handles_, associated_endpoint_handles_);
-  return *this;
-}
-
-void Message::Reset() {
-  CloseHandles();
-  handles_.clear();
-  associated_endpoint_handles_.clear();
-  buffer_.reset();
-}
-
 void Message::Initialize(size_t capacity, bool zero_initialized) {
   DCHECK(!buffer_);
   buffer_.reset(new internal::MessageBuffer(capacity, zero_initialized));
@@ -76,52 +36,19 @@
   handles_.swap(*handles);
 }
 
-const uint8_t* Message::payload() const {
-  if (version() < 2)
-    return data() + header()->num_bytes;
+void Message::MoveTo(Message* destination) {
+  DCHECK(this != destination);
 
-  return static_cast<const uint8_t*>(header_v2()->payload.Get());
-}
+  // No copy needed.
+  std::swap(destination->buffer_, buffer_);
+  std::swap(destination->handles_, handles_);
 
-uint32_t Message::payload_num_bytes() const {
-  DCHECK_GE(data_num_bytes(), header()->num_bytes);
-  size_t num_bytes;
-  if (version() < 2) {
-    num_bytes = data_num_bytes() - header()->num_bytes;
-  } else {
-    auto payload = reinterpret_cast<uintptr_t>(header_v2()->payload.Get());
-    if (!payload) {
-      num_bytes = 0;
-    } else {
-      auto payload_end =
-          reinterpret_cast<uintptr_t>(header_v2()->payload_interface_ids.Get());
-      if (!payload_end)
-        payload_end = reinterpret_cast<uintptr_t>(data() + data_num_bytes());
-      DCHECK_GE(payload_end, payload);
-      num_bytes = payload_end - payload;
-    }
-  }
-  DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max());
-  return static_cast<uint32_t>(num_bytes);
-}
-
-uint32_t Message::payload_num_interface_ids() const {
-  auto* array_pointer =
-      version() < 2 ? nullptr : header_v2()->payload_interface_ids.Get();
-  return array_pointer ? static_cast<uint32_t>(array_pointer->size()) : 0;
-}
-
-const uint32_t* Message::payload_interface_ids() const {
-  auto* array_pointer =
-      version() < 2 ? nullptr : header_v2()->payload_interface_ids.Get();
-  return array_pointer ? array_pointer->storage() : nullptr;
+  CloseHandles();
+  handles_.clear();
+  buffer_.reset();
 }
 
 ScopedMessageHandle Message::TakeMojoMessage() {
-  // If there are associated endpoints transferred,
-  // SerializeAssociatedEndpointHandles() must be called before this method.
-  DCHECK(associated_endpoint_handles_.empty());
-
   if (handles_.empty())  // Fast path for the common case: No handles.
     return buffer_->TakeMessage();
 
@@ -153,7 +80,6 @@
 }
 
 void Message::NotifyBadMessage(const std::string& error) {
-  DCHECK(buffer_);
   buffer_->NotifyBadMessage(error);
 }
 
@@ -165,88 +91,6 @@
   }
 }
 
-void Message::SerializeAssociatedEndpointHandles(
-    AssociatedGroupController* group_controller) {
-  if (associated_endpoint_handles_.empty())
-    return;
-
-  DCHECK_GE(version(), 2u);
-  DCHECK(header_v2()->payload_interface_ids.is_null());
-
-  size_t size = associated_endpoint_handles_.size();
-  auto* data = internal::Array_Data<uint32_t>::New(size, buffer());
-  header_v2()->payload_interface_ids.Set(data);
-
-  for (size_t i = 0; i < size; ++i) {
-    ScopedInterfaceEndpointHandle& handle = associated_endpoint_handles_[i];
-
-    DCHECK(handle.pending_association());
-    data->storage()[i] =
-        group_controller->AssociateInterface(std::move(handle));
-  }
-  associated_endpoint_handles_.clear();
-}
-
-bool Message::DeserializeAssociatedEndpointHandles(
-    AssociatedGroupController* group_controller) {
-  associated_endpoint_handles_.clear();
-
-  uint32_t num_ids = payload_num_interface_ids();
-  if (num_ids == 0)
-    return true;
-
-  associated_endpoint_handles_.reserve(num_ids);
-  uint32_t* ids = header_v2()->payload_interface_ids.Get()->storage();
-  bool result = true;
-  for (uint32_t i = 0; i < num_ids; ++i) {
-    auto handle = group_controller->CreateLocalEndpointHandle(ids[i]);
-    if (IsValidInterfaceId(ids[i]) && !handle.is_valid()) {
-      // |ids[i]| itself is valid but handle creation failed. In that case, mark
-      // deserialization as failed but continue to deserialize the rest of
-      // handles.
-      result = false;
-    }
-
-    associated_endpoint_handles_.push_back(std::move(handle));
-    ids[i] = kInvalidInterfaceId;
-  }
-  return result;
-}
-
-PassThroughFilter::PassThroughFilter() {}
-
-PassThroughFilter::~PassThroughFilter() {}
-
-bool PassThroughFilter::Accept(Message* message) { return true; }
-
-SyncMessageResponseContext::SyncMessageResponseContext()
-    : outer_context_(current()) {
-  g_tls_sync_response_context.Get().Set(this);
-}
-
-SyncMessageResponseContext::~SyncMessageResponseContext() {
-  DCHECK_EQ(current(), this);
-  g_tls_sync_response_context.Get().Set(outer_context_);
-}
-
-// static
-SyncMessageResponseContext* SyncMessageResponseContext::current() {
-  return g_tls_sync_response_context.Get().Get();
-}
-
-void SyncMessageResponseContext::ReportBadMessage(const std::string& error) {
-  GetBadMessageCallback().Run(error);
-}
-
-const ReportBadMessageCallback&
-SyncMessageResponseContext::GetBadMessageCallback() {
-  if (bad_message_callback_.is_null()) {
-    bad_message_callback_ =
-        base::Bind(&DoNotifyBadMessage, base::Passed(&response_));
-  }
-  return bad_message_callback_;
-}
-
 MojoResult ReadMessage(MessagePipeHandle handle, Message* message) {
   MojoResult rv;
 
@@ -278,55 +122,4 @@
   return MOJO_RESULT_OK;
 }
 
-void ReportBadMessage(const std::string& error) {
-  internal::MessageDispatchContext* context =
-      internal::MessageDispatchContext::current();
-  DCHECK(context);
-  context->GetBadMessageCallback().Run(error);
-}
-
-ReportBadMessageCallback GetBadMessageCallback() {
-  internal::MessageDispatchContext* context =
-      internal::MessageDispatchContext::current();
-  DCHECK(context);
-  return context->GetBadMessageCallback();
-}
-
-namespace internal {
-
-MessageHeaderV2::MessageHeaderV2() = default;
-
-MessageDispatchContext::MessageDispatchContext(Message* message)
-    : outer_context_(current()), message_(message) {
-  g_tls_message_dispatch_context.Get().Set(this);
-}
-
-MessageDispatchContext::~MessageDispatchContext() {
-  DCHECK_EQ(current(), this);
-  g_tls_message_dispatch_context.Get().Set(outer_context_);
-}
-
-// static
-MessageDispatchContext* MessageDispatchContext::current() {
-  return g_tls_message_dispatch_context.Get().Get();
-}
-
-const ReportBadMessageCallback&
-MessageDispatchContext::GetBadMessageCallback() {
-  if (bad_message_callback_.is_null()) {
-    bad_message_callback_ =
-        base::Bind(&DoNotifyBadMessage, base::Passed(message_));
-  }
-  return bad_message_callback_;
-}
-
-// static
-void SyncMessageResponseSetup::SetCurrentSyncResponseMessage(Message* message) {
-  SyncMessageResponseContext* context = SyncMessageResponseContext::current();
-  if (context)
-    context->response_ = std::move(*message);
-}
-
-}  // namespace internal
-
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.cc b/mojo/public/cpp/bindings/lib/message_buffer.cc
index cc12ef6..af79cfd 100644
--- a/mojo/public/cpp/bindings/lib/message_buffer.cc
+++ b/mojo/public/cpp/bindings/lib/message_buffer.cc
@@ -13,35 +13,54 @@
 
 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);
 
-  void* buffer = nullptr;
-  if (capacity != 0) {
-    rv = GetMessageBuffer(message_.get(), &buffer);
+  if (capacity == 0) {
+    buffer_ = nullptr;
+  } else {
+    rv = GetMessageBuffer(message_.get(), &buffer_);
     CHECK_EQ(rv, MOJO_RESULT_OK);
 
     if (zero_initialized)
-      memset(buffer, 0, capacity);
+      memset(buffer_, 0, capacity);
   }
-  Initialize(buffer, capacity);
 }
 
 MessageBuffer::MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes) {
   message_ = std::move(message);
+  data_num_bytes_ = num_bytes;
 
-  void* buffer = nullptr;
-  if (num_bytes != 0) {
-    MojoResult rv = GetMessageBuffer(message_.get(), &buffer);
+  if (num_bytes == 0) {
+    buffer_ = nullptr;
+  } else {
+    MojoResult rv = GetMessageBuffer(message_.get(), &buffer_);
     CHECK_EQ(rv, MOJO_RESULT_OK);
   }
-  Initialize(buffer, num_bytes);
 }
 
 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);
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.h b/mojo/public/cpp/bindings/lib/message_buffer.h
index 96d5140..0382131 100644
--- a/mojo/public/cpp/bindings/lib/message_buffer.h
+++ b/mojo/public/cpp/bindings/lib/message_buffer.h
@@ -5,6 +5,7 @@
 #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>
@@ -16,7 +17,7 @@
 namespace mojo {
 namespace internal {
 
-// A fixed-size Buffer using a Mojo message object for storage.
+// 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.
@@ -25,14 +26,24 @@
   // Initializes this buffer from an existing Mojo MessageHandle.
   MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes);
 
-  ~MessageBuffer();
+  ~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);
 };
diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc
index 6806a73..4ffa180 100644
--- a/mojo/public/cpp/bindings/lib/message_builder.cc
+++ b/mojo/public/cpp/bindings/lib/message_builder.cc
@@ -4,10 +4,11 @@
 
 #include "mojo/public/cpp/bindings/lib/message_builder.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/buffer.h"
-#include "mojo/public/cpp/bindings/lib/message_internal.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 {
@@ -18,52 +19,38 @@
   (*header)->num_bytes = sizeof(Header);
 }
 
-MessageBuilder::MessageBuilder(uint32_t name,
-                               uint32_t flags,
-                               size_t payload_size,
-                               size_t payload_interface_id_count) {
-  if (payload_interface_id_count > 0) {
-    // Version 2
-    InitializeMessage(
-        sizeof(MessageHeaderV2) + Align(payload_size) +
-        ArrayDataTraits<uint32_t>::GetStorageSize(
-            static_cast<uint32_t>(payload_interface_id_count)));
+MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) {
+  InitializeMessage(sizeof(MessageHeader) + payload_size);
 
-    MessageHeaderV2* header;
-    Allocate(message_.buffer(), &header);
-    header->version = 2;
-    header->name = name;
-    header->flags = flags;
-    // The payload immediately follows the header.
-    header->payload.Set(header + 1);
-  } else if (flags &
-             (Message::kFlagExpectsResponse | Message::kFlagIsResponse)) {
-    // Version 1
-    InitializeMessage(sizeof(MessageHeaderV1) + payload_size);
-
-    MessageHeaderV1* header;
-    Allocate(message_.buffer(), &header);
-    header->version = 1;
-    header->name = name;
-    header->flags = flags;
-  } else {
-    InitializeMessage(sizeof(MessageHeader) + payload_size);
-
-    MessageHeader* header;
-    Allocate(message_.buffer(), &header);
-    header->version = 0;
-    header->name = name;
-    header->flags = flags;
-  }
+  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
index 8a4d5c4..a5a050f 100644
--- a/mojo/public/cpp/bindings/lib/message_builder.h
+++ b/mojo/public/cpp/bindings/lib/message_builder.h
@@ -8,30 +8,24 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
 #include "mojo/public/cpp/bindings/message.h"
 
 namespace mojo {
-
 class Message;
 
 namespace internal {
 
-class Buffer;
-
-class MOJO_CPP_BINDINGS_EXPORT MessageBuilder {
+class MessageBuilder {
  public:
-  MessageBuilder(uint32_t name,
-                 uint32_t flags,
-                 size_t payload_size,
-                 size_t payload_interface_id_count);
+  MessageBuilder(uint32_t name, size_t payload_size);
   ~MessageBuilder();
 
   Buffer* buffer() { return message_.buffer(); }
   Message* message() { return &message_; }
 
- private:
+ protected:
+  MessageBuilder();
   void InitializeMessage(size_t size);
 
   Message message_;
@@ -39,6 +33,51 @@
   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
 
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
index 9f8c627..10f7774 100644
--- a/mojo/public/cpp/bindings/lib/message_header_validator.cc
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -4,8 +4,6 @@
 
 #include "mojo/public/cpp/bindings/message_header_validator.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_context.h"
 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
 #include "mojo/public/cpp/bindings/lib/validation_util.h"
@@ -13,40 +11,40 @@
 namespace mojo {
 namespace {
 
-// TODO(yzshen): Define a mojom struct for message header and use the generated
-// validation and data view code.
 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:
-  do {
-    if (header->version == 0) {
-      if (header->num_bytes == sizeof(internal::MessageHeader))
-        break;
-    } else if (header->version == 1) {
-      if (header->num_bytes == sizeof(internal::MessageHeaderV1))
-        break;
-    } else if (header->version == 2) {
-      if (header->num_bytes == sizeof(internal::MessageHeaderV2))
-        break;
-    } else if (header->version > 2) {
-      if (header->num_bytes >= sizeof(internal::MessageHeaderV2))
-        break;
+  if (header->version == 0) {
+    if (header->num_bytes != sizeof(internal::MessageHeader)) {
+      internal::ReportValidationError(
+          validation_context,
+          internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+      return false;
     }
-    internal::ReportValidationError(
-        validation_context,
-        internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
-    return false;
-  } while (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.
-  constexpr uint32_t kRequestIdFlags =
-      Message::kFlagExpectsResponse | Message::kFlagIsResponse;
-  if (header->version == 0 && (header->flags & kRequestIdFlags)) {
+  if (header->version < 1 && ((header->flags & Message::kFlagExpectsResponse) ||
+                              (header->flags & Message::kFlagIsResponse))) {
     internal::ReportValidationError(
         validation_context,
         internal::VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID);
@@ -54,60 +52,25 @@
   }
 
   // These flags are mutually exclusive.
-  if ((header->flags & kRequestIdFlags) == kRequestIdFlags) {
+  if ((header->flags & Message::kFlagExpectsResponse) &&
+      (header->flags & Message::kFlagIsResponse)) {
     internal::ReportValidationError(
         validation_context,
         internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
     return false;
   }
 
-  if (header->version < 2)
-    return true;
-
-  auto* header_v2 = static_cast<const internal::MessageHeaderV2*>(header);
-  // For the payload pointer:
-  // - Check that the pointer can be safely decoded.
-  // - Claim one byte that the pointer points to. It makes sure not only the
-  //   address is within the message, but also the address precedes the array
-  //   storing interface IDs (which is important for safely calculating the
-  //   payload size).
-  // - Validation of the payload contents will be done separately based on the
-  //   payload type.
-  if (!header_v2->payload.is_null() &&
-      (!internal::ValidatePointer(header_v2->payload, validation_context) ||
-       !validation_context->ClaimMemory(header_v2->payload.Get(), 1))) {
-    return false;
-  }
-
-  const internal::ContainerValidateParams validate_params(0, false, nullptr);
-  if (!internal::ValidateContainer(header_v2->payload_interface_ids,
-                                   validation_context, &validate_params)) {
-    return false;
-  }
-
-  if (!header_v2->payload_interface_ids.is_null()) {
-    size_t num_ids = header_v2->payload_interface_ids.Get()->size();
-    const uint32_t* ids = header_v2->payload_interface_ids.Get()->storage();
-    for (size_t i = 0; i < num_ids; ++i) {
-      if (!IsValidInterfaceId(ids[i]) || IsMasterInterfaceId(ids[i])) {
-        internal::ReportValidationError(
-            validation_context,
-            internal::VALIDATION_ERROR_ILLEGAL_INTERFACE_ID);
-        return false;
-      }
-    }
-  }
-
   return true;
 }
 
 }  // namespace
 
-MessageHeaderValidator::MessageHeaderValidator()
-    : MessageHeaderValidator("MessageHeaderValidator") {}
+MessageHeaderValidator::MessageHeaderValidator(MessageReceiver* sink)
+    : MessageHeaderValidator("MessageHeaderValidator", sink) {}
 
-MessageHeaderValidator::MessageHeaderValidator(const std::string& description)
-    : description_(description) {
+MessageHeaderValidator::MessageHeaderValidator(const std::string& description,
+                                               MessageReceiver* sink)
+    : MessageFilter(sink), description_(description) {
 }
 
 void MessageHeaderValidator::SetDescription(const std::string& description) {
@@ -115,10 +78,10 @@
 }
 
 bool MessageHeaderValidator::Accept(Message* message) {
-  // Pass 0 as number of handles and associated endpoint handles because we
-  // don't expect any in the header, even if |message| contains handles.
+  // 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, 0, message, description_);
+      message->data(), message->data_num_bytes(), 0, message, description_);
 
   if (!internal::ValidateStructHeaderAndClaimMemory(message->data(),
                                                     &validation_context))
@@ -127,7 +90,7 @@
   if (!IsValidMessageHeader(message->header(), &validation_context))
     return false;
 
-  return true;
+  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
index 6693198..63edffd 100644
--- a/mojo/public/cpp/bindings/lib/message_internal.h
+++ b/mojo/public/cpp/bindings/lib/message_internal.h
@@ -7,22 +7,11 @@
 
 #include <stdint.h>
 
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
 
 namespace mojo {
-
-class Message;
-
 namespace internal {
 
-template <typename T>
-class Array_Data;
-
 #pragma pack(push, 1)
 
 struct MessageHeader : internal::StructHeader {
@@ -38,44 +27,16 @@
 };
 static_assert(sizeof(MessageHeader) == 24, "Bad sizeof(MessageHeader)");
 
-struct MessageHeaderV1 : 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(MessageHeaderV1) == 32, "Bad sizeof(MessageHeaderV1)");
-
-struct MessageHeaderV2 : MessageHeaderV1 {
-  MessageHeaderV2();
-  GenericPointer payload;
-  Pointer<Array_Data<uint32_t>> payload_interface_ids;
-};
-static_assert(sizeof(MessageHeaderV2) == 48, "Bad sizeof(MessageHeaderV2)");
+static_assert(sizeof(MessageHeaderWithRequestID) == 32,
+              "Bad sizeof(MessageHeaderWithRequestID)");
 
 #pragma pack(pop)
 
-class MOJO_CPP_BINDINGS_EXPORT MessageDispatchContext {
- public:
-  explicit MessageDispatchContext(Message* message);
-  ~MessageDispatchContext();
-
-  static MessageDispatchContext* current();
-
-  const base::Callback<void(const std::string&)>& GetBadMessageCallback();
-
- private:
-  MessageDispatchContext* outer_context_;
-  Message* message_;
-  base::Callback<void(const std::string&)> bad_message_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(MessageDispatchContext);
-};
-
-class MOJO_CPP_BINDINGS_EXPORT SyncMessageResponseSetup {
- public:
-  static void SetCurrentSyncResponseMessage(Message* message);
-};
-
 }  // namespace internal
 }  // namespace mojo
 
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index 2da459a..dcfbab1 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -15,9 +15,9 @@
 #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/lib/may_auto_lock.h"
 #include "mojo/public/cpp/bindings/sync_handle_watcher.h"
 
 namespace mojo {
@@ -36,7 +36,6 @@
         id_(id),
         closed_(false),
         peer_closed_(false),
-        handle_created_(false),
         client_(nullptr),
         event_signalled_(false) {}
 
@@ -51,31 +50,16 @@
 
   bool closed() const { return closed_; }
   void set_closed() {
-    router_->AssertLockAcquired();
+    router_->lock_.AssertAcquired();
     closed_ = true;
   }
 
   bool peer_closed() const { return peer_closed_; }
   void set_peer_closed() {
-    router_->AssertLockAcquired();
+    router_->lock_.AssertAcquired();
     peer_closed_ = true;
   }
 
-  bool handle_created() const { return handle_created_; }
-  void set_handle_created() {
-    router_->AssertLockAcquired();
-    handle_created_ = true;
-  }
-
-  const base::Optional<DisconnectReason>& disconnect_reason() const {
-    return disconnect_reason_;
-  }
-  void set_disconnect_reason(
-      const base::Optional<DisconnectReason>& disconnect_reason) {
-    router_->AssertLockAcquired();
-    disconnect_reason_ = disconnect_reason;
-  }
-
   base::SingleThreadTaskRunner* task_runner() const {
     return task_runner_.get();
   }
@@ -84,7 +68,7 @@
 
   void AttachClient(InterfaceEndpointClient* client,
                     scoped_refptr<base::SingleThreadTaskRunner> runner) {
-    router_->AssertLockAcquired();
+    router_->lock_.AssertAcquired();
     DCHECK(!client_);
     DCHECK(!closed_);
     DCHECK(runner->BelongsToCurrentThread());
@@ -96,7 +80,7 @@
   // This method must be called on the same thread as the corresponding
   // AttachClient() call.
   void DetachClient() {
-    router_->AssertLockAcquired();
+    router_->lock_.AssertAcquired();
     DCHECK(client_);
     DCHECK(task_runner_->BelongsToCurrentThread());
     DCHECK(!closed_);
@@ -107,36 +91,18 @@
   }
 
   void SignalSyncMessageEvent() {
-    router_->AssertLockAcquired();
+    router_->lock_.AssertAcquired();
     if (event_signalled_)
       return;
 
+    EnsureEventMessagePipeExists();
     event_signalled_ = true;
-    if (!sync_message_event_sender_.is_valid())
-      return;
-
     MojoResult result =
         WriteMessageRaw(sync_message_event_sender_.get(), nullptr, 0, nullptr,
                         0, MOJO_WRITE_MESSAGE_FLAG_NONE);
     DCHECK_EQ(MOJO_RESULT_OK, result);
   }
 
-  void ResetSyncMessageSignal() {
-    router_->AssertLockAcquired();
-
-    if (!event_signalled_)
-      return;
-
-    event_signalled_ = false;
-    if (!sync_message_event_receiver_.is_valid())
-      return;
-
-    MojoResult result =
-        ReadMessageRaw(sync_message_event_receiver_.get(), nullptr, nullptr,
-                       nullptr, nullptr, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
-    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
@@ -166,7 +132,7 @@
   friend class base::RefCounted<InterfaceEndpoint>;
 
   ~InterfaceEndpoint() override {
-    router_->AssertLockAcquired();
+    router_->lock_.AssertAcquired();
 
     DCHECK(!client_);
     DCHECK(closed_);
@@ -176,23 +142,26 @@
 
   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_);
 
-    MayAutoLock locker(&router_->lock_);
-    scoped_refptr<InterfaceEndpoint> self_protector(this);
+      bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_);
 
-    bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_);
+      if (!more_to_process)
+        ResetSyncMessageSignal();
 
-    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.
-    if (!more_to_process && peer_closed_) {
+      // 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.
@@ -206,21 +175,12 @@
       return;
 
     {
-      MayAutoLock locker(&router_->lock_);
+      base::AutoLock locker(router_->lock_);
+      EnsureEventMessagePipeExists();
 
-      if (!sync_message_event_sender_.is_valid()) {
-        MojoResult result =
-            CreateMessagePipe(nullptr, &sync_message_event_sender_,
-                              &sync_message_event_receiver_);
-        DCHECK_EQ(MOJO_RESULT_OK, result);
-
-        if (event_signalled_) {
-          // Reset the flag so that SignalSyncMessageEvent() will actually
-          // signal using the newly-created message pipe.
-          event_signalled_ = false;
-          SignalSyncMessageEvent();
-        }
-      }
+      auto iter = router_->sync_message_tasks_.find(id_);
+      if (iter != router_->sync_message_tasks_.end() && !iter->second.empty())
+        SignalSyncMessageEvent();
     }
 
     sync_watcher_.reset(new SyncHandleWatcher(
@@ -228,6 +188,31 @@
         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.
 
@@ -242,12 +227,6 @@
   // Whether the peer endpoint has been closed.
   bool peer_closed_;
 
-  // Whether there is already a ScopedInterfaceEndpointHandle created for this
-  // endpoint.
-  bool handle_created_;
-
-  base::Optional<DisconnectReason> disconnect_reason_;
-
   // 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.
@@ -271,53 +250,13 @@
   DISALLOW_COPY_AND_ASSIGN(InterfaceEndpoint);
 };
 
-// MessageWrapper objects are always destroyed under the router's lock. On
-// destruction, if the message it wrappers contains
-// ScopedInterfaceEndpointHandles (which cannot be destructed under the
-// router's lock), the wrapper unlocks to clean them up.
-class MultiplexRouter::MessageWrapper {
- public:
-  MessageWrapper() = default;
-
-  MessageWrapper(MultiplexRouter* router, Message message)
-      : router_(router), value_(std::move(message)) {}
-
-  MessageWrapper(MessageWrapper&& other)
-      : router_(other.router_), value_(std::move(other.value_)) {}
-
-  ~MessageWrapper() {
-    if (value_.associated_endpoint_handles()->empty())
-      return;
-
-    router_->AssertLockAcquired();
-    {
-      MayAutoUnlock unlocker(&router_->lock_);
-      value_.mutable_associated_endpoint_handles()->clear();
-    }
-  }
-
-  MessageWrapper& operator=(MessageWrapper&& other) {
-    router_ = other.router_;
-    value_ = std::move(other.value_);
-    return *this;
-  }
-
-  Message& value() { return value_; }
-
- private:
-  MultiplexRouter* router_ = nullptr;
-  Message value_;
-
-  DISALLOW_COPY_AND_ASSIGN(MessageWrapper);
-};
-
 struct MultiplexRouter::Task {
  public:
   // Doesn't take ownership of |message| but takes its contents.
-  static std::unique_ptr<Task> CreateMessageTask(
-      MessageWrapper message_wrapper) {
+  static std::unique_ptr<Task> CreateMessageTask(Message* message) {
     Task* task = new Task(MESSAGE);
-    task->message_wrapper = std::move(message_wrapper);
+    task->message.reset(new Message);
+    message->MoveTo(task->message.get());
     return base::WrapUnique(task);
   }
   static std::unique_ptr<Task> CreateNotifyErrorTask(
@@ -332,7 +271,7 @@
   bool IsMessageTask() const { return type == MESSAGE; }
   bool IsNotifyErrorTask() const { return type == NOTIFY_ERROR; }
 
-  MessageWrapper message_wrapper;
+  std::unique_ptr<Message> message;
   scoped_refptr<InterfaceEndpoint> endpoint_to_notify;
 
   enum Type { MESSAGE, NOTIFY_ERROR };
@@ -340,56 +279,36 @@
 
  private:
   explicit Task(Type in_type) : type(in_type) {}
-
-  DISALLOW_COPY_AND_ASSIGN(Task);
 };
 
 MultiplexRouter::MultiplexRouter(
-    ScopedMessagePipeHandle message_pipe,
-    Config config,
     bool set_interface_id_namesapce_bit,
+    ScopedMessagePipeHandle message_pipe,
     scoped_refptr<base::SingleThreadTaskRunner> runner)
-    : set_interface_id_namespace_bit_(set_interface_id_namesapce_bit),
-      task_runner_(runner),
-      header_validator_(nullptr),
-      filters_(this),
+    : AssociatedGroupController(base::ThreadTaskRunnerHandle::Get()),
+      set_interface_id_namespace_bit_(set_interface_id_namesapce_bit),
+      header_validator_(this),
       connector_(std::move(message_pipe),
-                 config == MULTI_INTERFACE ? Connector::MULTI_THREADED_SEND
-                                           : Connector::SINGLE_THREADED_SEND,
+                 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),
-      paused_(false),
       testing_mode_(false) {
-  DCHECK(task_runner_->BelongsToCurrentThread());
-
-  if (config == MULTI_INTERFACE)
-    lock_.emplace();
-
-  if (config == SINGLE_INTERFACE_WITH_SYNC_METHODS ||
-      config == MULTI_INTERFACE) {
-    // Always participate in sync handle watching in multi-interface mode,
-    // 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(&filters_);
+  // 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)));
-
-  std::unique_ptr<MessageHeaderValidator> header_validator =
-      base::MakeUnique<MessageHeaderValidator>();
-  header_validator_ = header_validator.get();
-  filters_.Append(std::move(header_validator));
 }
 
 MultiplexRouter::~MultiplexRouter() {
-  MayAutoLock locker(&lock_);
+  base::AutoLock locker(lock_);
 
   sync_message_tasks_.clear();
   tasks_.clear();
@@ -400,66 +319,40 @@
     // because it may remove the corresponding value from the map.
     ++iter;
 
-    if (!endpoint->closed()) {
-      // This happens when a NotifyPeerEndpointClosed message been received, but
-      // the interface ID hasn't been used to create local endpoint handle.
-      DCHECK(!endpoint->client());
-      DCHECK(endpoint->peer_closed());
-      UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
-    } else {
-      UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
-    }
+    DCHECK(endpoint->closed());
+    UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
   }
 
   DCHECK(endpoints_.empty());
 }
 
-void MultiplexRouter::SetMasterInterfaceName(const char* name) {
+void MultiplexRouter::SetMasterInterfaceName(const std::string& name) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  header_validator_->SetDescription(
-      std::string(name) + " [master] MessageHeaderValidator");
+  header_validator_.SetDescription(name + " [master] MessageHeaderValidator");
   control_message_handler_.SetDescription(
-      std::string(name) + " [master] PipeControlMessageHandler");
-  connector_.SetWatcherHeapProfilerTag(name);
+      name + " [master] PipeControlMessageHandler");
 }
 
-InterfaceId MultiplexRouter::AssociateInterface(
-    ScopedInterfaceEndpointHandle handle_to_send) {
-  if (!handle_to_send.pending_association())
-    return kInvalidInterfaceId;
-
+void MultiplexRouter::CreateEndpointHandlePair(
+    ScopedInterfaceEndpointHandle* local_endpoint,
+    ScopedInterfaceEndpointHandle* remote_endpoint) {
+  base::AutoLock locker(lock_);
   uint32_t id = 0;
-  {
-    MayAutoLock locker(&lock_);
-    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 (base::ContainsKey(endpoints_, id));
+  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);
-    endpoint->set_handle_created();
-  }
+  InterfaceEndpoint* endpoint = new InterfaceEndpoint(this, id);
+  endpoints_[id] = endpoint;
+  if (encountered_error_)
+    UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
 
-  if (!NotifyAssociation(&handle_to_send, id)) {
-    // The peer handle of |handle_to_send|, which is supposed to join this
-    // associated group, has been closed.
-    {
-      MayAutoLock locker(&lock_);
-      InterfaceEndpoint* endpoint = FindEndpoint(id);
-      if (endpoint)
-        UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
-    }
-
-    control_message_proxy_.NotifyPeerEndpointClosed(
-        id, handle_to_send.disconnect_reason());
-  }
-  return id;
+  *local_endpoint = CreateScopedInterfaceEndpointHandle(id, true);
+  *remote_endpoint = CreateScopedInterfaceEndpointHandle(id, false);
 }
 
 ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle(
@@ -467,12 +360,10 @@
   if (!IsValidInterfaceId(id))
     return ScopedInterfaceEndpointHandle();
 
-  MayAutoLock locker(&lock_);
+  base::AutoLock locker(lock_);
   bool inserted = false;
   InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted);
   if (inserted) {
-    DCHECK(!endpoint->handle_created());
-
     if (encountered_error_)
       UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
   } else {
@@ -480,32 +371,34 @@
     // notification that the peer endpoint has closed.
     CHECK(!endpoint->closed());
     CHECK(endpoint->peer_closed());
-
-    if (endpoint->handle_created())
-      return ScopedInterfaceEndpointHandle();
   }
-
-  endpoint->set_handle_created();
-  return CreateScopedInterfaceEndpointHandle(id);
+  return CreateScopedInterfaceEndpointHandle(id, true);
 }
 
-void MultiplexRouter::CloseEndpointHandle(
-    InterfaceId id,
-    const base::Optional<DisconnectReason>& reason) {
+void MultiplexRouter::CloseEndpointHandle(InterfaceId id, bool is_local) {
   if (!IsValidInterfaceId(id))
     return;
 
-  MayAutoLock locker(&lock_);
-  DCHECK(base::ContainsKey(endpoints_, id));
+  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) || reason) {
-    MayAutoUnlock unlocker(&lock_);
-    control_message_proxy_.NotifyPeerEndpointClosed(id, reason);
-  }
+  if (!IsMasterInterfaceId(id))
+    control_message_proxy_.NotifyPeerEndpointClosed(id);
 
   ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr);
 }
@@ -519,8 +412,8 @@
   DCHECK(IsValidInterfaceId(id));
   DCHECK(client);
 
-  MayAutoLock locker(&lock_);
-  DCHECK(base::ContainsKey(endpoints_, id));
+  base::AutoLock locker(lock_);
+  DCHECK(ContainsKey(endpoints_, id));
 
   InterfaceEndpoint* endpoint = endpoints_[id].get();
   endpoint->AttachClient(client, std::move(runner));
@@ -538,8 +431,8 @@
 
   DCHECK(IsValidInterfaceId(id));
 
-  MayAutoLock locker(&lock_);
-  DCHECK(base::ContainsKey(endpoints_, id));
+  base::AutoLock locker(lock_);
+  DCHECK(ContainsKey(endpoints_, id));
 
   InterfaceEndpoint* endpoint = endpoints_[id].get();
   endpoint->DetachClient();
@@ -563,51 +456,21 @@
   OnPipeConnectionError();
 }
 
-void MultiplexRouter::PauseIncomingMethodCallProcessing() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  connector_.PauseIncomingMethodCallProcessing();
-
-  MayAutoLock locker(&lock_);
-  paused_ = true;
-
-  for (auto iter = endpoints_.begin(); iter != endpoints_.end(); ++iter)
-    iter->second->ResetSyncMessageSignal();
-}
-
-void MultiplexRouter::ResumeIncomingMethodCallProcessing() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  connector_.ResumeIncomingMethodCallProcessing();
-
-  MayAutoLock locker(&lock_);
-  paused_ = false;
-
-  for (auto iter = endpoints_.begin(); iter != endpoints_.end(); ++iter) {
-    auto sync_iter = sync_message_tasks_.find(iter->first);
-    if (iter->second->peer_closed() ||
-        (sync_iter != sync_message_tasks_.end() &&
-         !sync_iter->second.empty())) {
-      iter->second->SignalSyncMessageEvent();
-    }
-  }
-
-  ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr);
-}
-
 bool MultiplexRouter::HasAssociatedEndpoints() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  MayAutoLock locker(&lock_);
+  base::AutoLock locker(lock_);
 
   if (endpoints_.size() > 1)
     return true;
   if (endpoints_.size() == 0)
     return false;
 
-  return !base::ContainsKey(endpoints_, kMasterInterfaceId);
+  return !ContainsKey(endpoints_, kMasterInterfaceId);
 }
 
 void MultiplexRouter::EnableTestingMode() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  MayAutoLock locker(&lock_);
+  base::AutoLock locker(lock_);
 
   testing_mode_ = true;
   connector_.set_enforce_errors_from_incoming_receiver(false);
@@ -616,13 +479,8 @@
 bool MultiplexRouter::Accept(Message* message) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (!message->DeserializeAssociatedEndpointHandles(this))
-    return false;
-
   scoped_refptr<MultiplexRouter> protector(this);
-  MayAutoLock locker(&lock_);
-
-  DCHECK(!paused_);
+  base::AutoLock locker(lock_);
 
   ClientCallBehavior client_call_behavior =
       connector_.during_sync_handle_watcher_callback()
@@ -636,16 +494,15 @@
   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(MessageWrapper(this, std::move(*message))));
+    tasks_.push_back(Task::CreateMessageTask(message));
     Task* task = tasks_.back().get();
 
-    if (task->message_wrapper.value().has_flag(Message::kFlagIsSync)) {
-      InterfaceId id = task->message_wrapper.value().interface_id();
+    if (task->message->has_flag(Message::kFlagIsSync)) {
+      InterfaceId id = task->message->interface_id();
       sync_message_tasks_[id].push_back(task);
-      InterfaceEndpoint* endpoint = FindEndpoint(id);
-      if (endpoint)
-        endpoint->SignalSyncMessageEvent();
+      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)
@@ -659,17 +516,14 @@
   return true;
 }
 
-bool MultiplexRouter::OnPeerAssociatedEndpointClosed(
-    InterfaceId id,
-    const base::Optional<DisconnectReason>& reason) {
-  DCHECK(!IsMasterInterfaceId(id) || reason);
+bool MultiplexRouter::OnPeerAssociatedEndpointClosed(InterfaceId id) {
+  lock_.AssertAcquired();
 
-  MayAutoLock locker(&lock_);
+  if (IsMasterInterfaceId(id))
+    return false;
+
   InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr);
 
-  if (reason)
-    endpoint->set_disconnect_reason(reason);
-
   // 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,
@@ -687,11 +541,26 @@
   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);
-  MayAutoLock locker(&lock_);
+  base::AutoLock locker(lock_);
 
   encountered_error_ = true;
 
@@ -716,21 +585,20 @@
 void MultiplexRouter::ProcessTasks(
     ClientCallBehavior client_call_behavior,
     base::SingleThreadTaskRunner* current_task_runner) {
-  AssertLockAcquired();
+  lock_.AssertAcquired();
 
   if (posted_to_process_tasks_)
     return;
 
-  while (!tasks_.empty() && !paused_) {
+  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_wrapper.value().IsNull() &&
-        task->message_wrapper.value().has_flag(Message::kFlagIsSync);
+    bool sync_message = task->IsMessageTask() && task->message &&
+                        task->message->has_flag(Message::kFlagIsSync);
     if (sync_message) {
-      id = task->message_wrapper.value().interface_id();
+      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();
@@ -740,8 +608,8 @@
         task->IsNotifyErrorTask()
             ? ProcessNotifyErrorTask(task.get(), client_call_behavior,
                                      current_task_runner)
-            : ProcessIncomingMessage(&task->message_wrapper.value(),
-                                     client_call_behavior, current_task_runner);
+            : ProcessIncomingMessage(task->message.get(), client_call_behavior,
+                                     current_task_runner);
 
     if (!processed) {
       if (sync_message) {
@@ -761,25 +629,21 @@
 }
 
 bool MultiplexRouter::ProcessFirstSyncMessageForEndpoint(InterfaceId id) {
-  AssertLockAcquired();
+  lock_.AssertAcquired();
 
   auto iter = sync_message_tasks_.find(id);
   if (iter == sync_message_tasks_.end())
     return false;
 
-  if (paused_)
-    return true;
-
   MultiplexRouter::Task* task = iter->second.front();
   iter->second.pop_front();
 
   DCHECK(task->IsMessageTask());
-  MessageWrapper message_wrapper = std::move(task->message_wrapper);
+  std::unique_ptr<Message> message(std::move(task->message));
 
-  // Note: after this call, |task| and |iter| may be invalidated.
+  // Note: after this call, |task| and  |iter| may be invalidated.
   bool processed = ProcessIncomingMessage(
-      &message_wrapper.value(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES,
-      nullptr);
+      message.get(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, nullptr);
   DCHECK(processed);
 
   iter = sync_message_tasks_.find(id);
@@ -799,9 +663,7 @@
     ClientCallBehavior client_call_behavior,
     base::SingleThreadTaskRunner* current_task_runner) {
   DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread());
-  DCHECK(!paused_);
-
-  AssertLockAcquired();
+  lock_.AssertAcquired();
   InterfaceEndpoint* endpoint = task->endpoint_to_notify.get();
   if (!endpoint->client())
     return true;
@@ -815,17 +677,14 @@
   DCHECK(endpoint->task_runner()->BelongsToCurrentThread());
 
   InterfaceEndpointClient* client = endpoint->client();
-  base::Optional<DisconnectReason> disconnect_reason(
-      endpoint->disconnect_reason());
-
   {
     // 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().
-    MayAutoUnlock unlocker(&lock_);
-    client->NotifyError(disconnect_reason);
+    base::AutoUnlock unlocker(lock_);
+    client->NotifyError();
   }
   return true;
 }
@@ -835,35 +694,47 @@
     ClientCallBehavior client_call_behavior,
     base::SingleThreadTaskRunner* current_task_runner) {
   DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread());
-  DCHECK(!paused_);
-  DCHECK(message);
-  AssertLockAcquired();
+  lock_.AssertAcquired();
 
-  if (message->IsNull()) {
+  if (!message) {
     // This is a sync message and has been processed during sync handle
     // watching.
     return true;
   }
 
   if (PipeControlMessageHandler::IsPipeControlMessage(message)) {
-    bool result = false;
-
-    {
-      MayAutoUnlock unlocker(&lock_);
-      result = control_message_handler_.Accept(message);
-    }
-
-    if (!result)
+    if (!control_message_handler_.Accept(message))
       RaiseErrorInNonTestingMode();
-
     return true;
   }
 
   InterfaceId id = message->interface_id();
   DCHECK(IsValidInterfaceId(id));
 
-  InterfaceEndpoint* endpoint = FindEndpoint(id);
-  if (!endpoint || endpoint->closed())
+  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()) {
@@ -897,7 +768,7 @@
     //
     // It is safe to call into |client| without the lock. Because |client| is
     // always accessed on the same thread, including DetachEndpointClient().
-    MayAutoUnlock unlocker(&lock_);
+    base::AutoUnlock unlocker(lock_);
     result = client->HandleIncomingMessage(message);
   }
   if (!result)
@@ -908,7 +779,7 @@
 
 void MultiplexRouter::MaybePostToProcessTasks(
     base::SingleThreadTaskRunner* task_runner) {
-  AssertLockAcquired();
+  lock_.AssertAcquired();
   if (posted_to_process_tasks_)
     return;
 
@@ -921,7 +792,7 @@
 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.
-  MayAutoLock locker(&lock_);
+  base::AutoLock locker(lock_);
   posted_to_process_tasks_ = false;
   scoped_refptr<base::SingleThreadTaskRunner> runner(
       std::move(posted_to_task_runner_));
@@ -931,20 +802,23 @@
 void MultiplexRouter::UpdateEndpointStateMayRemove(
     InterfaceEndpoint* endpoint,
     EndpointStateUpdateType type) {
-  if (type == ENDPOINT_CLOSED) {
-    endpoint->set_closed();
-  } else {
-    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();
+  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() {
-  AssertLockAcquired();
+  lock_.AssertAcquired();
   if (!testing_mode_)
     RaiseError();
 }
@@ -952,35 +826,24 @@
 MultiplexRouter::InterfaceEndpoint* MultiplexRouter::FindOrInsertEndpoint(
     InterfaceId id,
     bool* inserted) {
-  AssertLockAcquired();
+  lock_.AssertAcquired();
   // Either |inserted| is nullptr or it points to a boolean initialized as
   // false.
   DCHECK(!inserted || !*inserted);
 
-  InterfaceEndpoint* endpoint = FindEndpoint(id);
-  if (!endpoint) {
+  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;
 }
 
-MultiplexRouter::InterfaceEndpoint* MultiplexRouter::FindEndpoint(
-    InterfaceId id) {
-  AssertLockAcquired();
-  auto iter = endpoints_.find(id);
-  return iter != endpoints_.end() ? iter->second.get() : nullptr;
-}
-
-void MultiplexRouter::AssertLockAcquired() {
-#if DCHECK_IS_ON()
-  if (lock_)
-    lock_->AssertAcquired();
-#endif
-}
-
 }  // namespace internal
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index cac138b..dc66e8e 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -12,19 +12,15 @@
 #include <memory>
 #include <string>
 
-#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "base/optional.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/bindings_export.h"
 #include "mojo/public/cpp/bindings/connector.h"
-#include "mojo/public/cpp/bindings/filter_chain.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"
@@ -38,6 +34,8 @@
 
 namespace mojo {
 
+class AssociatedGroup;
+
 namespace internal {
 
 // MultiplexRouter supports routing messages for multiple interfaces over a
@@ -49,51 +47,31 @@
 // 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.
-//
-// NOTE: CloseMessagePipe() or PassMessagePipe() MUST be called on |runner|'s
-// thread before this object is destroyed.
-class MOJO_CPP_BINDINGS_EXPORT MultiplexRouter
-    : NON_EXPORTED_BASE(public MessageReceiver),
+class MultiplexRouter
+    : public MessageReceiver,
       public AssociatedGroupController,
-      NON_EXPORTED_BASE(public PipeControlMessageHandlerDelegate) {
+      public PipeControlMessageHandlerDelegate {
  public:
-  enum Config {
-    // There is only the master interface running on this router. Please note
-    // that because of interface versioning, the other side of the message pipe
-    // may use a newer master interface definition which passes associated
-    // interfaces. In that case, this router may still receive pipe control
-    // messages or messages targetting associated interfaces.
-    SINGLE_INTERFACE,
-    // Similar to the mode above, there is only the master interface running on
-    // this router. Besides, the master interface has sync methods.
-    SINGLE_INTERFACE_WITH_SYNC_METHODS,
-    // There may be associated interfaces running on this router.
-    MULTI_INTERFACE
-  };
-
   // If |set_interface_id_namespace_bit| is true, the interface IDs generated by
   // this router will have the highest bit set.
-  MultiplexRouter(ScopedMessagePipeHandle message_pipe,
-                  Config config,
-                  bool set_interface_id_namespace_bit,
+  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.
-  // |name| must be a string literal.
-  void SetMasterInterfaceName(const char* name);
+  void SetMasterInterfaceName(const std::string& name);
 
   // ---------------------------------------------------------------------------
   // The following public methods are safe to call from any threads.
 
   // AssociatedGroupController implementation:
-  InterfaceId AssociateInterface(
-      ScopedInterfaceEndpointHandle handle_to_send) override;
+  void CreateEndpointHandlePair(
+      ScopedInterfaceEndpointHandle* local_endpoint,
+      ScopedInterfaceEndpointHandle* remote_endpoint) override;
   ScopedInterfaceEndpointHandle CreateLocalEndpointHandle(
       InterfaceId id) override;
-  void CloseEndpointHandle(
-      InterfaceId id,
-      const base::Optional<DisconnectReason>& reason) override;
+  void CloseEndpointHandle(InterfaceId id, bool is_local) override;
   InterfaceEndpointController* AttachEndpointClient(
       const ScopedInterfaceEndpointHandle& handle,
       InterfaceEndpointClient* endpoint_client,
@@ -124,8 +102,14 @@
   }
 
   // See Binding for details of pause/resume.
-  void PauseIncomingMethodCallProcessing();
-  void ResumeIncomingMethodCallProcessing();
+  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;
@@ -147,13 +131,8 @@
     return connector_.handle();
   }
 
-  bool SimulateReceivingMessageForTesting(Message* message) {
-    return filters_.Accept(message);
-  }
-
  private:
   class InterfaceEndpoint;
-  class MessageWrapper;
   struct Task;
 
   ~MultiplexRouter() override;
@@ -162,9 +141,8 @@
   bool Accept(Message* message) override;
 
   // PipeControlMessageHandlerDelegate implementation:
-  bool OnPeerAssociatedEndpointClosed(
-      InterfaceId id,
-      const base::Optional<DisconnectReason>& reason) override;
+  bool OnPeerAssociatedEndpointClosed(InterfaceId id) override;
+  bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) override;
 
   void OnPipeConnectionError();
 
@@ -224,30 +202,19 @@
   void RaiseErrorInNonTestingMode();
 
   InterfaceEndpoint* FindOrInsertEndpoint(InterfaceId id, bool* inserted);
-  InterfaceEndpoint* FindEndpoint(InterfaceId id);
-
-  void AssertLockAcquired();
 
   // Whether to set the namespace bit when generating interface IDs. Please see
   // comments of kInterfaceIdNamespaceMask.
   const bool set_interface_id_namespace_bit_;
 
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  // Owned by |filters_| below.
-  MessageHeaderValidator* header_validator_;
-
-  FilterChain filters_;
+  MessageHeaderValidator header_validator_;
   Connector connector_;
 
   base::ThreadChecker thread_checker_;
 
   // Protects the following members.
-  // Not set in Config::SINGLE_INTERFACE* mode.
-  mutable base::Optional<base::Lock> lock_;
+  mutable base::Lock lock_;
   PipeControlMessageHandler control_message_handler_;
-
-  // NOTE: It is unsafe to call into this object while holding |lock_|.
   PipeControlMessageProxy control_message_proxy_;
 
   std::map<InterfaceId, scoped_refptr<InterfaceEndpoint>> endpoints_;
@@ -262,8 +229,6 @@
 
   bool encountered_error_;
 
-  bool paused_;
-
   bool testing_mode_;
 
   DISALLOW_COPY_AND_ASSIGN(MultiplexRouter);
diff --git a/mojo/public/cpp/bindings/lib/native_struct.cc b/mojo/public/cpp/bindings/lib/native_struct.cc
index 7b1a1a6..837b75a 100644
--- a/mojo/public/cpp/bindings/lib/native_struct.cc
+++ b/mojo/public/cpp/bindings/lib/native_struct.cc
@@ -4,31 +4,27 @@
 
 #include "mojo/public/cpp/bindings/native_struct.h"
 
-#include "mojo/public/cpp/bindings/lib/hash_util.h"
-
 namespace mojo {
 
 // static
 NativeStructPtr NativeStruct::New() {
-  return NativeStructPtr(base::in_place);
+  NativeStructPtr rv;
+  internal::StructHelper<NativeStruct>::Initialize(&rv);
+  return rv;
 }
 
-NativeStruct::NativeStruct() {}
+NativeStruct::NativeStruct() : data(nullptr) {}
 
 NativeStruct::~NativeStruct() {}
 
 NativeStructPtr NativeStruct::Clone() const {
   NativeStructPtr rv(New());
-  rv->data = data;
+  rv->data = data.Clone();
   return rv;
 }
 
 bool NativeStruct::Equals(const NativeStruct& other) const {
-  return data == other.data;
-}
-
-size_t NativeStruct::Hash(size_t seed) const {
-  return internal::Hash(seed, data);
+  return data.Equals(other.data);
 }
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/native_struct_data.h b/mojo/public/cpp/bindings/lib/native_struct_data.h
index 1c7cd81..5c58774 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_data.h
+++ b/mojo/public/cpp/bindings/lib/native_struct_data.h
@@ -7,16 +7,16 @@
 
 #include <vector>
 
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #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 MOJO_CPP_BINDINGS_EXPORT NativeStruct_Data {
+class NativeStruct_Data {
  public:
   static bool Validate(const void* data, ValidationContext* validation_context);
 
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
index fa0dbf3..ac06059 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
+++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
@@ -15,8 +15,7 @@
     SerializationContext* context) {
   if (!input)
     return 0;
-  return internal::PrepareToSerialize<ArrayDataView<uint8_t>>(input->data,
-                                                              context);
+  return internal::PrepareToSerialize<Array<uint8_t>>(input->data, context);
 }
 
 // static
@@ -32,8 +31,8 @@
 
   Array_Data<uint8_t>* data = nullptr;
   const ContainerValidateParams params(0, false, nullptr);
-  internal::Serialize<ArrayDataView<uint8_t>>(input->data, buffer, &data,
-                                              &params, context);
+  internal::Serialize<Array<uint8_t>>(input->data, buffer, &data, &params,
+                                      context);
   *output = reinterpret_cast<NativeStruct_Data*>(data);
 }
 
@@ -45,8 +44,7 @@
   Array_Data<uint8_t>* data = reinterpret_cast<Array_Data<uint8_t>*>(input);
 
   NativeStructPtr result(NativeStruct::New());
-  if (!internal::Deserialize<ArrayDataView<uint8_t>>(data, &result->data,
-                                                     context)) {
+  if (!internal::Deserialize<Array<uint8_t>>(data, &result->data, context)) {
     output = nullptr;
     return false;
   }
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.h b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
index 457435b..e64b862 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_serialization.h
+++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
@@ -13,14 +13,12 @@
 #include "base/logging.h"
 #include "base/pickle.h"
 #include "ipc/ipc_param_traits.h"
-#include "mojo/public/cpp/bindings/bindings_export.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"
-#include "mojo/public/cpp/bindings/native_struct_data_view.h"
 
 namespace mojo {
 namespace internal {
@@ -104,7 +102,7 @@
   }
 };
 
-struct MOJO_CPP_BINDINGS_EXPORT UnmappedNativeStructSerializerImpl {
+struct UnmappedNativeStructSerializerImpl {
   static size_t PrepareToSerialize(const NativeStructPtr& input,
                                    SerializationContext* context);
   static void Serialize(const NativeStructPtr& input,
@@ -125,7 +123,7 @@
     : public UnmappedNativeStructSerializerImpl {};
 
 template <typename MaybeConstUserType>
-struct Serializer<NativeStructDataView, MaybeConstUserType>
+struct Serializer<NativeStructPtr, MaybeConstUserType>
     : public NativeStructSerializerImpl<MaybeConstUserType> {};
 
 }  // namespace internal
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
index d451c05..7ee9f8a 100644
--- a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
@@ -5,10 +5,8 @@
 #include "mojo/public/cpp/bindings/pipe_control_message_handler.h"
 
 #include "base/logging.h"
-#include "mojo/public/cpp/bindings/interface_id.h"
 #include "mojo/public/cpp/bindings/lib/message_builder.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.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"
@@ -43,9 +41,8 @@
 }
 
 bool PipeControlMessageHandler::Validate(Message* message) {
-  internal::ValidationContext validation_context(message->payload(),
-                                                 message->payload_num_bytes(),
-                                                 0, 0, message, description_);
+  internal::ValidationContext validation_context(
+      message->data(), message->data_num_bytes(), 0, message, description_);
 
   if (message->name() == pipe_control::kRunOrClosePipeMessageId) {
     if (!internal::ValidateMessageIsRequestWithoutResponse(
@@ -61,25 +58,22 @@
 }
 
 bool PipeControlMessageHandler::RunOrClosePipe(Message* message) {
-  internal::SerializationContext context;
   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::RunOrClosePipeMessageParamsDataView>(
-      params, &params_ptr, &context);
+  internal::Deserialize<pipe_control::RunOrClosePipeMessageParamsPtr>(
+      params, &params_ptr, &context_);
 
   if (params_ptr->input->is_peer_associated_endpoint_closed_event()) {
-    const auto& event =
-        params_ptr->input->get_peer_associated_endpoint_closed_event();
-
-    base::Optional<DisconnectReason> reason;
-    if (event->disconnect_reason) {
-      reason.emplace(event->disconnect_reason->custom_reason,
-                     event->disconnect_reason->description);
-    }
-    return delegate_->OnPeerAssociatedEndpointClosed(event->id, reason);
+    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 "
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
index 701108e..55ee64b 100644
--- a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
@@ -11,28 +11,33 @@
 #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 {
 
-Message ConstructRunOrClosePipeMessage(
-    pipe_control::RunOrClosePipeInputPtr input_ptr) {
-  internal::SerializationContext context;
+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);
 
-  auto params_ptr = pipe_control::RunOrClosePipeMessageParams::New();
-  params_ptr->input = std::move(input_ptr);
-
-  size_t size = internal::PrepareToSerialize<
-      pipe_control::RunOrClosePipeMessageParamsDataView>(params_ptr, &context);
-  internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId, 0,
-                                   size, 0);
+  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::RunOrClosePipeMessageParamsDataView>(
-      params_ptr, builder.buffer(), &params, &context);
+  internal::Serialize<pipe_control::RunOrClosePipeMessageParamsPtr>(
+      params_ptr, builder.buffer(), &params, context);
   builder.message()->set_interface_id(kInvalidInterfaceId);
-  return std::move(*builder.message());
+  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
@@ -40,30 +45,30 @@
 PipeControlMessageProxy::PipeControlMessageProxy(MessageReceiver* receiver)
     : receiver_(receiver) {}
 
-void PipeControlMessageProxy::NotifyPeerEndpointClosed(
-    InterfaceId id,
-    const base::Optional<DisconnectReason>& reason) {
-  Message message(ConstructPeerEndpointClosedMessage(id, reason));
-  bool ok = receiver_->Accept(&message);
-  ALLOW_UNUSED_LOCAL(ok);
-}
-
-// static
-Message PipeControlMessageProxy::ConstructPeerEndpointClosedMessage(
-    InterfaceId id,
-    const base::Optional<DisconnectReason>& reason) {
-  auto event = pipe_control::PeerAssociatedEndpointClosedEvent::New();
+void PipeControlMessageProxy::NotifyPeerEndpointClosed(InterfaceId id) {
+  DCHECK(!IsMasterInterfaceId(id));
+  pipe_control::PeerAssociatedEndpointClosedEventPtr event(
+      pipe_control::PeerAssociatedEndpointClosedEvent::New());
   event->id = id;
-  if (reason) {
-    event->disconnect_reason = pipe_control::DisconnectReason::New();
-    event->disconnect_reason->custom_reason = reason->custom_reason;
-    event->disconnect_reason->description = reason->description;
-  }
 
-  auto input = pipe_control::RunOrClosePipeInput::New();
+  pipe_control::RunOrClosePipeInputPtr input(
+      pipe_control::RunOrClosePipeInput::New());
   input->set_peer_associated_endpoint_closed_event(std::move(event));
 
-  return ConstructRunOrClosePipeMessage(std::move(input));
+  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
index c134507..f54c3f7 100644
--- a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
+++ b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
@@ -4,379 +4,69 @@
 
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 
-#include "base/bind.h"
 #include "base/logging.h"
-#include "base/synchronization/lock.h"
 #include "mojo/public/cpp/bindings/associated_group_controller.h"
-#include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
 
 namespace mojo {
 
-// ScopedInterfaceEndpointHandle::State ----------------------------------------
-
-// State could be called from multiple threads.
-class ScopedInterfaceEndpointHandle::State
-    : public base::RefCountedThreadSafe<State> {
- public:
-  State() = default;
-
-  State(InterfaceId id,
-        scoped_refptr<AssociatedGroupController> group_controller)
-      : id_(id), group_controller_(group_controller) {}
-
-  void InitPendingState(scoped_refptr<State> peer) {
-    DCHECK(!lock_);
-    DCHECK(!pending_association_);
-
-    lock_.emplace();
-    pending_association_ = true;
-    peer_state_ = std::move(peer);
-  }
-
-  void Close(const base::Optional<DisconnectReason>& reason) {
-    scoped_refptr<AssociatedGroupController> cached_group_controller;
-    InterfaceId cached_id = kInvalidInterfaceId;
-    scoped_refptr<State> cached_peer_state;
-
-    {
-      internal::MayAutoLock locker(&lock_);
-
-      if (!association_event_handler_.is_null()) {
-        association_event_handler_.Reset();
-        runner_ = nullptr;
-      }
-
-      if (!pending_association_) {
-        if (IsValidInterfaceId(id_)) {
-          // Intentionally keep |group_controller_| unchanged.
-          // That is because the callback created by
-          // CreateGroupControllerGetter() could still be used after this point,
-          // potentially from another thread. We would like it to continue
-          // returning the same group controller.
-          //
-          // Imagine there is a ThreadSafeForwarder A:
-          // (1) On the IO thread, A's underlying associated interface pointer
-          //     is closed.
-          // (2) On the proxy thread, the user makes a call on A to pass an
-          //     associated request B_asso_req. The callback returned by
-          //     CreateGroupControllerGetter() is used to associate B_asso_req.
-          // (3) On the proxy thread, the user immediately binds B_asso_ptr_info
-          //     to B_asso_ptr and makes calls on it.
-          //
-          // If we reset |group_controller_| in step (1), step (2) won't be able
-          // to associate B_asso_req. Therefore, in step (3) B_asso_ptr won't be
-          // able to serialize associated endpoints or send message because it
-          // is still in "pending_association" state and doesn't have a group
-          // controller.
-          //
-          // We could "address" this issue by ignoring messages if there isn't a
-          // group controller. But the side effect is that we cannot detect
-          // programming errors of "using associated interface pointer before
-          // sending associated request".
-
-          cached_group_controller = group_controller_;
-          cached_id = id_;
-          id_ = kInvalidInterfaceId;
-        }
-      } else {
-        pending_association_ = false;
-        cached_peer_state = std::move(peer_state_);
-      }
-    }
-
-    if (cached_group_controller) {
-      cached_group_controller->CloseEndpointHandle(cached_id, reason);
-    } else if (cached_peer_state) {
-      cached_peer_state->OnPeerClosedBeforeAssociation(reason);
-    }
-  }
-
-  void SetAssociationEventHandler(AssociationEventCallback handler) {
-    internal::MayAutoLock locker(&lock_);
-
-    if (!pending_association_ && !IsValidInterfaceId(id_))
-      return;
-
-    association_event_handler_ = std::move(handler);
-    if (association_event_handler_.is_null()) {
-      runner_ = nullptr;
-      return;
-    }
-
-    runner_ = base::ThreadTaskRunnerHandle::Get();
-    if (!pending_association_) {
-      runner_->PostTask(
-          FROM_HERE,
-          base::Bind(
-              &ScopedInterfaceEndpointHandle::State::RunAssociationEventHandler,
-              this, runner_, ASSOCIATED));
-    } else if (!peer_state_) {
-      runner_->PostTask(
-          FROM_HERE,
-          base::Bind(
-              &ScopedInterfaceEndpointHandle::State::RunAssociationEventHandler,
-              this, runner_, PEER_CLOSED_BEFORE_ASSOCIATION));
-    }
-  }
-
-  bool NotifyAssociation(
-      InterfaceId id,
-      scoped_refptr<AssociatedGroupController> peer_group_controller) {
-    scoped_refptr<State> cached_peer_state;
-    {
-      internal::MayAutoLock locker(&lock_);
-
-      DCHECK(pending_association_);
-      pending_association_ = false;
-      cached_peer_state = std::move(peer_state_);
-    }
-
-    if (cached_peer_state) {
-      cached_peer_state->OnAssociated(id, std::move(peer_group_controller));
-      return true;
-    }
-    return false;
-  }
-
-  bool is_valid() const {
-    internal::MayAutoLock locker(&lock_);
-    return pending_association_ || IsValidInterfaceId(id_);
-  }
-
-  bool pending_association() const {
-    internal::MayAutoLock locker(&lock_);
-    return pending_association_;
-  }
-
-  InterfaceId id() const {
-    internal::MayAutoLock locker(&lock_);
-    return id_;
-  }
-
-  AssociatedGroupController* group_controller() const {
-    internal::MayAutoLock locker(&lock_);
-    return group_controller_.get();
-  }
-
-  const base::Optional<DisconnectReason>& disconnect_reason() const {
-    internal::MayAutoLock locker(&lock_);
-    return disconnect_reason_;
-  }
-
- private:
-  friend class base::RefCountedThreadSafe<State>;
-
-  ~State() {
-    DCHECK(!pending_association_);
-    DCHECK(!IsValidInterfaceId(id_));
-  }
-
-  // Called by the peer, maybe from a different thread.
-  void OnAssociated(InterfaceId id,
-                    scoped_refptr<AssociatedGroupController> group_controller) {
-    AssociationEventCallback handler;
-    {
-      internal::MayAutoLock locker(&lock_);
-
-      // There may be race between Close() of endpoint A and
-      // NotifyPeerAssociation() of endpoint A_peer on different threads.
-      // Therefore, it is possible that endpoint A has been closed but it
-      // still gets OnAssociated() call from its peer.
-      if (!pending_association_)
-        return;
-
-      pending_association_ = false;
-      peer_state_ = nullptr;
-      id_ = id;
-      group_controller_ = std::move(group_controller);
-
-      if (!association_event_handler_.is_null()) {
-        if (runner_->BelongsToCurrentThread()) {
-          handler = std::move(association_event_handler_);
-          runner_ = nullptr;
-        } else {
-          runner_->PostTask(FROM_HERE,
-                            base::Bind(&ScopedInterfaceEndpointHandle::State::
-                                           RunAssociationEventHandler,
-                                       this, runner_, ASSOCIATED));
-        }
-      }
-    }
-
-    if (!handler.is_null())
-      std::move(handler).Run(ASSOCIATED);
-  }
-
-  // Called by the peer, maybe from a different thread.
-  void OnPeerClosedBeforeAssociation(
-      const base::Optional<DisconnectReason>& reason) {
-    AssociationEventCallback handler;
-    {
-      internal::MayAutoLock locker(&lock_);
-
-      // There may be race between Close()/NotifyPeerAssociation() of endpoint
-      // A and Close() of endpoint A_peer on different threads.
-      // Therefore, it is possible that endpoint A is not in pending association
-      // state but still gets OnPeerClosedBeforeAssociation() call from its
-      // peer.
-      if (!pending_association_)
-        return;
-
-      disconnect_reason_ = reason;
-      // NOTE: This handle itself is still pending.
-      peer_state_ = nullptr;
-
-      if (!association_event_handler_.is_null()) {
-        if (runner_->BelongsToCurrentThread()) {
-          handler = std::move(association_event_handler_);
-          runner_ = nullptr;
-        } else {
-          runner_->PostTask(
-              FROM_HERE,
-              base::Bind(&ScopedInterfaceEndpointHandle::State::
-                             RunAssociationEventHandler,
-                         this, runner_, PEER_CLOSED_BEFORE_ASSOCIATION));
-        }
-      }
-    }
-
-    if (!handler.is_null())
-      std::move(handler).Run(PEER_CLOSED_BEFORE_ASSOCIATION);
-  }
-
-  void RunAssociationEventHandler(
-      scoped_refptr<base::SingleThreadTaskRunner> posted_to_runner,
-      AssociationEvent event) {
-    AssociationEventCallback handler;
-
-    {
-      internal::MayAutoLock locker(&lock_);
-      if (posted_to_runner == runner_) {
-        runner_ = nullptr;
-        handler = std::move(association_event_handler_);
-      }
-    }
-
-    if (!handler.is_null())
-      std::move(handler).Run(event);
-  }
-
-  // Protects the following members if the handle is initially set to pending
-  // association.
-  mutable base::Optional<base::Lock> lock_;
-
-  bool pending_association_ = false;
-  base::Optional<DisconnectReason> disconnect_reason_;
-
-  scoped_refptr<State> peer_state_;
-
-  AssociationEventCallback association_event_handler_;
-  scoped_refptr<base::SingleThreadTaskRunner> runner_;
-
-  InterfaceId id_ = kInvalidInterfaceId;
-  scoped_refptr<AssociatedGroupController> group_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(State);
-};
-
-// ScopedInterfaceEndpointHandle -----------------------------------------------
-
-// static
-void ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(
-    ScopedInterfaceEndpointHandle* handle0,
-    ScopedInterfaceEndpointHandle* handle1) {
-  ScopedInterfaceEndpointHandle result0;
-  ScopedInterfaceEndpointHandle result1;
-  result0.state_->InitPendingState(result1.state_);
-  result1.state_->InitPendingState(result0.state_);
-
-  *handle0 = std::move(result0);
-  *handle1 = std::move(result1);
-}
-
 ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle()
-    : state_(new State) {}
+    : ScopedInterfaceEndpointHandle(kInvalidInterfaceId, true, nullptr) {}
 
 ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
     ScopedInterfaceEndpointHandle&& other)
-    : state_(new State) {
-  state_.swap(other.state_);
+    : id_(other.id_), is_local_(other.is_local_) {
+  group_controller_.swap(other.group_controller_);
+  other.id_ = kInvalidInterfaceId;
 }
 
 ScopedInterfaceEndpointHandle::~ScopedInterfaceEndpointHandle() {
-  state_->Close(base::nullopt);
+  reset();
 }
 
 ScopedInterfaceEndpointHandle& ScopedInterfaceEndpointHandle::operator=(
     ScopedInterfaceEndpointHandle&& other) {
   reset();
-  state_.swap(other.state_);
+  swap(other);
+
   return *this;
 }
 
-bool ScopedInterfaceEndpointHandle::is_valid() const {
-  return state_->is_valid();
-}
-
-bool ScopedInterfaceEndpointHandle::pending_association() const {
-  return state_->pending_association();
-}
-
-InterfaceId ScopedInterfaceEndpointHandle::id() const {
-  return state_->id();
-}
-
-AssociatedGroupController* ScopedInterfaceEndpointHandle::group_controller()
-    const {
-  return state_->group_controller();
-}
-
-const base::Optional<DisconnectReason>&
-ScopedInterfaceEndpointHandle::disconnect_reason() const {
-  return state_->disconnect_reason();
-}
-
-void ScopedInterfaceEndpointHandle::SetAssociationEventHandler(
-    AssociationEventCallback handler) {
-  state_->SetAssociationEventHandler(std::move(handler));
-}
-
 void ScopedInterfaceEndpointHandle::reset() {
-  ResetInternal(base::nullopt);
+  if (!IsValidInterfaceId(id_))
+    return;
+
+  group_controller_->CloseEndpointHandle(id_, is_local_);
+
+  id_ = kInvalidInterfaceId;
+  is_local_ = true;
+  group_controller_ = nullptr;
 }
 
-void ScopedInterfaceEndpointHandle::ResetWithReason(
-    uint32_t custom_reason,
-    const std::string& description) {
-  ResetInternal(DisconnectReason(custom_reason, description));
+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)
-    : state_(new State(id, std::move(group_controller))) {
-  DCHECK(!IsValidInterfaceId(state_->id()) || state_->group_controller());
-}
-
-bool ScopedInterfaceEndpointHandle::NotifyAssociation(
-    InterfaceId id,
-    scoped_refptr<AssociatedGroupController> peer_group_controller) {
-  return state_->NotifyAssociation(id, peer_group_controller);
-}
-
-void ScopedInterfaceEndpointHandle::ResetInternal(
-    const base::Optional<DisconnectReason>& reason) {
-  scoped_refptr<State> new_state(new State);
-  state_->Close(reason);
-  state_.swap(new_state);
-}
-
-base::Callback<AssociatedGroupController*()>
-ScopedInterfaceEndpointHandle::CreateGroupControllerGetter() const {
-  // We allow this callback to be run on any thread. If this handle is created
-  // in non-pending state, we don't have a lock but it should still be safe
-  // because the group controller never changes.
-  return base::Bind(&State::group_controller, state_);
+    : 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
index 359b02b..6d7dd8e 100644
--- a/mojo/public/cpp/bindings/lib/serialization.h
+++ b/mojo/public/cpp/bindings/lib/serialization.h
@@ -8,16 +8,19 @@
 #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/buffer.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"
@@ -41,7 +44,7 @@
   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 std::vector.
+  // practice, this should never happen for mojo::Array (backed by std::vector).
   bool need_copy = !IsAligned(result_buffer);
 
   if (need_copy) {
@@ -50,9 +53,9 @@
     DCHECK(IsAligned(result_buffer));
   }
 
-  Buffer buffer;
+  FixedBuffer buffer;
   buffer.Initialize(result_buffer, size);
-  typename MojomTypeTraits<MojomType>::Data* data = nullptr;
+  typename MojomType::Struct::Data_* data = nullptr;
   Serialize<MojomType>(*input, &buffer, &data, &context);
 
   if (need_copy) {
@@ -67,11 +70,13 @@
 bool StructDeserializeImpl(const DataArrayType& input, UserType* output) {
   static_assert(BelongsTo<MojomType, MojomTypeCategory::STRUCT>::value,
                 "Unexpected type.");
-  using DataType = typename MojomTypeTraits<MojomType>::Data;
+  using DataType = typename MojomType::Struct::Data_;
 
-  // TODO(sammc): Use DataArrayType::empty() once WTF::Vector::empty() exists.
+  if (input.is_null())
+    return false;
+
   void* input_buffer =
-      input.size() == 0
+      input.empty()
           ? nullptr
           : const_cast<void*>(reinterpret_cast<const void*>(&input.front()));
 
@@ -84,7 +89,7 @@
     memcpy(input_buffer, &input.front(), input.size());
   }
 
-  ValidationContext validation_context(input_buffer, input.size(), 0, 0);
+  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);
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.cc b/mojo/public/cpp/bindings/lib/serialization_context.cc
index e2fd5c6..7fd80be 100644
--- a/mojo/public/cpp/bindings/lib/serialization_context.cc
+++ b/mojo/public/cpp/bindings/lib/serialization_context.cc
@@ -7,6 +7,7 @@
 #include <limits>
 
 #include "base/logging.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
 #include "mojo/public/cpp/system/core.h"
 
 namespace mojo {
@@ -49,6 +50,10 @@
 
 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());
 }
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.h b/mojo/public/cpp/bindings/lib/serialization_context.h
index a34fe3d..64d2a1a 100644
--- a/mojo/public/cpp/bindings/lib/serialization_context.h
+++ b/mojo/public/cpp/bindings/lib/serialization_context.h
@@ -12,16 +12,18 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
+#include "base/memory/ref_counted.h"
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "mojo/public/cpp/system/handle.h"
 
 namespace mojo {
+
+class AssociatedGroupController;
+
 namespace internal {
 
 // A container for handles during serialization/deserialization.
-class MOJO_CPP_BINDINGS_EXPORT SerializedHandleVector {
+class SerializedHandleVector {
  public:
   SerializedHandleVector();
   ~SerializedHandleVector();
@@ -52,23 +54,21 @@
 };
 
 // Context information for serialization/deserialization routines.
-struct MOJO_CPP_BINDINGS_EXPORT SerializationContext {
+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;
-
-  // The number of ScopedInterfaceEndpointHandles that need to be serialized.
-  // It is calculated by PrepareToSerialize().
-  uint32_t associated_endpoint_count = 0;
-
-  // Stashes ScopedInterfaceEndpointHandles encoded in a message by index.
-  std::vector<ScopedInterfaceEndpointHandle> associated_endpoint_handles;
 };
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/serialization_forward.h b/mojo/public/cpp/bindings/lib/serialization_forward.h
index 55c9982..5bed126 100644
--- a/mojo/public/cpp/bindings/lib/serialization_forward.h
+++ b/mojo/public/cpp/bindings/lib/serialization_forward.h
@@ -12,7 +12,6 @@
 #include "mojo/public/cpp/bindings/map_traits.h"
 #include "mojo/public/cpp/bindings/string_traits.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
-#include "mojo/public/cpp/bindings/union_traits.h"
 
 // This file is included by serialization implementation files to avoid circular
 // includes.
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h
index 6e0c758..5e65891 100644
--- a/mojo/public/cpp/bindings/lib/string_serialization.h
+++ b/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -11,14 +11,14 @@
 #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_data_view.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<StringDataView, MaybeConstUserType> {
+struct Serializer<String, MaybeConstUserType> {
   using UserType = typename std::remove_const<MaybeConstUserType>::type;
   using Traits = StringTraits<UserType>;
 
@@ -60,7 +60,7 @@
                           SerializationContext* context) {
     if (!input)
       return CallSetToNullIfExists<Traits>(output);
-    return Traits::Read(StringDataView(input, context), output);
+    return Traits::Read(StringDataView(input), output);
   }
 };
 
diff --git a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
index 203f6f5..19fa907 100644
--- a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
+++ b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
@@ -16,7 +16,7 @@
 struct UTF8AdaptorInfo {
   explicit UTF8AdaptorInfo(const WTF::String& input) : utf8_adaptor(input) {
 #if DCHECK_IS_ON()
-    original_size_in_bytes = input.charactersSizeInBytes();
+    original_size_in_bytes = static_cast<size_t>(input.sizeInBytes());
 #endif
   }
 
@@ -34,7 +34,8 @@
   UTF8AdaptorInfo* adaptor = static_cast<UTF8AdaptorInfo*>(context);
 
 #if DCHECK_IS_ON()
-  DCHECK_EQ(adaptor->original_size_in_bytes, input.charactersSizeInBytes());
+  DCHECK_EQ(adaptor->original_size_in_bytes,
+            static_cast<size_t>(input.sizeInBytes()));
 #endif
   return adaptor;
 }
diff --git a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
index 585a8f0..3d864af 100644
--- a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
+++ b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
@@ -6,7 +6,6 @@
 
 #if ENABLE_SYNC_CALL_RESTRICTIONS
 
-#include "base/debug/leak_annotations.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/threading/thread_local.h"
@@ -38,7 +37,7 @@
   size_t scoped_allow_count_ = 0;
 };
 
-base::LazyInstance<base::ThreadLocalPointer<SyncCallSettings>>::DestructorAtExit
+base::LazyInstance<base::ThreadLocalPointer<SyncCallSettings>>
     g_sync_call_settings = LAZY_INSTANCE_INITIALIZER;
 
 // static
@@ -46,7 +45,6 @@
   SyncCallSettings* result = g_sync_call_settings.Pointer()->Get();
   if (!result) {
     result = new SyncCallSettings();
-    ANNOTATE_LEAKING_OBJECT_PTR(result);
     DCHECK_EQ(result, g_sync_call_settings.Pointer()->Get());
   }
   return result;
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
index 5ae763b..f6372d9 100644
--- a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
@@ -13,7 +13,7 @@
 namespace mojo {
 namespace {
 
-base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>::Leaky
+base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>
     g_current_sync_handle_watcher = LAZY_INSTANCE_INITIALIZER;
 
 }  // namespace
@@ -34,7 +34,7 @@
                                         const HandleCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (base::ContainsKey(handles_, handle))
+  if (ContainsKey(handles_, handle))
     return false;
 
   MojoResult result = MojoAddHandle(wait_set_handle_.get().value(),
@@ -48,7 +48,7 @@
 
 void SyncHandleRegistry::UnregisterHandle(const Handle& handle) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!base::ContainsKey(handles_, handle))
+  if (!ContainsKey(handles_, handle))
     return;
 
   MojoResult result =
@@ -107,19 +107,6 @@
 
 SyncHandleRegistry::~SyncHandleRegistry() {
   DCHECK(thread_checker_.CalledOnValidThread());
-
-  // This object may be destructed after the thread local storage slot used by
-  // |g_current_sync_handle_watcher| is reset during thread shutdown.
-  // For example, another slot in the thread local storage holds a referrence to
-  // this object, and that slot is cleaned up after
-  // |g_current_sync_handle_watcher|.
-  if (!g_current_sync_handle_watcher.Pointer()->Get())
-    return;
-
-  // If this breaks, it is likely that the global variable is bulit into and
-  // accessed from multiple modules.
-  DCHECK_EQ(this, g_current_sync_handle_watcher.Pointer()->Get());
-
   g_current_sync_handle_watcher.Pointer()->Set(nullptr);
 }
 
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/validation_context.cc b/mojo/public/cpp/bindings/lib/validation_context.cc
index ad0a364..a95e07e 100644
--- a/mojo/public/cpp/bindings/lib/validation_context.cc
+++ b/mojo/public/cpp/bindings/lib/validation_context.cc
@@ -4,7 +4,13 @@
 
 #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 {
@@ -12,39 +18,69 @@
 ValidationContext::ValidationContext(const void* data,
                                      size_t data_num_bytes,
                                      size_t num_handles,
-                                     size_t num_associated_endpoint_handles,
                                      Message* message,
-                                     const base::StringPiece& description,
-                                     int stack_depth)
+                                     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)),
-      associated_endpoint_handle_begin_(0),
-      associated_endpoint_handle_end_(
-          static_cast<uint32_t>(num_associated_endpoint_handles)),
-      stack_depth_(stack_depth) {
-  // Check whether the calculation of |data_end_| or static_cast from size_t to
-  // uint32_t causes overflow.
-  // They shouldn't happen but they do, set the corresponding range to empty.
+      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;
   }
-  if (associated_endpoint_handle_end_ < num_associated_endpoint_handles) {
-    NOTREACHED();
-    associated_endpoint_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
index ed6c654..6045ca8 100644
--- a/mojo/public/cpp/bindings/lib/validation_context.h
+++ b/mojo/public/cpp/bindings/lib/validation_context.h
@@ -8,28 +8,23 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
 
-static const int kMaxRecursionDepth = 100;
-
 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 MOJO_CPP_BINDINGS_EXPORT ValidationContext {
+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.
-  // [0, num_associated_endpoint_handles) specifies the initial valid range of
-  // associated endpoint handle indices.
   //
   // If provided, |message| and |description| provide additional information
   // to use when reporting validation errors. In addition if |message| is
@@ -38,10 +33,8 @@
   ValidationContext(const void* data,
                     size_t data_num_bytes,
                     size_t num_handles,
-                    size_t num_associated_endpoint_handles,
                     Message* message = nullptr,
-                    const base::StringPiece& description = "",
-                    int stack_depth = 0);
+                    const base::StringPiece& description = "");
 
   ~ValidationContext();
 
@@ -50,97 +43,24 @@
   // 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) {
-    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 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) {
-    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;
-  }
-
-  // Claims the specified encoded associated endpoint handle.
-  // The method succeeds if:
-  // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|.
-  // - the handle is contained inside the valid range of associated endpoint
-  //   handle indices. In this case, the valid range is shinked to begin right
-  //   after the claimed handle.
-  bool ClaimAssociatedEndpointHandle(
-      const AssociatedEndpointHandle_Data& encoded_handle) {
-    uint32_t index = encoded_handle.value;
-    if (index == kEncodedInvalidHandleValue)
-      return true;
-
-    if (index < associated_endpoint_handle_begin_ ||
-        index >= associated_endpoint_handle_end_)
-      return false;
-
-    // |index| + 1 shouldn't overflow, because |index| is not the max value of
-    // uint32_t (it is less than |associated_endpoint_handle_end_|).
-    associated_endpoint_handle_begin_ = index + 1;
-    return true;
-  }
+  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 {
-    uintptr_t begin = reinterpret_cast<uintptr_t>(position);
-    uintptr_t end = begin + num_bytes;
-
-    return InternalIsValidRange(begin, end);
-  }
-
-  // This object should be created on the stack once every time we recurse down
-  // into a subfield during validation to make sure we don't recurse too deep
-  // and blow the stack.
-  class ScopedDepthTracker {
-   public:
-    // |ctx| must outlive this object.
-    explicit ScopedDepthTracker(ValidationContext* ctx) : ctx_(ctx) {
-      ++ctx_->stack_depth_;
-    }
-
-    ~ScopedDepthTracker() { --ctx_->stack_depth_; }
-
-   private:
-    ValidationContext* ctx_;
-
-    DISALLOW_COPY_AND_ASSIGN(ScopedDepthTracker);
-  };
-
-  // Returns true if the recursion depth limit has been reached.
-  bool ExceedsMaxDepth() WARN_UNUSED_RESULT {
-    return stack_depth_ > kMaxRecursionDepth;
-  }
+  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 {
-    return end > begin && begin >= data_begin_ && end <= data_end_;
-  }
+  bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const;
 
   Message* const message_;
   const base::StringPiece description_;
@@ -153,13 +73,6 @@
   uint32_t handle_begin_;
   uint32_t handle_end_;
 
-  // [associated_endpoint_handle_begin_, associated_endpoint_handle_end_) is the
-  // valid associated endpoint handle index range.
-  uint32_t associated_endpoint_handle_begin_;
-  uint32_t associated_endpoint_handle_end_;
-
-  int stack_depth_;
-
   DISALLOW_COPY_AND_ASSIGN(ValidationContext);
 };
 
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.cc b/mojo/public/cpp/bindings/lib/validation_errors.cc
index 904f5e4..90652de 100644
--- a/mojo/public/cpp/bindings/lib/validation_errors.cc
+++ b/mojo/public/cpp/bindings/lib/validation_errors.cc
@@ -14,7 +14,6 @@
 ValidationErrorObserverForTesting* g_validation_error_observer = nullptr;
 SerializationWarningObserverForTesting* g_serialization_warning_observer =
     nullptr;
-bool g_suppress_logging = false;
 
 }  // namespace
 
@@ -56,8 +55,6 @@
       return "VALIDATION_ERROR_UNKNOWN_ENUM_VALUE";
     case VALIDATION_ERROR_DESERIALIZATION_FAILED:
       return "VALIDATION_ERROR_DESERIALIZATION_FAILED";
-    case VALIDATION_ERROR_MAX_RECURSION_DEPTH:
-      return "VALIDATION_ERROR_MAX_RECURSION_DEPTH";
   }
 
   return "Unknown error";
@@ -72,10 +69,8 @@
   }
 
   if (description) {
-    if (!g_suppress_logging) {
-      LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error)
-                 << " (" << description << ")";
-    }
+    LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) << " ("
+               << description << ")";
     if (context->message()) {
       context->message()->NotifyBadMessage(
           base::StringPrintf("Validation failed for %s [%s (%s)]",
@@ -83,8 +78,7 @@
                              ValidationErrorToString(error), description));
     }
   } else {
-    if (!g_suppress_logging)
-      LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
+    LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
     if (context->message()) {
       context->message()->NotifyBadMessage(
           base::StringPrintf("Validation failed for %s [%s]",
@@ -94,25 +88,6 @@
   }
 }
 
-void ReportValidationErrorForMessage(
-    mojo::Message* message,
-    ValidationError error,
-    const char* description) {
-  ValidationContext validation_context(nullptr, 0, 0, 0, message, description);
-  ReportValidationError(&validation_context, error);
-}
-
-ScopedSuppressValidationErrorLoggingForTests
-    ::ScopedSuppressValidationErrorLoggingForTests()
-    : was_suppressed_(g_suppress_logging) {
-  g_suppress_logging = true;
-}
-
-ScopedSuppressValidationErrorLoggingForTests
-    ::~ScopedSuppressValidationErrorLoggingForTests() {
-  g_suppress_logging = was_suppressed_;
-}
-
 ValidationErrorObserverForTesting::ValidationErrorObserverForTesting(
     const base::Closure& callback)
     : last_error_(VALIDATION_ERROR_NONE), callback_(callback) {
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.h b/mojo/public/cpp/bindings/lib/validation_errors.h
index 122418d..ec0aa27 100644
--- a/mojo/public/cpp/bindings/lib/validation_errors.h
+++ b/mojo/public/cpp/bindings/lib/validation_errors.h
@@ -8,13 +8,9 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/validation_context.h"
 
 namespace mojo {
-
-class Message;
-
 namespace internal {
 
 enum ValidationError {
@@ -71,41 +67,17 @@
   // Message deserialization failure, for example due to rejection by custom
   // validation logic.
   VALIDATION_ERROR_DESERIALIZATION_FAILED,
-  // The message contains a too deeply nested value, for example a recursively
-  // defined field which runtime value is too large.
-  VALIDATION_ERROR_MAX_RECURSION_DEPTH,
 };
 
-MOJO_CPP_BINDINGS_EXPORT const char* ValidationErrorToString(
-    ValidationError error);
+const char* ValidationErrorToString(ValidationError error);
 
-MOJO_CPP_BINDINGS_EXPORT void ReportValidationError(
-    ValidationContext* context,
-    ValidationError error,
-    const char* description = nullptr);
-
-MOJO_CPP_BINDINGS_EXPORT void ReportValidationErrorForMessage(
-    mojo::Message* message,
-    ValidationError error,
-    const char* description = nullptr);
-
-// This class may be used by tests to suppress validation error logging. This is
-// not thread-safe and must only be instantiated on the main thread with no
-// other threads using Mojo bindings at the time of construction or destruction.
-class MOJO_CPP_BINDINGS_EXPORT ScopedSuppressValidationErrorLoggingForTests {
- public:
-  ScopedSuppressValidationErrorLoggingForTests();
-  ~ScopedSuppressValidationErrorLoggingForTests();
-
- private:
-  const bool was_suppressed_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedSuppressValidationErrorLoggingForTests);
-};
+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 MOJO_CPP_BINDINGS_EXPORT ValidationErrorObserverForTesting {
+class ValidationErrorObserverForTesting {
  public:
   explicit ValidationErrorObserverForTesting(const base::Closure& callback);
   ~ValidationErrorObserverForTesting();
@@ -127,11 +99,11 @@
 //
 // The function returns true if the error is recorded (by a
 // SerializationWarningObserverForTesting object), false otherwise.
-MOJO_CPP_BINDINGS_EXPORT bool ReportSerializationWarning(ValidationError error);
+bool ReportSerializationWarning(ValidationError error);
 
 // Only used by serialization tests and when there is only one thread doing
 // message serialization.
-class MOJO_CPP_BINDINGS_EXPORT SerializationWarningObserverForTesting {
+class SerializationWarningObserverForTesting {
  public:
   SerializationWarningObserverForTesting();
   ~SerializationWarningObserverForTesting();
diff --git a/mojo/public/cpp/bindings/lib/validation_util.cc b/mojo/public/cpp/bindings/lib/validation_util.cc
index 7614df5..9e63521 100644
--- a/mojo/public/cpp/bindings/lib/validation_util.cc
+++ b/mojo/public/cpp/bindings/lib/validation_util.cc
@@ -16,6 +16,16 @@
 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)) {
@@ -46,17 +56,20 @@
   return true;
 }
 
-bool ValidateNonInlinedUnionHeaderAndClaimMemory(
-    const void* data,
-    ValidationContext* validation_context) {
+bool ValidateUnionHeaderAndClaimMemory(const void* data,
+                                       bool inlined,
+                                       ValidationContext* validation_context) {
   if (!IsAligned(data)) {
     ReportValidationError(validation_context,
                           VALIDATION_ERROR_MISALIGNED_OBJECT);
     return false;
   }
 
-  if (!validation_context->ClaimMemory(data, kUnionDataSize) ||
-      *static_cast<const uint32_t*>(data) != kUnionDataSize) {
+  // 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;
@@ -100,12 +113,41 @@
   return true;
 }
 
-bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input) {
-  return input.handle.is_valid();
+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 IsHandleOrInterfaceValid(const AssociatedEndpointHandle_Data& input) {
-  return input.is_valid();
+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) {
@@ -130,7 +172,7 @@
 }
 
 bool ValidateHandleOrInterfaceNonNullable(
-    const AssociatedEndpointHandle_Data& input,
+    const AssociatedInterfaceRequest_Data& input,
     const char* error_message,
     ValidationContext* validation_context) {
   if (IsHandleOrInterfaceValid(input))
@@ -170,7 +212,7 @@
 
 bool ValidateHandleOrInterface(const AssociatedInterface_Data& input,
                                ValidationContext* validation_context) {
-  if (validation_context->ClaimAssociatedEndpointHandle(input.handle))
+  if (!IsMasterInterfaceId(input.interface_id))
     return true;
 
   ReportValidationError(validation_context,
@@ -178,9 +220,9 @@
   return false;
 }
 
-bool ValidateHandleOrInterface(const AssociatedEndpointHandle_Data& input,
+bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input,
                                ValidationContext* validation_context) {
-  if (validation_context->ClaimAssociatedEndpointHandle(input))
+  if (!IsMasterInterfaceId(input.interface_id))
     return true;
 
   ReportValidationError(validation_context,
diff --git a/mojo/public/cpp/bindings/lib/validation_util.h b/mojo/public/cpp/bindings/lib/validation_util.h
index ea5a991..c883392 100644
--- a/mojo/public/cpp/bindings/lib/validation_util.h
+++ b/mojo/public/cpp/bindings/lib/validation_util.h
@@ -7,7 +7,6 @@
 
 #include <stdint.h>
 
-#include "mojo/public/cpp/bindings/bindings_export.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"
@@ -20,15 +19,7 @@
 
 // Checks whether decoding the pointer will overflow and produce a pointer
 // smaller than |offset|.
-inline 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 ValidateEncodedPointer(const uint64_t* offset);
 
 template <typename T>
 bool ValidatePointer(const Pointer<T>& input,
@@ -47,32 +38,30 @@
 // |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.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateStructHeaderAndClaimMemory(
-    const void* data,
-    ValidationContext* validation_context);
+bool ValidateStructHeaderAndClaimMemory(const void* data,
+                                        ValidationContext* validation_context);
 
 // Validates that |data| contains a valid union header, in terms of alignment
-// and size. It checks that the memory range [data, data + kUnionDataSize) is
-// not marked as occupied by other objects in |validation_context|. On success,
-// the memory range is marked as occupied.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateNonInlinedUnionHeaderAndClaimMemory(
-    const void* data,
-    ValidationContext* validation_context);
+// 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.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsRequestWithoutResponse(
+bool ValidateMessageIsRequestWithoutResponse(
     const Message* message,
     ValidationContext* validation_context);
 
 // Validates that the message is a request expecting a response.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsRequestExpectingResponse(
+bool ValidateMessageIsRequestExpectingResponse(
     const Message* message,
     ValidationContext* validation_context);
 
 // Validates that the message is a response.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsResponse(
-    const Message* message,
-    ValidationContext* validation_context);
+bool ValidateMessageIsResponse(const Message* message,
+                               ValidationContext* validation_context);
 
 // Validates that the message payload is a valid struct of type ParamsType.
 template <typename ParamsType>
@@ -81,6 +70,13 @@
   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>
@@ -109,28 +105,24 @@
   return false;
 }
 
-MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid(
-    const AssociatedInterface_Data& input);
-MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid(
-    const AssociatedEndpointHandle_Data& input);
-MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid(
-    const Interface_Data& input);
-MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid(
-    const Handle_Data& input);
+bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input);
+bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input);
+bool IsHandleOrInterfaceValid(const Interface_Data& input);
+bool IsHandleOrInterfaceValid(const Handle_Data& input);
 
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable(
+bool ValidateHandleOrInterfaceNonNullable(
     const AssociatedInterface_Data& input,
     const char* error_message,
     ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable(
-    const AssociatedEndpointHandle_Data& input,
+bool ValidateHandleOrInterfaceNonNullable(
+    const AssociatedInterfaceRequest_Data& input,
     const char* error_message,
     ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable(
+bool ValidateHandleOrInterfaceNonNullable(
     const Interface_Data& input,
     const char* error_message,
     ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable(
+bool ValidateHandleOrInterfaceNonNullable(
     const Handle_Data& input,
     const char* error_message,
     ValidationContext* validation_context);
@@ -139,12 +131,6 @@
 bool ValidateContainer(const Pointer<T>& input,
                        ValidationContext* validation_context,
                        const ContainerValidateParams* validate_params) {
-  ValidationContext::ScopedDepthTracker depth_tracker(validation_context);
-  if (validation_context->ExceedsMaxDepth()) {
-    ReportValidationError(validation_context,
-                          VALIDATION_ERROR_MAX_RECURSION_DEPTH);
-    return false;
-  }
   return ValidatePointer(input, validation_context) &&
          T::Validate(input.Get(), validation_context, validate_params);
 }
@@ -152,12 +138,6 @@
 template <typename T>
 bool ValidateStruct(const Pointer<T>& input,
                     ValidationContext* validation_context) {
-  ValidationContext::ScopedDepthTracker depth_tracker(validation_context);
-  if (validation_context->ExceedsMaxDepth()) {
-    ReportValidationError(validation_context,
-                          VALIDATION_ERROR_MAX_RECURSION_DEPTH);
-    return false;
-  }
   return ValidatePointer(input, validation_context) &&
          T::Validate(input.Get(), validation_context);
 }
@@ -165,40 +145,24 @@
 template <typename T>
 bool ValidateInlinedUnion(const T& input,
                           ValidationContext* validation_context) {
-  ValidationContext::ScopedDepthTracker depth_tracker(validation_context);
-  if (validation_context->ExceedsMaxDepth()) {
-    ReportValidationError(validation_context,
-                          VALIDATION_ERROR_MAX_RECURSION_DEPTH);
-    return false;
-  }
   return T::Validate(&input, validation_context, true);
 }
 
 template <typename T>
 bool ValidateNonInlinedUnion(const Pointer<T>& input,
                              ValidationContext* validation_context) {
-  ValidationContext::ScopedDepthTracker depth_tracker(validation_context);
-  if (validation_context->ExceedsMaxDepth()) {
-    ReportValidationError(validation_context,
-                          VALIDATION_ERROR_MAX_RECURSION_DEPTH);
-    return false;
-  }
   return ValidatePointer(input, validation_context) &&
          T::Validate(input.Get(), validation_context, false);
 }
 
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface(
-    const AssociatedInterface_Data& input,
-    ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface(
-    const AssociatedEndpointHandle_Data& input,
-    ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface(
-    const Interface_Data& input,
-    ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface(
-    const Handle_Data& input,
-    ValidationContext* validation_context);
+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
diff --git a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
index cb24bc4..edbf27b 100644
--- a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
+++ b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
@@ -7,14 +7,14 @@
 
 #include <type_traits>
 
-#include "mojo/public/cpp/bindings/clone_traits.h"
-#include "mojo/public/cpp/bindings/lib/equals_traits.h"
+#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> {
@@ -22,7 +22,7 @@
     WTF::Vector<T> result;
     result.reserveCapacity(input.size());
     for (const auto& element : input)
-      result.push_back(mojo::Clone(element));
+      result.append(internal::Clone(element));
 
     return result;
   }
@@ -34,13 +34,11 @@
     WTF::HashMap<K, V> result;
     auto input_end = input.end();
     for (auto it = input.begin(); it != input_end; ++it)
-      result.add(mojo::Clone(it->key), mojo::Clone(it->value));
+      result.add(internal::Clone(it->key), internal::Clone(it->value));
     return result;
   }
 };
 
-namespace internal {
-
 template <typename T>
 struct EqualsTraits<WTF::Vector<T>, false> {
   static bool Equals(const WTF::Vector<T>& a, const WTF::Vector<T>& b) {
diff --git a/mojo/public/cpp/bindings/lib/wtf_serialization.h b/mojo/public/cpp/bindings/lib/wtf_serialization.h
index 0f112b9..132e19c 100644
--- a/mojo/public/cpp/bindings/lib/wtf_serialization.h
+++ b/mojo/public/cpp/bindings/lib/wtf_serialization.h
@@ -5,7 +5,9 @@
 #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"
 
diff --git a/mojo/public/cpp/bindings/map.h b/mojo/public/cpp/bindings/map.h
index c1ba075..d4c7952 100644
--- a/mojo/public/cpp/bindings/map.h
+++ b/mojo/public/cpp/bindings/map.h
@@ -5,37 +5,299 @@
 #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 {
 
-// TODO(yzshen): These conversion functions should be removed and callsites
-// should be revisited and changed to use the same map type.
-template <typename Key, typename Value>
-std::unordered_map<Key, Value> MapToUnorderedMap(
-    const std::map<Key, Value>& input) {
-  return std::unordered_map<Key, Value>(input.begin(), input.end());
-}
+// 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;
 
-template <typename Key, typename Value>
-std::unordered_map<Key, Value> MapToUnorderedMap(std::map<Key, Value>&& input) {
-  return std::unordered_map<Key, Value>(std::make_move_iterator(input.begin()),
-                                        std::make_move_iterator(input.end()));
-}
+  // Map keys cannot be move only classes.
+  static_assert(!internal::IsMoveOnlyType<Key>::value,
+                "Map keys cannot be move only types.");
 
-template <typename Key, typename Value>
-std::map<Key, Value> UnorderedMapToMap(
-    const std::unordered_map<Key, Value>& input) {
-  return std::map<Key, Value>(input.begin(), input.end());
-}
+  using Iterator = typename std::map<Key, Value>::iterator;
+  using ConstIterator = typename std::map<Key, Value>::const_iterator;
 
-template <typename Key, typename Value>
-std::map<Key, Value> UnorderedMapToMap(std::unordered_map<Key, Value>&& input) {
-  return std::map<Key, Value>(std::make_move_iterator(input.begin()),
-                              std::make_move_iterator(input.end()));
-}
+  // 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
 
diff --git a/mojo/public/cpp/bindings/map_data_view.h b/mojo/public/cpp/bindings/map_data_view.h
deleted file mode 100644
index a65bb9e..0000000
--- a/mojo/public/cpp/bindings/map_data_view.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_MAP_DATA_VIEW_H_
-
-#include "base/logging.h"
-#include "mojo/public/cpp/bindings/array_data_view.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.h"
-#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
-
-namespace mojo {
-
-template <typename K, typename V>
-class MapDataView {
- public:
-  using Data_ = typename internal::MojomTypeTraits<MapDataView<K, V>>::Data;
-
-  MapDataView() {}
-
-  MapDataView(Data_* data, internal::SerializationContext* context)
-      : keys_(data ? data->keys.Get() : nullptr, context),
-        values_(data ? data->values.Get() : nullptr, context) {}
-
-  bool is_null() const {
-    DCHECK_EQ(keys_.is_null(), values_.is_null());
-    return keys_.is_null();
-  }
-
-  size_t size() const {
-    DCHECK_EQ(keys_.size(), values_.size());
-    return keys_.size();
-  }
-
-  ArrayDataView<K>& keys() { return keys_; }
-  const ArrayDataView<K>& keys() const { return keys_; }
-
-  template <typename U>
-  bool ReadKeys(U* output) {
-    return internal::Deserialize<ArrayDataView<K>>(keys_.data_, output,
-                                                   keys_.context_);
-  }
-
-  ArrayDataView<V>& values() { return values_; }
-  const ArrayDataView<V>& values() const { return values_; }
-
-  template <typename U>
-  bool ReadValues(U* output) {
-    return internal::Deserialize<ArrayDataView<V>>(values_.data_, output,
-                                                   values_.context_);
-  }
-
- private:
-  ArrayDataView<K> keys_;
-  ArrayDataView<V> values_;
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/map_traits.h b/mojo/public/cpp/bindings/map_traits.h
index 5c0d8b2..01dd66d 100644
--- a/mojo/public/cpp/bindings/map_traits.h
+++ b/mojo/public/cpp/bindings/map_traits.h
@@ -37,13 +37,13 @@
 //     static const V& GetValue(CustomConstIterator& iterator);
 //
 //     // Returning false results in deserialization failure and causes the
-//     // message pipe receiving it to be disconnected. |IK| and |IV| are
-//     // separate input key/value template parameters that allows for the
-//     // the key/value types to be forwarded.
-//     template <typename IK, typename IV>
+//     // message pipe receiving it to be disconnected.
 //     static bool Insert(CustomMap<K, V>& input,
-//                        IK&& key,
-//                        IV&& value);
+//                        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);
 //   };
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
index 83a4399..ff79a20 100644
--- a/mojo/public/cpp/bindings/map_traits_stl.h
+++ b/mojo/public/cpp/bindings/map_traits_stl.h
@@ -94,10 +94,14 @@
   static V& GetValue(Iterator& iterator) { return iterator->second; }
   static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
 
-  template <typename IK, typename IV>
-  static bool Insert(std::unordered_map<K, V>& input, IK&& key, IV&& value) {
-    input.insert(
-        std::make_pair(std::forward<IK>(key), std::forward<IV>(value)));
+  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;
   }
 
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
index dd68b36..4392201 100644
--- a/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h
+++ b/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h
@@ -46,13 +46,20 @@
   static V& GetValue(Iterator& iterator) { return iterator->value; }
   static const V& GetValue(ConstIterator& iterator) { return iterator->value; }
 
-  template <typename IK, typename IV>
-  static bool Insert(WTF::HashMap<K, V>& input, IK&& key, IV&& 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";
+      LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
       return false;
     }
-    input.insert(std::forward<IK>(key), std::forward<IV>(value));
+    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;
   }
 
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index 65d6cec..e758432 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -13,46 +13,26 @@
 #include <string>
 #include <vector>
 
-#include "base/callback.h"
-#include "base/compiler_specific.h"
 #include "base/logging.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/lib/message_buffer.h"
 #include "mojo/public/cpp/bindings/lib/message_internal.h"
-#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "mojo/public/cpp/system/message.h"
 
 namespace mojo {
 
-class AssociatedGroupController;
-
-using ReportBadMessageCallback = base::Callback<void(const std::string& error)>;
-
 // 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 MOJO_CPP_BINDINGS_EXPORT Message {
+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(Message&& other);
-
   ~Message();
 
-  Message& operator=(Message&& other);
-
-  // Resets the Message to an uninitialized state. Upon reset, the Message
-  // exists as if it were default-constructed: it has no data buffer and owns no
-  // handles.
-  void Reset();
-
-  // Indicates whether this Message is uninitialized.
-  bool IsNull() const { return !buffer_; }
-
   // Initializes a Message with enough space for |capacity| bytes.
   void Initialize(size_t capacity, bool zero_initialized);
 
@@ -61,9 +41,10 @@
                                  uint32_t num_bytes,
                                  std::vector<Handle>* handles);
 
-  uint32_t data_num_bytes() const {
-    return static_cast<uint32_t>(buffer_->size());
-  }
+  // 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 {
@@ -76,30 +57,12 @@
   const internal::MessageHeader* header() const {
     return static_cast<const internal::MessageHeader*>(buffer_->data());
   }
+
   internal::MessageHeader* header() {
-    return static_cast<internal::MessageHeader*>(buffer_->data());
+    return const_cast<internal::MessageHeader*>(
+        static_cast<const Message*>(this)->header());
   }
 
-  const internal::MessageHeaderV1* header_v1() const {
-    DCHECK_GE(version(), 1u);
-    return static_cast<const internal::MessageHeaderV1*>(buffer_->data());
-  }
-  internal::MessageHeaderV1* header_v1() {
-    DCHECK_GE(version(), 1u);
-    return static_cast<internal::MessageHeaderV1*>(buffer_->data());
-  }
-
-  const internal::MessageHeaderV2* header_v2() const {
-    DCHECK_GE(version(), 2u);
-    return static_cast<const internal::MessageHeaderV2*>(buffer_->data());
-  }
-  internal::MessageHeaderV2* header_v2() {
-    DCHECK_GE(version(), 2u);
-    return static_cast<internal::MessageHeaderV2*>(buffer_->data());
-  }
-
-  uint32_t version() const { return header()->version; }
-
   uint32_t interface_id() const { return header()->interface_id; }
   void set_interface_id(uint32_t id) { header()->interface_id = id; }
 
@@ -107,32 +70,32 @@
   bool has_flag(uint32_t flag) const { return !!(header()->flags & flag); }
 
   // Access the request_id field (if present).
-  uint64_t request_id() const { return header_v1()->request_id; }
+  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) {
-    header_v1()->request_id = request_id;
+    DCHECK(has_request_id());
+    static_cast<internal::MessageHeaderWithRequestID*>(header())
+        ->request_id = request_id;
   }
 
   // Access the payload.
-  const uint8_t* payload() const;
+  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;
-
-  uint32_t payload_num_interface_ids() const;
-  const uint32_t* payload_interface_ids() const;
+  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_; }
 
-  const std::vector<ScopedInterfaceEndpointHandle>*
-  associated_endpoint_handles() const {
-    return &associated_endpoint_handles_;
-  }
-  std::vector<ScopedInterfaceEndpointHandle>*
-  mutable_associated_endpoint_handles() {
-    return &associated_endpoint_handles_;
-  }
-
   // Access the underlying Buffer interface.
   internal::Buffer* buffer() { return buffer_.get(); }
 
@@ -145,22 +108,11 @@
   // rejected by bindings validation code.
   void NotifyBadMessage(const std::string& error);
 
-  // Serializes |associated_endpoint_handles_| into the payload_interface_ids
-  // field.
-  void SerializeAssociatedEndpointHandles(
-      AssociatedGroupController* group_controller);
-
-  // Deserializes |associated_endpoint_handles_| from the payload_interface_ids
-  // field.
-  bool DeserializeAssociatedEndpointHandles(
-      AssociatedGroupController* group_controller);
-
  private:
   void CloseHandles();
 
   std::unique_ptr<internal::MessageBuffer> buffer_;
   std::vector<Handle> handles_;
-  std::vector<ScopedInterfaceEndpointHandle> associated_endpoint_handles_;
 
   DISALLOW_COPY_AND_ASSIGN(Message);
 };
@@ -234,57 +186,6 @@
       WARN_UNUSED_RESULT = 0;
 };
 
-class MOJO_CPP_BINDINGS_EXPORT PassThroughFilter
-    : NON_EXPORTED_BASE(public MessageReceiver) {
- public:
-  PassThroughFilter();
-  ~PassThroughFilter() override;
-
-  // MessageReceiver:
-  bool Accept(Message* message) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PassThroughFilter);
-};
-
-namespace internal {
-class SyncMessageResponseSetup;
-}
-
-// An object which should be constructed on the stack immediately before making
-// a sync request for which the caller wishes to perform custom validation of
-// the response value(s). It is illegal to make more than one sync call during
-// the lifetime of the topmost SyncMessageResponseContext, but it is legal to
-// nest contexts to support reentrancy.
-//
-// Usage should look something like:
-//
-//     SyncMessageResponseContext response_context;
-//     foo_interface->SomeSyncCall(&response_value);
-//     if (response_value.IsBad())
-//       response_context.ReportBadMessage("Bad response_value!");
-//
-class MOJO_CPP_BINDINGS_EXPORT SyncMessageResponseContext {
- public:
-  SyncMessageResponseContext();
-  ~SyncMessageResponseContext();
-
-  static SyncMessageResponseContext* current();
-
-  void ReportBadMessage(const std::string& error);
-
-  const ReportBadMessageCallback& GetBadMessageCallback();
-
- private:
-  friend class internal::SyncMessageResponseSetup;
-
-  SyncMessageResponseContext* outer_context_;
-  Message response_;
-  ReportBadMessageCallback bad_message_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(SyncMessageResponseContext);
-};
-
 // 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
@@ -294,22 +195,6 @@
 // NOTE: The message hasn't been validated and may be malformed!
 MojoResult ReadMessage(MessagePipeHandle handle, Message* message);
 
-// Reports the currently dispatching Message as bad. Note that this is only
-// legal to call from directly within the stack frame of a message dispatch. If
-// you need to do asynchronous work before you can determine the legitimacy of
-// a message, use TakeBadMessageCallback() and retain its result until you're
-// ready to invoke or discard it.
-MOJO_CPP_BINDINGS_EXPORT
-void ReportBadMessage(const std::string& error);
-
-// Acquires a callback which may be run to report the currently dispatching
-// Message as bad. Note that this is only legal to call from directly within the
-// stack frame of a message dispatch, but the returned callback may be called
-// exactly once any time thereafter to report the message as bad. This may only
-// be called once per message.
-MOJO_CPP_BINDINGS_EXPORT
-ReportBadMessageCallback GetBadMessageCallback();
-
 }  // 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
index 50c19db..3bcbd0a 100644
--- a/mojo/public/cpp/bindings/message_header_validator.h
+++ b/mojo/public/cpp/bindings/message_header_validator.h
@@ -5,17 +5,16 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_
 
-#include "base/compiler_specific.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
 
 namespace mojo {
 
-class MOJO_CPP_BINDINGS_EXPORT MessageHeaderValidator
-    : NON_EXPORTED_BASE(public MessageReceiver) {
+class MessageHeaderValidator : public MessageFilter {
  public:
-  MessageHeaderValidator();
-  explicit MessageHeaderValidator(const std::string& description);
+  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.
diff --git a/mojo/public/cpp/bindings/native_struct.h b/mojo/public/cpp/bindings/native_struct.h
index ac27250..882c970 100644
--- a/mojo/public/cpp/bindings/native_struct.h
+++ b/mojo/public/cpp/bindings/native_struct.h
@@ -5,10 +5,7 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_
 
-#include <vector>
-
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/bindings_export.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"
@@ -20,7 +17,7 @@
 
 // Native-only structs correspond to "[Native] struct Foo;" definitions in
 // mojom.
-class MOJO_CPP_BINDINGS_EXPORT NativeStruct {
+class NativeStruct {
  public:
   using Data_ = internal::NativeStruct_Data;
 
@@ -41,9 +38,8 @@
 
   NativeStructPtr Clone() const;
   bool Equals(const NativeStruct& other) const;
-  size_t Hash(size_t seed) const;
 
-  base::Optional<std::vector<uint8_t>> data;
+  Array<uint8_t> data;
 };
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/native_struct_data_view.h b/mojo/public/cpp/bindings/native_struct_data_view.h
deleted file mode 100644
index 613bd7a..0000000
--- a/mojo/public/cpp/bindings/native_struct_data_view.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_DATA_VIEW_H_
-
-#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.h"
-
-namespace mojo {
-
-class NativeStructDataView {
- public:
-  using Data_ = internal::NativeStruct_Data;
-
-  NativeStructDataView() {}
-
-  NativeStructDataView(Data_* data, internal::SerializationContext* context)
-      : data_(data) {}
-
-  bool is_null() const { return !data_; }
-
-  size_t size() const { return data_->data.size(); }
-
-  uint8_t operator[](size_t index) const { return data_->data.at(index); }
-
-  const uint8_t* data() const { return data_->data.storage(); }
-
- private:
-  Data_* data_ = nullptr;
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_DATA_VIEW_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
index a5c04da..b387b06 100644
--- a/mojo/public/cpp/bindings/pipe_control_message_handler.h
+++ b/mojo/public/cpp/bindings/pipe_control_message_handler.h
@@ -5,11 +5,9 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_
 
-#include <string>
-
-#include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.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 {
@@ -17,8 +15,7 @@
 class PipeControlMessageHandlerDelegate;
 
 // Handler for messages defined in pipe_control_messages.mojom.
-class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageHandler
-    : NON_EXPORTED_BASE(public MessageReceiver) {
+class PipeControlMessageHandler : public MessageReceiver {
  public:
   explicit PipeControlMessageHandler(
       PipeControlMessageHandlerDelegate* delegate);
@@ -46,6 +43,7 @@
 
   std::string description_;
   PipeControlMessageHandlerDelegate* const delegate_;
+  internal::SerializationContext context_;
 
   DISALLOW_COPY_AND_ASSIGN(PipeControlMessageHandler);
 };
diff --git a/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h
index 16fd918..06f0695 100644
--- a/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h
+++ b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h
@@ -5,8 +5,6 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_
 
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
 #include "mojo/public/cpp/bindings/interface_id.h"
 
 namespace mojo {
@@ -16,9 +14,8 @@
   // 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,
-      const base::Optional<DisconnectReason>& reason) = 0;
+  virtual bool OnPeerAssociatedEndpointClosed(InterfaceId id) = 0;
+  virtual bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) = 0;
 
  protected:
   virtual ~PipeControlMessageHandlerDelegate() {}
diff --git a/mojo/public/cpp/bindings/pipe_control_message_proxy.h b/mojo/public/cpp/bindings/pipe_control_message_proxy.h
index 52c408f..7f3e006 100644
--- a/mojo/public/cpp/bindings/pipe_control_message_proxy.h
+++ b/mojo/public/cpp/bindings/pipe_control_message_proxy.h
@@ -6,36 +6,26 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_PROXY_H_
 
 #include "base/macros.h"
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.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 MessageReceiver;
 
 // Proxy for request messages defined in pipe_control_messages.mojom.
-//
-// NOTE: This object may be used from multiple threads.
-class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageProxy {
+class PipeControlMessageProxy {
  public:
-  // Doesn't take ownership of |receiver|. If This PipeControlMessageProxy will
-  // be used from multiple threads, |receiver| must be thread-safe.
+  // Doesn't take ownership of |receiver|. It must outlive this object.
   explicit PipeControlMessageProxy(MessageReceiver* receiver);
 
-  void NotifyPeerEndpointClosed(InterfaceId id,
-                                const base::Optional<DisconnectReason>& reason);
-
-  static Message ConstructPeerEndpointClosedMessage(
-      InterfaceId id,
-      const base::Optional<DisconnectReason>& reason);
+  void NotifyPeerEndpointClosed(InterfaceId id);
+  void NotifyEndpointClosedBeforeSent(InterfaceId id);
 
  private:
   // Not owned.
   MessageReceiver* receiver_;
+  internal::SerializationContext context_;
 
   DISALLOW_COPY_AND_ASSIGN(PipeControlMessageProxy);
 };
diff --git a/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h b/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h
deleted file mode 100644
index 4d40cdf..0000000
--- a/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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_RAW_PTR_IMPL_REF_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_RAW_PTR_IMPL_REF_TRAITS_H_
-
-namespace mojo {
-
-// Default traits for a binding's implementation reference type. This
-// corresponds to a raw pointer.
-template <typename Interface>
-struct RawPtrImplRefTraits {
-  using PointerType = Interface*;
-
-  static bool IsNull(PointerType ptr) { return !ptr; }
-  static Interface* GetRawPointer(PointerType* ptr) { return *ptr; }
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_RAW_PTR_IMPL_REF_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h
index 16527cf..1f45b0c 100644
--- a/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h
+++ b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h
@@ -5,16 +5,8 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_
 
-#include <string>
-
-#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/optional.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
 #include "mojo/public/cpp/bindings/interface_id.h"
 
 namespace mojo {
@@ -23,16 +15,8 @@
 
 // ScopedInterfaceEndpointHandle refers to one end of an interface, either the
 // implementation side or the client side.
-// Threading: At any given time, a ScopedInterfaceEndpointHandle should only
-// be accessed from a single thread.
-class MOJO_CPP_BINDINGS_EXPORT ScopedInterfaceEndpointHandle {
+class ScopedInterfaceEndpointHandle {
  public:
-  // Creates a pair of handles representing the two endpoints of an interface,
-  // which are not yet associated with a message pipe.
-  static void CreatePairPendingAssociation(
-      ScopedInterfaceEndpointHandle* handle0,
-      ScopedInterfaceEndpointHandle* handle1);
-
   // Creates an invalid endpoint handle.
   ScopedInterfaceEndpointHandle();
 
@@ -43,77 +27,39 @@
   ScopedInterfaceEndpointHandle& operator=(
       ScopedInterfaceEndpointHandle&& other);
 
-  bool is_valid() const;
+  bool is_valid() const { return IsValidInterfaceId(id_); }
 
-  // Returns true if the interface hasn't associated with a message pipe.
-  bool pending_association() const;
-
-  // Returns kInvalidInterfaceId when in pending association state or the handle
-  // is invalid.
-  InterfaceId id() const;
-
-  // Returns null when in pending association state or the handle is invalid.
-  AssociatedGroupController* group_controller() const;
-
-  // Returns the disconnect reason if the peer handle is closed before
-  // association and specifies a custom disconnect reason.
-  const base::Optional<DisconnectReason>& disconnect_reason() const;
-
-  enum AssociationEvent {
-    // The interface has been associated with a message pipe.
-    ASSOCIATED,
-    // The peer of this object has been closed before association.
-    PEER_CLOSED_BEFORE_ASSOCIATION
-  };
-
-  using AssociationEventCallback = base::OnceCallback<void(AssociationEvent)>;
-  // Note:
-  // - |handler| won't run if the handle is invalid. Otherwise, |handler| is run
-  //   on the calling thread asynchronously, even if the interface has already
-  //   been associated or the peer has been closed before association.
-  // - |handler| won't be called after this object is destroyed or reset.
-  // - A null |handler| can be used to cancel the previous callback.
-  void SetAssociationEventHandler(AssociationEventCallback handler);
+  bool is_local() const { return is_local_; }
 
   void reset();
-  void ResetWithReason(uint32_t custom_reason, const std::string& description);
+  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;
-  friend class AssociatedGroup;
 
-  class State;
-
-  // Used by 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);
 
-  // Used by AssociatedGroupController.
-  // The peer of this handle will join |peer_group_controller|.
-  bool NotifyAssociation(
-      InterfaceId id,
-      scoped_refptr<AssociatedGroupController> peer_group_controller);
-
-  void ResetInternal(const base::Optional<DisconnectReason>& reason);
-
-  // Used by AssociatedGroup.
-  // It is safe to run the returned callback on any thread, or after this handle
-  // is destroyed.
-  // The return value of the getter:
-  //   - If the getter is retrieved when the handle is invalid, the return value
-  //     of the getter will always be null.
-  //   - If the getter is retrieved when the handle is valid and non-pending,
-  //     the return value of the getter will be non-null and remain unchanged
-  //     even if the handle is later reset.
-  //   - If the getter is retrieved when the handle is valid but pending
-  //     asssociation, the return value of the getter will initially be null,
-  //     change to non-null when the handle is associated, and remain unchanged
-  //     ever since.
-  base::Callback<AssociatedGroupController*()> CreateGroupControllerGetter()
-      const;
-
-  scoped_refptr<State> state_;
+  InterfaceId id_;
+  bool is_local_;
+  scoped_refptr<AssociatedGroupController> group_controller_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceEndpointHandle);
 };
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_data_view.h b/mojo/public/cpp/bindings/string_data_view.h
deleted file mode 100644
index 2b091b4..0000000
--- a/mojo/public/cpp/bindings/string_data_view.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_STRING_DATA_VIEW_H_
-
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.h"
-
-namespace mojo {
-
-// Access to the contents of a serialized string.
-class StringDataView {
- public:
-  StringDataView() {}
-
-  StringDataView(internal::String_Data* data,
-                 internal::SerializationContext* context)
-      : data_(data) {}
-
-  bool is_null() const { return !data_; }
-
-  const char* storage() const { return data_->storage(); }
-
-  size_t size() const { return data_->size(); }
-
- private:
-  internal::String_Data* data_ = nullptr;
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_STRING_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/string_traits.h b/mojo/public/cpp/bindings/string_traits.h
index 7d3075a..a6ade6f 100644
--- a/mojo/public/cpp/bindings/string_traits.h
+++ b/mojo/public/cpp/bindings/string_traits.h
@@ -5,10 +5,26 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_
 
-#include "mojo/public/cpp/bindings/string_data_view.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.
 //
@@ -24,7 +40,6 @@
 //     static size_t GetSize(const CustomString& input);
 //     static const char* GetData(const CustomString& input);
 //
-//     // The caller guarantees that |!input.is_null()|.
 //     static bool Read(StringDataView input, CustomString* output);
 //   };
 //
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_string16.h b/mojo/public/cpp/bindings/string_traits_string16.h
index f96973a..5a08908 100644
--- a/mojo/public/cpp/bindings/string_traits_string16.h
+++ b/mojo/public/cpp/bindings/string_traits_string16.h
@@ -6,13 +6,12 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STRING16_H_
 
 #include "base/strings/string16.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/string_traits.h"
 
 namespace mojo {
 
 template <>
-struct MOJO_CPP_BINDINGS_EXPORT StringTraits<base::string16> {
+struct StringTraits<base::string16> {
   static bool IsNull(const base::string16& input) {
     // base::string16 is always converted to non-null mojom string.
     return false;
diff --git a/mojo/public/cpp/bindings/strong_binding.h b/mojo/public/cpp/bindings/strong_binding.h
index f4b4a06..7fb7eea 100644
--- a/mojo/public/cpp/bindings/strong_binding.h
+++ b/mojo/public/cpp/bindings/strong_binding.h
@@ -5,54 +5,94 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_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/weak_ptr.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/filter_chain.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 {
 
-template <typename Interface>
-class StrongBinding;
-
-template <typename Interface>
-using StrongBindingPtr = base::WeakPtr<StrongBinding<Interface>>;
-
 // This connects an interface implementation strongly to a pipe. When a
-// connection error is detected the implementation is deleted. If the task
-// runner that a StrongBinding is bound on is stopped, the connection error
-// handler will not be invoked and the implementation will not be deleted.
+// connection error is detected the implementation is deleted. Deleting the
+// connector also closes the pipe.
 //
-// To use, call StrongBinding<T>::Create() (see below) or the helper
-// MakeStrongBinding function:
+// Example of an implementation that is always bound strongly to a pipe
 //
-//   mojo::MakeStrongBinding(base::MakeUnique<FooImpl>(),
-//                           std::move(foo_request));
+//   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:
-  // Create a new StrongBinding instance. The instance owns itself, cleaning up
-  // only in the event of a pipe connection error. Returns a WeakPtr to the new
-  // StrongBinding instance.
-  static StrongBindingPtr<Interface> Create(
-      std::unique_ptr<Interface> impl,
-      InterfaceRequest<Interface> request) {
-    StrongBinding* binding =
-        new StrongBinding(std::move(impl), std::move(request));
-    return binding->weak_factory_.GetWeakPtr();
+  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.
@@ -62,64 +102,25 @@
   void set_connection_error_handler(const base::Closure& error_handler) {
     DCHECK(binding_.is_bound());
     connection_error_handler_ = error_handler;
-    connection_error_with_reason_handler_.Reset();
   }
 
-  void set_connection_error_with_reason_handler(
-      const ConnectionErrorWithReasonCallback& error_handler) {
-    DCHECK(binding_.is_bound());
-    connection_error_with_reason_handler_ = error_handler;
-    connection_error_handler_.Reset();
-  }
+  Interface* impl() { return binding_.impl(); }
+  // Exposed for testing, should not generally be used.
+  internal::Router* internal_router() { return binding_.internal_router(); }
 
-  // Forces the binding to close. This destroys the StrongBinding instance.
-  void Close() { delete this; }
-
-  Interface* impl() { return impl_.get(); }
-
-  // Sends a message on the underlying message pipe and runs the current
-  // message loop until its response is received. This can be used in tests to
-  // verify that no message was sent on a message pipe in response to some
-  // stimulus.
-  void FlushForTesting() { binding_.FlushForTesting(); }
-
- private:
-  StrongBinding(std::unique_ptr<Interface> impl,
-                InterfaceRequest<Interface> request)
-      : impl_(std::move(impl)),
-        binding_(impl_.get(), std::move(request)),
-        weak_factory_(this) {
-    binding_.set_connection_error_with_reason_handler(
-        base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this)));
-  }
-
-  ~StrongBinding() {}
-
-  void OnConnectionError(uint32_t custom_reason,
-                         const std::string& description) {
+  void OnConnectionError() {
     if (!connection_error_handler_.is_null())
       connection_error_handler_.Run();
-    else if (!connection_error_with_reason_handler_.is_null())
-      connection_error_with_reason_handler_.Run(custom_reason, description);
-    Close();
+    delete binding_.impl();
   }
 
-  std::unique_ptr<Interface> impl_;
+ private:
   base::Closure connection_error_handler_;
-  ConnectionErrorWithReasonCallback connection_error_with_reason_handler_;
   Binding<Interface> binding_;
-  base::WeakPtrFactory<StrongBinding> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(StrongBinding);
 };
 
-template <typename Interface, typename Impl>
-StrongBindingPtr<Interface> MakeStrongBinding(
-    std::unique_ptr<Impl> impl,
-    InterfaceRequest<Interface> request) {
-  return StrongBinding<Interface>::Create(std::move(impl), std::move(request));
-}
-
 }  // 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
index b135312..92f2728 100644
--- a/mojo/public/cpp/bindings/struct_ptr.h
+++ b/mojo/public/cpp/bindings/struct_ptr.h
@@ -5,26 +5,23 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
 
-#include <functional>
-#include <memory>
 #include <new>
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/lib/hash_util.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
 
 namespace mojo {
 namespace internal {
 
-constexpr size_t kHashSeed = 31;
-
 template <typename Struct>
-class StructPtrWTFHelper;
-
-template <typename Struct>
-class InlinedStructPtrWTFHelper;
+class StructHelper {
+ public:
+  template <typename Ptr>
+  static void Initialize(Ptr* ptr) {
+    ptr->Initialize();
+  }
+};
 
 }  // namespace internal
 
@@ -34,34 +31,35 @@
  public:
   using Struct = S;
 
-  StructPtr() = default;
-  StructPtr(decltype(nullptr)) {}
+  StructPtr() : ptr_(nullptr) {}
+  StructPtr(decltype(nullptr)) : ptr_(nullptr) {}
 
-  ~StructPtr() = default;
+  ~StructPtr() { delete ptr_; }
 
   StructPtr& operator=(decltype(nullptr)) {
     reset();
     return *this;
   }
 
-  StructPtr(StructPtr&& other) { Take(&other); }
+  StructPtr(StructPtr&& other) : ptr_(nullptr) { Take(&other); }
   StructPtr& operator=(StructPtr&& other) {
     Take(&other);
     return *this;
   }
 
-  template <typename... Args>
-  StructPtr(base::in_place_t, Args&&... args)
-      : ptr_(new Struct(std::forward<Args>(args)...)) {}
-
   template <typename U>
   U To() const {
     return TypeConverter<U, StructPtr>::Convert(*this);
   }
 
-  void reset() { ptr_.reset(); }
+  void reset() {
+    if (ptr_) {
+      delete ptr_;
+      ptr_ = nullptr;
+    }
+  }
 
-  bool is_null() const { return !ptr_; }
+  bool is_null() const { return ptr_ == nullptr; }
 
   Struct& operator*() const {
     DCHECK(ptr_);
@@ -69,9 +67,9 @@
   }
   Struct* operator->() const {
     DCHECK(ptr_);
-    return ptr_.get();
+    return ptr_;
   }
-  Struct* get() const { return ptr_.get(); }
+  Struct* get() const { return ptr_; }
 
   void Swap(StructPtr* other) { std::swap(ptr_, other->ptr_); }
 
@@ -80,52 +78,52 @@
   // that it contains Mojo handles).
   StructPtr Clone() const { return is_null() ? StructPtr() : ptr_->Clone(); }
 
-  // Compares the pointees (which might both be null).
-  // TODO(tibell): Get rid of Equals in favor of the operator. Same for Hash.
   bool Equals(const StructPtr& other) const {
     if (is_null() || other.is_null())
       return is_null() && other.is_null();
     return ptr_->Equals(*other.ptr_);
   }
 
-  // Hashes based on the pointee (which might be null).
-  size_t Hash(size_t seed) const {
-    if (is_null())
-      return internal::HashCombine(seed, 0);
-    return ptr_->Hash(seed);
-  }
+ private:
+  // TODO(dcheng): Use an explicit conversion operator.
+  typedef Struct* StructPtr::*Testable;
 
-  explicit operator bool() const { return !is_null(); }
+ public:
+  operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; }
 
  private:
-  friend class internal::StructPtrWTFHelper<Struct>;
+  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);
   }
 
-  std::unique_ptr<Struct> ptr_;
+  Struct* ptr_;
 
   DISALLOW_COPY_AND_ASSIGN(StructPtr);
 };
 
-template <typename T>
-bool operator==(const StructPtr<T>& lhs, const StructPtr<T>& rhs) {
-  return lhs.Equals(rhs);
-}
-template <typename T>
-bool operator!=(const StructPtr<T>& lhs, const StructPtr<T>& rhs) {
-  return !(lhs == rhs);
-}
-
 // Designed to be used when Struct is small and copyable.
 template <typename S>
 class InlinedStructPtr {
  public:
   using Struct = S;
 
-  InlinedStructPtr() : state_(NIL) {}
-  InlinedStructPtr(decltype(nullptr)) : state_(NIL) {}
+  InlinedStructPtr() : is_null_(true) {}
+  InlinedStructPtr(decltype(nullptr)) : is_null_(true) {}
 
   ~InlinedStructPtr() {}
 
@@ -134,150 +132,79 @@
     return *this;
   }
 
-  InlinedStructPtr(InlinedStructPtr&& other) : state_(NIL) { Take(&other); }
+  InlinedStructPtr(InlinedStructPtr&& other) : is_null_(true) { Take(&other); }
   InlinedStructPtr& operator=(InlinedStructPtr&& other) {
     Take(&other);
     return *this;
   }
 
-  template <typename... Args>
-  InlinedStructPtr(base::in_place_t, Args&&... args)
-      : value_(std::forward<Args>(args)...), state_(VALID) {}
-
   template <typename U>
   U To() const {
     return TypeConverter<U, InlinedStructPtr>::Convert(*this);
   }
 
   void reset() {
-    state_ = NIL;
+    is_null_ = true;
     value_. ~Struct();
     new (&value_) Struct();
   }
 
-  bool is_null() const { return state_ == NIL; }
+  bool is_null() const { return is_null_; }
 
   Struct& operator*() const {
-    DCHECK(state_ == VALID);
+    DCHECK(!is_null_);
     return value_;
   }
   Struct* operator->() const {
-    DCHECK(state_ == VALID);
+    DCHECK(!is_null_);
     return &value_;
   }
   Struct* get() const { return &value_; }
 
   void Swap(InlinedStructPtr* other) {
     std::swap(value_, other->value_);
-    std::swap(state_, other->state_);
+    std::swap(is_null_, other->is_null_);
   }
 
   InlinedStructPtr Clone() const {
     return is_null() ? InlinedStructPtr() : value_.Clone();
   }
-
-  // Compares the pointees (which might both be null).
   bool Equals(const InlinedStructPtr& other) const {
     if (is_null() || other.is_null())
       return is_null() && other.is_null();
     return value_.Equals(other.value_);
   }
 
-  // Hashes based on the pointee (which might be null).
-  size_t Hash(size_t seed) const {
-    if (is_null())
-      return internal::HashCombine(seed, 0);
-    return value_.Hash(seed);
-  }
+ private:
+  // TODO(dcheng): Use an explicit conversion operator.
+  typedef Struct InlinedStructPtr::*Testable;
 
-  explicit operator bool() const { return !is_null(); }
+ public:
+  operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; }
 
  private:
-  friend class internal::InlinedStructPtrWTFHelper<Struct>;
+  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);
   }
 
-  enum State {
-    VALID,
-    NIL,
-    DELETED,  // For use in WTF::HashMap only
-  };
-
   mutable Struct value_;
-  State state_;
+  bool is_null_;
 
   DISALLOW_COPY_AND_ASSIGN(InlinedStructPtr);
 };
 
-template <typename T>
-bool operator==(const InlinedStructPtr<T>& lhs,
-                const InlinedStructPtr<T>& rhs) {
-  return lhs.Equals(rhs);
-}
-template <typename T>
-bool operator!=(const InlinedStructPtr<T>& lhs,
-                const InlinedStructPtr<T>& rhs) {
-  return !(lhs == rhs);
-}
-
-namespace internal {
-
-template <typename Struct>
-class StructPtrWTFHelper {
- public:
-  static bool IsHashTableDeletedValue(const StructPtr<Struct>& value) {
-    return value.ptr_.get() == reinterpret_cast<Struct*>(1u);
-  }
-
-  static void ConstructDeletedValue(mojo::StructPtr<Struct>& slot) {
-    // |slot| refers to a previous, real value that got deleted and had its
-    // destructor run, so this is the first time the "deleted value" has its
-    // constructor called.
-    //
-    // Dirty trick: implant an invalid pointer in |ptr_|. Destructor isn't
-    // called for deleted buckets, so this is okay.
-    new (&slot) StructPtr<Struct>();
-    slot.ptr_.reset(reinterpret_cast<Struct*>(1u));
-  }
-};
-
-template <typename Struct>
-class InlinedStructPtrWTFHelper {
- public:
-  static bool IsHashTableDeletedValue(const InlinedStructPtr<Struct>& value) {
-    return value.state_ == InlinedStructPtr<Struct>::DELETED;
-  }
-
-  static void ConstructDeletedValue(mojo::InlinedStructPtr<Struct>& slot) {
-    // |slot| refers to a previous, real value that got deleted and had its
-    // destructor run, so this is the first time the "deleted value" has its
-    // constructor called.
-    new (&slot) InlinedStructPtr<Struct>();
-    slot.state_ = InlinedStructPtr<Struct>::DELETED;
-  }
-};
-
-}  // namespace internal
 }  // namespace mojo
 
-namespace std {
-
-template <typename T>
-struct hash<mojo::StructPtr<T>> {
-  size_t operator()(const mojo::StructPtr<T>& value) const {
-    return value.Hash(mojo::internal::kHashSeed);
-  }
-};
-
-template <typename T>
-struct hash<mojo::InlinedStructPtr<T>> {
-  size_t operator()(const mojo::InlinedStructPtr<T>& value) const {
-    return value.Hash(mojo::internal::kHashSeed);
-  }
-};
-
-}  // namespace std
-
 #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
index 6cc070f..0f0bea7 100644
--- a/mojo/public/cpp/bindings/struct_traits.h
+++ b/mojo/public/cpp/bindings/struct_traits.h
@@ -8,11 +8,7 @@
 namespace mojo {
 
 // This must be specialized for any type |T| to be serialized/deserialized as
-// a mojom struct. |DataViewType| is the corresponding data view type of the
-// mojom struct. For example, if the mojom struct is example.Foo,
-// |DataViewType| will be example::FooDataView, which can also be referred to by
-// example::Foo::DataView (in chromium) and example::blink::Foo::DataView (in
-// blink).
+// 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
@@ -24,21 +20,19 @@
 //      from |input|.
 //
 //      Serializable form of a field:
-//        Value or reference of the same type used in the generated stuct
-//        wrapper type, or the following alternatives:
+//        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,
-//          WTF::String (in blink).
+//          Supported by default: base::StringPiece, std::string.
 //
 //        - array:
 //          Value or reference of any type that has an ArrayTraits defined.
-//          Supported by default: std::vector, CArray, WTF::Vector (in blink)
+//          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, std::unordered_map,
-//          WTF::HashMap (in blink).
+//          Supported by default: std::map.
 //
 //        - struct:
 //          Value or reference of any type that has a StructTraits defined.
@@ -46,10 +40,6 @@
 //        - enum:
 //          Value of any type that has an EnumTraits defined.
 //
-//      For any nullable string/struct/array/map/union field you could also
-//      return value or reference of base::Optional<T>/WTF::Optional<T>, if T
-//      has the right *Traits 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
@@ -59,13 +49,13 @@
 //      Getters for fields of other types are called once.
 //
 //   2. A static Read() method to set the contents of a |T| instance from a
-//      DataViewType.
+//      |MojomType|DataView (e.g., if |MojomType| is test::Example, the data
+//      view will be test::ExampleDataView).
 //
-//        static bool Read(DataViewType data, T* output);
+//        static bool Read(|MojomType|DataView data, T* output);
 //
-//      The generated DataViewType provides a convenient, inexpensive view of a
-//      serialized struct's field data. The caller guarantees that
-//      |!data.is_null()|.
+//      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
@@ -121,12 +111,9 @@
 // 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[move_only]" ]
-//      or
-//        type_mappings = [ "MojomType=T[copyable_pass_by_value]" ]
-//
-//    - if another type U's StructTraits/UnionTraits has a getter for T, it
-//      needs to return non-const reference/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:
 //
@@ -141,7 +128,7 @@
 //
 // StructTraits for Foo:
 //   template <>
-//   struct StructTraits<FooDataView, CustomFoo> {
+//   struct StructTraits<Foo, CustomFoo> {
 //     // Optional methods dealing with null:
 //     static bool IsNull(const CustomFoo& input);
 //     static void SetToNull(CustomFoo* output);
@@ -157,7 +144,7 @@
 //     static bool Read(FooDataView data, CustomFoo* output);
 //   };
 //
-template <typename DataViewType, typename T>
+template <typename MojomType, typename T>
 struct StructTraits;
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h
index 39a77a8..e8fc1c4 100644
--- a/mojo/public/cpp/bindings/sync_call_restrictions.h
+++ b/mojo/public/cpp/bindings/sync_call_restrictions.h
@@ -7,7 +7,6 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_restrictions.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 
 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
 #define ENABLE_SYNC_CALL_RESTRICTIONS 1
@@ -15,12 +14,8 @@
 #define ENABLE_SYNC_CALL_RESTRICTIONS 0
 #endif
 
-namespace leveldb {
-class LevelDBMojoProxy;
-}
-
 namespace ui {
-class Gpu;
+class GpuService;
 }
 
 namespace views {
@@ -41,7 +36,7 @@
 // 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 MOJO_CPP_BINDINGS_EXPORT SyncCallRestrictions {
+class SyncCallRestrictions {
  public:
 #if ENABLE_SYNC_CALL_RESTRICTIONS
   // Checks whether the current thread is allowed to make sync calls, and causes
@@ -55,9 +50,7 @@
  private:
   // DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to mojo/OWNERS first.
   // BEGIN ALLOWED USAGE.
-  friend class ui::Gpu;  // http://crbug.com/620058
-  // LevelDBMojoProxy makes same-process sync calls from the DB thread.
-  friend class leveldb::LevelDBMojoProxy;
+  friend class ui::GpuService;  // http://crbug.com/620058
   // END ALLOWED USAGE.
 
   // BEGIN USAGE THAT NEEDS TO BE FIXED.
diff --git a/mojo/public/cpp/bindings/sync_handle_registry.h b/mojo/public/cpp/bindings/sync_handle_registry.h
index b5415af..6c0701e 100644
--- a/mojo/public/cpp/bindings/sync_handle_registry.h
+++ b/mojo/public/cpp/bindings/sync_handle_registry.h
@@ -11,7 +11,6 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/system/core.h"
 
 namespace mojo {
@@ -20,8 +19,7 @@
 // be watched together.
 //
 // This class is not thread safe.
-class MOJO_CPP_BINDINGS_EXPORT SyncHandleRegistry
-    : public base::RefCounted<SyncHandleRegistry> {
+class SyncHandleRegistry : public base::RefCounted<SyncHandleRegistry> {
  public:
   // Returns a thread-local object.
   static scoped_refptr<SyncHandleRegistry> current();
diff --git a/mojo/public/cpp/bindings/sync_handle_watcher.h b/mojo/public/cpp/bindings/sync_handle_watcher.h
index eff73dd..36b796b 100644
--- a/mojo/public/cpp/bindings/sync_handle_watcher.h
+++ b/mojo/public/cpp/bindings/sync_handle_watcher.h
@@ -8,7 +8,6 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
 #include "mojo/public/cpp/bindings/sync_handle_registry.h"
 #include "mojo/public/cpp/system/core.h"
 
@@ -26,7 +25,7 @@
 // associated endpoints on different threads.
 //
 // This class is not thread safe.
-class MOJO_CPP_BINDINGS_EXPORT SyncHandleWatcher {
+class SyncHandleWatcher {
  public:
   // Note: |handle| must outlive this object.
   SyncHandleWatcher(const Handle& handle,
diff --git a/mojo/public/cpp/bindings/thread_safe_interface_ptr.h b/mojo/public/cpp/bindings/thread_safe_interface_ptr.h
deleted file mode 100644
index bab6d22..0000000
--- a/mojo/public/cpp/bindings/thread_safe_interface_ptr.h
+++ /dev/null
@@ -1,278 +0,0 @@
-// 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_THREAD_SAFE_INTERFACE_PTR_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/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.h"
-#include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/message.h"
-
-namespace mojo {
-
-// Instances of this class may be used from any thread to serialize |Interface|
-// messages and forward them elsewhere. In general you should use one of the
-// ThreadSafeInterfacePtrBase helper aliases defined below, but this type may be
-// useful if you need/want to manually manage the lifetime of the underlying
-// proxy object which will be used to ultimately send messages.
-template <typename Interface>
-class ThreadSafeForwarder : public MessageReceiverWithResponder {
- public:
-  using ProxyType = typename Interface::Proxy_;
-  using ForwardMessageCallback = base::Callback<void(Message)>;
-  using ForwardMessageWithResponderCallback =
-      base::Callback<void(Message, std::unique_ptr<MessageReceiver>)>;
-
-  // Constructs a ThreadSafeForwarder through which Messages are forwarded to
-  // |forward| or |forward_with_responder| by posting to |task_runner|.
-  //
-  // Any message sent through this forwarding interface will dispatch its reply,
-  // if any, back to the thread which called the corresponding interface method.
-  ThreadSafeForwarder(
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-      const ForwardMessageCallback& forward,
-      const ForwardMessageWithResponderCallback& forward_with_responder,
-      const AssociatedGroup& associated_group)
-      : proxy_(this),
-        task_runner_(task_runner),
-        forward_(forward),
-        forward_with_responder_(forward_with_responder),
-        associated_group_(associated_group) {}
-
-  ~ThreadSafeForwarder() override {}
-
-  ProxyType& proxy() { return proxy_; }
-
- private:
-  // MessageReceiverWithResponder implementation:
-  bool Accept(Message* message) override {
-    if (!message->associated_endpoint_handles()->empty()) {
-      // If this DCHECK fails, it is likely because:
-      // - This is a non-associated interface pointer setup using
-      //     PtrWrapper::BindOnTaskRunner(
-      //         InterfacePtrInfo<InterfaceType> ptr_info);
-      //   Please see the TODO in that method.
-      // - This is an associated interface which hasn't been associated with a
-      //   message pipe. In other words, the corresponding
-      //   AssociatedInterfaceRequest hasn't been sent.
-      DCHECK(associated_group_.GetController());
-      message->SerializeAssociatedEndpointHandles(
-          associated_group_.GetController());
-    }
-    task_runner_->PostTask(FROM_HERE,
-                           base::Bind(forward_, base::Passed(message)));
-    return true;
-  }
-
-  bool AcceptWithResponder(Message* message,
-                           MessageReceiver* response_receiver) override {
-    if (!message->associated_endpoint_handles()->empty()) {
-      // Please see comment for the DCHECK in the previous method.
-      DCHECK(associated_group_.GetController());
-      message->SerializeAssociatedEndpointHandles(
-          associated_group_.GetController());
-    }
-    auto responder = base::MakeUnique<ForwardToCallingThread>(
-        base::WrapUnique(response_receiver));
-    task_runner_->PostTask(
-        FROM_HERE, base::Bind(forward_with_responder_, base::Passed(message),
-                              base::Passed(&responder)));
-    return true;
-  }
-
-  class ForwardToCallingThread : public MessageReceiver {
-   public:
-    explicit ForwardToCallingThread(std::unique_ptr<MessageReceiver> responder)
-        : responder_(std::move(responder)),
-          caller_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
-    }
-
-   private:
-    bool Accept(Message* message) {
-      // The current instance will be deleted when this method returns, so we
-      // have to relinquish the responder's ownership so it does not get
-      // deleted.
-      caller_task_runner_->PostTask(FROM_HERE,
-          base::Bind(&ForwardToCallingThread::CallAcceptAndDeleteResponder,
-                     base::Passed(std::move(responder_)),
-                     base::Passed(std::move(*message))));
-      return true;
-    }
-
-    static void CallAcceptAndDeleteResponder(
-        std::unique_ptr<MessageReceiver> responder,
-        Message message) {
-      ignore_result(responder->Accept(&message));
-    }
-
-    std::unique_ptr<MessageReceiver> responder_;
-    scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
-  };
-
-  ProxyType proxy_;
-  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  const ForwardMessageCallback forward_;
-  const ForwardMessageWithResponderCallback forward_with_responder_;
-  AssociatedGroup associated_group_;
-
-  DISALLOW_COPY_AND_ASSIGN(ThreadSafeForwarder);
-};
-
-template <typename InterfacePtrType>
-class ThreadSafeInterfacePtrBase
-    : public base::RefCountedThreadSafe<
-          ThreadSafeInterfacePtrBase<InterfacePtrType>> {
- public:
-  using InterfaceType = typename InterfacePtrType::InterfaceType;
-  using PtrInfoType = typename InterfacePtrType::PtrInfoType;
-
-  explicit ThreadSafeInterfacePtrBase(
-      std::unique_ptr<ThreadSafeForwarder<InterfaceType>> forwarder)
-      : forwarder_(std::move(forwarder)) {}
-
-  // Creates a ThreadSafeInterfacePtrBase wrapping an underlying non-thread-safe
-  // InterfacePtrType which is bound to the calling thread. All messages sent
-  // via this thread-safe proxy will internally be sent by first posting to this
-  // (the calling) thread's TaskRunner.
-  static scoped_refptr<ThreadSafeInterfacePtrBase> Create(
-      InterfacePtrType interface_ptr) {
-    scoped_refptr<PtrWrapper> wrapper =
-        new PtrWrapper(std::move(interface_ptr));
-    return new ThreadSafeInterfacePtrBase(wrapper->CreateForwarder());
-  }
-
-  // Creates a ThreadSafeInterfacePtrBase which binds the underlying
-  // non-thread-safe InterfacePtrType on the specified TaskRunner. All messages
-  // sent via this thread-safe proxy will internally be sent by first posting to
-  // that TaskRunner.
-  static scoped_refptr<ThreadSafeInterfacePtrBase> Create(
-      PtrInfoType ptr_info,
-      const scoped_refptr<base::SingleThreadTaskRunner>& bind_task_runner) {
-    scoped_refptr<PtrWrapper> wrapper = new PtrWrapper(bind_task_runner);
-    wrapper->BindOnTaskRunner(std::move(ptr_info));
-    return new ThreadSafeInterfacePtrBase(wrapper->CreateForwarder());
-  }
-
-  InterfaceType* get() { return &forwarder_->proxy(); }
-  InterfaceType* operator->() { return get(); }
-  InterfaceType& operator*() { return *get(); }
-
- private:
-  friend class base::RefCountedThreadSafe<
-      ThreadSafeInterfacePtrBase<InterfacePtrType>>;
-
-  struct PtrWrapperDeleter;
-
-  // Helper class which owns an |InterfacePtrType| instance on an appropriate
-  // thread. This is kept alive as long its bound within some
-  // ThreadSafeForwarder's callbacks.
-  class PtrWrapper
-      : public base::RefCountedThreadSafe<PtrWrapper, PtrWrapperDeleter> {
-   public:
-    explicit PtrWrapper(InterfacePtrType ptr)
-        : PtrWrapper(base::ThreadTaskRunnerHandle::Get()) {
-      ptr_ = std::move(ptr);
-      associated_group_ = *ptr_.internal_state()->associated_group();
-    }
-
-    explicit PtrWrapper(
-        const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
-        : task_runner_(task_runner) {}
-
-    void BindOnTaskRunner(AssociatedInterfacePtrInfo<InterfaceType> ptr_info) {
-      associated_group_ = AssociatedGroup(ptr_info.handle());
-      task_runner_->PostTask(FROM_HERE, base::Bind(&PtrWrapper::Bind, this,
-                                                   base::Passed(&ptr_info)));
-    }
-
-    void BindOnTaskRunner(InterfacePtrInfo<InterfaceType> ptr_info) {
-      // TODO(yzhsen): At the momment we don't have a group controller
-      // available. That means the user won't be able to pass associated
-      // endpoints on this interface (at least not immediately). In order to fix
-      // this, we need to create a MultiplexRouter immediately and bind it to
-      // the interface pointer on the |task_runner_|. Therefore, MultiplexRouter
-      // should be able to be created on a thread different than the one that it
-      // is supposed to listen on. crbug.com/682334
-      task_runner_->PostTask(FROM_HERE, base::Bind(&PtrWrapper::Bind, this,
-                                                   base::Passed(&ptr_info)));
-    }
-
-    std::unique_ptr<ThreadSafeForwarder<InterfaceType>> CreateForwarder() {
-      return base::MakeUnique<ThreadSafeForwarder<InterfaceType>>(
-          task_runner_, base::Bind(&PtrWrapper::Accept, this),
-          base::Bind(&PtrWrapper::AcceptWithResponder, this),
-          associated_group_);
-    }
-
-   private:
-    friend struct PtrWrapperDeleter;
-
-    ~PtrWrapper() {}
-
-    void Bind(PtrInfoType ptr_info) {
-      DCHECK(task_runner_->RunsTasksOnCurrentThread());
-      ptr_.Bind(std::move(ptr_info));
-    }
-
-    void Accept(Message message) {
-      ptr_.internal_state()->ForwardMessage(std::move(message));
-    }
-
-    void AcceptWithResponder(Message message,
-                             std::unique_ptr<MessageReceiver> responder) {
-      ptr_.internal_state()->ForwardMessageWithResponder(std::move(message),
-                                                         std::move(responder));
-    }
-
-    void DeleteOnCorrectThread() const {
-      if (!task_runner_->RunsTasksOnCurrentThread()) {
-        // NOTE: This is only called when there are no more references to
-        // |this|, so binding it unretained is both safe and necessary.
-        task_runner_->PostTask(FROM_HERE,
-                               base::Bind(&PtrWrapper::DeleteOnCorrectThread,
-                                          base::Unretained(this)));
-      } else {
-        delete this;
-      }
-    }
-
-    InterfacePtrType ptr_;
-    const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-    AssociatedGroup associated_group_;
-
-    DISALLOW_COPY_AND_ASSIGN(PtrWrapper);
-  };
-
-  struct PtrWrapperDeleter {
-    static void Destruct(const PtrWrapper* interface_ptr) {
-      interface_ptr->DeleteOnCorrectThread();
-    }
-  };
-
-  ~ThreadSafeInterfacePtrBase() {}
-
-  const std::unique_ptr<ThreadSafeForwarder<InterfaceType>> forwarder_;
-
-  DISALLOW_COPY_AND_ASSIGN(ThreadSafeInterfacePtrBase);
-};
-
-template <typename Interface>
-using ThreadSafeAssociatedInterfacePtr =
-    ThreadSafeInterfacePtrBase<AssociatedInterfacePtr<Interface>>;
-
-template <typename Interface>
-using ThreadSafeInterfacePtr =
-    ThreadSafeInterfacePtrBase<InterfacePtr<Interface>>;
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/bindings/type_converter.h b/mojo/public/cpp/bindings/type_converter.h
index 395eeb4..1446ab3 100644
--- a/mojo/public/cpp/bindings/type_converter.h
+++ b/mojo/public/cpp/bindings/type_converter.h
@@ -7,15 +7,8 @@
 
 #include <stdint.h>
 
-#include <vector>
-
 namespace mojo {
 
-// NOTE: TypeConverter is deprecated. Please consider StructTraits /
-// UnionTraits / EnumTraits / ArrayTraits / MapTraits / StringTraits if you
-// would like to convert between custom types and the wire format of mojom
-// types.
-//
 // Specialize the following class:
 //   template <typename T, typename U> struct TypeConverter;
 // to perform type conversion for Mojom-defined structs and arrays. Here, T is
@@ -81,9 +74,6 @@
 template <typename T, typename U>
 struct TypeConverter;
 
-template <typename T, typename U>
-inline T ConvertTo(const U& obj);
-
 // The following specialization is useful when you are converting between
 // Array<POD> and std::vector<POD>.
 template <typename T>
@@ -91,18 +81,6 @@
   static T Convert(const T& obj) { return obj; }
 };
 
-template <typename T, typename Container>
-struct TypeConverter<std::vector<T>, Container> {
-  static std::vector<T> Convert(const Container& container) {
-    std::vector<T> output;
-    output.reserve(container.size());
-    for (const auto& obj : container) {
-      output.push_back(ConvertTo<T>(obj));
-    }
-    return output;
-  }
-};
-
 // The following helper function is useful for shorthand. The compiler can infer
 // the input type, so you can write:
 //   OutputType out = ConvertTo<OutputType>(input);
diff --git a/mojo/public/cpp/bindings/union_traits.h b/mojo/public/cpp/bindings/union_traits.h
deleted file mode 100644
index 292ee58..0000000
--- a/mojo/public/cpp/bindings/union_traits.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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_UNION_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_UNION_TRAITS_H_
-
-namespace mojo {
-
-// This must be specialized for any type |T| to be serialized/deserialized as
-// a mojom union. |DataViewType| is the corresponding data view type of the
-// mojom union. For example, if the mojom union is example.Foo, |DataViewType|
-// will be example::FooDataView, which can also be referred to by
-// example::Foo::DataView (in chromium) and example::blink::Foo::DataView (in
-// blink).
-//
-// Similar to StructTraits, each specialization of UnionTraits implements the
-// following methods:
-//   1. Getters for each field in the Mojom type.
-//   2. Read() method.
-//   3. [Optional] IsNull() and SetToNull().
-//   4. [Optional] SetUpContext() and TearDownContext().
-// Please see the documentation of StructTraits for details of these methods.
-//
-// Unlike StructTraits, there is one more method to implement:
-//   5. A static GetTag() method indicating which field is the current active
-//      field for serialization:
-//
-//        static DataViewType::Tag GetTag(const T& input);
-//
-//      During serialization, only the field getter corresponding to this tag
-//      will be called.
-//
-template <typename DataViewType, typename T>
-struct UnionTraits;
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_UNION_TRAITS_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
index 0dc7af9..8dcec71 100644
--- a/mojo/public/cpp/system/BUILD.gn
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -2,26 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# Deletes libsystem.dylib from the build dir, since it shadows
-# /usr/lib/libSystem.dylib on macOS.
-# TODO(thakis): Remove this after a while.
-action("clean_up_old_dylib") {
-  script = "//build/rm.py"
-  stamp = "$target_gen_dir/clean_up_stamp"
-  outputs = [
-    stamp,
-  ]
-  args = [
-    "--stamp",
-    rebase_path(stamp, root_build_dir),
-    "-f",
-    "libsystem.dylib",
-  ]
-}
-
-component("system") {
-  output_name = "mojo_public_system_cpp"
-
+source_set("system") {
   sources = [
     "buffer.cc",
     "buffer.h",
@@ -33,7 +14,6 @@
     "message_pipe.h",
     "platform_handle.cc",
     "platform_handle.h",
-    "system_export.h",
     "watcher.cc",
     "watcher.h",
   ]
@@ -42,9 +22,4 @@
     "//base",
     "//mojo/public/c/system",
   ]
-  deps = [
-    ":clean_up_old_dylib",
-  ]
-
-  defines = [ "MOJO_CPP_SYSTEM_IMPLEMENTATION" ]
 }
diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h
index 1ae923c..449c6ce 100644
--- a/mojo/public/cpp/system/buffer.h
+++ b/mojo/public/cpp/system/buffer.h
@@ -20,7 +20,6 @@
 #include "base/logging.h"
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/cpp/system/handle.h"
-#include "mojo/public/cpp/system/system_export.h"
 
 namespace mojo {
 namespace internal {
@@ -42,8 +41,7 @@
 
 // A strongly-typed representation of a |MojoHandle| referring to a shared
 // buffer.
-class MOJO_CPP_SYSTEM_EXPORT SharedBufferHandle
-    : NON_EXPORTED_BASE(public Handle) {
+class SharedBufferHandle : public Handle {
  public:
   enum class AccessMode {
     READ_WRITE,
diff --git a/mojo/public/cpp/system/platform_handle.cc b/mojo/public/cpp/system/platform_handle.cc
index 42e4aba..e4a6088 100644
--- a/mojo/public/cpp/system/platform_handle.cc
+++ b/mojo/public/cpp/system/platform_handle.cc
@@ -4,11 +4,6 @@
 
 #include "mojo/public/cpp/system/platform_handle.h"
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-#include <mach/mach.h>
-#include "base/mac/mach_logging.h"
-#endif
-
 namespace mojo {
 
 namespace {
@@ -66,13 +61,6 @@
     const base::SharedMemoryHandle& memory_handle,
     size_t size,
     bool read_only) {
-#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
-  if (memory_handle.fd == base::kInvalidPlatformFile)
-    return ScopedSharedBufferHandle();
-#else
-  if (!memory_handle.IsValid())
-    return ScopedSharedBufferHandle();
-#endif
   MojoPlatformHandle platform_handle;
   platform_handle.struct_size = sizeof(MojoPlatformHandle);
   platform_handle.type = kPlatformSharedBufferHandleType;
@@ -103,8 +91,6 @@
                                     base::SharedMemoryHandle* memory_handle,
                                     size_t* size,
                                     bool* read_only) {
-  if (!handle.is_valid())
-    return MOJO_RESULT_INVALID_ARGUMENT;
   MojoPlatformHandle platform_handle;
   platform_handle.struct_size = sizeof(MojoPlatformHandle);
 
@@ -140,39 +126,4 @@
   return MOJO_RESULT_OK;
 }
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-ScopedHandle WrapMachPort(mach_port_t port) {
-  kern_return_t kr =
-      mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1);
-  MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
-      << "MachPortAttachmentMac mach_port_mod_refs";
-  if (kr != KERN_SUCCESS)
-    return ScopedHandle();
-
-  MojoPlatformHandle platform_handle;
-  platform_handle.struct_size = sizeof(MojoPlatformHandle);
-  platform_handle.type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
-  platform_handle.value = static_cast<uint64_t>(port);
-
-  MojoHandle mojo_handle;
-  MojoResult result = MojoWrapPlatformHandle(&platform_handle, &mojo_handle);
-  CHECK_EQ(result, MOJO_RESULT_OK);
-
-  return ScopedHandle(Handle(mojo_handle));
-}
-
-MojoResult UnwrapMachPort(ScopedHandle handle, mach_port_t* port) {
-  MojoPlatformHandle platform_handle;
-  platform_handle.struct_size = sizeof(MojoPlatformHandle);
-  MojoResult result =
-      MojoUnwrapPlatformHandle(handle.release().value(), &platform_handle);
-  if (result != MOJO_RESULT_OK)
-    return result;
-
-  CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT);
-  *port = static_cast<mach_port_t>(platform_handle.value);
-  return MOJO_RESULT_OK;
-}
-#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
-
 }  // namespace mojo
diff --git a/mojo/public/cpp/system/platform_handle.h b/mojo/public/cpp/system/platform_handle.h
index 801264e..2a81734 100644
--- a/mojo/public/cpp/system/platform_handle.h
+++ b/mojo/public/cpp/system/platform_handle.h
@@ -22,7 +22,6 @@
 #include "mojo/public/c/system/platform_handle.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/handle.h"
-#include "mojo/public/cpp/system/system_export.h"
 
 #if defined(OS_WIN)
 #include <windows.h>
@@ -51,18 +50,15 @@
 #endif  // defined(OS_POSIX)
 
 // Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object.
-MOJO_CPP_SYSTEM_EXPORT
 ScopedHandle WrapPlatformFile(base::PlatformFile platform_file);
 
 // Unwraps a PlatformFile from a Mojo handle.
-MOJO_CPP_SYSTEM_EXPORT
 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.
-MOJO_CPP_SYSTEM_EXPORT
 ScopedSharedBufferHandle WrapSharedMemoryHandle(
     const base::SharedMemoryHandle& memory_handle,
     size_t size,
@@ -70,22 +66,10 @@
 
 // Unwraps a base::SharedMemoryHandle from a Mojo handle. The caller assumes
 // responsibility for the lifetime of the SharedMemoryHandle.
-MOJO_CPP_SYSTEM_EXPORT MojoResult
-UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,
-                         base::SharedMemoryHandle* memory_handle,
-                         size_t* size,
-                         bool* read_only);
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-// Wraps a mach_port_t as a Mojo handle. This takes a reference to the
-// Mach port.
-MOJO_CPP_SYSTEM_EXPORT ScopedHandle WrapMachPort(mach_port_t port);
-
-// Unwraps a mach_port_t from a Mojo handle. The caller gets ownership of the
-// Mach port.
-MOJO_CPP_SYSTEM_EXPORT MojoResult UnwrapMachPort(ScopedHandle handle,
-                                                 mach_port_t* port);
-#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
+MojoResult UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,
+                                    base::SharedMemoryHandle* memory_handle,
+                                    size_t* size,
+                                    bool* read_only);
 
 }  // namespace mojo
 
diff --git a/mojo/public/cpp/system/system_export.h b/mojo/public/cpp/system/system_export.h
deleted file mode 100644
index c9bb140..0000000
--- a/mojo/public/cpp/system/system_export.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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_SYSTEM_EXPORT_H_
-#define MOJO_PUBLIC_CPP_SYSTEM_SYSTEM_EXPORT_H_
-
-#if defined(COMPONENT_BUILD)
-
-#if defined(WIN32)
-
-#if defined(MOJO_CPP_SYSTEM_IMPLEMENTATION)
-#define MOJO_CPP_SYSTEM_EXPORT __declspec(dllexport)
-#else
-#define MOJO_CPP_SYSTEM_EXPORT __declspec(dllimport)
-#endif
-
-#else  // !defined(WIN32)
-
-#if defined(MOJO_CPP_SYSTEM_IMPLEMENTATION)
-#define MOJO_CPP_SYSTEM_EXPORT __attribute((visibility("default")))
-#else
-#define MOJO_CPP_SYSTEM_EXPORT
-#endif
-
-#endif  // defined(WIN32)
-
-#else  // !defined(COMPONENT_BUILD)
-
-#define MOJO_CPP_SYSTEM_EXPORT
-
-#endif  // defined(COMPONENT_BUILD)
-
-#endif  // MOJO_PUBLIC_CPP_SYSTEM_SYSTEM_EXPORT_H_
diff --git a/mojo/public/cpp/system/watcher.cc b/mojo/public/cpp/system/watcher.cc
index 55dcf40..d9319fb 100644
--- a/mojo/public/cpp/system/watcher.cc
+++ b/mojo/public/cpp/system/watcher.cc
@@ -7,17 +7,49 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/macros.h"
-#include "base/trace_event/heap_profiler.h"
+#include "base/message_loop/message_loop.h"
 #include "mojo/public/c/system/functions.h"
 
 namespace mojo {
 
-Watcher::Watcher(const tracked_objects::Location& from_here,
-                 scoped_refptr<base::SingleThreadTaskRunner> runner)
+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()),
-      heap_profiler_tag_(from_here.file_name()),
       weak_factory_(this) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   weak_self_ = weak_factory_.GetWeakPtr();
@@ -40,6 +72,7 @@
   DCHECK(!IsWatching());
   DCHECK(!callback.is_null());
 
+  message_loop_observer_.reset(new MessageLoopObserver(this));
   callback_ = callback;
   handle_ = handle;
   MojoResult result = MojoWatch(handle_.value(), signals,
@@ -48,6 +81,7 @@
   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;
@@ -65,6 +99,7 @@
 
   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);
@@ -77,15 +112,14 @@
 
   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()) {
-    TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_);
+  if (!callback.is_null())
     callback.Run(result);
-  }
 }
 
 // static
@@ -99,7 +133,6 @@
   // 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_) {
diff --git a/mojo/public/cpp/system/watcher.h b/mojo/public/cpp/system/watcher.h
index 236788b..82f3e81 100644
--- a/mojo/public/cpp/system/watcher.h
+++ b/mojo/public/cpp/system/watcher.h
@@ -5,6 +5,8 @@
 #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"
@@ -14,14 +16,13 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "mojo/public/c/system/types.h"
 #include "mojo/public/cpp/system/handle.h"
-#include "mojo/public/cpp/system/system_export.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 MOJO_CPP_SYSTEM_EXPORT Watcher {
+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
@@ -34,11 +35,15 @@
   //
   //   |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)>;
 
-  Watcher(const tracked_objects::Location& from_here,
-          scoped_refptr<base::SingleThreadTaskRunner> runner =
-              base::ThreadTaskRunnerHandle::Get());
+  explicit Watcher(scoped_refptr<base::SingleThreadTaskRunner> runner =
+                       base::ThreadTaskRunnerHandle::Get());
 
   // NOTE: This destructor automatically calls |Cancel()| if the Watcher is
   // still active.
@@ -75,13 +80,10 @@
   Handle handle() const { return handle_; }
   ReadyCallback ready_callback() const { return callback_; }
 
- // Sets the tag used by the heap profiler.
- // |tag| must be a const string literal.
- void set_heap_profiler_tag(const char* heap_profiler_tag) {
-   heap_profiler_tag_ = heap_profiler_tag;
- }
-
  private:
+  class MessageLoopObserver;
+  friend class MessageLoopObserver;
+
   void OnHandleReady(MojoResult result);
 
   static void CallOnHandleReady(uintptr_t context,
@@ -98,6 +100,8 @@
   // 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.
@@ -111,10 +115,6 @@
   // The callback to call when the handle is signaled.
   ReadyCallback callback_;
 
-  // Tag used to ID memory allocations that originated from notifications in
-  // this watcher.
-  const char* heap_profiler_tag_ = nullptr;
-
   base::WeakPtrFactory<Watcher> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Watcher);
diff --git a/mojo/public/cpp/test_support/BUILD.gn b/mojo/public/cpp/test_support/BUILD.gn
index efa1712..3d33b4a 100644
--- a/mojo/public/cpp/test_support/BUILD.gn
+++ b/mojo/public/cpp/test_support/BUILD.gn
@@ -2,6 +2,7 @@
 # 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
 
diff --git a/mojo/public/interfaces/BUILD.gn b/mojo/public/interfaces/BUILD.gn
index fb11ec2..654145b 100644
--- a/mojo/public/interfaces/BUILD.gn
+++ b/mojo/public/interfaces/BUILD.gn
@@ -4,6 +4,8 @@
 
 group("interfaces") {
   deps = [
+    "application",
     "bindings",
+    "network",
   ]
 }
diff --git a/mojo/public/interfaces/bindings/BUILD.gn b/mojo/public/interfaces/bindings/BUILD.gn
index eab75c1..c7421b9 100644
--- a/mojo/public/interfaces/bindings/BUILD.gn
+++ b/mojo/public/interfaces/bindings/BUILD.gn
@@ -5,13 +5,8 @@
 import("../../tools/bindings/mojom.gni")
 
 mojom("bindings") {
-  visibility = []
   sources = [
     "interface_control_messages.mojom",
     "pipe_control_messages.mojom",
   ]
-
-  export_class_attribute = "MOJO_CPP_BINDINGS_EXPORT"
-  export_define = "MOJO_CPP_BINDINGS_IMPLEMENTATION"
-  export_header = "mojo/public/cpp/bindings/bindings_export.h"
 }
diff --git a/mojo/public/interfaces/bindings/interface_control_messages.mojom b/mojo/public/interfaces/bindings/interface_control_messages.mojom
index 0a19042..2143c06 100644
--- a/mojo/public/interfaces/bindings/interface_control_messages.mojom
+++ b/mojo/public/interfaces/bindings/interface_control_messages.mojom
@@ -2,11 +2,12 @@
 // 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.interfacecontrol"]
-module mojo.interface_control;
+[JavaPackage="org.chromium.mojo.bindings"]
+module mojo;
 
-// For each user-defined interface, some control functions are provided by the
-// interface endpoints at both sides.
+// 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);
@@ -14,54 +15,75 @@
 // 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 {
-  RunInput input;
-};
-union RunInput {
+  // 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;
-  FlushForTesting flush_for_testing;
 };
 
 struct RunResponseMessageParams {
-  RunOutput? output;
-};
-union RunOutput {
+  // 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.
-// Sent by the interface client side.
 struct QueryVersion {
 };
 struct QueryVersionResult {
   uint32 version;
 };
 
-// Sent by either side of the interface.
-struct FlushForTesting {
-};
-
 ////////////////////////////////////////////////////////////////////////////////
 // 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 {
-  RunOrClosePipeInput input;
-};
-union RunOrClosePipeInput {
+  // 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.
-// Sent by the interface client side.
 struct RequireVersion {
   uint32 version;
 };
diff --git a/mojo/public/interfaces/bindings/pipe_control_messages.mojom b/mojo/public/interfaces/bindings/pipe_control_messages.mojom
index c1453fc..c743ffe 100644
--- a/mojo/public/interfaces/bindings/pipe_control_messages.mojom
+++ b/mojo/public/interfaces/bindings/pipe_control_messages.mojom
@@ -25,21 +25,26 @@
 
 union RunOrClosePipeInput {
   PeerAssociatedEndpointClosedEvent peer_associated_endpoint_closed_event;
-};
-
-// A user-defined reason about why the interface is disconnected.
-struct DisconnectReason {
-  uint32 custom_reason;
-  string description;
+  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 omitted if the endpoint belongs to the master interface and
-// there is no disconnect reason specified.
+// 
+// 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;
-  DisconnectReason? disconnect_reason;
 };
+
+// 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
index 9b10db0..0a48076 100644
--- a/mojo/public/interfaces/bindings/tests/BUILD.gn
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -17,9 +17,7 @@
     "sample_service.mojom",
     "scoping.mojom",
     "serialization_test_structs.mojom",
-    "test_bad_messages.mojom",
     "test_constants.mojom",
-    "test_data_view.mojom",
     "test_native_types.mojom",
     "test_structs.mojom",
     "test_sync_methods.mojom",
@@ -29,59 +27,8 @@
     ":test_mojom_import",
     ":test_mojom_import2",
   ]
-}
 
-component("test_export_component") {
-  testonly = true
-  deps = [
-    ":test_export",
-  ]
-}
-
-if (!is_ios) {
-  component("test_export_blink_component") {
-    testonly = true
-    deps = [
-      ":test_export_blink",
-    ]
-  }
-}
-
-mojom("test_export") {
-  testonly = true
-  sources = [
-    "test_export.mojom",
-  ]
-  export_class_attribute = "MOJO_TEST_EXPORT"
-  export_define = "MOJO_TEST_IMPLEMENTATION=1"
-  export_header = "mojo/public/cpp/bindings/tests/mojo_test_export.h"
-  if (!is_ios) {
-    export_class_attribute_blink = "MOJO_TEST_BLINK_EXPORT"
-    export_define_blink = "MOJO_TEST_BLINK_IMPLEMENTATION=1"
-    export_header_blink =
-        "mojo/public/cpp/bindings/tests/mojo_test_blink_export.h"
-  }
-  visibility = [ ":test_export_component" ]
-  if (!is_ios) {
-    visibility_blink = [ ":test_export_blink_component" ]
-  }
-}
-
-mojom("test_exported_import") {
-  testonly = true
-  sources = [
-    "test_import.mojom",
-  ]
-  public_deps = [
-    ":test_export",
-  ]
-
-  overridden_deps = [ ":test_export" ]
-  component_deps = [ ":test_export_component" ]
-  if (!is_ios) {
-    overridden_deps_blink = [ ":test_export" ]
-    component_deps_blink = [ ":test_export_blink_component" ]
-  }
+  use_new_wrapper_types = true
 }
 
 mojom("test_mojom_import") {
@@ -89,6 +36,7 @@
   sources = [
     "sample_import.mojom",
   ]
+  use_new_wrapper_types = true
 }
 
 mojom("test_mojom_import_wrapper") {
@@ -114,6 +62,7 @@
     ":test_mojom_import",
     ":test_mojom_import_wrapper_wrapper",
   ]
+  use_new_wrapper_types = true
 }
 
 mojom("test_struct_traits_interfaces") {
@@ -121,6 +70,7 @@
   sources = [
     "struct_with_traits.mojom",
   ]
+  use_new_wrapper_types = true
 }
 
 mojom("test_interfaces_experimental") {
@@ -128,6 +78,7 @@
   sources = [
     "test_unions.mojom",
   ]
+  use_new_wrapper_types = true
 }
 
 mojom("test_associated_interfaces") {
@@ -138,10 +89,7 @@
     "test_associated_interfaces.mojom",
     "validation_test_associated_interfaces.mojom",
   ]
-
-  public_deps = [
-    ":test_interfaces",
-  ]
+  use_new_wrapper_types = true
 }
 
 mojom("versioning_test_service_interfaces") {
@@ -149,6 +97,7 @@
   sources = [
     "versioning_test_service.mojom",
   ]
+  use_new_wrapper_types = true
 }
 
 mojom("versioning_test_client_interfaces") {
@@ -156,6 +105,7 @@
   sources = [
     "versioning_test_client.mojom",
   ]
+  use_new_wrapper_types = true
 }
 
 mojom("test_wtf_types") {
@@ -164,6 +114,7 @@
   sources = [
     "test_wtf_types.mojom",
   ]
+  use_new_wrapper_types = true
 }
 
 mojom("test_no_sources") {
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
index b797fea..efac7ed 100644
--- 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
@@ -1,26 +1,13 @@
 [dist4]message_header  // num_bytes
-[u4]2                  // version
+[u4]0                  // version
 [u4]0                  // interface ID
 [u4]0                  // name
 [u4]0                  // flags
 [u4]0                  // padding
-[u8]0                  // request_id
-[dist8]payload
-[dist8]payload_interface_ids
 [anchr]message_header
 
-[anchr]payload
 [dist4]method0_params  // num_bytes
 [u4]0                  // version
-[u4]1                  // associated interface pointer: interface ID index
+[u4]4                  // associated interface pointer: interface ID
 [u4]1                  // associated interface pointer: version
 [anchr]method0_params
-
-[anchr]payload_interface_ids
-[dist4]interface_id_array  // num_bytes
-[u4]3                      // num_elements : It is okay to have IDs that are not
-                           // referred to.
-[u4]4
-[u4]5
-[u4]8
-[anchr]interface_id_array
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
index b785ed1..dd16401 100644
--- 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
@@ -1,25 +1,14 @@
 [dist4]message_header  // num_bytes
-[u4]2                  // version
+[u4]0                  // version
 [u4]0                  // interface ID
 [u4]0                  // name
 [u4]0                  // flags
 [u4]0                  // padding
-[u8]0                  // request_id
-[dist8]payload
-[dist8]payload_interface_ids
 [anchr]message_header
 
-[anchr]payload
 [dist4]method0_params  // num_bytes
 [u4]0                  // version
-[u4]0xFFFFFFFF         // associated interface pointer: Unexpected invalid
-                       // interface ID index.
+[u4]0xFFFFFFFF         // associated interface pointer: unexpected invalid
+                       // interface ID
 [u4]1                  // associated interface pointer: version
 [anchr]method0_params
-
-[anchr]payload_interface_ids
-[dist4]interface_id_array  // num_bytes
-[u4]2                      // num_elements
-[u4]3
-[u4]4
-[anchr]interface_id_array
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
index efa2162..7ebbe07 100644
--- 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
@@ -1,26 +1,13 @@
 [dist4]message_header  // num_bytes
-[u4]2                  // version
+[u4]0                  // version
 [u4]0                  // interface ID
 [u4]1                  // name
 [u4]0                  // flags
 [u4]0                  // padding
-[u8]0                  // request_id
-[dist8]payload
-[dist8]payload_interface_ids
 [anchr]message_header
 
-[anchr]payload
 [dist4]method1_params  // num_bytes
 [u4]0                  // version
-[u4]1                  // associated interface request: interface ID index
+[u4]4                  // associated interface request
 [u4]0                  // padding
 [anchr]method1_params
-
-[anchr]payload_interface_ids
-[dist4]interface_id_array  // num_bytes
-[u4]3                      // num_elements : It is okay to have IDs that are not
-                           // referred to.
-[u4]4
-[u4]5
-[u4]8
-[anchr]interface_id_array
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
index 5a66aad..d74a306 100644
--- 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
@@ -1,27 +1,14 @@
 [dist4]message_header  // num_bytes
-[u4]2                  // version
+[u4]0                  // version
 [u4]0                  // interface ID
 [u4]1                  // name
 [u4]0                  // flags
 [u4]0                  // padding
-[u8]0                  // request_id
-[dist8]payload
-[dist8]payload_interface_ids
 [anchr]message_header
 
-[anchr]payload
 [dist4]method1_params  // num_bytes
 [u4]0                  // version
-[u4]0xFFFFFFFF         // associated interface request: Unexpected invalid
-                       // interface ID index.
+[u4]0xFFFFFFFF         // associated interface request: unexpected invalid
+                       // interface ID
 [u4]0                  // padding
 [anchr]method1_params
-
-[anchr]payload_interface_ids
-[dist4]interface_id_array  // num_bytes
-[u4]3                      // num_elements : It is okay to have IDs that are not
-                           // referred to.
-[u4]4
-[u4]5
-[u4]8
-[anchr]interface_id_array
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
index 13df01e..da22db1 100644
--- 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
@@ -1,15 +1,11 @@
 [dist4]message_header  // num_bytes
-[u4]2                  // version
+[u4]0                  // version
 [u4]0                  // interface ID
 [u4]3                  // name
 [u4]0                  // flags
 [u4]0                  // padding
-[u8]0                  // request_id
-[dist8]payload
-[dist8]payload_interface_ids
 [anchr]message_header
 
-[anchr]payload
 [dist4]method3_params  // num_bytes
 [u4]0                  // version
 [dist8]param0_ptr      // param0
@@ -18,18 +14,8 @@
 [anchr]param0_ptr
 [dist4]associated_interface_array  // num_bytes
 [u4]2                              // num_elements
-[u4]2                              // interface ID index
+[u4]4                              // interface ID
 [u4]14                             // version
-[u4]3                              // interface ID index
+[u4]5                              // interface ID
 [u4]18                             // version
 [anchr]associated_interface_array
-
-[anchr]payload_interface_ids
-[dist4]interface_id_array  // num_bytes
-[u4]4                      // num_elements : It is okay to have IDs that are not
-                           // referred to.
-[u4]4
-[u4]5
-[u4]8
-[u4]19
-[anchr]interface_id_array
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
index 2e163be..788cefa 100644
--- 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
@@ -1,15 +1,11 @@
 [dist4]message_header  // num_bytes
-[u4]2                  // version
+[u4]0                  // version
 [u4]0                  // interface ID
 [u4]3                  // name
 [u4]0                  // flags
 [u4]0                  // padding
-[u8]0                  // request_id
-[dist8]payload
-[dist8]payload_interface_ids
 [anchr]message_header
 
-[anchr]payload
 [dist4]method3_params  // num_bytes
 [u4]0                  // version
 [dist8]param0_ptr      // param0
@@ -18,19 +14,8 @@
 [anchr]param0_ptr
 [dist4]associated_interface_array  // num_bytes
 [u4]2                              // num_elements
-[u4]2                              // interface ID index
+[u4]0xFFFFFFFF                     // unexpected invalid interface ID
 [u4]14                             // version
-[u4]0xFFFFFFFF                     // interface ID index: Unexpected invalid
-                                   // value.
+[u4]5                              // interface ID
 [u4]18                             // version
 [anchr]associated_interface_array
-
-[anchr]payload_interface_ids
-[dist4]interface_id_array  // num_bytes
-[u4]4                      // num_elements : It is okay to have IDs that are not
-                           // referred to.
-[u4]4
-[u4]5
-[u4]8
-[u4]19
-[anchr]interface_id_array
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
index 0a46e0a..1cd3484 100644
--- 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
@@ -15,7 +15,7 @@
 [anchr]enum_array_0
 [dist4]enum_array_0_member  // num_bytes
 [u4]2                       // num_elements
-[u4]1
 [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/rect.mojom b/mojo/public/interfaces/bindings/tests/rect.mojom
index 4ecc4d9..833c76b 100644
--- a/mojo/public/interfaces/bindings/tests/rect.mojom
+++ b/mojo/public/interfaces/bindings/tests/rect.mojom
@@ -12,20 +12,11 @@
   int32 height;
 };
 
-// A copy of Rect that is typemapped differently in the chromium and blink
-// variants.
+// 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;
 };
-
-// A copy of Rect that is typemapped to the same custom type in the chromium and
-// blink variants.
-struct SharedTypemappedRect {
-  int32 x;
-  int32 y;
-  int32 width;
-  int32 height;
-};
\ No newline at end of file
diff --git a/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom b/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom
index b50409e..b1b7437 100644
--- a/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom
+++ b/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom
@@ -23,7 +23,6 @@
   string f_string;
   string f_string2;
   array<string> f_string_array;
-  array<string> f_string_set;
   NestedStructWithTraits f_struct;
   array<NestedStructWithTraits> f_struct_array;
   map<string, NestedStructWithTraits> f_struct_map;
@@ -34,50 +33,28 @@
   StructWithTraits f_struct;
 };
 
-// Maps to a pass-by-value trivial struct.
-struct TrivialStructWithTraits {
-  int32 value;
-};
-
-// Maps to a move-only struct.
-struct MoveOnlyStructWithTraits {
+struct PassByValueStructWithTraits {
   handle f_handle;
 };
 
-// The custom type for MoveOnlyStructWithTraits is not clonable. Test that
+// The custom type for PassByValueStructWithTraits is not clonable. Test that
 // this container can compile as long as Clone() is not used.
-struct MoveOnlyStructWithTraitsContainer {
-  MoveOnlyStructWithTraits f_struct;
+struct PassByValueStructWithTraitsContainer {
+  PassByValueStructWithTraits f_struct;
 };
 
-struct StructWithTraitsForUniquePtr {
+struct StructWithTraitsForUniquePtrTest {
   int32 f_int32;
 };
 
-union UnionWithTraits {
-  int32 f_int32;
-  NestedStructWithTraits f_struct;
-};
-
 interface TraitsTestService {
   EchoStructWithTraits(StructWithTraits s) => (StructWithTraits passed);
 
-  EchoTrivialStructWithTraits(TrivialStructWithTraits s) =>
-      (TrivialStructWithTraits passed);
-
-  EchoMoveOnlyStructWithTraits(MoveOnlyStructWithTraits s) =>
-      (MoveOnlyStructWithTraits passed);
-
-  EchoNullableMoveOnlyStructWithTraits(MoveOnlyStructWithTraits? s) =>
-      (MoveOnlyStructWithTraits? passed);
+  EchoPassByValueStructWithTraits(PassByValueStructWithTraits s) =>
+      (PassByValueStructWithTraits passed);
 
   EchoEnumWithTraits(EnumWithTraits e) => (EnumWithTraits passed);
 
-  EchoStructWithTraitsForUniquePtr(StructWithTraitsForUniquePtr e) => (
-      StructWithTraitsForUniquePtr passed);
-
-  EchoNullableStructWithTraitsForUniquePtr(StructWithTraitsForUniquePtr? e) => (
-      StructWithTraitsForUniquePtr? passed);
-
-  EchoUnionWithTraits(UnionWithTraits u) => (UnionWithTraits 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
index adc4e7e..534cfd8 100644
--- a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom
@@ -4,8 +4,6 @@
 
 module mojo.test;
 
-import "mojo/public/interfaces/bindings/tests/ping_service.mojom";
-
 interface FooInterface {};
 
 struct StructContainsAssociated {
@@ -44,11 +42,3 @@
   GetSender(associated IntegerSender& sender);
   AsyncGetSender() => (associated IntegerSender sender);
 };
-
-interface AssociatedPingProvider {
-  GetPing(associated PingService& request);
-};
-
-interface AssociatedPingProviderProvider {
-  GetPingProvider(associated AssociatedPingProvider& request);
-};
diff --git a/mojo/public/interfaces/bindings/tests/test_constants.mojom b/mojo/public/interfaces/bindings/tests/test_constants.mojom
index 272e603..462d512 100644
--- a/mojo/public/interfaces/bindings/tests/test_constants.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_constants.mojom
@@ -42,16 +42,12 @@
 const float kFloatNegativeInfinity = float.NEGATIVE_INFINITY;
 const float kFloatNaN = float.NAN;
 
-const string kStringValue = "test string contents";
-
 struct StructWithConstants {
   const int8 kInt8Value = 5;
   const float kFloatValue = 765.432;
-  const string kStringValue = "struct test string contents";
 };
 
 interface InterfaceWithConstants {
   const uint32 kUint32Value = 20100722;
   const double kDoubleValue = 12.34567;
-  const string kStringValue = "interface test string contents";
 };
diff --git a/mojo/public/interfaces/bindings/tests/test_native_types.mojom b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
index 3df4318..46c6f69 100644
--- a/mojo/public/interfaces/bindings/tests/test_native_types.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
@@ -34,5 +34,4 @@
 interface RectService {
   AddRect(TypemappedRect r);
   GetLargestRect() => (TypemappedRect largest);
-  PassSharedRect(SharedTypemappedRect r) => (SharedTypemappedRect passed);
 };
diff --git a/mojo/public/interfaces/bindings/tests/test_structs.mojom b/mojo/public/interfaces/bindings/tests/test_structs.mojom
index 03a0a20..2709d49 100644
--- a/mojo/public/interfaces/bindings/tests/test_structs.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_structs.mojom
@@ -126,8 +126,6 @@
   map<float, float> f9;
   map<double, double> f10;
   map<string, string> f11;
-  // TODO(tibell): JS/Java don't support struct as key.
-  // map<Rect, Rect> f12;
 };
 
 // Used to verify that various map value types can be encoded and decoded
@@ -360,16 +358,6 @@
   bool f_bool;
 };
 
-// A struct where the fields are not sorted by their ordinals.
-struct ReorderedStruct {
-  [MinVersion=2]
-  int32 a@3 = 3;
-  [MinVersion=4]
-  int32 b@6 = 6;
-  [MinVersion=1]
-  int32 c@1 = 1;
-};
-
 // Used to verify that interfaces that are struct members can be defined in the
 // same file.
 
@@ -392,23 +380,3 @@
 struct ContainsInterfaceRequest {
   SomeInterface& request;
 };
-
-// Used to verify that boolean fields are correctly serialized/deserialized.
-
-struct SingleBoolStruct {
-  bool value;
-};
-
-// Used to verify that structs containing typemapped types can be hashed (if the
-// typemapped type itself is hashable).
-
-struct ContainsHashable {
-  TypemappedRect rect;
-};
-
-// Used to test that nested structs can be hashed. The nested struct mustn't be
-// nullable.
-
-struct SimpleNestedStruct {
-  ContainsOther nested;
-};
diff --git a/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom
index 183f184..2fdbea8 100644
--- a/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom
@@ -25,26 +25,13 @@
 };
 
 struct TestWTFStruct {
-  enum NestedEnum {
-    E0,
-    E1,
-  };
   string str;
   int32 integer;
 };
 
 interface TestWTF {
-  enum NestedEnum {
-    E0,
-    E1,
-  };
   EchoString(string? str) => (string? str);
   EchoStringArray(array<string?>? arr) => (array<string?>? arr);
   EchoStringMap(map<string, string?>? str_map)
       => (map<string, string?>? str_map);
 };
-
-enum TopLevelEnum {
-  E0,
-  E1,
-};
diff --git a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
index ab69045..c46c0a5 100644
--- a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
+++ b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
@@ -56,18 +56,6 @@
   ENUM_B_2
 };
 
-// A non-extensible enum with no values is valid, but about as useless as
-// you would expect: it will fail validation for all values.
-enum EmptyEnum {};
-
-[Extensible]
-enum ExtensibleEmptyEnum {};
-
-union UnionA {
-  StructA struct_a;
-  bool b;
-};
-
 // 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
@@ -96,11 +84,6 @@
   Method15(array<EnumA>? param0, array<EnumB>? param1);
   Method16(map<EnumA, EnumA>? param0);
   Method17(array<InterfaceA> param0);
-  Method18(UnionA? param0);
-  Method19(Recursive recursive);
-  Method20(map<StructB, uint8> param0);
-  Method21(ExtensibleEmptyEnum param0);
-  Method22(EmptyEnum param0);
 };
 
 struct BasicStruct {
@@ -128,8 +111,3 @@
     A, B, C, D
   };
 };
-
-// This is used to test that deeply recursive structures don't blow the stack.
-struct Recursive {
-  Recursive? recursive;
-};
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
index 0780641..e33faaa 100644
--- a/mojo/public/java/BUILD.gn
+++ b/mojo/public/java/BUILD.gn
@@ -4,8 +4,9 @@
 
 import("//build/config/android/rules.gni")
 
-android_library("system_java") {
+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",
@@ -19,11 +20,10 @@
     "system/src/org/chromium/mojo/system/SharedBufferHandle.java",
     "system/src/org/chromium/mojo/system/UntypedHandle.java",
     "system/src/org/chromium/mojo/system/RunLoop.java",
-    "system/src/org/chromium/mojo/system/Watcher.java",
   ]
 }
 
-android_library("bindings_java") {
+android_library("bindings") {
   java_files = [
     "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java",
     "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java",
@@ -56,7 +56,7 @@
   ]
 
   deps = [
-    ":system_java",
+    ":system",
     "//base:base_java",
   ]
 
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
index f77399d..6146316 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
@@ -4,8 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.mojo.system.AsyncWaiter;
 import org.chromium.mojo.system.Handle;
-import org.chromium.mojo.system.Watcher;
 
 /**
  * Helper functions.
@@ -189,9 +189,9 @@
     /**
      * Returns an {@link AsyncWaiter} to use with the given handle, or |null| if none if available.
      */
-    static Watcher getWatcherForHandle(Handle handle) {
+    static AsyncWaiter getDefaultAsyncWaiterForHandle(Handle handle) {
         if (handle.getCore() != null) {
-            return handle.getCore().getWatcher();
+            return handle.getCore().getDefaultAsyncWaiter();
         } else {
             return null;
         }
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
index 2aa5ea6..3686bcf 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
@@ -4,13 +4,13 @@
 
 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 org.chromium.mojo.system.Watcher;
 
 import java.nio.ByteBuffer;
 
@@ -27,7 +27,7 @@
     /**
      * The callback that is notified when the state of the owned handle changes.
      */
-    private final WatcherCallback mWatcherCallback = new WatcherCallback();
+    private final AsyncWaiterCallback mAsyncWaiterCallback = new AsyncWaiterCallback();
 
     /**
      * The owned message pipe.
@@ -35,9 +35,9 @@
     private final MessagePipeHandle mMessagePipeHandle;
 
     /**
-     * A watcher which is notified when a new message is available on the owned message pipe.
+     * A waiter which is notified when a new message is available on the owned message pipe.
      */
-    private final Watcher mWatcher;
+    private final AsyncWaiter mAsyncWaiter;
 
     /**
      * The {@link MessageReceiver} to which received messages are sent.
@@ -45,6 +45,11 @@
     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;
@@ -54,16 +59,17 @@
      * {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|.
      */
     public Connector(MessagePipeHandle messagePipeHandle) {
-        this(messagePipeHandle, BindingsHelper.getWatcherForHandle(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, Watcher watcher) {
+    public Connector(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
+        mCancellable = null;
         mMessagePipeHandle = messagePipeHandle;
-        mWatcher = watcher;
+        mAsyncWaiter = asyncWaiter;
     }
 
     /**
@@ -85,7 +91,8 @@
      * Start listening for incoming messages.
      */
     public void start() {
-        mWatcher.start(mMessagePipeHandle, Core.HandleSignals.READABLE, mWatcherCallback);
+        assert mCancellable == null;
+        registerAsyncWaiterForRead();
     }
 
     /**
@@ -133,21 +140,32 @@
         }
     }
 
-    private class WatcherCallback implements Watcher.Callback {
+    private class AsyncWaiterCallback implements AsyncWaiter.Callback {
+
         /**
-         * @see org.chromium.mojo.system.Watcher.Callback#onResult(int)
+         * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int)
          */
         @Override
         public void onResult(int result) {
-            Connector.this.onWatcherResult(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.Watcher.Callback#onResult(int)
+     * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int)
      */
-    private void onWatcherResult(int result) {
+    private void onAsyncWaiterResult(int result) {
+        mCancellable = null;
         if (result == MojoResult.OK) {
             readOutstandingMessages();
         } else {
@@ -157,12 +175,26 @@
 
     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() {
@@ -175,14 +207,18 @@
                 return;
             }
         } while (result.getValue());
-        if (result.getMojoResult() != MojoResult.SHOULD_WAIT) {
+        if (result.getMojoResult() == MojoResult.SHOULD_WAIT) {
+            registerAsyncWaiterForRead();
+        } else {
             onError(new MojoException(result.getMojoResult()));
         }
     }
 
     private void cancelIfActive() {
-        mWatcher.cancel();
-        mWatcher.destroy();
+        if (mCancellable != null) {
+            mCancellable.cancel();
+            mCancellable = null;
+        }
     }
 
     /**
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
index 64ff1c0..755fb01 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
@@ -29,15 +29,11 @@
         /**
          * Minimal value for the next handle to deserialize.
          */
-        private int mMinNextClaimedHandle;
+        private int mMinNextClaimedHandle = 0;
         /**
          * Minimal value of the start of the next memory to claim.
          */
-        private long mMinNextMemory;
-        /**
-         * The current nesting level when decoding.
-         */
-        private long mStackDepth;
+        private long mMinNextMemory = 0;
 
         /**
          * The maximal memory accessible.
@@ -50,17 +46,11 @@
         private final long mNumberOfHandles;
 
         /**
-         * The maximum nesting level when decoding.
-         */
-        private static final int MAX_RECURSION_DEPTH = 100;
-
-        /**
          * Constructor.
          */
         Validator(long maxMemory, int numberOfHandles) {
             mMaxMemory = maxMemory;
             mNumberOfHandles = numberOfHandles;
-            mStackDepth = 0;
         }
 
         public void claimHandle(int handle) {
@@ -89,17 +79,6 @@
             }
             mMinNextMemory = BindingsHelper.align(end);
         }
-
-        public void increaseStackDepth() {
-            ++mStackDepth;
-            if (mStackDepth >= MAX_RECURSION_DEPTH) {
-                throw new DeserializationException("Recursion depth limit exceeded.");
-            }
-        }
-
-        public void decreaseStackDepth() {
-            --mStackDepth;
-        }
     }
 
     /**
@@ -765,12 +744,4 @@
             throw new DeserializationException("Buffer is smaller than expected.");
         }
     }
-
-    public void increaseStackDepth() {
-        mValidator.increaseStackDepth();
-    }
-
-    public void decreaseStackDepth() {
-        mValidator.decreaseStackDepth();
-    }
 }
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
index bb49cbc..c621d9b 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
@@ -4,6 +4,8 @@
 
 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;
@@ -11,8 +13,6 @@
 import org.chromium.mojo.system.MojoResult;
 import org.chromium.mojo.system.Pair;
 import org.chromium.mojo.system.ResultAnd;
-import org.chromium.mojo.system.Watcher;
-import org.chromium.mojo.system.Watcher.Callback;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -58,23 +58,32 @@
          */
         private final Object mLock;
         /**
-         * The {@link Watcher} to get notified of new message availability on |mReadHandle|.
+         * The {@link AsyncWaiter} to get notified of new message availability on |mReadHandle|.
          */
-        private final Watcher mWatcher;
+        private final AsyncWaiter mWaiter;
 
         /**
          * Constructor.
          */
         public PipedExecutor(Core core) {
-            mWatcher = core.getWatcher();
-            assert mWatcher != null;
+            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>();
-            mWatcher.start(mReadHandle, Core.HandleSignals.READABLE, this);
+            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);
         }
 
         /**
@@ -90,6 +99,14 @@
         }
 
         /**
+         * @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() {
@@ -97,8 +114,6 @@
                 mWriteHandle.close();
                 mPendingActions.clear();
             }
-            mWatcher.cancel();
-            mWatcher.destroy();
             mReadHandle.close();
         }
 
@@ -111,6 +126,7 @@
                 ResultAnd<ReadMessageResult> readMessageResult =
                         mReadHandle.readMessage(NOTIFY_BUFFER, 0, MessagePipeHandle.ReadFlags.NONE);
                 if (readMessageResult.getMojoResult() == MojoResult.OK) {
+                    asyncWait();
                     return true;
                 }
             } catch (MojoException e) {
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
index e3be8b3..c2bbc8e 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
@@ -6,14 +6,6 @@
 
 import org.chromium.mojo.bindings.Callbacks.Callback1;
 import org.chromium.mojo.bindings.Interface.AbstractProxy.HandlerImpl;
-import org.chromium.mojo.bindings.interfacecontrol.QueryVersion;
-import org.chromium.mojo.bindings.interfacecontrol.RequireVersion;
-import org.chromium.mojo.bindings.interfacecontrol.RunInput;
-import org.chromium.mojo.bindings.interfacecontrol.RunMessageParams;
-import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeInput;
-import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeMessageParams;
-import org.chromium.mojo.bindings.interfacecontrol.RunOutput;
-import org.chromium.mojo.bindings.interfacecontrol.RunResponseMessageParams;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.MessagePipeHandle;
 import org.chromium.mojo.system.MojoException;
@@ -107,12 +99,12 @@
             /**
              * The {@link ConnectionErrorHandler} that will be notified of errors.
              */
-            private ConnectionErrorHandler mErrorHandler;
+            private ConnectionErrorHandler mErrorHandler = null;
 
             /**
              * The currently known version of the interface.
              */
-            private int mVersion;
+            private int mVersion = 0;
 
             /**
              * Constructor.
@@ -194,18 +186,15 @@
             @Override
             public void queryVersion(final Callback1<Integer> callback) {
                 RunMessageParams message = new RunMessageParams();
-                message.input = new RunInput();
-                message.input.setQueryVersion(new QueryVersion());
+                message.reserved0 = 16;
+                message.reserved1 = 0;
+                message.queryVersion = new QueryVersion();
 
                 InterfaceControlMessagesHelper.sendRunMessage(getCore(), mMessageReceiver, message,
                         new Callback1<RunResponseMessageParams>() {
                             @Override
                             public void call(RunResponseMessageParams response) {
-                                if (response.output != null
-                                        && response.output.which()
-                                                == RunOutput.Tag.QueryVersionResult) {
-                                    mVersion = response.output.getQueryVersionResult().version;
-                                }
+                                mVersion = response.queryVersionResult.version;
                                 callback.call(mVersion);
                             }
                         });
@@ -221,9 +210,10 @@
                 }
                 mVersion = version;
                 RunOrClosePipeMessageParams message = new RunOrClosePipeMessageParams();
-                message.input = new RunOrClosePipeInput();
-                message.input.setRequireVersion(new RequireVersion());
-                message.input.getRequireVersion().version = version;
+                message.reserved0 = 16;
+                message.reserved1 = 0;
+                message.requireVersion = new RequireVersion();
+                message.requireVersion.version = version;
                 InterfaceControlMessagesHelper.sendRunOrClosePipeMessage(
                         getCore(), mMessageReceiver, message);
             }
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
index 51f543d..939fb93 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
@@ -7,14 +7,6 @@
 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.bindings.interfacecontrol.InterfaceControlMessagesConstants;
-import org.chromium.mojo.bindings.interfacecontrol.QueryVersionResult;
-import org.chromium.mojo.bindings.interfacecontrol.RunInput;
-import org.chromium.mojo.bindings.interfacecontrol.RunMessageParams;
-import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeInput;
-import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeMessageParams;
-import org.chromium.mojo.bindings.interfacecontrol.RunOutput;
-import org.chromium.mojo.bindings.interfacecontrol.RunResponseMessageParams;
 import org.chromium.mojo.system.Core;
 
 /**
@@ -72,16 +64,11 @@
      */
     public static <I extends Interface, P extends Proxy> boolean handleRun(
             Core core, Manager<I, P> manager, ServiceMessage message, MessageReceiver responder) {
-        Message payload = message.getPayload();
-        RunMessageParams query = RunMessageParams.deserialize(payload);
         RunResponseMessageParams response = new RunResponseMessageParams();
-        response.output = new RunOutput();
-        if (query.input.which() == RunInput.Tag.QueryVersion) {
-            response.output.setQueryVersionResult(new QueryVersionResult());
-            response.output.getQueryVersionResult().version = manager.getVersion();
-        } else {
-            response.output = null;
-        }
+        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,
@@ -97,9 +84,6 @@
             Manager<I, P> manager, ServiceMessage message) {
         Message payload = message.getPayload();
         RunOrClosePipeMessageParams query = RunOrClosePipeMessageParams.deserialize(payload);
-        if (query.input.which() == RunOrClosePipeInput.Tag.RequireVersion) {
-            return query.input.getRequireVersion().version <= manager.getVersion();
-        }
-        return false;
+        return query.requireVersion.version <= manager.getVersion();
     }
 }
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
index 996c457..0d270cc 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
@@ -29,7 +29,7 @@
     /**
      * This message interpreted as a message for a mojo service with an appropriate header.
      */
-    private ServiceMessage mWithHeader;
+    private ServiceMessage mWithHeader = null;
 
     /**
      * Constructor.
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
index a278cc5..16b29b4 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
@@ -6,9 +6,9 @@
 
 import android.annotation.SuppressLint;
 
+import org.chromium.mojo.system.AsyncWaiter;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.MessagePipeHandle;
-import org.chromium.mojo.system.Watcher;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -48,7 +48,7 @@
      * {@link MessageReceiver} used to return responses to the caller.
      */
     class ResponderThunk implements MessageReceiver {
-        private boolean mAcceptWasInvoked;
+        private boolean mAcceptWasInvoked = false;
 
         /**
          * @see
@@ -109,23 +109,23 @@
     private final Executor mExecutor;
 
     /**
-     * Constructor that will use the default {@link Watcher}.
+     * Constructor that will use the default {@link AsyncWaiter}.
      *
      * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
      */
     public RouterImpl(MessagePipeHandle messagePipeHandle) {
-        this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle));
+        this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
     }
 
     /**
      * Constructor.
      *
      * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
-     * @param watcher the {@link Watcher} to use to get notification of new messages on the
+     * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of new messages on the
      *            handle.
      */
-    public RouterImpl(MessagePipeHandle messagePipeHandle, Watcher watcher) {
-        mConnector = new Connector(messagePipeHandle, watcher);
+    public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
+        mConnector = new Connector(messagePipeHandle, asyncWaiter);
         mConnector.setIncomingMessageReceiver(new HandleIncomingMessageThunk());
         Core core = messagePipeHandle.getCore();
         if (core != null) {
@@ -171,20 +171,23 @@
         assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG);
 
         // Compute a request id for being able to route the response.
-        long requestId = mNextRequestId++;
-        // Reserve 0 in case we want it to convey special meaning in the future.
-        if (requestId == 0) {
-            requestId = mNextRequestId++;
+        // 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);
         }
-        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;
     }
 
@@ -227,11 +230,15 @@
             return false;
         } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) {
             long requestId = header.getRequestId();
-            MessageReceiver responder = mResponders.get(requestId);
-            if (responder == null) {
-                return false;
+            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);
             }
-            mResponders.remove(requestId);
             return responder.accept(message);
         } else {
             if (mIncomingMessageReceiver != null) {
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
index 14f4e1e..85cc97c 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
@@ -6,8 +6,6 @@
 
 import org.chromium.mojo.system.Core;
 
-import java.nio.ByteBuffer;
-
 /**
  * Base class for all mojo structs.
  */
@@ -51,23 +49,6 @@
     }
 
     /**
-     * Similar to the method above, but returns the serialization result as |ByteBuffer|.
-     *
-     * @throws UnsupportedOperationException if the struct contains interfaces or handles.
-     * @throws SerializationException on serialization failure.
-     */
-    public ByteBuffer serialize() {
-        // If the struct contains interfaces which require a non-null |Core| instance, it will throw
-        // UnsupportedOperationException.
-        Message message = serialize(null);
-
-        if (!message.getHandles().isEmpty())
-            throw new UnsupportedOperationException("Handles are discarded.");
-
-        return message.getData();
-    }
-
-    /**
      * Returns the serialization of the struct prepended with the given header.
      *
      * @param header the header to prepend to the returned message.
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
index e5c6d08..ba0e5c6 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -305,9 +305,9 @@
     public UntypedHandle acquireNativeHandle(int handle);
 
     /**
-     * Returns an implementation of {@link Watcher}.
+     * Returns a default implementation of {@link AsyncWaiter}.
      */
-    public Watcher getWatcher();
+    public AsyncWaiter getDefaultAsyncWaiter();
 
     /**
      * Returns a new run loop.
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java b/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
deleted file mode 100644
index 9c70161..0000000
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.
-
-package org.chromium.mojo.system;
-
-import org.chromium.mojo.system.Core.HandleSignals;
-
-/**
- * Watches a handle for signals being satisfied.
- */
-public interface Watcher {
-    /**
-     * Callback passed to {@link Watcher#start}.
-     */
-    public interface Callback {
-        /**
-         * Called when the handle is ready.
-         */
-        public void onResult(int result);
-    }
-
-    /**
-     * Starts watching a handle.
-     */
-    int start(Handle handle, HandleSignals signals, Callback callback);
-
-    /**
-     * Cancels an already-started watch.
-     */
-    void cancel();
-
-    /**
-     * Destroys the underlying implementation. Other methods will fail after destroy has been
-     * called.
-     */
-    void destroy();
-}
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn
index 0fae4b4..eda7e04 100644
--- a/mojo/public/js/BUILD.gn
+++ b/mojo/public/js/BUILD.gn
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings"
-
 source_set("js") {
   sources = [
     "constants.cc",
@@ -13,43 +11,34 @@
 
 group("bindings") {
   data = [
-    "$interfaces_bindings_gen_dir/interface_control_messages.mojom.js",
     "bindings.js",
     "buffer.js",
     "codec.js",
+    "connection.js",
     "connector.js",
+    "constants.cc",
+    "constants.h",
     "core.js",
-    "interface_types.js",
-    "lib/control_message_handler.js",
-    "lib/control_message_proxy.js",
     "router.js",
     "support.js",
     "threading.js",
     "unicode.js",
     "validator.js",
   ]
-
-  deps = [
-    "//mojo/public/interfaces/bindings:bindings__generator",
-  ]
 }
 
 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/",
-    "tests/codec_unittest.js",
-    "tests/connection_unittest.js",
-    "tests/core_unittest.js",
-    "tests/interface_ptr_unittest.js",
-    "tests/sample_service_unittest.js",
-    "tests/struct_unittest.js",
-    "tests/union_unittest.js",
-    "tests/validation_test_input_parser.js",
-    "tests/validation_unittest.js",
   ]
-
   public_deps = [
     ":bindings",
   ]
diff --git a/mojo/public/js/bindings.js b/mojo/public/js/bindings.js
index f3e40d2..2fdcae3 100644
--- a/mojo/public/js/bindings.js
+++ b/mojo/public/js/bindings.js
@@ -3,283 +3,115 @@
 // found in the LICENSE file.
 
 define("mojo/public/js/bindings", [
-  "mojo/public/js/core",
-  "mojo/public/js/lib/control_message_proxy",
-  "mojo/public/js/interface_types",
   "mojo/public/js/router",
-], function(core, controlMessageProxy, types, router) {
+  "mojo/public/js/core",
+], function(router, core) {
 
-  // ---------------------------------------------------------------------------
+  var Router = router.Router;
 
-  function makeRequest(interfacePtr) {
-    var pipe = core.createMessagePipe();
-    interfacePtr.ptr.bind(new types.InterfacePtrInfo(pipe.handle0, 0));
-    return new types.InterfaceRequest(pipe.handle1);
+  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;
   }
 
-  // ---------------------------------------------------------------------------
-
-  // Operations used to setup/configure an interface pointer. Exposed as the
-  // |ptr| field of generated interface pointer classes.
-  // |ptrInfoOrHandle| could be omitted and passed into bind() later.
-  function InterfacePtrController(interfaceType, ptrInfoOrHandle) {
-    this.version = 0;
-
-    this.interfaceType_ = interfaceType;
-    this.router_ = null;
-    this.proxy_ = null;
-
-    // |router_| is lazily initialized. |handle_| is valid between bind() and
-    // the initialization of |router_|.
-    this.handle_ = null;
-    this.controlMessageProxy_ = null;
-
-    if (ptrInfoOrHandle)
-      this.bind(ptrInfoOrHandle);
+  // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom.
+  ProxyProperties.prototype.getLocalDelegate = function() {
+    return this.local && StubBindings(this.local).delegate;
   }
 
-  InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) {
-    this.reset();
+  // 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");
+  }
 
-    if (ptrInfoOrHandle instanceof types.InterfacePtrInfo) {
-      this.version = ptrInfoOrHandle.version;
-      this.handle_ = ptrInfoOrHandle.handle;
-    } else {
-      this.handle_ = ptrInfoOrHandle;
-    }
-  };
+  ProxyProperties.prototype.close = function() {
+    this.connection.close();
+  }
 
-  InterfacePtrController.prototype.isBound = function() {
-    return this.router_ !== null || this.handle_ !== null;
-  };
+  // Public stub class properties that are managed at runtime by the JS
+  // bindings. See StubBindings below.
+  function StubProperties(delegate) {
+    this.delegate = delegate;
+  }
 
-  // Although users could just discard the object, reset() closes the pipe
-  // immediately.
-  InterfacePtrController.prototype.reset = function() {
-    this.version = 0;
-    if (this.router_) {
-      this.router_.close();
-      this.router_ = null;
+  StubProperties.prototype.close = function() {
+    this.connection.close();
+  }
 
-      this.proxy_ = null;
-    }
-    if (this.handle_) {
-      core.close(this.handle_);
-      this.handle_ = null;
-    }
-  };
+  // The base class for generated proxy classes.
+  function ProxyBase(receiver) {
+    this[kProxyProperties] = new ProxyProperties(receiver);
 
-  InterfacePtrController.prototype.setConnectionErrorHandler
-      = function(callback) {
-    if (!this.isBound())
-      throw new Error("Cannot set connection error handler if not bound.");
+    // TODO(hansmuller): Temporary, for Chrome backwards compatibility.
+    if (receiver instanceof Router)
+      this.receiver_ = receiver;
+  }
 
-    this.configureProxyIfNecessary_();
-    this.router_.setErrorHandler(callback);
-  };
+  // The base class for generated stub classes.
+  function StubBase(delegate) {
+    this[kStubProperties] = new StubProperties(delegate);
+  }
 
-  InterfacePtrController.prototype.passInterface = function() {
-    var result;
-    if (this.router_) {
-      // TODO(yzshen): Fix Router interface to support extracting handle.
-      result = new types.InterfacePtrInfo(
-          this.router_.connector_.handle_, this.version);
-      this.router_.connector_.handle_ = null;
-    } else {
-      // This also handles the case when this object is not bound.
-      result = new types.InterfacePtrInfo(this.handle_, this.version);
-      this.handle_ = null;
-    }
+  // TODO(hansmuller): remove everything except the connection property doc
+  // after 'Client=' has been removed from Mojom.
 
-    this.reset();
-    return result;
-  };
-
-  InterfacePtrController.prototype.getProxy = function() {
-    this.configureProxyIfNecessary_();
-    return this.proxy_;
-  };
-
-  InterfacePtrController.prototype.enableTestingMode = function() {
-    this.configureProxyIfNecessary_();
-    return this.router_.enableTestingMode();
-  };
-
-  InterfacePtrController.prototype.configureProxyIfNecessary_ = function() {
-    if (!this.handle_)
-      return;
-
-    this.router_ = new router.Router(this.handle_);
-    this.handle_ = null;
-    this.router_ .setPayloadValidators([this.interfaceType_.validateResponse]);
-
-    this.controlMessageProxy_ = new
-        controlMessageProxy.ControlMessageProxy(this.router_);
-
-    this.proxy_ = new this.interfaceType_.proxyClass(this.router_);
-  };
-
-  InterfacePtrController.prototype.queryVersion = function() {
-    function onQueryVersion(version) {
-      this.version = version;
-      return version;
-    }
-
-    this.configureProxyIfNecessary_();
-    return this.controlMessageProxy_.queryVersion().then(
-      onQueryVersion.bind(this));
-  };
-
-  InterfacePtrController.prototype.requireVersion = function(version) {
-    this.configureProxyIfNecessary_();
-
-    if (this.version >= version) {
-      return;
-    }
-    this.version = version;
-    this.controlMessageProxy_.requireVersion(version);
-  };
-
-  // ---------------------------------------------------------------------------
-
-  // |request| could be omitted and passed into bind() later.
+  // 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.
   //
-  // Example:
+  // 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.
   //
-  //    // FooImpl implements mojom.Foo.
-  //    function FooImpl() { ... }
-  //    FooImpl.prototype.fooMethod1 = function() { ... }
-  //    FooImpl.prototype.fooMethod2 = function() { ... }
+  // ProxyBindings(proxy).local  - The "local" stub object whose delegate
+  //   implements the proxy's Mojo client interface.
   //
-  //    var fooPtr = new mojom.FooPtr();
-  //    var request = makeRequest(fooPtr);
-  //    var binding = new Binding(mojom.Foo, new FooImpl(), request);
-  //    fooPtr.fooMethod1();
-  function Binding(interfaceType, impl, requestOrHandle) {
-    this.interfaceType_ = interfaceType;
-    this.impl_ = impl;
-    this.router_ = null;
-    this.stub_ = null;
+  // 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|.
 
-    if (requestOrHandle)
-      this.bind(requestOrHandle);
+  function ProxyBindings(proxy) {
+    return (proxy instanceof ProxyBase) ? proxy[kProxyProperties] : proxy;
   }
 
-  Binding.prototype.isBound = function() {
-    return this.router_ !== null;
-  };
+  // TODO(hansmuller): remove the remote doc after 'Client=' has been
+  // removed from Mojom.
 
-  Binding.prototype.createInterfacePtrAndBind = function() {
-    var ptr = new this.interfaceType_.ptrClass();
-    // TODO(yzshen): Set the version of the interface pointer.
-    this.bind(makeRequest(ptr));
-    return ptr;
+  // 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;
   }
 
-  Binding.prototype.bind = function(requestOrHandle) {
-    this.close();
-
-    var handle = requestOrHandle instanceof types.InterfaceRequest ?
-        requestOrHandle.handle : requestOrHandle;
-    if (!core.isHandle(handle))
-      return;
-
-    this.stub_ = new this.interfaceType_.stubClass(this.impl_);
-    this.router_ = new router.Router(handle, this.interfaceType_.kVersion);
-    this.router_.setIncomingReceiver(this.stub_);
-    this.router_ .setPayloadValidators([this.interfaceType_.validateRequest]);
-  };
-
-  Binding.prototype.close = function() {
-    if (!this.isBound())
-      return;
-
-    this.router_.close();
-    this.router_ = null;
-    this.stub_ = null;
-  };
-
-  Binding.prototype.setConnectionErrorHandler
-      = function(callback) {
-    if (!this.isBound())
-      throw new Error("Cannot set connection error handler if not bound.");
-    this.router_.setErrorHandler(callback);
-  };
-
-  Binding.prototype.unbind = function() {
-    if (!this.isBound())
-      return new types.InterfaceRequest(null);
-
-    var result = new types.InterfaceRequest(this.router_.connector_.handle_);
-    this.router_.connector_.handle_ = null;
-    this.close();
-    return result;
-  };
-
-  Binding.prototype.enableTestingMode = function() {
-    return this.router_.enableTestingMode();
-  };
-
-  // ---------------------------------------------------------------------------
-
-  function BindingSetEntry(bindingSet, interfaceType, impl, requestOrHandle,
-                           bindingId) {
-    this.bindingSet_ = bindingSet;
-    this.bindingId_ = bindingId;
-    this.binding_ = new Binding(interfaceType, impl, requestOrHandle);
-
-    this.binding_.setConnectionErrorHandler(function() {
-      this.bindingSet_.onConnectionError(bindingId);
-    }.bind(this));
-  }
-
-  BindingSetEntry.prototype.close = function() {
-    this.binding_.close();
-  };
-
-  function BindingSet(interfaceType) {
-    this.interfaceType_ = interfaceType;
-    this.nextBindingId_ = 0;
-    this.bindings_ = new Map();
-    this.errorHandler_ = null;
-  }
-
-  BindingSet.prototype.isEmpty = function() {
-    return this.bindings_.size == 0;
-  };
-
-  BindingSet.prototype.addBinding = function(impl, requestOrHandle) {
-    this.bindings_.set(
-        this.nextBindingId_,
-        new BindingSetEntry(this, this.interfaceType_, impl, requestOrHandle,
-                            this.nextBindingId_));
-    ++this.nextBindingId_;
-  };
-
-  BindingSet.prototype.closeAllBindings = function() {
-    for (var entry of this.bindings_.values())
-      entry.close();
-    this.bindings_.clear();
-  };
-
-  BindingSet.prototype.setConnectionErrorHandler = function(callback) {
-    this.errorHandler_ = callback;
-  };
-
-  BindingSet.prototype.onConnectionError = function(bindingId) {
-    this.bindings_.delete(bindingId);
-
-    if (this.errorHandler_)
-      this.errorHandler_();
-  };
-
   var exports = {};
-  exports.InterfacePtrInfo = types.InterfacePtrInfo;
-  exports.InterfaceRequest = types.InterfaceRequest;
-  exports.makeRequest = makeRequest;
-  exports.InterfacePtrController = InterfacePtrController;
-  exports.Binding = Binding;
-  exports.BindingSet = BindingSet;
-
+  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/codec.js b/mojo/public/js/codec.js
index ff5d31a..4003b51 100644
--- a/mojo/public/js/codec.js
+++ b/mojo/public/js/codec.js
@@ -3,10 +3,9 @@
 // found in the LICENSE file.
 
 define("mojo/public/js/codec", [
-  "mojo/public/js/buffer",
-  "mojo/public/js/interface_types",
   "mojo/public/js/unicode",
-], function(buffer, types, unicode) {
+  "mojo/public/js/buffer",
+], function(unicode, buffer) {
 
   var kErrorUnsigned = "Passing negative value to unsigned";
   var kErrorArray = "Passing non Array for array type";
@@ -304,12 +303,8 @@
   };
 
   Encoder.prototype.encodeHandle = function(handle) {
-    if (handle) {
-      this.handles.push(handle);
-      this.writeUint32(this.handles.length - 1);
-    } else {
-      this.writeUint32(kEncodedInvalidHandleValue);
-    }
+    this.handles.push(handle);
+    this.writeUint32(this.handles.length - 1);
   };
 
   Encoder.prototype.encodeString = function(val) {
@@ -716,20 +711,6 @@
     encoder.writeDouble(val);
   };
 
-  function Enum(cls) {
-    this.cls = cls;
-  }
-
-  Enum.prototype.encodedSize = 4;
-
-  Enum.prototype.decode = function(decoder) {
-    return decoder.readInt32();
-  };
-
-  Enum.prototype.encode = function(encoder, val) {
-    encoder.writeInt32(val);
-  };
-
   function PointerTo(cls) {
     this.cls = cls;
   }
@@ -807,54 +788,33 @@
 
   NullableHandle.encode = Handle.encode;
 
-  function Interface(cls) {
-    this.cls = cls;
+  function Interface() {
   }
 
-  Interface.prototype.encodedSize = 8;
+  Interface.encodedSize = 8;
 
-  Interface.prototype.decode = function(decoder) {
-    var interfacePtrInfo = new types.InterfacePtrInfo(
-        decoder.decodeHandle(), decoder.readUint32());
-    var interfacePtr = new this.cls();
-    interfacePtr.ptr.bind(interfacePtrInfo);
-    return interfacePtr;
+  Interface.decode = function(decoder) {
+    var handle = decoder.decodeHandle();
+    // Ignore the version field for now.
+    decoder.readUint32();
+
+    return handle;
   };
 
-  Interface.prototype.encode = function(encoder, val) {
-    var interfacePtrInfo =
-        val ? val.ptr.passInterface() : new types.InterfacePtrInfo(null, 0);
-    encoder.encodeHandle(interfacePtrInfo.handle);
-    encoder.writeUint32(interfacePtrInfo.version);
+  Interface.encode = function(encoder, val) {
+    encoder.encodeHandle(val);
+    // Set the version field to 0 for now.
+    encoder.writeUint32(0);
   };
 
-  function NullableInterface(cls) {
-    Interface.call(this, cls);
+  function NullableInterface() {
   }
 
-  NullableInterface.prototype = Object.create(Interface.prototype);
+  NullableInterface.encodedSize = Interface.encodedSize;
 
-  function InterfaceRequest() {
-  }
+  NullableInterface.decode = Interface.decode;
 
-  InterfaceRequest.encodedSize = 4;
-
-  InterfaceRequest.decode = function(decoder) {
-    return new types.InterfaceRequest(decoder.decodeHandle());
-  };
-
-  InterfaceRequest.encode = function(encoder, val) {
-    encoder.encodeHandle(val ? val.handle : null);
-  };
-
-  function NullableInterfaceRequest() {
-  }
-
-  NullableInterfaceRequest.encodedSize = InterfaceRequest.encodedSize;
-
-  NullableInterfaceRequest.decode = InterfaceRequest.decode;
-
-  NullableInterfaceRequest.encode = InterfaceRequest.encode;
+  NullableInterface.encode = Interface.encode;
 
   function MapOf(keyClass, valueClass) {
     this.keyClass = keyClass;
@@ -903,7 +863,6 @@
   exports.Float = Float;
   exports.Double = Double;
   exports.String = String;
-  exports.Enum = Enum;
   exports.NullableString = NullableString;
   exports.PointerTo = PointerTo;
   exports.NullablePointerTo = NullablePointerTo;
@@ -914,8 +873,6 @@
   exports.NullableHandle = NullableHandle;
   exports.Interface = Interface;
   exports.NullableInterface = NullableInterface;
-  exports.InterfaceRequest = InterfaceRequest;
-  exports.NullableInterfaceRequest = NullableInterfaceRequest;
   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
index ee16be8..674f36b 100644
--- a/mojo/public/js/connector.js
+++ b/mojo/public/js/connector.js
@@ -82,12 +82,6 @@
     return this.error_;
   };
 
-  Connector.prototype.waitForNextMessageForTesting = function() {
-    var wait = core.wait(this.handle_, core.HANDLE_SIGNAL_READABLE,
-                         core.DEADLINE_INDEFINITE);
-    this.readMore_(wait.result);
-  };
-
   Connector.prototype.readMore_ = function(result) {
     for (;;) {
       var read = core.readMessage(this.handle_,
@@ -104,12 +98,29 @@
       }
       var messageBuffer = new buffer.Buffer(read.buffer);
       var message = new codec.Message(messageBuffer, read.handles);
-      if (this.incomingReceiver_)
-        this.incomingReceiver_.accept(message);
+      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
index 58cf274..d29f5cb 100644
--- a/mojo/public/js/constants.cc
+++ b/mojo/public/js/constants.cc
@@ -9,15 +9,10 @@
 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 kControlMessageHandlerModuleName[] =
-    "mojo/public/js/lib/control_message_handler";
-const char kControlMessageProxyModuleName[] =
-    "mojo/public/js/lib/control_message_proxy";
-const char kInterfaceControlMessagesMojom[] =
-    "mojo/public/interfaces/bindings/interface_control_messages.mojom";
-const char kInterfaceTypesModuleName[] = "mojo/public/js/interface_types";
-const char kRouterModuleName[] = "mojo/public/js/router";
 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
index 9d32d20..de75a90 100644
--- a/mojo/public/js/constants.h
+++ b/mojo/public/js/constants.h
@@ -11,13 +11,10 @@
 extern const char kBindingsModuleName[];
 extern const char kBufferModuleName[];
 extern const char kCodecModuleName[];
+extern const char kConnectionModuleName[];
 extern const char kConnectorModuleName[];
-extern const char kControlMessageHandlerModuleName[];
-extern const char kControlMessageProxyModuleName[];
-extern const char kInterfaceControlMessagesMojom[];
-extern const char kInterfaceTypesModuleName[];
-extern const char kRouterModuleName[];
 extern const char kUnicodeModuleName[];
+extern const char kRouterModuleName[];
 extern const char kValidatorModuleName[];
 
 }  // namespace mojo
diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js
index ef480ee..b89a956 100644
--- a/mojo/public/js/core.js
+++ b/mojo/public/js/core.js
@@ -114,27 +114,6 @@
 var READ_DATA_FLAG_PEEK;
 
 /**
- * MojoCreateSharedBufferOptionsFlags: Used to specify options to
- *   |createSharedBuffer()|.
- * See core.h for more information.
- */
-var CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
-
-/**
- * MojoDuplicateBufferHandleOptionsFlags: Used to specify options to
- *   |duplicateBufferHandle()|.
- * See core.h for more information.
- */
-var DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE;
-var DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY;
-
-/**
- * MojoMapBufferFlags: Used to specify options to |mapBuffer()|.
- * See core.h for more information.
- */
-var MAP_BUFFER_FLAG_NONE;
-
-/**
  * Closes the given |handle|. See MojoClose for more info.
  * @param {MojoHandle} Handle to close.
  * @return {MojoResult} Result code.
@@ -257,57 +236,3 @@
  * @return true or false
  */
 function isHandle(value) { [native code] }
-
-/**
- * Creates shared buffer of specified size |num_bytes|.
- * See MojoCreateSharedBuffer for more information including error codes.
- *
- * @param {number} num_bytes Size of the memory to be allocated for shared
- * @param {MojoCreateSharedBufferOptionsFlags} flags Flags.
- *   buffer.
- * @return {object} An object of the form {
- *     result,  // |RESULT_OK| on success, error code otherwise.
- *     handle,  // An MojoHandle for shared buffer (only on success).
- *   }
- */
-function createSharedBuffer(num_bytes, flags) { [native code] }
-
-/**
- * Duplicates the |buffer_handle| to a shared buffer. Duplicated handle can be
- * sent to another process over message pipe. See MojoDuplicateBufferHandle for
- * more information including error codes.
- *
- * @param {MojoHandle} buffer_handle MojoHandle.
- * @param {MojoCreateSharedBufferOptionsFlags} flags Flags.
- * @return {object} An object of the form {
- *     result,  // |RESULT_OK| on success, error code otherwise.
- *     handle,  // A duplicated MojoHandle for shared buffer (only on success).
- *   }
- */
-function duplicateBufferHandle(buffer_handle, flags) { [native code] }
-
-/**
- * Maps the part (at offset |offset| of length |num_bytes|) of the buffer given
- * by |buffer_handle| into ArrayBuffer memory |buffer|, with options specified
- * by |flags|. See MojoMapBuffer for more information including error codes.
- *
- * @param {MojoHandle} buffer_handle A sharedBufferHandle returned by
- *   createSharedBuffer.
- * @param {number} offset Offset.
- * @param {number} num_bytes Size of the memory to be mapped.
- * @param {MojoMapBufferFlags} flags Flags.
- * @return {object} An object of the form {
- *     result,  // |RESULT_OK| on success, error code otherwise.
- *     buffer,  // An ArrayBuffer (only on success).
- *   }
- */
-function mapBuffer(buffer_handle, offset, num_bytes, flags) { [native code] }
-
-/**
- * Unmaps buffer that was mapped using mapBuffer.
- * See MojoUnmapBuffer for more information including error codes.
- *
- * @param {ArrayBuffer} buffer ArrayBuffer.
- * @return {MojoResult} Result code.
- */
-function unmapBuffer(buffer) { [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
index e94c5eb..e3db0a6 100644
--- a/mojo/public/js/router.js
+++ b/mojo/public/js/router.js
@@ -3,20 +3,17 @@
 // found in the LICENSE file.
 
 define("mojo/public/js/router", [
-  "console",
   "mojo/public/js/codec",
   "mojo/public/js/core",
   "mojo/public/js/connector",
-  "mojo/public/js/lib/control_message_handler",
   "mojo/public/js/validator",
-], function(console, codec, core, connector, controlMessageHandler, validator) {
+], function(codec, core, connector, validator) {
 
   var Connector = connector.Connector;
   var MessageReader = codec.MessageReader;
   var Validator = validator.Validator;
-  var ControlMessageHandler = controlMessageHandler.ControlMessageHandler;
 
-  function Router(handle, interface_version, connectorFactory) {
+  function Router(handle, connectorFactory) {
     if (!core.isHandle(handle))
       throw new Error("Router constructor: Not a handle");
     if (connectorFactory === undefined)
@@ -27,12 +24,6 @@
     this.nextRequestID_ = 0;
     this.completers_ = new Map();
     this.payloadValidators_ = [];
-    this.testingController_ = null;
-
-    if (interface_version !== undefined) {
-      this.controlMessageHandler_ = new
-          ControlMessageHandler(interface_version);
-    }
 
     this.connector_.setIncomingReceiver({
         accept: this.handleIncomingMessage_.bind(this),
@@ -45,7 +36,6 @@
   Router.prototype.close = function() {
     this.completers_.clear();  // Drop any responders.
     this.connector_.close();
-    this.testingController_ = null;
   };
 
   Router.prototype.accept = function(message) {
@@ -91,11 +81,6 @@
     return this.connector_.encounteredError();
   };
 
-  Router.prototype.enableTestingMode = function() {
-    this.testingController_ = new RouterTestingController(this.connector_);
-    return this.testingController_;
-  };
-
   Router.prototype.handleIncomingMessage_ = function(message) {
     var noError = validator.validationError.NONE;
     var messageValidator = new Validator(message);
@@ -110,17 +95,8 @@
   };
 
   Router.prototype.handleValidIncomingMessage_ = function(message) {
-    if (this.testingController_)
-      return;
-
     if (message.expectsResponse()) {
-      if (controlMessageHandler.isControlMessage(message)) {
-        if (this.controlMessageHandler_) {
-          this.controlMessageHandler_.acceptWithResponder(message, this);
-        } else {
-          this.close();
-        }
-      } else if (this.incomingReceiver_) {
+      if (this.incomingReceiver_) {
         this.incomingReceiver_.acceptWithResponder(message, this);
       } else {
         // If we receive a request expecting a response when the client is not
@@ -131,39 +107,17 @@
       var reader = new MessageReader(message);
       var requestID = reader.requestID;
       var completer = this.completers_.get(requestID);
-      if (completer) {
-        this.completers_.delete(requestID);
-        completer.resolve(message);
-      } else {
-        console.log("Unexpected response with request ID: " + requestID);
-      }
+      this.completers_.delete(requestID);
+      completer.resolve(message);
     } else {
-      if (controlMessageHandler.isControlMessage(message)) {
-        if (this.controlMessageHandler_) {
-          var ok = this.controlMessageHandler_.accept(message);
-          if (ok) return;
-        }
-        this.close();
-      } else if (this.incomingReceiver_) {
+      if (this.incomingReceiver_)
         this.incomingReceiver_.accept(message);
-      }
     }
-  };
+  }
 
   Router.prototype.handleInvalidIncomingMessage_ = function(message, error) {
-    if (!this.testingController_) {
-      // TODO(yzshen): Consider notifying the embedder.
-      // TODO(yzshen): This should also trigger connection error handler.
-      // Consider making accept() return a boolean and let the connector deal
-      // with this, as the C++ code does.
-      console.log("Invalid message: " + validator.validationError[error]);
-
-      this.close();
-      return;
-    }
-
-    this.testingController_.onInvalidIncomingMessage(error);
-  };
+    this.close();
+  }
 
   Router.prototype.handleConnectionError_ = function(result) {
     this.completers_.forEach(function(value) {
@@ -174,30 +128,25 @@
     this.close();
   };
 
-  // The RouterTestingController is used in unit tests. It defeats valid message
-  // handling and delgates invalid message handling.
+  // The TestRouter subclass is only intended to be used in unit tests.
+  // It defeats valid message handling and delgates invalid message handling.
 
-  function RouterTestingController(connector) {
-    this.connector_ = connector;
-    this.invalidMessageHandler_ = null;
+  function TestRouter(handle, connectorFactory) {
+    Router.call(this, handle, connectorFactory);
   }
 
-  RouterTestingController.prototype.waitForNextMessage = function() {
-    this.connector_.waitForNextMessageForTesting();
+  TestRouter.prototype = Object.create(Router.prototype);
+
+  TestRouter.prototype.handleValidIncomingMessage_ = function() {
   };
 
-  RouterTestingController.prototype.setInvalidIncomingMessageHandler =
-      function(callback) {
-    this.invalidMessageHandler_ = callback;
-  };
-
-  RouterTestingController.prototype.onInvalidIncomingMessage =
-      function(error) {
-    if (this.invalidMessageHandler_)
-      this.invalidMessageHandler_(error);
-  };
+  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/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
index 49ab5c9..cfe5037 100644
--- a/mojo/public/js/threading.js
+++ b/mojo/public/js/threading.js
@@ -7,7 +7,7 @@
 // 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 Service implemented in JS
+// 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.
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
index fee742d..cbf7521 100644
--- a/mojo/public/js/validator.js
+++ b/mojo/public/js/validator.js
@@ -24,15 +24,10 @@
         'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP',
     INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE',
     UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION',
-    UNKNOWN_ENUM_VALUE: 'VALIDATION_ERROR_UNKNOWN_ENUM_VALUE',
   };
 
   var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
 
-  function isEnumClass(cls) {
-    return cls instanceof codec.Enum;
-  }
-
   function isStringClass(cls) {
     return cls === codec.String || cls === codec.NullableString;
   }
@@ -42,18 +37,12 @@
   }
 
   function isInterfaceClass(cls) {
-    return cls instanceof codec.Interface;
-  }
-
-  function isInterfaceRequestClass(cls) {
-    return cls === codec.InterfaceRequest ||
-        cls === codec.NullableInterfaceRequest;
+    return cls === codec.Interface || cls === codec.NullableInterface;
   }
 
   function isNullable(type) {
     return type === codec.NullableString || type === codec.NullableHandle ||
         type === codec.NullableInterface ||
-        type === codec.NullableInterfaceRequest ||
         type instanceof codec.NullableArrayOf ||
         type instanceof codec.NullablePointerTo;
   }
@@ -87,7 +76,7 @@
       return false;
 
     return true;
-  };
+  }
 
   Validator.prototype.claimRange = function(start, numBytes) {
     if (this.isValidRange(start, numBytes)) {
@@ -95,7 +84,7 @@
       return true;
     }
     return false;
-  };
+  }
 
   Validator.prototype.claimHandle = function(index) {
     if (index === codec.kEncodedInvalidHandleValue)
@@ -107,13 +96,6 @@
     // This is safe because handle indices are uint32.
     this.handleIndex = index + 1;
     return true;
-  };
-
-  Validator.prototype.validateEnum = function(offset, enumClass) {
-    // Note: Assumes that enums are always 32 bits! But this matches
-    // mojom::generate::pack::PackedField::GetSizeForKind, so it should be okay.
-    var value = this.message.buffer.getInt32(offset);
-    return enumClass.validate(value);
   }
 
   Validator.prototype.validateHandle = function(offset, nullable) {
@@ -125,19 +107,15 @@
 
     if (!this.claimHandle(index))
       return validationError.ILLEGAL_HANDLE;
-
     return validationError.NONE;
-  };
+  }
 
   Validator.prototype.validateInterface = function(offset, nullable) {
     return this.validateHandle(offset, nullable);
-  };
+  }
 
-  Validator.prototype.validateInterfaceRequest = function(offset, nullable) {
-    return this.validateHandle(offset, nullable);
-  };
-
-  Validator.prototype.validateStructHeader = function(offset, minNumBytes) {
+  Validator.prototype.validateStructHeader =
+      function(offset, minNumBytes, minVersion) {
     if (!codec.isAligned(offset))
       return validationError.MISALIGNED_OBJECT;
 
@@ -145,44 +123,20 @@
       return validationError.ILLEGAL_MEMORY_RANGE;
 
     var numBytes = this.message.buffer.getUint32(offset);
+    var version = this.message.buffer.getUint32(offset + 4);
 
-    if (numBytes < minNumBytes)
+    // 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.validateStructVersion = function(offset, versionSizes) {
-    var numBytes = this.message.buffer.getUint32(offset);
-    var version = this.message.buffer.getUint32(offset + 4);
-
-    if (version <= versionSizes[versionSizes.length - 1].version) {
-      // Scan in reverse order to optimize for more recent versionSizes.
-      for (var i = versionSizes.length - 1; i >= 0; --i) {
-        if (version >= versionSizes[i].version) {
-          if (numBytes == versionSizes[i].numBytes)
-            break;
-          return validationError.UNEXPECTED_STRUCT_HEADER;
-        }
-      }
-    } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) {
-      return validationError.UNEXPECTED_STRUCT_HEADER;
-    }
-
-    return validationError.NONE;
-  };
-
-  Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) {
-    var structVersion = this.message.buffer.getUint32(offset + 4);
-    return fieldVersion <= structVersion;
-  };
+  }
 
   Validator.prototype.validateMessageHeader = function() {
-
-    var err = this.validateStructHeader(0, codec.kMessageHeaderSize);
+    var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0);
     if (err != validationError.NONE)
       return err;
 
@@ -208,28 +162,7 @@
       return validationError.MESSAGE_HEADER_INVALID_FLAGS;
 
     return validationError.NONE;
-  };
-
-  Validator.prototype.validateMessageIsRequestWithoutResponse = function() {
-    if (this.message.isResponse() || this.message.expectsResponse()) {
-      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
-    }
-    return validationError.NONE;
-  };
-
-  Validator.prototype.validateMessageIsRequestExpectingResponse = function() {
-    if (this.message.isResponse() || !this.message.expectsResponse()) {
-      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
-    }
-    return validationError.NONE;
-  };
-
-  Validator.prototype.validateMessageIsResponse = function() {
-    if (this.message.expectsResponse() || !this.message.isResponse()) {
-      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
@@ -240,7 +173,7 @@
       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);
@@ -263,7 +196,7 @@
 
     return this.validateArray(arrayOffset, elementSize, elementType,
                               expectedDimensionSizes, currentDimension);
-  };
+  }
 
   Validator.prototype.validateStructPointer = function(
       offset, structClass, nullable) {
@@ -276,7 +209,7 @@
           validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
 
     return structClass.validate(this, structOffset);
-  };
+  }
 
   Validator.prototype.validateUnion = function(
       offset, unionClass, nullable) {
@@ -287,7 +220,7 @@
     }
 
     return unionClass.validate(this, offset);
-  };
+  }
 
   Validator.prototype.validateNestedUnion = function(
       offset, unionClass, nullable) {
@@ -300,7 +233,7 @@
           validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
 
     return this.validateUnion(unionOffset, unionClass, nullable);
-  };
+  }
 
   // This method assumes that the array at arrayPointerOffset has
   // been validated.
@@ -308,7 +241,7 @@
   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) {
@@ -323,7 +256,7 @@
           validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
 
     var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize;
-    var err = this.validateStructHeader(structOffset, mapEncodedSize);
+    var err = this.validateStructHeader(structOffset, mapEncodedSize, 0);
     if (err !== validationError.NONE)
         return err;
 
@@ -356,12 +289,12 @@
       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
@@ -404,9 +337,6 @@
     if (isInterfaceClass(elementType))
       return this.validateInterfaceElements(
           elementsOffset, numElements, nullable);
-    if (isInterfaceRequestClass(elementType))
-      return this.validateInterfaceRequestElements(
-          elementsOffset, numElements, nullable);
     if (isStringClass(elementType))
       return this.validateArrayElements(
           elementsOffset, numElements, codec.Uint8, nullable, [0], 0);
@@ -417,12 +347,9 @@
       return this.validateArrayElements(
           elementsOffset, numElements, elementType.cls, nullable,
           expectedDimensionSizes, currentDimension + 1);
-    if (isEnumClass(elementType))
-      return this.validateEnumElements(elementsOffset, numElements,
-                                       elementType.cls);
 
     return validationError.NONE;
-  };
+  }
 
   // Note: the |offset + i * elementSize| computation in the validateFooElements
   // methods below is "safe" because elementSize <= 8, offset and
@@ -438,11 +365,11 @@
         return err;
     }
     return validationError.NONE;
-  };
+  }
 
   Validator.prototype.validateInterfaceElements =
       function(offset, numElements, nullable) {
-    var elementSize = codec.Interface.prototype.encodedSize;
+    var elementSize = codec.Interface.encodedSize;
     for (var i = 0; i < numElements; i++) {
       var elementOffset = offset + i * elementSize;
       var err = this.validateInterface(elementOffset, nullable);
@@ -450,19 +377,7 @@
         return err;
     }
     return validationError.NONE;
-  };
-
-  Validator.prototype.validateInterfaceRequestElements =
-      function(offset, numElements, nullable) {
-    var elementSize = codec.InterfaceRequest.encodedSize;
-    for (var i = 0; i < numElements; i++) {
-      var elementOffset = offset + i * elementSize;
-      var err = this.validateInterfaceRequest(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 =
@@ -478,7 +393,7 @@
         return err;
     }
     return validationError.NONE;
-  };
+  }
 
   Validator.prototype.validateStructElements =
       function(offset, numElements, structClass, nullable) {
@@ -491,19 +406,7 @@
         return err;
     }
     return validationError.NONE;
-  };
-
-  Validator.prototype.validateEnumElements =
-      function(offset, numElements, enumClass) {
-    var elementSize = codec.Enum.prototype.encodedSize;
-    for (var i = 0; i < numElements; i++) {
-      var elementOffset = offset + i * elementSize;
-      var err = this.validateEnum(elementOffset, enumClass);
-      if (err != validationError.NONE)
-        return err;
-    }
-    return validationError.NONE;
-  };
+  }
 
   var exports = {};
   exports.validationError = validationError;
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/tools/bindings/BUILD.gn b/mojo/public/tools/bindings/BUILD.gn
index 153d110..eeea5c5 100644
--- a/mojo/public/tools/bindings/BUILD.gn
+++ b/mojo/public/tools/bindings/BUILD.gn
@@ -16,9 +16,7 @@
     "$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-shared-internal.h.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module-shared.cc.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module-shared.h.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",
@@ -27,15 +25,11 @@
     "$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_traits_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_traits_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_data_view_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_data_view_definition.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_traits_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_traits_definition.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",
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
index bb0fc43..ef19cc3 100644
--- a/mojo/public/tools/bindings/blink_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/blink_bindings_configuration.gni
@@ -9,25 +9,21 @@
 _typemap_imports = [
   "//mojo/public/cpp/bindings/tests/blink_typemaps.gni",
   "//third_party/WebKit/Source/platform/mojo/blink_typemaps.gni",
-  "//third_party/WebKit/public/blink_typemaps.gni",
-  "//third_party/WebKit/public/public_typemaps.gni",
 ]
 _typemaps = []
 
 foreach(typemap_import, _typemap_imports) {
-  # Avoid reassignment error by assigning to empty scope first.
-  _imported = {
-  }
   _imported = read_file(typemap_import, "scope")
   _typemaps += _imported.typemaps
 }
 
 typemaps = []
 foreach(typemap, _typemaps) {
-  typemaps += [ {
-        filename = typemap
-        config = read_file(typemap, "scope")
-      } ]
+  typemaps += [ read_file(typemap, "scope") ]
 }
 
-blacklist = []
+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
index 831157f..44c2ae6 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -3,78 +3,28 @@
 # found in the LICENSE file.
 
 _typemap_imports = [
-  "//ash/public/interfaces/typemaps.gni",
   "//cc/ipc/typemaps.gni",
-  "//chrome/browser/media/router/mojo/typemaps.gni",
-  "//chrome/common/extensions/typemaps.gni",
-  "//chrome/common/importer/typemaps.gni",
-  "//chrome/typemaps.gni",
-  "//components/arc/common/typemaps.gni",
-  "//components/metrics/public/cpp/typemaps.gni",
-  "//components/typemaps.gni",
-  "//content/common/bluetooth/typemaps.gni",
-  "//content/common/indexed_db/typemaps.gni",
-  "//content/common/presentation/typemaps.gni",
-  "//content/common/typemaps.gni",
-  "//content/public/common/typemaps.gni",
   "//device/bluetooth/public/interfaces/typemaps.gni",
-  "//device/gamepad/public/interfaces/typemaps.gni",
-  "//device/generic_sensor/public/interfaces/typemaps.gni",
-  "//device/usb/public/interfaces/typemaps.gni",
+  "//components/arc/common/typemaps.gni",
+  "//components/typemaps.gni",
   "//gpu/ipc/common/typemaps.gni",
-  "//media/capture/mojo/typemaps.gni",
   "//media/mojo/interfaces/typemaps.gni",
   "//mojo/common/typemaps.gni",
   "//mojo/public/cpp/bindings/tests/chromium_typemaps.gni",
-  "//net/interfaces/typemaps.gni",
-  "//services/preferences/public/cpp/typemaps.gni",
-  "//services/resource_coordinator/public/cpp/typemaps.gni",
-  "//services/service_manager/public/cpp/typemaps.gni",
-  "//services/ui/gpu/interfaces/typemaps.gni",
-  "//services/ui/public/interfaces/ime/typemaps.gni",
-  "//services/video_capture/public/interfaces/typemaps.gni",
   "//skia/public/interfaces/typemaps.gni",
-  "//third_party/WebKit/public/public_typemaps.gni",
-  "//ui/base/mojo/typemaps.gni",
-  "//ui/display/mojo/typemaps.gni",
   "//ui/events/devices/mojo/typemaps.gni",
   "//ui/events/mojo/typemaps.gni",
   "//ui/gfx/typemaps.gni",
-  "//ui/message_center/mojo/typemaps.gni",
   "//url/mojo/typemaps.gni",
 ]
-
-_typemap_imports_mac = [ "//content/common/typemaps_mac.gni" ]
-
 _typemaps = []
+
 foreach(typemap_import, _typemap_imports) {
-  # Avoid reassignment error by assigning to empty scope first.
-  _imported = {
-  }
   _imported = read_file(typemap_import, "scope")
   _typemaps += _imported.typemaps
 }
 
 typemaps = []
 foreach(typemap, _typemaps) {
-  typemaps += [ {
-        filename = typemap
-        config = read_file(typemap, "scope")
-      } ]
-}
-
-_typemaps_mac = []
-foreach(typemap_import, _typemap_imports_mac) {
-  _imported = {
-  }
-  _imported = read_file(typemap_import, "scope")
-  _typemaps_mac += _imported.typemaps
-}
-
-typemaps_mac = []
-foreach(typemap, _typemaps_mac) {
-  typemaps_mac += [ {
-        filename = typemap
-        config = read_file(typemap, "scope")
-      } ]
+  typemaps += [ read_file(typemap, "scope") ]
 }
diff --git a/mojo/public/tools/bindings/generate_type_mappings.py b/mojo/public/tools/bindings/generate_type_mappings.py
index 824f804..e2fbf31 100755
--- a/mojo/public/tools/bindings/generate_type_mappings.py
+++ b/mojo/public/tools/bindings/generate_type_mappings.py
@@ -97,9 +97,8 @@
 
     mojom_type = match_result.group(1)
     native_type = match_result.group(2)
-    attributes = []
-    if match_result.group(3):
-      attributes = match_result.group(3).split(',')
+    # 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" %
@@ -107,11 +106,7 @@
 
     result[mojom_type] = {
         'typename': native_type,
-        'non_copyable_non_movable': 'non_copyable_non_movable' in attributes,
-        'move_only': 'move_only' in attributes,
-        'copyable_pass_by_value': 'copyable_pass_by_value' in attributes,
-        'nullable_is_same_type': 'nullable_is_same_type' in attributes,
-        'hashable': 'hashable' in attributes,
+        'move_only': move_only,
         'public_headers': values['public_headers'],
         'traits_headers': values['traits_headers'],
     }
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
index f0d503e..0a446a7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
@@ -1,55 +1,22 @@
 {#---
   Macro for enum definition, and the declaration of associated functions.
 ---#}
-
 {%- macro enum_decl(enum) %}
-{%-   set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %}
-enum class {{enum_name}} : int32_t {
-{%-   for field in enum.fields %}
-{%-     if field.value %}
+enum class {{enum.name}} : int32_t {
+{%- for field in enum.fields %}
+{%-    if field.value %}
   {{field.name}} = {{field.value|expression_to_text}},
-{%-     else %}
+{%-    else %}
   {{field.name}},
-{%-     endif %}
-{%-   endfor %}
+{%-    endif %}
+{%- endfor %}
 };
-
-inline std::ostream& operator<<(std::ostream& os, {{enum_name}} value) {
-{%- if enum.fields %}
-  switch(value) {
-{%-   for _, values in enum.fields|groupby('numeric_value') %}
-    case {{enum_name}}::{{values[0].name}}:
-      return os << "{{enum_name}}::
-{%-     if values|length > 1 -%}
-      {{'{'}}
-{%-     endif -%}
-      {{values|map(attribute='name')|join(', ')}}
-{%-     if values|length > 1 -%}
-      {{'}'}}
-{%-     endif -%}
-      ";
-{%-   endfor %}
-    default:
-      return os << "Unknown {{enum_name}} value: " << static_cast<int32_t>(value);
-  }
-{%- else %}
-  return os << "Unknown {{enum_name}} value: " << static_cast<int32_t>(value);
-{%- endif %}
-}
-
-{#-   Returns true if the given enum value exists in this version of enum. #}
-inline bool IsKnownEnumValue({{enum_name}} value) {
-  return {{enum|get_name_for_kind(internal=True,
-                                  flatten_nested_kind=True)}}::IsKnownValue(
-      static_cast<int32_t>(value));
-}
 {%- endmacro %}
 
 {%- macro enum_data_decl(enum) %}
-{%-   set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %}
-struct {{enum_name}}_Data {
+struct {{enum.name}}_Data {
  public:
-  static bool constexpr kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
+  static bool const kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
 
   static bool IsKnownValue(int32_t value) {
 {%- if enum.fields %}
@@ -75,57 +42,42 @@
 };
 {%- endmacro %}
 
-{%- macro enum_hash(enum) %}
-{%-   set enum_name = enum|get_qualified_name_for_kind(
-          flatten_nested_kind=True) %}
-template <>
-struct hash<{{enum_name}}>
-    : public mojo::internal::EnumHashImpl<{{enum_name}}> {};
+{#--- 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 enum_hash_blink(enum) %}
-{%-   set enum_name = enum|get_qualified_name_for_kind(
-          flatten_nested_kind=True, include_variant=False) %}
-{%-   set hash_fn_name = enum|wtf_hash_fn_name_for_enum %}
-{#    We need two unused enum values: #}
-{%-   set empty_value = -1000000 %}
-{%-   set deleted_value = -1000001 %}
-{%-   set empty_value_unused = "false" if empty_value in enum|all_enum_values else "true" %}
-{%-   set deleted_value_unused = "false" if empty_value in enum|all_enum_values else "true" %}
-namespace WTF {
-struct {{hash_fn_name}} {
-  static unsigned hash(const {{enum_name}}& value) {
-    typedef base::underlying_type<{{enum_name}}>::type utype;
-    return DefaultHash<utype>::Hash().hash(static_cast<utype>(value));
-  }
-  static bool equal(const {{enum_name}}& left, const {{enum_name}}& right) {
-    return left == right;
-  }
-  static const bool safeToCompareToEmptyOrDeleted = true;
-};
+{%- 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 DefaultHash<{{enum_name}}> {
-  using Hash = {{hash_fn_name}};
-};
-
-template <>
-struct HashTraits<{{enum_name}}>
-    : public GenericHashTraits<{{enum_name}}> {
-  static_assert({{empty_value_unused}},
-                "{{empty_value}} is a reserved enum value");
-  static_assert({{deleted_value_unused}},
-                "{{deleted_value}} is a reserved enum value");
-  static const bool hasIsEmptyValueFunction = true;
-  static bool isEmptyValue(const {{enum_name}}& value) {
-    return value == static_cast<{{enum_name}}>({{empty_value}});
-  }
-  static void constructDeletedValue({{enum_name}}& slot, bool) {
-    slot = static_cast<{{enum_name}}>({{deleted_value}});
-  }
-  static bool isDeletedValue(const {{enum_name}}& value) {
-    return value == static_cast<{{enum_name}}>({{deleted_value}});
-  }
-};
-}  // namespace WTF
+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
index d7d0e5d..e42128d 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
@@ -1,5 +1,4 @@
-{%- set mojom_type = enum|get_qualified_name_for_kind(
-        flatten_nested_kind=True) %}
+{%- set mojom_type = enum|get_qualified_name_for_kind %}
 
 template <>
 struct EnumTraits<{{mojom_type}}, {{mojom_type}}> {
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
index 7f64974..0249ef3 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
@@ -1,7 +1,5 @@
 {%- import "interface_macros.tmpl" as interface_macros %}
 class {{interface.name}}Proxy;
-
-template <typename ImplRefTraits>
 class {{interface.name}}Stub;
 
 class {{interface.name}}RequestValidator;
@@ -9,18 +7,15 @@
 class {{interface.name}}ResponseValidator;
 {%- endif %}
 
-class {{export_attribute}} {{interface.name}}
-    : public {{interface.name}}InterfaceBase {
+class {{interface.name}} {
  public:
   static const char Name_[];
-  static constexpr uint32_t Version_ = {{interface.version}};
-  static constexpr bool PassesAssociatedKinds_ = {% if interface|passes_associated_kinds %}true{% else %}false{% endif %};
-  static constexpr bool HasSyncMethods_ = {% if interface|has_sync_methods %}true{% else %}false{% endif %};
+  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;
-
-  template <typename ImplRefTraits>
-  using Stub_ = {{interface.name}}Stub<ImplRefTraits>;
+  using Stub_ = {{interface.name}}Stub;
 
   using RequestValidator_ = {{interface.name}}RequestValidator;
 {%- if interface|has_callbacks %}
@@ -29,21 +24,23 @@
   using ResponseValidator_ = mojo::PassThroughFilter;
 {%- endif %}
 
-{#--- Metadata #}
-  enum MethodMinVersions : uint32_t {
-{%- for method in interface.methods %}
-    k{{method.name}}MinVersion = {{method.min_version|default(0, true)}},
-{%- endfor %}
-  };
-
 {#--- Enums #}
+{% from "enum_macros.tmpl" import enum_decl -%}
 {%- for enum in interface.enums %}
-  using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}};
+{%-   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 %}
-  static {{constant|format_constant_declaration(nested=True)}};
+{%-   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 #}
@@ -58,8 +55,8 @@
 {%-     endif %}
 
   using {{method.name}}Callback = {{interface_macros.declare_callback(method,
-      for_blink, use_once_callback)}};
+      for_blink, use_new_wrapper_types)}};
 {%-   endif %}
-  virtual void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) = 0;
+  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
index a23b107..4017fc5 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -5,22 +5,24 @@
 {%- set proxy_name = interface.name ~ "Proxy" %}
 {%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}
 
-{%- macro alloc_params(struct, params, message, description) %}
-  mojo::internal::SerializationContext serialization_context;
-  serialization_context.handles.Swap(({{message}})->mutable_handles());
-  serialization_context.associated_endpoint_handles.swap(
-      *({{message}})->mutable_associated_endpoint_handles());
+{%- 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.name}}DataView input_data_view({{params}},
+                                          {{serialization_context}});
   {{struct_macros.deserialize(struct, "input_data_view", "p_%s", "success")}}
   if (!success) {
-    ReportValidationErrorForMessage(
-        {{message}},
-        mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
+    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 %}
@@ -39,17 +41,18 @@
                             serialization_context)}}
   ({{serialization_context}})->handles.Swap(
       builder.message()->mutable_handles());
-  ({{serialization_context}})->associated_endpoint_handles.swap(
-      *builder.message()->mutable_associated_endpoint_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_string_kind %}
-const char {{interface.name}}::{{constant.name}}[] = {{constant|constant_value}};
+{%-   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 %}
 
@@ -71,11 +74,12 @@
     : 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 %})
-      : result_(result)
+      : serialization_context_(std::move(group_controller)), result_(result)
 {%-       for param in method.response_parameters -%}
         , out_{{param.name}}_(out_{{param.name}})
 {%-       endfor %} {
@@ -83,6 +87,7 @@
   }
   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}}_;
@@ -95,14 +100,13 @@
       reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
           message->mutable_payload());
 
-{%-       set desc = class_name~"::"~method.name~" response" %}
-  {{alloc_params(method.response_param_struct, "params", "message", desc)}}
+  {{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 %}
-  mojo::internal::SyncMessageResponseSetup::SetCurrentSyncResponseMessage(
-      message);
   *result_ = true;
   return true;
 }
@@ -112,16 +116,15 @@
     : public mojo::MessageReceiver {
  public:
   {{class_name}}_{{method.name}}_ForwardToCallback(
-{%-     if use_once_callback %}
-      {{class_name}}::{{method.name}}Callback callback
-{%-     else %}
-      const {{class_name}}::{{method.name}}Callback& callback
-{%-     endif %}
-      ) : callback_(std::move(callback)) {
+      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(
@@ -130,19 +133,18 @@
       reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
           message->mutable_payload());
 
-{%-     set desc = class_name~"::"~method.name~" response" %}
-  {{alloc_params(method.response_param_struct, "params", "message", desc)}}
-  if (!callback_.is_null()) {
-    mojo::internal::MessageDispatchContext context(message);
-    std::move(callback_).Run({{pass_params(method.response_parameters)}});
-  }
+  {{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)
-    : receiver_(receiver) {
+    : ControlMessageProxy(receiver) {
 }
 
 {#--- Proxy definitions #}
@@ -156,22 +158,19 @@
 {%-   if method.sync %}
 bool {{proxy_name}}::{{method.name}}(
     {{interface_macros.declare_sync_method_params("param_", method)}}) {
-  mojo::internal::SerializationContext serialization_context;
   {{struct_macros.get_serialized_size(params_struct, "param_%s",
-                                      "&serialization_context")}}
+                                      "&serialization_context_")}}
 
-  mojo::internal::MessageBuilder builder(
-      {{message_name}},
-      mojo::Message::kFlagIsSync | mojo::Message::kFlagExpectsResponse,
-      size, serialization_context.associated_endpoint_count);
+  mojo::internal::RequestMessageBuilder builder({{message_name}}, size,
+                                                mojo::Message::kFlagIsSync);
 
   {{build_message(params_struct, "param_%s", params_description,
-                  "&serialization_context")}}
+                  "&serialization_context_")}}
 
   bool result = false;
   mojo::MessageReceiver* responder =
       new {{class_name}}_{{method.name}}_HandleSyncResponse(
-          &result
+          serialization_context_.group_controller, &result
 {%-     for param in method.response_parameters -%}
           , param_{{param.name}}
 {%-     endfor %});
@@ -182,26 +181,23 @@
 {%-   endif %}
 
 void {{proxy_name}}::{{method.name}}(
-    {{interface_macros.declare_request_params("in_", method, use_once_callback)}}) {
-  mojo::internal::SerializationContext serialization_context;
+    {{interface_macros.declare_request_params("in_", method)}}) {
   {{struct_macros.get_serialized_size(params_struct, "in_%s",
-                                      "&serialization_context")}}
+                                      "&serialization_context_")}}
 
 {%- if method.response_parameters != None %}
-  constexpr uint32_t kFlags = mojo::Message::kFlagExpectsResponse;
+  mojo::internal::RequestMessageBuilder builder({{message_name}}, size);
 {%- else %}
-  constexpr uint32_t kFlags = 0;
+  mojo::internal::MessageBuilder builder({{message_name}}, size);
 {%- endif %}
-  mojo::internal::MessageBuilder builder(
-      {{message_name}}, kFlags, size,
-      serialization_context.associated_endpoint_count);
 
   {{build_message(params_struct, "in_%s", params_description,
-                  "&serialization_context")}}
+                  "&serialization_context_")}}
 
 {%- if method.response_parameters != None %}
   mojo::MessageReceiver* responder =
-      new {{class_name}}_{{method.name}}_ForwardToCallback(std::move(callback));
+      new {{class_name}}_{{method.name}}_ForwardToCallback(
+          callback, serialization_context_.group_controller);
   if (!receiver_->AcceptWithResponder(builder.message(), responder))
     delete responder;
 {%- else %}
@@ -221,23 +217,42 @@
 {%-     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 {
+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) {
-    std::unique_ptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy(
-        new {{class_name}}_{{method.name}}_ProxyToResponder(
-            request_id, is_sync, responder));
+      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,
-                      base::Passed(&proxy));
+                      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 Service destroying the callback without running it
+      // 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.");
@@ -248,43 +263,31 @@
     delete responder_;
   }
 
- private:
-  {{class_name}}_{{method.name}}_ProxyToResponder(
-      uint64_t request_id,
-      bool is_sync,
-      mojo::MessageReceiverWithStatus* responder)
-      : request_id_(request_id),
-        is_sync_(is_sync),
-        responder_(responder) {
-  }
-
   void Run(
       {{interface_macros.declare_responder_params(
-          "in_", method.response_parameters, for_blink)}});
+          "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)}}) {
-  mojo::internal::SerializationContext serialization_context;
+        "in_", method.response_parameters, for_blink,
+        use_new_wrapper_types)}}) {
   {{struct_macros.get_serialized_size(response_params_struct, "in_%s",
-                                      "&serialization_context")}}
-
-  uint32_t flags = (is_sync_ ? mojo::Message::kFlagIsSync : 0) |
-                   mojo::Message::kFlagIsResponse;
-  mojo::internal::MessageBuilder builder(
-      {{message_name}}, flags, size,
-      serialization_context.associated_endpoint_count);
-  builder.message()->set_request_id(request_id_);
-
+                                      "&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")}}
+                  "&serialization_context_")}}
   bool ok = responder_->Accept(builder.message());
   ALLOW_UNUSED_LOCAL(ok);
   // TODO(darin): !ok returned here indicates a malformed message, and that may
@@ -296,12 +299,18 @@
 {%-   endif -%}
 {%- endfor %}
 
-{#--- StubDispatch definition #}
+{{class_name}}Stub::{{class_name}}Stub()
+    : sink_(nullptr),
+      control_message_handler_({{interface.name}}::Version_) {
+}
 
-// static
-bool {{class_name}}StubDispatch::Accept(
-    {{interface.name}}* impl,
-    mojo::Message* message) {
+{{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 %}
@@ -311,14 +320,13 @@
           reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
               message->mutable_payload());
 
-{%-       set desc = class_name~"::"~method.name %}
-      {{alloc_params(method.param_struct, "params", "message", desc)|
-          indent(4)}}
-      // A null |impl| means no implementation was bound.
-      assert(impl);
+      {{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}}");
-      mojo::internal::MessageDispatchContext context(message);
-      impl->{{method.name}}({{pass_params(method.parameters)}});
+      sink_->{{method.name}}({{pass_params(method.parameters)}});
       return true;
 {%-     else %}
       break;
@@ -330,11 +338,10 @@
   return false;
 }
 
-// static
-bool {{class_name}}StubDispatch::AcceptWithResponder(
-    {{interface.name}}* impl,
-    mojo::Message* message,
-    mojo::MessageReceiverWithStatus* responder) {
+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 %}
@@ -344,19 +351,20 @@
           reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
               message->mutable_payload());
 
-{%-       set desc = class_name~"::"~method.name %}
-      {{alloc_params(method.param_struct, "params", "message", desc)|
-          indent(4)}}
+      {{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);
-      // A null |impl| means no implementation was bound.
-      assert(impl);
+              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}}");
-      mojo::internal::MessageDispatchContext context(message);
-      impl->{{method.name}}(
-{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}std::move(callback));
+      sink_->{{method.name}}(
+{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback);
       return true;
 {%-     else %}
       break;
@@ -370,14 +378,22 @@
 
 {#--- Request validator definitions #}
 
+{{class_name}}RequestValidator::{{class_name}}RequestValidator(
+    mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
 bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
-  if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
-    return true;
+  assert(sink_);
 
   mojo::internal::ValidationContext validation_context(
-    message->payload(), message->payload_num_bytes(),
-    message->handles()->size(), message->payload_num_interface_ids(), message,
-    "{{class_name}} RequestValidator");
+    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 %}
@@ -398,7 +414,7 @@
                   message, &validation_context)) {
         return false;
       }
-      return true;
+      return sink_->Accept(message);
     }
 {%- endfor %}
     default:
@@ -414,14 +430,22 @@
 
 {#--- Response validator definitions #}
 {% if interface|has_callbacks %}
+{{class_name}}ResponseValidator::{{class_name}}ResponseValidator(
+    mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
 bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
-  if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
-    return true;
+  assert(sink_);
 
   mojo::internal::ValidationContext validation_context(
-    message->payload(), message->payload_num_bytes(),
-    message->handles()->size(), message->payload_num_interface_ids(), message,
-    "{{class_name}} ResponseValidator");
+    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;
@@ -433,7 +457,7 @@
                     message, &validation_context)) {
         return false;
       }
-      return true;
+      return sink_->Accept(message);
     }
 {%- endfor %}
     default:
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
index 8649273..4bec4c6 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
@@ -5,35 +5,39 @@
 {%-   endfor %}
 {%- endmacro %}
 
-{%- macro declare_responder_params(prefix, parameters, for_blink) %}
+{%- 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_once_callback) -%}
-{%-   if use_once_callback -%}
-base::OnceCallback<void(
-{%-   else -%}
+{%- macro declare_callback(method, for_blink, use_new_wrapper_types) -%}
 base::Callback<void(
-{%-   endif -%}
 {%-   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, use_once_callback) -%}
+{%- macro declare_request_params(prefix, method) -%}
 {{declare_params(prefix, method.parameters)}}
 {%-   if method.response_parameters != None -%}
-{%-     if method.parameters %}, {% endif -%}
-{%-     if use_once_callback -%}
-{{method.name}}Callback callback
-{%-     else -%}
+{%- if method.parameters %}, {% endif -%}
 const {{method.name}}Callback& callback
-{%-     endif -%}
 {%-   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
index 0a158ec..477116b 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
@@ -1,6 +1,7 @@
 {%- import "interface_macros.tmpl" as interface_macros %}
-class {{export_attribute}} {{interface.name}}Proxy
-    : public {{interface.name}} {
+class {{interface.name}}Proxy
+    : public {{interface.name}},
+      public mojo::internal::ControlMessageProxy {
  public:
   explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver);
 
@@ -8,9 +9,13 @@
 {%-   if method.sync %}
   bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) override;
 {%-   endif %}
-  void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) override;
+  void {{method.name}}({{interface_macros.declare_request_params("", method)}}) override;
 {%- endfor %}
 
+  mojo::internal::SerializationContext* serialization_context() {
+    return &serialization_context_;
+  }
+
  private:
-  mojo::MessageReceiverWithResponder* receiver_;
+  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
index a00d148..29917ea 100644
--- 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
@@ -1,4 +1,6 @@
-class {{export_attribute}} {{interface.name}}RequestValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) {
+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
index e2caa02..5893bfd 100644
--- 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
@@ -1,4 +1,6 @@
-class {{export_attribute}} {{interface.name}}ResponseValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) {
+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
index 9f01348..30b5de7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -1,40 +1,19 @@
-class {{export_attribute}} {{interface.name}}StubDispatch {
+class {{interface.name}}Stub : public mojo::MessageReceiverWithResponderStatus {
  public:
-  static bool Accept({{interface.name}}* impl, mojo::Message* message);
-  static bool AcceptWithResponder({{interface.name}}* impl,
-                                  mojo::Message* message,
-                                  mojo::MessageReceiverWithStatus* responder);
-};
-
-template <typename ImplRefTraits =
-              mojo::RawPtrImplRefTraits<{{interface.name}}>>
-class {{interface.name}}Stub
-    : public NON_EXPORTED_BASE(mojo::MessageReceiverWithResponderStatus) {
- public:
-  using ImplPointerType = typename ImplRefTraits::PointerType;
-
-  {{interface.name}}Stub() {}
-  ~{{interface.name}}Stub() override {}
-
-  void set_sink(ImplPointerType sink) { sink_ = std::move(sink); }
-  ImplPointerType& sink() { return sink_; }
-
-  bool Accept(mojo::Message* message) override {
-    if (ImplRefTraits::IsNull(sink_))
-      return false;
-    return {{interface.name}}StubDispatch::Accept(
-        ImplRefTraits::GetRawPointer(&sink_), message);
+  {{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 AcceptWithResponder(
-      mojo::Message* message,
-      mojo::MessageReceiverWithStatus* responder) override {
-    if (ImplRefTraits::IsNull(sink_))
-      return false;
-    return {{interface.name}}StubDispatch::AcceptWithResponder(
-        ImplRefTraits::GetRawPointer(&sink_), message, responder);
-  }
+  bool Accept(mojo::Message* message) override;
+  bool AcceptWithResponder(mojo::Message* message,
+      mojo::MessageReceiverWithStatus* responder) override;
 
  private:
-  ImplPointerType sink_;
+  {{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-shared-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
deleted file mode 100644
index 964b254..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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.
-
-{%- set header_guard = "%s_SHARED_INTERNAL_H_"|format(
-        module.path|upper|replace("/","_")|replace(".","_")|
-            replace("-", "_")) %}
-
-#ifndef {{header_guard}}
-#define {{header_guard}}
-
-#include <stdint.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/map_data_internal.h"
-#include "mojo/public/cpp/bindings/lib/native_enum_data.h"
-#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
-
-{%- for import in imports %}
-#include "{{import.module.path}}-shared-internal.h"
-{%- endfor %}
-
-namespace mojo {
-namespace internal {
-class ValidationContext;
-}
-}
-
-{%- for namespace in namespaces_as_array %}
-namespace {{namespace}} {
-{%- 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 all_enums %}
-{%-   if enum|is_native_only_kind %}
-using {{enum|get_name_for_kind(flatten_nested_kind=True)}}_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 parameter definitions #}
-{%- for interface in interfaces %}
-{%-   for method in interface.methods %}
-{%-     set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
-constexpr uint32_t {{method_name}} = {{method.ordinal}};
-{%-     set struct = method.param_struct %}
-{%      include "struct_declaration.tmpl" %}
-{%-     if method.response_parameters != None %}
-{%-       set struct = method.response_param_struct %}
-{%        include "struct_declaration.tmpl" %}
-{%-     endif %}
-{%-   endfor %}
-{%- endfor %}
-
-#pragma pack(pop)
-
-}  // namespace internal
-{%- for namespace in namespaces_as_array|reverse %}
-}  // namespace {{namespace}}
-{%- endfor %}
-
-#endif  // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl
deleted file mode 100644
index 645bb69..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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.
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4065)
-#endif
-
-#include "{{module.path}}-shared.h"
-
-#include <utility>
-
-#include "base/logging.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"
-
-{%- for header in extra_traits_headers %}
-#include "{{header}}"
-{%- endfor %}
-
-{%- for namespace in namespaces_as_array %}
-namespace {{namespace}} {
-{%- endfor %}
-
-namespace internal {
-
-{#--- Union definitions #}
-{%- for union in unions %}
-{%    include "union_definition.tmpl" %}
-{%- endfor %}
-
-{#--- Struct definitions #}
-{%- for struct in structs %}
-{%-   if not struct|is_native_only_kind %}
-{%      include "struct_definition.tmpl" %}
-{%-   endif %}
-{%- endfor %}
-
-{#--- Interface parameter definitions #}
-{%- for interface in interfaces %}
-{%-   for method in interface.methods %}
-{%-     set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
-{%-     set struct = method.param_struct %}
-{%      include "struct_definition.tmpl" %}
-{%-     if method.response_parameters != None %}
-{%-       set struct = method.response_param_struct %}
-{%        include "struct_definition.tmpl" %}
-{%-     endif %}
-{%-   endfor %}
-{%- endfor %}
-
-}  // namespace internal
-
-{%- for namespace in namespaces_as_array|reverse %}
-}  // namespace {{namespace}}
-{%- endfor %}
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
deleted file mode 100644
index dd13466..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
+++ /dev/null
@@ -1,212 +0,0 @@
-// 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.
-
-{%- set header_guard = "%s_SHARED_H_"|format(
-        module.path|upper|replace("/","_")|replace(".","_")|
-            replace("-", "_")) %}
-
-{%- macro mojom_type_traits(kind) %}
-template <>
-struct MojomTypeTraits<{{kind|get_qualified_name_for_kind}}DataView> {
-  using Data = {{kind|get_qualified_name_for_kind(internal=True)}};
-{%-  if kind|is_union_kind %}
-  using DataAsArrayElement = Data;
-  static constexpr MojomTypeCategory category = MojomTypeCategory::UNION;
-{%-  else %}
-  using DataAsArrayElement = Pointer<Data>;
-  static constexpr MojomTypeCategory category = MojomTypeCategory::STRUCT;
-{%-  endif %}
-};
-{%- endmacro %}
-
-{%- macro namespace_begin() %}
-{%-   for namespace in namespaces_as_array %}
-namespace {{namespace}} {
-{%-   endfor %}
-{%- endmacro %}
-
-{%- macro namespace_end() %}
-{%-   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/compiler_specific.h"
-#include "mojo/public/cpp/bindings/array_data_view.h"
-#include "mojo/public/cpp/bindings/enum_traits.h"
-#include "mojo/public/cpp/bindings/interface_data_view.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/map_data_view.h"
-#include "mojo/public/cpp/bindings/native_enum.h"
-#include "mojo/public/cpp/bindings/native_struct_data_view.h"
-#include "mojo/public/cpp/bindings/string_data_view.h"
-#include "{{module.path}}-shared-internal.h"
-{%- for import in imports %}
-#include "{{import.module.path}}-shared.h"
-{%- endfor %}
-
-{{namespace_begin()}}
-
-{#--- Struct Forward Declarations -#}
-{%- for struct in structs %}
-{%-   if struct|is_native_only_kind %}
-using {{struct.name}}DataView = mojo::NativeStructDataView;
-{%-   else %}
-class {{struct.name}}DataView;
-{%-   endif %}
-{%  endfor %}
-
-{#--- Union Forward Declarations -#}
-{%- for union in unions %}
-class {{union.name}}DataView;
-{%- endfor %}
-
-{{namespace_end()}}
-
-namespace mojo {
-namespace internal {
-
-{%- for struct in structs %}
-{%-   if not struct|is_native_only_kind %}
-{{mojom_type_traits(struct)}}
-{%-   endif %}
-{%- endfor %}
-
-{%- for union in unions %}
-{{mojom_type_traits(union)}}
-{%- endfor %}
-
-}  // namespace internal
-}  // namespace mojo
-
-{{namespace_begin()}}
-
-{#--- Enums #}
-{%- from "enum_macros.tmpl" import enum_decl%}
-{%- for enum in all_enums %}
-{%-   if enum|is_native_only_kind %}
-using {{enum|get_name_for_kind(flatten_nested_kind=True)}} = mojo::NativeEnum;
-{%-   else %}
-{{enum_decl(enum)}}
-{%-   endif %}
-{%- endfor %}
-
-{#--- Interfaces #}
-{%- if interfaces %}
-// Interface base classes. They are used for type safety check.
-{%- endif %}
-{%- for interface in interfaces %}
-class {{interface.name}}InterfaceBase {};
-
-using {{interface.name}}PtrDataView =
-    mojo::InterfacePtrDataView<{{interface.name}}InterfaceBase>;
-using {{interface.name}}RequestDataView =
-    mojo::InterfaceRequestDataView<{{interface.name}}InterfaceBase>;
-using {{interface.name}}AssociatedPtrInfoDataView =
-    mojo::AssociatedInterfacePtrInfoDataView<{{interface.name}}InterfaceBase>;
-using {{interface.name}}AssociatedRequestDataView =
-    mojo::AssociatedInterfaceRequestDataView<{{interface.name}}InterfaceBase>;
-
-{%- endfor %}
-
-{#--- Structs #}
-{%- for struct in structs %}
-{%-   if not struct|is_native_only_kind %}
-{%      include "struct_data_view_declaration.tmpl" %}
-{%-   endif %}
-{%- endfor %}
-
-{#--- Interface parameter definitions #}
-{%- for interface in interfaces %}
-{%-   for method in interface.methods %}
-{%-     set struct = method.param_struct %}
-{%      include "struct_data_view_declaration.tmpl" %}
-{%-     if method.response_parameters != None %}
-{%-       set struct = method.response_param_struct %}
-{%        include "struct_data_view_declaration.tmpl" %}
-{%-     endif %}
-{%-   endfor %}
-{%- endfor %}
-
-{#--- Unions #}
-{%- for union in unions %}
-{%    include "union_data_view_declaration.tmpl" %}
-{%- endfor %}
-
-{{namespace_end()}}
-
-namespace std {
-
-{%- from "enum_macros.tmpl" import enum_hash %}
-{%- for enum in all_enums %}
-{%-   if not enum|is_native_only_kind %}
-{{enum_hash(enum)}}
-{%-   endif %}
-{%- endfor %}
-
-}  // namespace std
-
-namespace mojo {
-
-{#--- Enum Serialization Helpers -#}
-{%- for enum in all_enums %}
-{%-   if not enum|is_native_only_kind %}
-{%      include "enum_serialization_declaration.tmpl" %}
-{%-   endif %}
-{%- 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
-
-{{namespace_begin()}}
-
-{%- for struct in structs %}
-{%-   if not struct|is_native_only_kind %}
-{%      include "struct_data_view_definition.tmpl" %}
-{%-   endif %}
-{%- endfor %}
-
-{%- for interface in interfaces %}
-{%-   for method in interface.methods %}
-{%-     set struct = method.param_struct %}
-{%      include "struct_data_view_definition.tmpl" %}
-{%-     if method.response_parameters != None %}
-{%-       set struct = method.response_param_struct %}
-{%        include "struct_data_view_definition.tmpl" %}
-{%-     endif %}
-{%-   endfor %}
-{%- endfor %}
-
-{%- for union in unions %}
-{%    include "union_data_view_definition.tmpl" %}
-{%- endfor %}
-
-{{namespace_end()}}
-
-#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
index 2c66a85..efb9db6 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -26,11 +26,13 @@
 
 #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 %}
@@ -50,16 +52,75 @@
 
 {#--- Constants #}
 {%- for constant in module.constants %}
-{%-   if constant.kind|is_string_kind %}
-const char {{constant.name}}[] = {{constant|constant_value}};
+{%-   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_string_kind %}
-const char {{struct.name}}::{{constant.name}}[] = {{constant|constant_value}};
+{%-     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 %}
@@ -68,6 +129,7 @@
 {%- for struct in structs %}
 {%-   if not struct|is_native_only_kind %}
 {%-     include "wrapper_class_definition.tmpl" %}
+{%-     include "struct_data_view_definition.tmpl" %}
 {%-   endif %}
 {%- endfor %}
 
@@ -93,17 +155,18 @@
 {#--- Struct Serialization Helpers -#}
 {%  for struct in structs %}
 {%-   if not struct|is_native_only_kind %}
-{%      include "struct_traits_definition.tmpl" %}
+{%      include "struct_serialization_definition.tmpl" %}
 {%-   endif %}
 {%- endfor %}
 
 {#--- Union Serialization Helpers #}
 {%- for union in unions %}
-{%-   include "union_traits_definition.tmpl" %}
+{%-   include "union_serialization_definition.tmpl" %}
 {%- endfor %}
 
 }  // namespace mojo
 
+
 #if defined(__clang__)
 #pragma clang diagnostic pop
 #elif defined(_MSC_VER)
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
index acdad5e..c4f3e07 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -12,6 +12,11 @@
         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}} {
@@ -35,30 +40,30 @@
 
 #include <stdint.h>
 
-#include <limits>
+#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/clone_traits.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/lib/equals_traits.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/lib/union_accessor.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/raw_ptr_impl_ref_traits.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 "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
-#include "mojo/public/cpp/bindings/union_traits.h"
-#include "{{module.path}}-shared.h"
+#include "{{variant_path}}-internal.h"
 {%- for import in imports %}
 {%-   if variant %}
 #include "{{"%s-%s.h"|format(import.module.path, variant)}}"
@@ -67,13 +72,11 @@
 {%-   endif %}
 {%- endfor %}
 {%- if not for_blink %}
-#include <string>
-#include <vector>
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/string.h"
 {%- else %}
-{# hash_util.h includes template specializations that should be present for
-   every use of {Inlined}StructPtr. #}
-#include "mojo/public/cpp/bindings/lib/wtf_hash_util.h"
-#include "third_party/WebKit/Source/wtf/HashFunctions.h"
+#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 %}
@@ -82,32 +85,42 @@
 #include "{{header}}"
 {%- endfor %}
 
-{%- if export_header %}
-#include "{{export_header}}"
-{%- endif %}
-
-{#--- WTF enum hashing #}
-{%- from "enum_macros.tmpl" import enum_hash_blink%}
-{%- if for_blink %}
-{%-   for enum in all_enums %}
-{%-     if not enum|is_native_only_kind %}
-{{enum_hash_blink(enum)}}
+{#--- 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()}}
 
-{#--- Enums #}
-{%- if variant %}
-{%-   for enum in enums %}
-using {{enum.name}} = {{enum.name}};  // Alias for definition in the parent namespace.
-{%-   endfor %}
-{%- endif %}
-
 {#--- Constants #}
 {%- for constant in module.constants %}
-{{constant|format_constant_declaration}};
+{#-   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 -#}
@@ -115,13 +128,9 @@
 class {{interface.name}};
 using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>;
 using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>;
-using ThreadSafe{{interface.name}}Ptr =
-    mojo::ThreadSafeInterfacePtr<{{interface.name}}>;
 using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>;
 using {{interface.name}}AssociatedPtr =
     mojo::AssociatedInterfacePtr<{{interface.name}}>;
-using ThreadSafe{{interface.name}}AssociatedPtr =
-    mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>;
 using {{interface.name}}AssociatedPtrInfo =
     mojo::AssociatedInterfacePtrInfo<{{interface.name}}>;
 using {{interface.name}}AssociatedRequest =
@@ -135,6 +144,7 @@
 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 %}
@@ -156,6 +166,27 @@
 {#--- 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 -#}
@@ -185,6 +216,7 @@
 {%  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 %}
 
@@ -199,6 +231,7 @@
 {%  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 %}
 
@@ -210,23 +243,67 @@
 {%-   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_traits_declaration.tmpl" %}
+{%      include "struct_serialization_declaration.tmpl" %}
 {%-   endif %}
 {%- endfor %}
 
 {#--- Union Serialization Helpers -#}
 {%  if unions %}
 {%-   for union in unions %}
-{%      include "union_traits_declaration.tmpl" %}
+{%      include "union_serialization_declaration.tmpl" %}
 {%-   endfor %}
 {%- endif %}
 
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
index 96e0d61..7c34939 100644
--- 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
@@ -1,56 +1,29 @@
 class {{struct.name}}DataView {
  public:
-  {{struct.name}}DataView() {}
-
   {{struct.name}}DataView(
       internal::{{struct.name}}_Data* data,
-      mojo::internal::SerializationContext* context)
-{%- if struct|requires_context_for_data_view %}
-      : data_(data), context_(context) {}
-{%- else %}
-      : data_(data) {}
-{%- endif %}
-
-  bool is_null() const { return !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_union_kind %}
-  inline void Get{{name|under_to_camel}}DataView(
-      {{kind|cpp_data_view_type}}* output);
-
+{%-   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>
-  WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
-{%-     if pf.min_version != 0 %}
-    auto* pointer = data_->header_.version >= {{pf.min_version}}
-                    ? &data_->{{name}} : nullptr;
-{%-     else %}
-    auto* pointer = &data_->{{name}};
-{%-     endif %}
+  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, output, context_);
-  }
-
-{%-   elif kind|is_object_kind %}
-  inline void Get{{name|under_to_camel}}DataView(
-      {{kind|cpp_data_view_type}}* output);
-
-  template <typename UserType>
-  WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
-{%-     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, output, context_);
+        pointer, value, context_);
   }
 
 {%-   elif kind|is_enum_kind %}
   template <typename UserType>
-  WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const {
+  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;
@@ -58,61 +31,23 @@
     auto data_value = data_->{{name}};
 {%-     endif %}
     return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
-        data_value, output);
+        data_value, value);
   }
 
-  {{kind|cpp_data_view_type}} {{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|cpp_data_view_type}}>(data_->{{name}});
-  }
+  {{kind|get_qualified_name_for_kind}} {{name}}() const;
 
-{%-   elif kind|is_any_handle_kind %}
-  {{kind|cpp_data_view_type}} Take{{name|under_to_camel}}() {
-    {{kind|cpp_data_view_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;
-  }
+{%-   elif kind|is_union_kind %}
+  bool Read{{name|under_to_camel}}({{kind|cpp_wrapper_type}}* value);
 
-{%-   elif kind|is_any_interface_kind %}
-  template <typename UserType>
-  UserType Take{{name|under_to_camel}}() {
-    UserType 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;
-  }
+{%-   elif kind|is_any_handle_or_interface_kind %}
+  {{kind|cpp_wrapper_type}} Take{{name|under_to_camel}}();
 
 {%-   else %}
-  {{kind|cpp_data_view_type}} {{name}}() const {
-{%-     if pf.min_version != 0 %}
-    if (data_->header_.version < {{pf.min_version}})
-      return {{kind|cpp_data_view_type}}{};
-{%-     endif %}
-    return data_->{{name}};
-  }
-
+  {{kind|cpp_wrapper_type}} {{name}}() const;
 {%-   endif %}
 {%- endfor %}
  private:
-  internal::{{struct.name}}_Data* data_ = nullptr;
-{%- if struct|requires_context_for_data_view %}
-  mojo::internal::SerializationContext* context_ = nullptr;
-{%- endif %}
+  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
index 95311dc..2be92b3 100644
--- 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
@@ -1,29 +1,59 @@
-{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
-{%-   set kind = pf.field.kind %}
-{%-   set name = pf.field.name %}
-
-{%-   if kind|is_union_kind %}
-inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView(
-    {{kind|cpp_data_view_type}}* output) {
-{%-     if pf.min_version != 0 %}
-  auto pointer = data_->header_.version >= {{pf.min_version}}
-                 ? &data_->{{name}} : nullptr;
-{%-     else %}
-  auto pointer = &data_->{{name}};
-{%-     endif %}
-  *output = {{kind|cpp_data_view_type}}(pointer, context_);
+{{struct.name}}DataView::{{struct.name}}DataView(
+    internal::{{struct.name}}_Data*  data,
+    mojo::internal::SerializationContext* context)
+    : data_(data), context_(context) {
+  DCHECK(data_);
 }
 
-{%-   elif kind|is_object_kind %}
-inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView(
-    {{kind|cpp_data_view_type}}* output) {
+{%- 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 %}
-  auto pointer = data_->header_.version >= {{pf.min_version}}
-                 ? data_->{{name}}.Get() : nullptr;
-{%-     else %}
-  auto pointer = data_->{{name}}.Get();
+  if (data_->header_.version < {{pf.min_version}})
+    return {{kind|get_qualified_name_for_kind}}{};
 {%-     endif %}
-  *output = {{kind|cpp_data_view_type}}(pointer, context_);
+  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
index 156f774..dd87be7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
@@ -2,13 +2,21 @@
 
 class {{class_name}} {
  public:
-  static {{class_name}}* New(mojo::internal::Buffer* buf) {
-    return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
-  }
+  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 %}
@@ -38,8 +46,7 @@
 {%- endif %}
 
  private:
-  {{class_name}}() : header_({sizeof(*this), {{struct.versions[-1].version}}}) {
-  }
+  {{class_name}}();
   ~{{class_name}}() = delete;
 };
 static_assert(sizeof({{class_name}}) == {{struct.versions[-1].num_bytes}},
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
index 60dca40..a2e5708 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
@@ -2,6 +2,11 @@
 {%- 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) {
@@ -15,7 +20,7 @@
   // the message comes from an older version.
   const {{class_name}}* object = static_cast<const {{class_name}}*>(data);
 
-  static constexpr struct {
+  static const struct {
     uint32_t version;
     uint32_t num_bytes;
   } kVersionSizes[] = {
@@ -68,3 +73,7 @@
   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
index bb5fb9c..c59f317 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -19,8 +19,7 @@
 {%- 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 or pf.field.kind|is_associated_kind %}
+{%-   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) %}
@@ -149,11 +148,8 @@
 {%-     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_kind %}
+{%-     elif kind|is_any_handle_or_interface_kind %}
   {{output_field}} = {{input}}.Take{{name|under_to_camel}}();
-{%-     elif kind|is_any_interface_kind %}
-  {{output_field}} =
-      {{input}}.Take{{name|under_to_camel}}<decltype({{output_field}})>();
 {%-     else %}
   {{output_field}} = {{input}}.{{name}}();
 {%-     endif %}
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
index 835178b..aede1a7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
@@ -1,13 +1,37 @@
 {%- import "struct_macros.tmpl" as struct_macros %}
-{%- set data_view = struct|get_qualified_name_for_kind ~ "DataView" %}
+{%- 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<{{data_view}}, MaybeConstUserType> {
+struct Serializer<{{mojom_type}}Ptr, MaybeConstUserType> {
   using UserType = typename std::remove_const<MaybeConstUserType>::type;
-  using Traits = StructTraits<{{data_view}}, UserType>;
+  using Traits = StructTraits<{{mojom_type}}, UserType>;
 
   static size_t PrepareToSerialize(MaybeConstUserType& input,
                                    SerializationContext* context) {
@@ -37,7 +61,7 @@
     {{struct_macros.serialize(
           struct, struct.name ~ " struct",
           "CallWithContext(Traits::%s, input, custom_context)", "result",
-          "buffer", "context", True)|indent(2)}}
+          "buffer", "context", True)|indent(4)}}
     *output = result;
 
     CustomContextHelper<Traits>::TearDown(input, custom_context);
@@ -49,7 +73,7 @@
     if (!input)
       return CallSetToNullIfExists<Traits>(output);
 
-    {{data_view}} data_view(input, context);
+    {{mojom_type}}DataView data_view(input, context);
     return Traits::Read(data_view, output);
   }
 };
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
similarity index 78%
rename from mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl
rename to mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
index f84337f..7421abc 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
@@ -2,8 +2,8 @@
 {%- set mojom_type = struct|get_qualified_name_for_kind %}
 
 // static
-bool StructTraits<{{mojom_type}}::DataView, {{mojom_type}}Ptr>::Read(
-    {{mojom_type}}::DataView input,
+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());
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl
deleted file mode 100644
index 1b7cf89..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl
+++ /dev/null
@@ -1,32 +0,0 @@
-{%- set mojom_type = struct|get_qualified_name_for_kind %}
-
-template <>
-struct {{export_attribute}} StructTraits<{{mojom_type}}::DataView,
-                                         {{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 %}
-{#    We want the field accessor to be const whenever possible to allow
-      structs to be used as map keys.
-      TODO(tibell): Make this check more precise to deal with e.g.
-          custom types which don't contain handles but require non-const
-          reference for serialization. #}
-{%-   set maybe_const = "" if field.kind|contains_handles_or_interfaces else "const" %}
-{%-   if return_ref %}
-  static {{maybe_const}} decltype({{mojom_type}}::{{field.name}})& {{field.name}}(
-      {{maybe_const}} {{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);
-};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl
deleted file mode 100644
index 2a9e02d..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl
+++ /dev/null
@@ -1,91 +0,0 @@
-class {{union.name}}DataView {
- public:
-  using Tag = internal::{{union.name}}_Data::{{union.name}}_Tag;
-
-  {{union.name}}DataView() {}
-
-  {{union.name}}DataView(
-      internal::{{union.name}}_Data* data,
-      mojo::internal::SerializationContext* context)
-{%- if union|requires_context_for_data_view %}
-      : data_(data), context_(context) {}
-{%- else %}
-      : data_(data) {}
-{%- endif %}
-
-  bool is_null() const {
-    // For inlined unions, |data_| is always non-null. In that case we need to
-    // check |data_->is_null()|.
-    return !data_ || data_->is_null();
-  }
-
-  Tag tag() const { return data_->tag; }
-
-{%- for field in union.fields %}
-{%-   set kind = field.kind %}
-{%-   set name = field.name %}
-  bool is_{{name}}() const { return data_->tag == Tag::{{name|upper}}; }
-
-{%-   if kind|is_object_kind %}
-  inline void Get{{name|under_to_camel}}DataView(
-      {{kind|cpp_data_view_type}}* output);
-
-  template <typename UserType>
-  WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
-    DCHECK(is_{{name}}());
-    return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
-        data_->data.f_{{name}}.Get(), output, context_);
-  }
-
-{%-   elif kind|is_enum_kind %}
-  template <typename UserType>
-  WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const {
-    DCHECK(is_{{name}}());
-    return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
-        data_->data.f_{{name}}, output);
-  }
-
-  {{kind|cpp_data_view_type}} {{name}}() const {
-    DCHECK(is_{{name}}());
-    return static_cast<{{kind|cpp_data_view_type}}>(
-        data_->data.f_{{name}});
-  }
-
-{%-   elif kind|is_any_handle_kind %}
-  {{kind|cpp_data_view_type}} Take{{name|under_to_camel}}() {
-    DCHECK(is_{{name}}());
-    {{kind|cpp_data_view_type}} result;
-    bool ret =
-        mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
-            &data_->data.f_{{name}}, &result, context_);
-    DCHECK(ret);
-    return result;
-  }
-
-{%-   elif kind|is_any_interface_kind %}
-  template <typename UserType>
-  UserType Take{{name|under_to_camel}}() {
-    DCHECK(is_{{name}}());
-    UserType result;
-    bool ret =
-        mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
-            &data_->data.f_{{name}}, &result, context_);
-    DCHECK(ret);
-    return result;
-  }
-
-{%-   else %}
-  {{kind|cpp_data_view_type}} {{name}}() const {
-    DCHECK(is_{{name}}());
-    return data_->data.f_{{name}};
-  }
-
-{%-   endif %}
-{%- endfor %}
-
- private:
-  internal::{{union.name}}_Data* data_ = nullptr;
-{%- if union|requires_context_for_data_view %}
-  mojo::internal::SerializationContext* context_ = nullptr;
-{%- endif %}
-};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl
deleted file mode 100644
index 6da9280..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl
+++ /dev/null
@@ -1,12 +0,0 @@
-{%- for field in union.fields %}
-{%-   set kind = field.kind %}
-{%-   set name = field.name %}
-
-{%-   if kind|is_object_kind %}
-inline void {{union.name}}DataView::Get{{name|under_to_camel}}DataView(
-    {{kind|cpp_data_view_type}}* output) {
-  DCHECK(is_{{name}}());
-  *output = {{kind|cpp_data_view_type}}(data_->data.f_{{name}}.Get(), context_);
-}
-{%-   endif %}
-{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
index 005ba76..be2db4c 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
@@ -6,28 +6,21 @@
  public:
   // Used to identify Mojom Union Data Classes.
   typedef void MojomUnionDataType;
-
-  {{class_name}}() {}
-  // Do nothing in the destructor since it won't be called when it is a
-  // non-inlined union.
+  static {{class_name}}* New(mojo::internal::Buffer* buf);
+  {{class_name}}();
+  // Do nothing in the destructor since it won't be called.
   ~{{class_name}}() {}
 
-  static {{class_name}}* New(mojo::internal::Buffer* buf) {
-    return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
-  }
-
   static bool Validate(const void* data,
                        mojo::internal::ValidationContext* validation_context,
                        bool inlined);
 
-  bool is_null() const { return size == 0; }
-
-  void set_null() {
-    size = 0U;
-    tag = static_cast<{{enum_name}}>(0);
-    data.unknown = 0U;
+  bool is_null() const {
+    return size == 0;
   }
 
+  void set_null();
+
   enum class {{enum_name}} : uint32_t {
 {%  for field in union.fields %}
     {{field.name|upper}},
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
index af5ea9f..fc6eddf 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
@@ -3,31 +3,24 @@
 {%- 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) {
-    DCHECK(!inlined);
+  if (!data)
     return true;
-  }
 
-  // If it is inlined, the alignment is already enforced by its enclosing
-  // object. We don't have to validate that.
-  DCHECK(!inlined || mojo::internal::IsAligned(data));
-
-  if (!inlined &&
-      !mojo::internal::ValidateNonInlinedUnionHeaderAndClaimMemory(
-          data, validation_context)) {
+  if (!ValidateUnionHeaderAndClaimMemory(data, inlined, validation_context))
     return false;
-  }
 
   const {{class_name}}* object = static_cast<const {{class_name}}*>(data);
   ALLOW_UNUSED_LOCAL(object);
 
-  if (inlined && object->is_null())
-    return true;
-
   switch (object->tag) {
 {%  for field in union.fields %}
     case {{enum_name}}::{{field.name|upper}}: {
@@ -45,3 +38,13 @@
     }
   }
 }
+
+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
index b589ae9..bab9045 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
@@ -1,141 +1,37 @@
-{%- set data_view = union|get_qualified_name_for_kind ~ "DataView" %}
+{%- set mojom_type = union|get_qualified_name_for_kind %}
 {%- set data_type = union|get_qualified_name_for_kind(internal=True) %}
 
 namespace internal {
 
-template <typename MaybeConstUserType>
-struct Serializer<{{data_view}}, MaybeConstUserType> {
-  using UserType = typename std::remove_const<MaybeConstUserType>::type;
-  using Traits = UnionTraits<{{data_view}}, UserType>;
+template <typename MojomType>
+struct UnionSerializerImpl;
 
-  static size_t PrepareToSerialize(MaybeConstUserType& input,
+template <>
+struct UnionSerializerImpl<{{mojom_type}}Ptr> {
+  static size_t PrepareToSerialize({{mojom_type}}Ptr& input,
                                    bool inlined,
-                                   SerializationContext* context) {
-    size_t size = inlined ? 0 : sizeof({{data_type}});
+                                   SerializationContext* context);
 
-    if (CallIsNullIfExists<Traits>(input))
-      return size;
-
-    void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
-    ALLOW_UNUSED_LOCAL(custom_context);
-
-    switch (CallWithContext(Traits::GetTag, input, custom_context)) {
-{%- for field in union.fields %}
-{%-   set name = field.name %}
-      case {{data_view}}::Tag::{{name|upper}}: {
-{%-   if field.kind|is_object_kind or field.kind|is_associated_kind %}
-{%-     set kind = field.kind %}
-{%-     set serializer_type = kind|unmapped_type_for_serializer %}
-        decltype(CallWithContext(Traits::{{name}}, input, custom_context))
-            in_{{name}} = CallWithContext(Traits::{{name}}, input,
-                                          custom_context);
-{%-     if kind|is_union_kind %}
-        size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
-            in_{{name}}, false, context);
-{%-     else %}
-        size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
-            in_{{name}}, context);
-{%-     endif %}
-{%-   endif %}
-        break;
-      }
-{%- endfor %}
-    }
-    return size;
-  }
-
-  static void Serialize(MaybeConstUserType& input,
+  static void Serialize({{mojom_type}}Ptr& input,
                         Buffer* buffer,
                         {{data_type}}** output,
                         bool inlined,
-                        SerializationContext* context) {
-    if (CallIsNullIfExists<Traits>(input)) {
-      if (inlined)
-        (*output)->set_null();
-      else
-        *output = nullptr;
-      return;
-    }
-
-    void* custom_context = CustomContextHelper<Traits>::GetNext(context);
-
-    if (!inlined)
-      *output = {{data_type}}::New(buffer);
-
-    {{data_type}}* result = *output;
-    ALLOW_UNUSED_LOCAL(result);
-    // TODO(azani): Handle unknown and objects.
-    // Set the not-null flag.
-    result->size = kUnionDataSize;
-    result->tag = CallWithContext(Traits::GetTag, input, custom_context);
-    switch (result->tag) {
-{%- for field in union.fields %}
-{%-   set name = field.name %}
-{%-   set kind = field.kind %}
-{%-   set serializer_type = kind|unmapped_type_for_serializer %}
-      case {{data_view}}::Tag::{{field.name|upper}}: {
-        decltype(CallWithContext(Traits::{{name}}, input, custom_context))
-            in_{{name}} = CallWithContext(Traits::{{name}}, input,
-                                          custom_context);
-{%-   if kind|is_object_kind %}
-        typename decltype(result->data.f_{{name}})::BaseType* ptr;
-{%-     if kind|is_union_kind %}
-        mojo::internal::Serialize<{{serializer_type}}>(
-            in_{{name}}, buffer, &ptr, false, context);
-{%-     elif kind|is_array_kind or kind|is_map_kind %}
-        const ContainerValidateParams {{name}}_validate_params(
-            {{kind|get_container_validate_params_ctor_args|indent(16)}});
-        mojo::internal::Serialize<{{serializer_type}}>(
-            in_{{name}}, buffer, &ptr, &{{name}}_validate_params, context);
-{%-     else %}
-        mojo::internal::Serialize<{{serializer_type}}>(
-            in_{{name}}, buffer, &ptr, context);
-{%-     endif %}
-        result->data.f_{{name}}.Set(ptr);
-{%-     if not kind|is_nullable_kind %}
-        MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
-            !ptr, mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
-            "null {{name}} in {{union.name}} union");
-{%-     endif %}
-
-{%-   elif kind|is_any_handle_or_interface_kind %}
-        mojo::internal::Serialize<{{serializer_type}}>(
-            in_{{name}}, &result->data.f_{{name}}, context);
-{%-     if not kind|is_nullable_kind %}
-        MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
-            !mojo::internal::IsHandleOrInterfaceValid(result->data.f_{{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 {{union.name}} union");
-{%-     endif %}
-
-{%-   elif kind|is_enum_kind %}
-        mojo::internal::Serialize<{{serializer_type}}>(
-            in_{{name}}, &result->data.f_{{name}});
-
-{%-   else %}
-        result->data.f_{{name}} = in_{{name}};
-{%-   endif %}
-        break;
-      }
-{%- endfor %}
-    }
-
-    CustomContextHelper<Traits>::TearDown(input, custom_context);
-  }
+                        SerializationContext* context);
 
   static bool Deserialize({{data_type}}* input,
-                          UserType* output,
-                          SerializationContext* context) {
-    if (!input || input->is_null())
-      return CallSetToNullIfExists<Traits>(output);
+                          {{mojom_type}}Ptr* output,
+                          SerializationContext* context);
+};
 
-    {{data_view}} data_view(input, context);
-    return Traits::Read(data_view, output);
-  }
+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/union_traits_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl
deleted file mode 100644
index 4933e57..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl
+++ /dev/null
@@ -1,24 +0,0 @@
-{%- set mojom_type = union|get_qualified_name_for_kind %}
-
-template <>
-struct {{export_attribute}} UnionTraits<{{mojom_type}}::DataView,
-                                        {{mojom_type}}Ptr> {
-  static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; }
-  static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); }
-
-  static {{mojom_type}}::Tag GetTag(const {{mojom_type}}Ptr& input) {
-    return input->which();
-  }
-
-{%- for field in union.fields %}
-{%-   set maybe_const_in = "" if field.kind|contains_handles_or_interfaces else "const" %}
-{%-   set maybe_const_out = "" if field.kind|contains_handles_or_interfaces or not field.kind|is_reference_kind else "const" %}
-{#    We want the field accessor to be const whenever possible to allow
-      structs to be used as map keys. #}
-  static {{maybe_const_out}} {{field.kind|cpp_union_trait_getter_return_type}} {{field.name}}({{maybe_const_in}} {{mojom_type}}Ptr& input) {
-    return input->get_{{field.name}}();
-  }
-{%- endfor %}
-
-  static bool Read({{mojom_type}}::DataView input, {{mojom_type}}Ptr* output);
-};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl
deleted file mode 100644
index cde3f95..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl
+++ /dev/null
@@ -1,47 +0,0 @@
-{%- set mojom_type = union|get_qualified_name_for_kind %}
-
-// static
-bool UnionTraits<{{mojom_type}}::DataView, {{mojom_type}}Ptr>::Read(
-    {{mojom_type}}::DataView input,
-    {{mojom_type}}Ptr* output) {
-  *output = {{mojom_type}}::New();
-  {{mojom_type}}Ptr& result = *output;
-
-  internal::UnionAccessor<{{mojom_type}}> result_acc(result.get());
-  switch (input.tag()) {
-{%- for field in union.fields %}
-    case {{mojom_type}}::Tag::{{field.name|upper}}: {
-{%-   set name = field.name %}
-{%-   set kind = field.kind %}
-{%-   set serializer_type = kind|unmapped_type_for_serializer %}
-{%-   if kind|is_object_kind %}
-      result_acc.SwitchActive({{mojom_type}}::Tag::{{name|upper}});
-      if (!input.Read{{name|under_to_camel}}(result_acc.data()->{{name}}))
-        return false;
-
-{%-   elif kind|is_any_handle_kind %}
-      auto result_{{name}} = input.Take{{name|under_to_camel}}();
-      result->set_{{name}}(std::move(result_{{name}}));
-
-{%-   elif kind|is_any_interface_kind %}
-      auto result_{{name}} =
-          input.Take{{name|under_to_camel}}<typename std::remove_reference<decltype(result->get_{{name}}())>::type>();
-      result->set_{{name}}(std::move(result_{{name}}));
-
-{%-   elif kind|is_enum_kind %}
-      decltype(result->get_{{name}}()) result_{{name}};
-      if (!input.Read{{name|under_to_camel}}(&result_{{name}}))
-        return false;
-      result->set_{{name}}(result_{{name}});
-
-{%-   else %}
-      result->set_{{name}}(input.{{name}}());
-{%-   endif %}
-      break;
-    }
-{%- endfor %}
-    default:
-      return false;
-  }
-  return true;
-}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
index a50a585..367be6d 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
@@ -66,7 +66,7 @@
 {#- 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,flatten_nested_kind=True)}}
+  if (!{{field.kind|get_qualified_name_for_kind(internal=True)}}
         ::Validate({{field_expr}}, validation_context))
     return false;
 {%- 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
index 9e6e46f..98ae23c 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -1,24 +1,29 @@
-class {{export_attribute}} {{struct.name}} {
+{% 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 -%}
-  using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}};
+{%-   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 %}
-  static {{constant|format_constant_declaration(nested=True)}};
+{%-   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 %}
 
-  template <typename... Args>
-  static {{struct.name}}Ptr New(Args&&... args) {
-    return {{struct.name}}Ptr(
-        base::in_place,
-        std::forward<Args>(args)...);
-  }
+  static {{struct.name}}Ptr New();
 
   template <typename U>
   static {{struct.name}}Ptr From(const U& u) {
@@ -30,15 +35,7 @@
     return mojo::TypeConverter<U, {{struct.name}}>::Convert(*this);
   }
 
-{% for constructor in struct|struct_constructors %}
-  {% if constructor.params|length == 1 %}explicit {% endif %}{{struct.name}}(
-{%-   for field in constructor.params %}
-{%-     set type = field.kind|cpp_wrapper_param_type %}
-{%-     set name = field.name %}
-      {{type}} {{name}}
-{%-     if not loop.last -%},{%- endif %}
-{%-   endfor %});
-{% endfor %}
+  {{struct.name}}();
   ~{{struct.name}}();
 
   // Clone() is a template so it is only instantiated if it is used. Thus, the
@@ -55,24 +52,20 @@
                 T, {{struct.name}}>::value>::type* = nullptr>
   bool Equals(const T& other) const;
 
-{%- if struct|is_hashable %}
-  size_t Hash(size_t seed) const;
-{%- endif %}
-
-{%- set serialization_result_type = "WTF::Vector<uint8_t>"
-        if for_blink else "std::vector<uint8_t>" %}
+{%- 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}}::DataView, {{serialization_result_type}}>(input);
+        {{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}}::DataView, {{serialization_result_type}}>(
+        {{struct.name}}Ptr, {{serialization_result_type}}>(
             input, output);
   }
 
@@ -82,10 +75,5 @@
 {%-   set name = field.name %}
   {{type}} {{name}};
 {%- endfor %}
-
-{%- if struct|contains_move_only_members %}
- private:
-  DISALLOW_COPY_AND_ASSIGN({{struct.name}});
-{%- endif %}
 };
 
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
index e75543f..0bb1cda 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
@@ -1,33 +1,15 @@
-{% for constructor in struct|struct_constructors %}
-{{struct.name}}::{{struct.name}}(
-{%-  for field in constructor.params  %}
-{%-    set type = field.kind|cpp_wrapper_param_type %}
-{%-    set name = field.name %}
-    {{type}} {{name}}_in
-{%-    if not loop.last -%},{%- endif %}
-{%-  endfor %})
-{%-  for field, is_parameter in constructor.fields %}
-{%-    set name = field.name %}
-    {% if loop.first %}:{% else %} {% endif %} {{name}}(
-{%-    if is_parameter -%}
-std::move({{name}}_in)
-{%-    else -%}
-{{ field|default_value }}
-{%-    endif -%}
-){%  if not loop.last %},{% endif %}
-{%-  endfor %} {}
-{% endfor %}
-{{struct.name}}::~{{struct.name}}() = default;
-
-{%- if struct|is_hashable %}
-size_t {{struct.name}}::Hash(size_t seed) const {
-{%-   for field in struct.fields %}
-{%-     if for_blink %}
-  seed = mojo::internal::WTFHash(seed, this->{{field.name}});
-{%-     else %}
-  seed = mojo::internal::Hash(seed, this->{{field.name}});
-{%-     endif %}
-{%-   endfor %}
-  return seed;
+// static
+{{struct.name}}Ptr {{struct.name}}::New() {
+  {{struct.name}}Ptr rv;
+  mojo::internal::StructHelper<{{struct.name}}>::Initialize(&rv);
+  return rv;
 }
-{%- endif %}
+
+{{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
index feb8615..f4aa314 100644
--- 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
@@ -1,11 +1,12 @@
 template <typename StructPtrType>
 {{struct.name}}Ptr {{struct.name}}::Clone() const {
-  return New(
+  // Use StructPtrType to prevent the compiler from trying to compile this
+  // without being asked.
+  StructPtrType rv(New());
 {%- for field in struct.fields %}
-      mojo::Clone({{field.name}})
-{%-   if not loop.last -%},{%- endif %}
+  rv->{{field.name}} = mojo::internal::Clone({{field.name}});
 {%- endfor %}
-  );
+  return rv;
 }
 
 template <typename T,
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
index 8b7cf9e..f62bc72 100644
--- 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
@@ -1,6 +1,5 @@
-class {{export_attribute}} {{union.name}} {
+class {{union.name}} {
  public:
-  using DataView = {{union.name}}DataView;
   using Data_ = internal::{{union.name}}_Data;
   using Tag = Data_::{{union.name}}_Tag;
 
@@ -33,27 +32,13 @@
                 T, {{union.name}}>::value>::type* = nullptr>
   bool Equals(const T& other) const;
 
-{%- if union|is_hashable %}
-  size_t Hash(size_t seed) const;
-{%- endif %}
-
   Tag which() const {
     return tag_;
   }
 
 {%  for field in union.fields %}
-  bool is_{{field.name}}() const { return tag_ == Tag::{{field.name|upper}}; }
-
-  {{field.kind|cpp_union_getter_return_type}} 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 %}
-  }
-
+  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 %}
 
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
index b9e416a..85cc4e6 100644
--- 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
@@ -1,6 +1,8 @@
 // static
 {{union.name}}Ptr {{union.name}}::New() {
-  return {{union.name}}Ptr(base::in_place);
+  {{union.name}}Ptr rv;
+  mojo::internal::StructHelper<{{union.name}}>::Initialize(&rv);
+  return rv;
 }
 
 {{union.name}}::{{union.name}}() {
@@ -14,6 +16,20 @@
 }
 
 {%  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 %}
@@ -63,23 +79,3 @@
 {%- endfor %}
   }
 }
-
-{%- if union|is_hashable %}
-size_t {{union.name}}::Hash(size_t seed) const {
-  seed = mojo::internal::HashCombine(seed, static_cast<uint32_t>(tag_));
-  switch (tag_) {
-{%  for field in union.fields %}
-    case Tag::{{field.name|upper}}:
-{%-   if for_blink %}
-      return mojo::internal::WTFHash(seed, data_.{{field.name}});
-{%-   else %}
-      return mojo::internal::Hash(seed, data_.{{field.name}});
-{%-   endif %}
-{%- endfor %}
-    default:
-      NOTREACHED();
-      return seed;
-  }
-}
-
-{%- endif %}
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
index 4c4851f..d3371d9 100644
--- 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
@@ -8,9 +8,9 @@
     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::Clone(*data_.{{field.name}}));
+      rv->set_{{field.name}}(mojo::internal::Clone(*data_.{{field.name}}));
 {%-   else %}
-      rv->set_{{field.name}}(mojo::Clone(data_.{{field.name}}));
+      rv->set_{{field.name}}(mojo::internal::Clone(data_.{{field.name}}));
 {%-   endif %}
       break;
 {%- endfor %}
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
index 4c0823c..5cf9a68 100644
--- a/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
@@ -1,19 +1,19 @@
-{%- from "constant_definition.tmpl" import constant_def %}
-{%- from "enum_definition.tmpl" import enum_def %}
+{% 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}})
+{% if ne %}!{% endif %}java.util.Arrays.deepEquals({{v1}}, {{v2}})
 {%-       else -%}
-{%- if ne %}!{%- endif %}java.util.Arrays.equals({{v1}}, {{v2}})
+{% if ne %}!{% endif %}java.util.Arrays.equals({{v1}}, {{v2}})
 {%-       endif -%}
 {%-     else -%}
-{%- if ne %}!{%- endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}})
+{% if ne %}!{% endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}})
 {%-     endif -%}
 {%-   else -%}
-{{v1}} {%- if ne %}!={%- else %}=={%- endif %} {{v2}}
+{{v1}} {% if ne %}!={% else %}=={% endif %} {{v2}}
 {%-   endif -%}
 {%- endmacro -%}
 
@@ -37,27 +37,27 @@
 {%-  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 %}
+{% 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 %}
+{%   else %}
 {
-{%-   endif %}
-{%-   if kind|is_pointer_array_kind %}
-{%-     set encodePointer = 'encodePointerArray' %}
-{%-   else %}
-{%-     set encodePointer = 'encodeUnionArray' %}
-{%-   endif %}
+{%   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 %}
+{% elif kind|is_map_kind %}
 if ({{variable}} == null) {
     encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
 } else {
@@ -74,28 +74,28 @@
     {{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 %}
+{% else %}
 encoder{{level}}.{{kind|encode_method(variable, offset, bit)}};
-{%- endif %}
-{%- endmacro %}
+{% endif %}
+{% endmacro %}
 
-{%- macro decode(variable, kind, offset, bit, level=0) %}
-{%- if kind|is_struct_kind or
+{% 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 %}
+{%   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 %}
+{%   else %}{# kind|is_pointer_array_kind or is_map_kind #}
+{%     if kind|is_nullable_kind %}
 if (decoder{{level+1}} == null) {
     {{variable}} = null;
 } else {
-{%-     else %}
+{%     else %}
 {
-{%-     endif %}
-{%-     if kind|is_map_kind %}
+{%     endif %}
+{%     if kind|is_map_kind %}
     decoder{{level+1}}.readDataHeaderForMap();
     {{kind.key_kind|java_type}}[] keys{{level}};
     {{kind.value_kind|java_type}}[] values{{level}};
@@ -109,69 +109,69 @@
     for (int index{{level}} = 0; index{{level}} < keys{{level}}.length; ++index{{level}}) {
         {{variable}}.put(keys{{level}}[index{{level}}],  values{{level}}[index{{level}}]);
     }
-{%-     else %}
+{%     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 %}
 }
-{%-   endif %}
-{%- elif kind|is_union_kind %}
+{%   endif %}
+{% elif kind|is_union_kind %}
 {{variable}} = {{kind|java_type}}.decode(decoder{{level}}, {{offset}});
-{%- else %}
+{% 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 kind|is_array_kind and kind.kind|is_enum_kind %}
+{%     if kind|is_nullable_kind %}
 if ({{variable}} != null) {
-{%-     else %}
+{%     else %}
 {
-{%-     endif %}
+{%     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 %}
+{%   elif kind|is_enum_kind %}
     {{kind|java_class_for_enum}}.validate({{variable}});
-{%-   endif %}
-{%- endif %}
-{%- endmacro %}
+{%   endif %}
+{% endif %}
+{% endmacro %}
 
-{%- macro struct_def(struct, inner_class=False) %}
+{% 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 -%}
+        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 %}
+{%  for constant in struct.constants %}
 
     {{constant_def(constant)|indent(4)}}
-{%-  endfor %}
-{%-  for enum in struct.enums %}
+{%  endfor %}
+{%  for enum in struct.enums %}
 
     {{enum_def(enum, false)|indent(4)}}
-{%- endfor %}
-{%- if struct.fields %}
+{% endfor %}
+{% if struct.fields %}
 
-{%-   for field in struct.fields %}
+{%   for field in struct.fields %}
     public {{field.kind|java_type}} {{field|name}};
-{%-   endfor %}
-{%- endif %}
+{%   endfor %}
+{% endif %}
 
     private {{struct|name}}(int version) {
         super(STRUCT_SIZE, version);
-{%- for field in struct.fields %}
-{%-   if field.default %}
+{% for field in struct.fields %}
+{%   if field.default %}
         {{field|name}} = {{field|default_value}};
-{%-   elif field.kind|is_any_handle_kind %}
+{%   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 %}
+{%   endif %}
+{% endfor %}
     }
 
     public {{struct|name}}() {
@@ -182,55 +182,36 @@
         return decode(new org.chromium.mojo.bindings.Decoder(message));
     }
 
-    /**
-     * Similar to the method above, but deserializes from a |ByteBuffer| instance.
-     *
-     * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure.
-     */
-    public static {{struct|name}} deserialize(java.nio.ByteBuffer data) {
-        if (data == null)
-            return null;
-
-        return deserialize(new org.chromium.mojo.bindings.Message(
-                data, new java.util.ArrayList<org.chromium.mojo.system.Handle>()));
-    }
-
     @SuppressWarnings("unchecked")
     public static {{struct|name}} decode(org.chromium.mojo.bindings.Decoder decoder0) {
         if (decoder0 == null) {
             return null;
         }
-        decoder0.increaseStackDepth();
-        {{struct|name}} result;
-        try {
-            org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY);
-            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(16)}}
-            }
-{%-   endfor %}
-{%- endfor %}
-        } finally {
-            decoder0.decreaseStackDepth();
+        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 %}
+{% if not struct.bytes %}
         encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO);
-{%- else %}
+{% 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 %}
+{% 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 %}
+{%   endfor %}
+{% endfor %}
     }
 
     /**
@@ -244,13 +225,13 @@
             return false;
         if (getClass() != object.getClass())
             return false;
-{%- if struct.fields|length %}
+{% if struct.fields|length %}
         {{struct|name}} other = ({{struct|name}}) object;
-{%-   for field in struct.fields %}
+{%   for field in struct.fields %}
         if ({{equality(field.kind, 'this.'~field|name, 'other.'~field|name, True)}})
             return false;
-{%-   endfor %}
-{%- endif %}
+{%   endfor %}
+{% endif %}
         return true;
     }
 
@@ -261,28 +242,28 @@
     public int hashCode() {
         final int prime = 31;
         int result = prime + getClass().hashCode();
-{%- for field in struct.fields %}
+{% for field in struct.fields %}
         result = prime * result + {{hash(field.kind, field|name)}};
-{%- endfor %}
+{% endfor %}
         return result;
     }
 }
-{%- endmacro %}
+{% endmacro %}
 
 
-{%- macro union_def(union) %}
+{% 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 %}
+{%   for field in union.fields %}
         public static final int {{field|ucc}} = {{loop.index0}};
-{%-   endfor %}
+{%   endfor %}
     };
 
     private int mTag_ = -1;
-{%-   for field in union.fields %}
+{%   for field in union.fields %}
     private {{field.kind|java_type}} m{{field|ucc}};
-{%-   endfor %}
+{%   endfor %}
 
     public int which() {
       return mTag_;
@@ -291,7 +272,7 @@
     public boolean isUnknown() {
       return mTag_ == -1;
     }
-{%-   for field in union.fields %}
+{%   for field in union.fields %}
 
     // TODO(rockot): Fix the findbugs error and remove this suppression.
     // See http://crbug.com/570386.
@@ -308,7 +289,7 @@
         assert mTag_ == Tag.{{field|ucc}};
         return m{{field|ucc}};
     }
-{%-   endfor %}
+{%   endfor %}
 
 
     @Override
@@ -316,20 +297,20 @@
         encoder0.encode(org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE, offset);
         encoder0.encode(mTag_, offset + 4);
         switch (mTag_) {
-{%-   for field in union.fields %}
+{%   for field in union.fields %}
             case Tag.{{field|ucc}}: {
-{%-     if field.kind|is_union_kind %}
+{%     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 %}
+{%     else %}
                 {{encode('m' ~ field|ucc, field.kind, 'offset + 8', 0)|indent(16)}}
-{%-     endif %}
+{%     endif %}
                 break;
             }
-{%-   endfor %}
+{%   endfor %}
             default: {
                 break;
             }
@@ -347,20 +328,20 @@
         }
         {{union|name}} result = new {{union|name}}();
         switch (dataHeader.elementsOrVersion) {
-{%-   for field in union.fields %}
+{%   for field in union.fields %}
             case Tag.{{field|ucc}}: {
-{%-     if field.kind|is_union_kind %}
+{%     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 %}
+{%     else %}
                 {{decode('result.m'~field|ucc, field.kind, 'offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0)|indent(16)}}
-{%-     endif %}
+{%     endif %}
                 result.mTag_ = Tag.{{field|ucc}};
                 break;
             }
-{%-   endfor %}
+{%   endfor %}
             default: {
                 break;
             }
@@ -383,10 +364,10 @@
         if (mTag_ != other.mTag_)
             return false;
         switch (mTag_) {
-{%-   for field in union.fields %}
+{%   for field in union.fields %}
             case Tag.{{field|ucc}}:
                 return {{equality(field.kind, 'm'~field|ucc, 'other.m'~field|ucc)}};
-{%-   endfor %}
+{%   endfor %}
             default:
                 break;
         }
@@ -402,12 +383,12 @@
         int result = prime + getClass().hashCode();
         result = prime * result + org.chromium.mojo.bindings.BindingsHelper.hashCode(mTag_);
         switch (mTag_) {
-{%-   for field in union.fields %}
+{%   for field in union.fields %}
             case Tag.{{field|ucc}}: {
                 result = prime * result + {{hash(field.kind, 'm'~field|ucc)}};
                 break;
             }
-{%-   endfor %}
+{%   endfor %}
             default: {
                 break;
             }
@@ -415,4 +396,4 @@
         return result;
     }
 }
-{%- endmacro %}
+{% endmacro %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
index c7dcbbc..3928e0c 100644
--- a/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
@@ -91,11 +91,11 @@
     }
     switch(header.getType()) {
 {%   if with_response %}
-        case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_MESSAGE_ID:
+        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.interfacecontrol.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID:
+        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 %}
@@ -113,7 +113,12 @@
 {%       else %}
             {{request_struct|name}}.deserialize(messageWithHeader.getPayload());
 {%       endif %}
-            getImpl().{{method|name}}({{run_callback('data', method.parameters)}}{% if with_response %}{% if method.parameters %}, {% endif %}new {{response_struct|name}}ProxyToResponder(getCore(), receiver, header.getRequestId()){% endif %});
+            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 %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
index 019b1b6..4ae0a9b 100644
--- a/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
@@ -1,33 +1,13 @@
 {%- macro enum_def(enum_name, enum) -%}
   {{enum_name}} = {};
-{%-   set prev_enum = 0 %}
-{%-   for field in enum.fields %}
-{%-     if field.value %}
+{%- set prev_enum = 0 %}
+{%- for field in enum.fields %}
+{%-   if field.value %}
   {{enum_name}}.{{field.name}} = {{field.value|expression_to_text}};
-{%-     elif loop.first %}
+{%-   elif loop.first %}
   {{enum_name}}.{{field.name}} = 0;
-{%-     else %}
+{%-   else %}
   {{enum_name}}.{{field.name}} = {{enum_name}}.{{enum.fields[loop.index0 - 1].name}} + 1;
-{%-     endif %}
-{%-   endfor %}
-
-  {{enum_name}}.isKnownEnumValue = function(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;
-  };
-
-  {{enum_name}}.validate = function(enumValue) {
-    var isExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
-    if (isExtensible || this.isKnownEnumValue(enumValue))
-      return validator.validationError.NONE;
-
-    return validator.validationError.UNKNOWN_ENUM_VALUE;
-  };
+{%- 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
index 54e2d4e..1b5cafa 100644
--- a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -2,21 +2,12 @@
   var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}};
 {%- endfor %}
 
-  function {{interface.name}}Ptr(handleOrPtrInfo) {
-    this.ptr = new bindings.InterfacePtrController({{interface.name}},
-                                                   handleOrPtrInfo);
-  }
-
   function {{interface.name}}Proxy(receiver) {
-    this.receiver_ = receiver;
+    bindings.ProxyBase.call(this, receiver);
   }
+  {{interface.name}}Proxy.prototype = Object.create(bindings.ProxyBase.prototype);
 
 {%- for method in interface.methods %}
-  {{interface.name}}Ptr.prototype.{{method.name|stylize_method}} = function() {
-    return {{interface.name}}Proxy.prototype.{{method.name|stylize_method}}
-        .apply(this.ptr.getProxy(), arguments);
-  };
-
   {{interface.name}}Proxy.prototype.{{method.name|stylize_method}} = function(
 {%- for parameter in method.parameters -%}
 {{parameter.name}}{% if not loop.last %}, {% endif %}
@@ -24,7 +15,7 @@
 ) {
     var params = new {{interface.name}}_{{method.name}}_Params();
 {%- for parameter in method.parameters %}
-    params.{{parameter.name}} = {{parameter.name}};
+    params.{{parameter.name}} = {{parameter|js_proxy_method_parameter_value}};
 {%- endfor %}
 
 {%- if method.response_parameters == None %}
@@ -56,13 +47,15 @@
 {%- endfor %}
 
   function {{interface.name}}Stub(delegate) {
-    this.delegate_ = 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 this.delegate_ && this.delegate_.{{js_method_name}} && this.delegate_.{{js_method_name}}({{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 %}
 
@@ -169,8 +162,6 @@
 
   var {{interface.name}} = {
     name: '{{namespace|replace(".","::")}}::{{interface.name}}',
-    kVersion: {{interface.version}},
-    ptrClass: {{interface.name}}Ptr,
     proxyClass: {{interface.name}}Proxy,
     stubClass: {{interface.name}}Stub,
     validateRequest: validate{{interface.name}}Request,
diff --git a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
index 3ce4ab6..6d7a1a2 100644
--- a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
@@ -3,19 +3,15 @@
 // found in the LICENSE file.
 
 define("{{module.path}}", [
-{%- if module.path != "mojo/public/interfaces/bindings/interface_control_messages.mojom" %}
     "mojo/public/js/bindings",
-{%- endif %}
     "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(
-{%- if module.path != "mojo/public/interfaces/bindings/interface_control_messages.mojom" -%}
-bindings, {% endif -%}
-codec, core, validator
+], function(bindings, codec, connection, core, validator
 {%- for import in imports -%}
     , {{import.unique_name}}
 {%- endfor -%}
diff --git a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
index ddfef72..86ea2ce 100644
--- a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
@@ -40,7 +40,6 @@
 {%- endfor %}
 {%- for interface in interfaces %}
   exports.{{interface.name}} = {{interface.name}};
-  exports.{{interface.name}}Ptr = {{interface.name}}Ptr;
 {#--- Interface Client #}
 {%-   if interface.client in interfaces|map(attribute='name') %}
   exports.{{interface.name}}.client = {{interface.client}};
diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
index e823e46..ca80d67 100644
--- a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -34,40 +34,16 @@
 
   {{struct.name}}.validate = function(messageValidator, offset) {
     var err;
-    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.versions[-1].version}});
     if (err !== validator.validationError.NONE)
         return err;
 
-    var kVersionSizes = [
-{%- for version in struct.versions %}
-      {version: {{version.version}}, numBytes: {{version.num_bytes}}}{% if not loop.last %},
-      {%- endif -%}
-{%  endfor %}
-    ];
-    err = messageValidator.validateStructVersion(offset, kVersionSizes);
-    if (err !== validator.validationError.NONE)
-        return err;
-
-{#- 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. #}
 {%- from "validation_macros.tmpl" import validate_struct_field %}
-{%- set last_checked_version = 0 %}
-{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %}
+{%- 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 %}
-{%    if field|is_object_field or field|is_any_handle_or_interface_field or
-          field|is_enum_field %}
-{%      if packed_field.min_version > last_checked_version %}
-{%        set last_checked_version = packed_field.min_version %}
-    // version check {{name}}
-    if (!messageValidator.isFieldInStructVersion(offset, {{packed_field.min_version}}))
-      return validator.validationError.NONE;
-{%-     endif -%}
 {{validate_struct_field(field, offset, name)|indent(4)}}
-{%-   endif %}
 {%- endfor %}
 
     return validator.validationError.NONE;
@@ -83,8 +59,7 @@
     var numberOfBytes = decoder.readUint32();
     var version = decoder.readUint32();
 {%- for byte in struct.bytes %}
-{%-   if byte.packed_fields|length >= 1 and
-         byte.packed_fields[0].field|is_bool_field %}
+{%-   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;
@@ -107,8 +82,7 @@
     encoder.writeUint32({{struct.versions[-1].version}});
 
 {%- for byte in struct.bytes %}
-{%-   if byte.packed_fields|length >= 1 and
-         byte.packed_fields[0].field|is_bool_field %}
+{%-   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}}
diff --git a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
index 4823feb..3c903bc 100644
--- a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
@@ -89,11 +89,7 @@
   switch (val.$tag) {
 {%-   for field in union.fields %}
     case {{union.name}}.Tags.{{field.name}}:
-{%-     if field|is_bool_field %}
-      encoder.writeUint8(val.{{field.name}} ? 1 : 0);
-{%-     else %}
       encoder.{{field.kind|union_encode_snippet}}val.{{field.name}});
-{%-     endif %}
       break;
 {%-   endfor %}
   }
@@ -115,11 +111,7 @@
   switch (tag) {
 {%-   for field in union.fields %}
     case {{union.name}}.Tags.{{field.name}}:
-{%-     if field|is_bool_field %}
-      result.{{field.name}} = decoder.readUint8() ? true : false;
-{%-     else %}
       result.{{field.name}} = decoder.{{field.kind|union_decode_snippet}};
-{%-     endif %}
       break;
 {%-   endfor %}
   }
diff --git a/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
index d4e15a7..7a39749 100644
--- a/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
@@ -6,7 +6,7 @@
 {%- macro _validate_field(field, offset, name) %}
 {%-   if field|is_string_pointer_field %}
 // validate {{name}}
-err = messageValidator.validateStringPointer({{offset}}, {{field|validate_nullable_params}})
+err = messageValidator.validateStringPointer({{offset}}, {{field|validate_string_params}})
 {{_check_err()}}
 {%-   elif field|is_array_pointer_field %}
 // validate {{name}}
@@ -22,19 +22,11 @@
 {{_check_err()}}
 {%-   elif field|is_interface_field %}
 // validate {{name}}
-err = messageValidator.validateInterface({{offset}}, {{field|validate_nullable_params}});
-{{_check_err()}}
-{%-   elif field|is_interface_request_field %}
-// validate {{name}}
-err = messageValidator.validateInterfaceRequest({{offset}}, {{field|validate_nullable_params}})
+err = messageValidator.validateInterface({{offset}}, {{field|validate_interface_params}});
 {{_check_err()}}
 {%-   elif field|is_handle_field %}
 // validate {{name}}
-err = messageValidator.validateHandle({{offset}}, {{field|validate_nullable_params}})
-{{_check_err()}}
-{%-   elif field|is_enum_field %}
-// validate {{name}}
-err = messageValidator.validateEnum({{offset}}, {{field|validate_enum_params}});
+err = messageValidator.validateHandle({{offset}}, {{field|validate_handle_params}})
 {{_check_err()}}
 {%-   endif %}
 {%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
index 38d222b..38b219c 100644
--- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -36,10 +36,10 @@
 # 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
-_export_attribute = None
 
 
 class _NameFormatter(object):
@@ -50,40 +50,22 @@
     self._variant = variant
 
   def Format(self, separator, prefixed=False, internal=False,
-             include_variant=False, add_same_module_namespaces=False,
-             flatten_nested_kind=False):
-    """Formats the name according to the given configuration.
-
-    Args:
-      separator: Separator between different parts of the name.
-      prefixed: Whether a leading separator should be added.
-      internal: Returns the name in the "internal" namespace.
-      include_variant: Whether to include variant as namespace. If |internal| is
-          True, then this flag is ignored and variant is not included.
-      add_same_module_namespaces: Includes all namespaces even if the token is
-          from the same module as the current mojom file.
-      flatten_nested_kind: It is allowed to define enums inside structs and
-          interfaces. If this flag is set to True, this method concatenates the
-          parent kind and the nested kind with '_', instead of treating the
-          parent kind as a scope."""
-
+             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 and not internal:
+      if include_variant and self._variant:
         parts.append(self._variant)
-    parts.extend(self._GetName(internal, flatten_nested_kind))
+    parts.extend(self._GetName(internal))
     return separator.join(parts)
 
-  def FormatForCpp(self, add_same_module_namespaces=False, internal=False,
-                   flatten_nested_kind=False):
+  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,
-        flatten_nested_kind=flatten_nested_kind)
+        internal=internal, include_variant=True)
 
   def FormatForMojom(self):
     return self.Format(".", add_same_module_namespaces=True)
@@ -92,32 +74,24 @@
     if not internal:
       return token.name
     if (mojom.IsStructKind(token) or mojom.IsUnionKind(token) or
-        mojom.IsEnumKind(token)):
+        mojom.IsInterfaceKind(token) or mojom.IsEnumKind(token)):
       return token.name + "_Data"
     return token.name
 
-  def _GetName(self, internal, flatten_nested_kind):
-    if isinstance(self._token, mojom.EnumValue):
-      name_parts = _NameFormatter(self._token.enum, self._variant)._GetName(
-          internal, flatten_nested_kind)
-      name_parts.append(self._token.name)
-      return name_parts
-
-    name_parts = []
+  def _GetName(self, internal):
+    name = []
     if internal:
-      name_parts.append("internal")
-
-    if (flatten_nested_kind and mojom.IsEnumKind(self._token) and
-        self._token.parent_kind):
-      name = "%s_%s" % (self._token.parent_kind.name,
-                        self._MapKindName(self._token, internal))
-      name_parts.append(name)
-      return name_parts
-
+      name.append("internal")
     if self._token.parent_kind:
-      name_parts.append(self._MapKindName(self._token.parent_kind, internal))
-    name_parts.append(self._MapKindName(self._token, internal))
-    return name_parts
+      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
@@ -142,30 +116,22 @@
       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, flatten_nested_kind=False,
-                   add_same_module_namespaces=False):
+def GetNameForKind(kind, internal=False):
+  return _NameFormatter(kind, _variant).FormatForCpp(internal=internal)
+
+def GetQualifiedNameForKind(kind, internal=False):
   return _NameFormatter(kind, _variant).FormatForCpp(
-      internal=internal, flatten_nested_kind=flatten_nested_kind,
-      add_same_module_namespaces=add_same_module_namespaces)
-
-def GetQualifiedNameForKind(kind, internal=False, flatten_nested_kind=False,
-                            include_variant=True):
-  return _NameFormatter(
-      kind, _variant if include_variant else None).FormatForCpp(
-          internal=internal, add_same_module_namespaces=True,
-          flatten_nested_kind=flatten_nested_kind)
-
-
-def GetWtfHashFnNameForEnum(enum):
-  return _NameFormatter(
-      enum, None).Format("_", internal=True, add_same_module_namespaces=True,
-                         flatten_nested_kind=True) + "HashFn"
-
+      internal=internal, add_same_module_namespaces=True)
 
 def GetFullMojomNameForKind(kind):
   return _NameFormatter(kind, _variant).FormatForMojom()
@@ -178,128 +144,60 @@
   return (mojom.IsStructKind(kind) or mojom.IsEnumKind(kind)) and \
       kind.native_only
 
-
-def IsHashableKind(kind):
-  """Check if the kind can be hashed.
-
-  Args:
-    kind: {Kind} The kind to check.
-
-  Returns:
-    {bool} True if a value of this kind can be hashed.
-  """
-  checked = set()
-  def Check(kind):
-    if kind.spec in checked:
-      return True
-    checked.add(kind.spec)
-    if mojom.IsNullableKind(kind):
-      return False
-    elif mojom.IsStructKind(kind):
-      if (IsTypemappedKind(kind) and
-          not _current_typemap[GetFullMojomNameForKind(kind)]["hashable"]):
-        return False
-      return all(Check(field.kind) for field in kind.fields)
-    elif mojom.IsEnumKind(kind):
-      return not IsTypemappedKind(kind) or _current_typemap[
-          GetFullMojomNameForKind(kind)]["hashable"]
-    elif mojom.IsUnionKind(kind):
-      return all(Check(field.kind) for field in kind.fields)
-    elif mojom.IsAnyHandleKind(kind):
-      return False
-    elif mojom.IsAnyInterfaceKind(kind):
-      return False
-    # TODO(tibell): Arrays and maps could be made hashable. We just don't have a
-    # use case yet.
-    elif mojom.IsArrayKind(kind):
-      return False
-    elif mojom.IsMapKind(kind):
-      return False
-    else:
-      return True
-  return Check(kind)
-
-
-def AllEnumValues(enum):
-  """Return all enum values associated with an enum.
-
-  Args:
-    enum: {mojom.Enum} The enum type.
-
-  Returns:
-   {Set[int]} The values.
-  """
-  return set(field.numeric_value for field in enum.fields)
-
-
 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 FormatConstantDeclaration(constant, nested=False):
-  if mojom.IsStringKind(constant.kind):
-    if nested:
-      return "const char %s[]" % constant.name
-    return "%sextern const char %s[]" % \
-        ((_export_attribute + " ") if _export_attribute else "", constant.name)
-  return "constexpr %s %s = %s" % (GetCppPodType(constant.kind), constant.name,
-                                   ConstantValue(constant))
-
-def GetCppWrapperType(kind, add_same_module_namespaces=False):
-  def _AddOptional(type_name):
-    pattern = "WTF::Optional<%s>" if _for_blink else "base::Optional<%s>"
-    return pattern % type_name
-
+def GetCppWrapperType(kind):
   if IsTypemappedKind(kind):
-    type_name = GetNativeTypeName(kind)
-    if (mojom.IsNullableKind(kind) and
-        not _current_typemap[GetFullMojomNameForKind(kind)][
-           "nullable_is_same_type"]):
-      type_name = _AddOptional(type_name)
-    return type_name
+    return GetNativeTypeName(kind)
   if mojom.IsEnumKind(kind):
-    return GetNameForKind(
-        kind, add_same_module_namespaces=add_same_module_namespaces)
+    return GetNameForKind(kind)
   if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
-    return "%sPtr" % GetNameForKind(
-        kind, add_same_module_namespaces=add_same_module_namespaces)
+    return "%sPtr" % GetNameForKind(kind)
   if mojom.IsArrayKind(kind):
-    pattern = "WTF::Vector<%s>" if _for_blink else "std::vector<%s>"
-    if mojom.IsNullableKind(kind):
-      pattern = _AddOptional(pattern)
-    return pattern % GetCppWrapperType(
-        kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+    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 = ("WTF::HashMap<%s, %s>" if _for_blink else
-               "std::unordered_map<%s, %s>")
-    if mojom.IsNullableKind(kind):
-      pattern = _AddOptional(pattern)
-    return pattern % (
-        GetCppWrapperType(
-            kind.key_kind,
-            add_same_module_namespaces=add_same_module_namespaces),
-        GetCppWrapperType(
-            kind.value_kind,
-            add_same_module_namespaces=add_same_module_namespaces))
+    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, add_same_module_namespaces=add_same_module_namespaces)
+    return "%sPtr" % GetNameForKind(kind)
   if mojom.IsInterfaceRequestKind(kind):
-    return "%sRequest" % GetNameForKind(
-        kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+    return "%sRequest" % GetNameForKind(kind.kind)
   if mojom.IsAssociatedInterfaceKind(kind):
-    return "%sAssociatedPtrInfo" % GetNameForKind(
-        kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+    return "%sAssociatedPtrInfo" % GetNameForKind(kind.kind)
   if mojom.IsAssociatedInterfaceRequestKind(kind):
-    return "%sAssociatedRequest" % GetNameForKind(
-        kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+    return "%sAssociatedRequest" % GetNameForKind(kind.kind)
   if mojom.IsStringKind(kind):
     if _for_blink:
       return "WTF::String"
-    type_name = "std::string"
-    return _AddOptional(type_name) if mojom.IsNullableKind(kind) else type_name
+    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):
@@ -322,22 +220,15 @@
   if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
     return True
   if mojom.IsArrayKind(kind):
-    return IsMoveOnlyKind(kind.kind)
+    return IsMoveOnlyKind(kind.kind) if _use_new_wrapper_types else True
   if mojom.IsMapKind(kind):
-    return IsMoveOnlyKind(kind.value_kind)
+    return IsMoveOnlyKind(kind.value_kind) if _use_new_wrapper_types else True
   if mojom.IsAnyHandleOrInterfaceKind(kind):
     return True
   return False
 
-def IsCopyablePassByValue(kind):
-  if not IsTypemappedKind(kind):
-    return False
-  return _current_typemap[GetFullMojomNameForKind(kind)][
-      "copyable_pass_by_value"]
-
 def ShouldPassParamByValue(kind):
-  return ((not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind) or
-      IsCopyablePassByValue(kind))
+  return (not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind)
 
 def GetCppWrapperParamType(kind):
   cpp_wrapper_type = GetCppWrapperType(kind)
@@ -363,7 +254,7 @@
   if mojom.IsAssociatedInterfaceKind(kind):
     return "mojo::internal::AssociatedInterface_Data"
   if mojom.IsAssociatedInterfaceRequestKind(kind):
-    return "mojo::internal::AssociatedEndpointHandle_Data"
+    return "mojo::internal::AssociatedInterfaceRequest_Data"
   if mojom.IsEnumKind(kind):
     return "int32_t"
   if mojom.IsStringKind(kind):
@@ -382,47 +273,27 @@
     return "%s&" % GetCppWrapperType(kind)
   return GetCppWrapperType(kind)
 
-def GetUnionTraitGetterReturnType(kind):
-  """Get field type used in UnionTraits template specialization.
-
-  The type may be qualified as UnionTraits specializations live outside the
-  namespace where e.g. structs are defined.
-
-  Args:
-    kind: {Kind} The type of the field.
-
-  Returns:
-    {str} The C++ type to use for the field.
-  """
-  if mojom.IsReferenceKind(kind):
-    return "%s&" % GetCppWrapperType(kind, add_same_module_namespaces=True)
-  return GetCppWrapperType(kind, add_same_module_namespaces=True)
-
-def GetCppDataViewType(kind, qualified=False):
-  def _GetName(input_kind):
-    return _NameFormatter(input_kind, None).FormatForCpp(
-        add_same_module_namespaces=qualified, flatten_nested_kind=True)
-
+def GetUnmappedTypeForSerializer(kind):
   if mojom.IsEnumKind(kind):
-    return _GetName(kind)
+    return GetQualifiedNameForKind(kind)
   if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
-    return "%sDataView" % _GetName(kind)
+    return "%sPtr" % GetQualifiedNameForKind(kind)
   if mojom.IsArrayKind(kind):
-    return "mojo::ArrayDataView<%s>" % GetCppDataViewType(kind.kind, qualified)
+    return "mojo::Array<%s>" % GetUnmappedTypeForSerializer(kind.kind)
   if mojom.IsMapKind(kind):
-    return ("mojo::MapDataView<%s, %s>" % (
-        GetCppDataViewType(kind.key_kind, qualified),
-        GetCppDataViewType(kind.value_kind, qualified)))
-  if mojom.IsStringKind(kind):
-    return "mojo::StringDataView"
+    return "mojo::Map<%s, %s>" % (
+        GetUnmappedTypeForSerializer(kind.key_kind),
+        GetUnmappedTypeForSerializer(kind.value_kind))
   if mojom.IsInterfaceKind(kind):
-    return "%sPtrDataView" % _GetName(kind)
+    return "%sPtr" % GetQualifiedNameForKind(kind)
   if mojom.IsInterfaceRequestKind(kind):
-    return "%sRequestDataView" % _GetName(kind.kind)
+    return "%sRequest" % GetQualifiedNameForKind(kind.kind)
   if mojom.IsAssociatedInterfaceKind(kind):
-    return "%sAssociatedPtrInfoDataView" % _GetName(kind.kind)
+    return "%sAssociatedPtrInfo" % GetQualifiedNameForKind(kind.kind)
   if mojom.IsAssociatedInterfaceRequestKind(kind):
-    return "%sAssociatedRequestDataView" % _GetName(kind.kind)
+    return "%sAssociatedRequest" % GetQualifiedNameForKind(kind.kind)
+  if mojom.IsStringKind(kind):
+    return "mojo::String"
   if mojom.IsGenericHandleKind(kind):
     return "mojo::ScopedHandle"
   if mojom.IsDataPipeConsumerKind(kind):
@@ -435,26 +306,18 @@
     return "mojo::ScopedSharedBufferHandle"
   return _kind_to_cpp_type[kind]
 
-def GetUnmappedTypeForSerializer(kind):
-  return GetCppDataViewType(kind, qualified=True)
-
 def TranslateConstants(token, kind):
   if isinstance(token, mojom.NamedValue):
-    return GetNameForKind(token, flatten_nested_kind=True)
+    return _NameFormatter(token, _variant).FormatForCpp()
 
   if isinstance(token, mojom.BuiltinValue):
-    if token.value == "double.INFINITY":
-      return "std::numeric_limits<double>::infinity()"
-    if token.value == "float.INFINITY":
-      return "std::numeric_limits<float>::infinity()"
-    if token.value == "double.NEGATIVE_INFINITY":
-      return "-std::numeric_limits<double>::infinity()"
-    if token.value == "float.NEGATIVE_INFINITY":
-      return "-std::numeric_limits<float>::infinity()"
-    if token.value == "double.NAN":
-      return "std::numeric_limits<double>::quiet_NaN()"
-    if token.value == "float.NAN":
-      return "std::numeric_limits<float>::quiet_NaN()"
+    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";
@@ -479,12 +342,6 @@
 def ExpressionToText(value, kind=None):
   return TranslateConstants(value, kind)
 
-def RequiresContextForDataView(kind):
-  for field in kind.fields:
-    if mojom.IsReferenceKind(field.kind):
-      return True
-  return False
-
 def ShouldInlineStruct(struct):
   # TODO(darin): Base this on the size of the wrapper class.
   if len(struct.fields) > 4:
@@ -494,69 +351,11 @@
       return False
   return True
 
-def ContainsMoveOnlyMembers(struct):
-  for field in struct.fields:
-    if IsMoveOnlyKind(field.kind):
-      return True
-  return False
-
 def ShouldInlineUnion(union):
   return not any(
       mojom.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind)
            for field in union.fields)
 
-
-class StructConstructor(object):
-  """Represents a constructor for a generated struct.
-
-  Fields:
-    fields: {[Field]} All struct fields in order.
-    params: {[Field]} The fields that are passed as params.
-  """
-
-  def __init__(self, fields, params):
-    self._fields = fields
-    self._params = set(params)
-
-  @property
-  def params(self):
-    return [field for field in self._fields if field in self._params]
-
-  @property
-  def fields(self):
-    for field in self._fields:
-      yield (field, field in self._params)
-
-
-def GetStructConstructors(struct):
-  """Returns a list of constructors for a struct.
-
-  Params:
-    struct: {Struct} The struct to return constructors for.
-
-  Returns:
-    {[StructConstructor]} A list of StructConstructors that should be generated
-    for |struct|.
-  """
-  if not mojom.IsStructKind(struct):
-    raise TypeError
-  # Types that are neither copyable nor movable can't be passed to a struct
-  # constructor so only generate a default constructor.
-  if any(IsTypemappedKind(field.kind) and _current_typemap[
-      GetFullMojomNameForKind(field.kind)]["non_copyable_non_movable"]
-         for field in struct.fields):
-    return [StructConstructor(struct.fields, [])]
-
-  param_counts = [0]
-  for version in struct.versions:
-    if param_counts[-1] != version.num_fields:
-      param_counts.append(version.num_fields)
-
-  ordinal_fields = sorted(struct.fields, key=lambda field: field.ordinal)
-  return (StructConstructor(struct.fields, ordinal_fields[:param_count])
-          for param_count in param_counts)
-
-
 def GetContainerValidateParamsCtorArgs(kind):
   if mojom.IsStringKind(kind):
     expected_num_elements = 0
@@ -579,8 +378,7 @@
     element_validate_params = GetNewContainerValidateParams(kind.kind)
     if mojom.IsEnumKind(kind.kind):
       enum_validate_func = ("%s::Validate" %
-                            GetQualifiedNameForKind(kind.kind, internal=True,
-                                                    flatten_nested_kind=True))
+                            GetQualifiedNameForKind(kind.kind, internal=True))
     else:
       enum_validate_func = "nullptr"
 
@@ -605,143 +403,57 @@
 class Generator(generator.Generator):
 
   cpp_filters = {
-    "all_enum_values": AllEnumValues,
     "constant_value": ConstantValue,
-    "contains_handles_or_interfaces": mojom.ContainsHandlesOrInterfaces,
-    "contains_move_only_members": ContainsMoveOnlyMembers,
     "cpp_wrapper_param_type": GetCppWrapperParamType,
-    "cpp_data_view_type": GetCppDataViewType,
     "cpp_field_type": GetCppFieldType,
     "cpp_union_field_type": GetCppUnionFieldType,
     "cpp_pod_type": GetCppPodType,
     "cpp_union_getter_return_type": GetUnionGetterReturnType,
-    "cpp_union_trait_getter_return_type": GetUnionTraitGetterReturnType,
     "cpp_wrapper_type": GetCppWrapperType,
     "default_value": DefaultValue,
     "expression_to_text": ExpressionToText,
-    "format_constant_declaration": FormatConstantDeclaration,
     "get_container_validate_params_ctor_args":
-        GetContainerValidateParamsCtorArgs,
+    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,
-    "requires_context_for_data_view": RequiresContextForDataView,
     "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_kind": mojom.IsAnyHandleKind,
-    "is_any_interface_kind": mojom.IsAnyInterfaceKind,
     "is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind,
     "is_associated_kind": mojom.IsAssociatedKind,
-    "is_hashable": IsHashableKind,
     "is_map_kind": mojom.IsMapKind,
     "is_nullable_kind": mojom.IsNullableKind,
     "is_object_kind": mojom.IsObjectKind,
-    "is_reference_kind": mojom.IsReferenceKind,
     "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_constructors": GetStructConstructors,
+    "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
     "stylize_method": generator.StudlyCapsToCamel,
     "under_to_camel": generator.UnderToCamel,
     "unmapped_type_for_serializer": GetUnmappedTypeForSerializer,
-    "wtf_hash_fn_name_for_enum": GetWtfHashFnNameForEnum,
   }
 
   def GetExtraTraitsHeaders(self):
     extra_headers = set()
-    for typemap in self._GetAllUsedTypemaps():
-      extra_headers.update(typemap.get("traits_headers", []))
-    return sorted(extra_headers)
-
-  def _GetAllUsedTypemaps(self):
-    """Returns the typemaps for types needed for serialization in this module.
-
-    A type is needed for serialization if it is contained by a struct or union
-    defined in this module, is a parameter of a message in an interface in
-    this module or is contained within another type needed for serialization.
-    """
-    used_typemaps = []
-    seen_types = set()
-    def AddKind(kind):
-      if (mojom.IsIntegralKind(kind) or mojom.IsStringKind(kind) or
-          mojom.IsDoubleKind(kind) or mojom.IsFloatKind(kind) or
-          mojom.IsAnyHandleKind(kind) or
-          mojom.IsInterfaceKind(kind) or
-          mojom.IsInterfaceRequestKind(kind) or
-          mojom.IsAssociatedKind(kind)):
-        pass
-      elif mojom.IsArrayKind(kind):
-        AddKind(kind.kind)
-      elif mojom.IsMapKind(kind):
-        AddKind(kind.key_kind)
-        AddKind(kind.value_kind)
-      else:
-        name = GetFullMojomNameForKind(kind)
-        if name in seen_types:
-          return
-        seen_types.add(name)
-
-        typemap = _current_typemap.get(name, None)
-        if typemap:
-          used_typemaps.append(typemap)
-        if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
-          for field in kind.fields:
-            AddKind(field.kind)
-
-    for kind in self.module.structs + self.module.unions:
-      for field in kind.fields:
-        AddKind(field.kind)
-
-    for interface in self.module.interfaces:
-      for method in interface.methods:
-        for parameter in method.parameters + (method.response_parameters or []):
-          AddKind(parameter.kind)
-
-    return used_typemaps
+    for entry in self.typemap.itervalues():
+      extra_headers.update(entry.get("traits_headers", []))
+    return list(extra_headers)
 
   def GetExtraPublicHeaders(self):
-    all_enums = list(self.module.enums)
-    for struct in self.module.structs:
-      all_enums.extend(struct.enums)
-    for interface in self.module.interfaces:
-      all_enums.extend(interface.enums)
-
-    types = set(GetFullMojomNameForKind(typename)
-                for typename in
-                self.module.structs + all_enums + self.module.unions)
-    headers = set()
-    for typename, typemap in self.typemap.iteritems():
-      if typename in types:
-        headers.update(typemap.get("public_headers", []))
-    return sorted(headers)
-
-  def _GetDirectlyUsedKinds(self):
-    for struct in self.module.structs + self.module.unions:
-      for field in struct.fields:
-        yield field.kind
-
-    for interface in self.module.interfaces:
-      for method in interface.methods:
-        for param in method.parameters + (method.response_parameters or []):
-          yield param.kind
+    extra_headers = set()
+    for entry in self.typemap.itervalues():
+      extra_headers.update(entry.get("public_headers", []))
+    return list(extra_headers)
 
   def GetJinjaExports(self):
-    structs = self.GetStructs()
-    interfaces = self.GetInterfaces()
-    all_enums = list(self.module.enums)
-    for struct in structs:
-      all_enums.extend(struct.enums)
-    for interface in interfaces:
-      all_enums.extend(interface.enums)
-
     return {
       "module": self.module,
       "namespace": self.module.namespace,
@@ -749,17 +461,14 @@
       "imports": self.module.imports,
       "kinds": self.module.kinds,
       "enums": self.module.enums,
-      "all_enums": all_enums,
-      "structs": structs,
+      "structs": self.GetStructs(),
       "unions": self.GetUnions(),
-      "interfaces": interfaces,
+      "interfaces": self.GetInterfaces(),
       "variant": self.variant,
       "extra_traits_headers": self.GetExtraTraitsHeaders(),
       "extra_public_headers": self.GetExtraPublicHeaders(),
       "for_blink": self.for_blink,
-      "use_once_callback": self.use_once_callback,
-      "export_attribute": self.export_attribute,
-      "export_header": self.export_header,
+      "use_new_wrapper_types": self.use_new_wrapper_types,
     }
 
   @staticmethod
@@ -774,45 +483,27 @@
   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()
 
-  @UseJinja("module-shared.h.tmpl")
-  def GenerateModuleSharedHeader(self):
-    return self.GetJinjaExports()
-
-  @UseJinja("module-shared-internal.h.tmpl")
-  def GenerateModuleSharedInternalHeader(self):
-    return self.GetJinjaExports()
-
-  @UseJinja("module-shared.cc.tmpl")
-  def GenerateModuleSharedSource(self):
-    return self.GetJinjaExports()
-
   def GenerateFiles(self, args):
-    if self.generate_non_variant_code:
-      self.Write(self.GenerateModuleSharedHeader(),
-                 self.MatchMojomFilePath("%s-shared.h" % self.module.name))
-      self.Write(
-          self.GenerateModuleSharedInternalHeader(),
-          self.MatchMojomFilePath("%s-shared-internal.h" % self.module.name))
-      self.Write(self.GenerateModuleSharedSource(),
-                 self.MatchMojomFilePath("%s-shared.cc" % self.module.name))
-    else:
-      global _current_typemap
-      _current_typemap = self.typemap
-      global _for_blink
-      _for_blink = self.for_blink
-      global _use_once_callback
-      _use_once_callback = self.use_once_callback
-      global _variant
-      _variant = self.variant
-      global _export_attribute
-      _export_attribute = self.export_attribute
-      suffix = "-%s" % self.variant if self.variant else ""
-      self.Write(self.GenerateModuleHeader(),
-                 self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix)))
-      self.Write(
-          self.GenerateModuleSource(),
-          self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix)))
+    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
index 9265b3a..481efbc 100644
--- a/mojo/public/tools/bindings/generators/mojom_java_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -10,8 +10,8 @@
 import os
 import re
 import shutil
-import sys
 import tempfile
+import zipfile
 
 from jinja2 import contextfilter
 
@@ -20,12 +20,6 @@
 import mojom.generate.module as mojom
 from mojom.generate.template_expander import UseJinja
 
-sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir,
-                             os.pardir, os.pardir, os.pardir, os.pardir,
-                             'build', 'android', 'gyp'))
-
-from util import build_utils
-
 
 GENERATOR_PREFIX = 'java'
 
@@ -225,8 +219,8 @@
     return ParseStringAttribute(module.attributes['JavaPackage'])
   # Default package.
   if module.namespace:
-    return 'org.chromium.' + module.namespace
-  return 'org.chromium'
+    return 'org.chromium.mojom.' + module.namespace
+  return 'org.chromium.mojom'
 
 def GetNameForKind(context, kind):
   def _GetNameHierachy(kind):
@@ -403,6 +397,14 @@
   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 = {
@@ -531,7 +533,7 @@
     with TempDir() as temp_java_root:
       self.output_dir = os.path.join(temp_java_root, package_path)
       self.DoGenerateFiles();
-      build_utils.ZipDir(zip_filename, temp_java_root)
+      ZipContentInto(temp_java_root, zip_filename)
 
     if args.java_output_directory:
       # If requested, generate the java files directly into indicated directory.
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py
index 0eedb31..e9a4883 100644
--- a/mojo/public/tools/bindings/generators/mojom_js_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -37,13 +37,9 @@
 
 
 def JavaScriptType(kind):
-  name = []
   if kind.imported_from:
-    name.append(kind.imported_from["unique_name"])
-  if kind.parent_kind:
-    name.append(kind.parent_kind.name)
-  name.append(kind.name)
-  return ".".join(name)
+    return kind.imported_from["unique_name"] + "." + kind.name
+  return kind.name
 
 
 def JavaScriptDefaultValue(field):
@@ -62,10 +58,9 @@
     return "null"
   if mojom.IsMapKind(field.kind):
     return "null"
-  if mojom.IsInterfaceKind(field.kind):
-    return "new %sPtr()" % JavaScriptType(field.kind)
-  if mojom.IsInterfaceRequestKind(field.kind):
-    return "new bindings.InterfaceRequest()"
+  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):
@@ -125,19 +120,16 @@
     element_type = ElementCodecType(kind.kind)
     return "new codec.%s(%s%s)" % (array_type, element_type, array_length)
   if mojom.IsInterfaceKind(kind):
-    return "new codec.%s(%sPtr)" % (
-        "NullableInterface" if mojom.IsNullableKind(kind) else "Interface",
-        JavaScriptType(kind))
+    return "codec.%s" % ("NullableInterface" if mojom.IsNullableKind(kind)
+        else "Interface")
   if mojom.IsInterfaceRequestKind(kind):
-    return "codec.%s" % (
-        "NullableInterfaceRequest" if mojom.IsNullableKind(kind)
-                                   else "InterfaceRequest")
+    return CodecType(mojom.MSGPIPE)
   if mojom.IsAssociatedInterfaceKind(kind):
     return "codec.AssociatedInterfaceNotSupported"
   if mojom.IsAssociatedInterfaceRequestKind(kind):
     return "codec.AssociatedInterfaceRequestNotSupported"
   if mojom.IsEnumKind(kind):
-    return "new codec.Enum(%s)" % JavaScriptType(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)
@@ -149,10 +141,9 @@
 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.IsAnyInterfaceKind(kind)):
+      mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)):
     return "decodeStruct(%s)" % CodecType(kind)
   if mojom.IsStructKind(kind):
     return "decodeStructPointer(%s)" % JavaScriptType(kind)
@@ -165,6 +156,8 @@
     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)
@@ -172,7 +165,7 @@
 
 def JavaScriptEncodeSnippet(kind):
   if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
-      mojom.IsAnyInterfaceKind(kind)):
+      mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)):
     return "encodeStruct(%s, " % CodecType(kind)
   if mojom.IsUnionKind(kind):
     return "encodeStruct(%s, " % JavaScriptType(kind)
@@ -185,6 +178,8 @@
     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)
@@ -233,9 +228,6 @@
        expected_dimension_sizes)
 
 
-def JavaScriptValidateEnumParams(field):
-  return JavaScriptType(field.kind)
-
 def JavaScriptValidateStructParams(field):
   nullable = JavaScriptNullableParam(field)
   struct_type = JavaScriptType(field.kind)
@@ -256,6 +248,42 @@
       (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:
@@ -288,9 +316,6 @@
 def IsArrayPointerField(field):
   return mojom.IsArrayKind(field.kind)
 
-def IsEnumField(field):
-  return mojom.IsEnumKind(field.kind)
-
 def IsStringPointerField(field):
   return mojom.IsStringKind(field.kind)
 
@@ -306,55 +331,38 @@
 def IsInterfaceField(field):
   return mojom.IsInterfaceKind(field.kind)
 
-def IsInterfaceRequestField(field):
-  return mojom.IsInterfaceRequestKind(field.kind)
-
 def IsUnionField(field):
   return mojom.IsUnionKind(field.kind)
 
-def IsBoolField(field):
-  return mojom.IsBoolKind(field.kind)
-
-def IsObjectField(field):
-  return mojom.IsObjectKind(field.kind)
-
-def IsAnyHandleOrInterfaceField(field):
-  return mojom.IsAnyHandleOrInterfaceKind(field.kind)
-
-def IsEnumField(field):
-  return mojom.IsEnumKind(field.kind)
-
 
 class Generator(generator.Generator):
 
   js_filters = {
-    "decode_snippet": JavaScriptDecodeSnippet,
     "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_any_handle_or_interface_field": IsAnyHandleOrInterfaceField,
     "is_array_pointer_field": IsArrayPointerField,
-    "is_bool_field": IsBoolField,
-    "is_enum_field": IsEnumField,
+    "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,
-    "is_interface_request_field": IsInterfaceRequestField,
-    "is_map_pointer_field": IsMapPointerField,
-    "is_object_field": IsObjectField,
-    "is_string_pointer_field": IsStringPointerField,
-    "is_struct_pointer_field": IsStructPointerField,
-    "is_union_field": IsUnionField,
     "js_type": JavaScriptType,
-    "payload_size": JavaScriptPayloadSize,
+    "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue,
+    "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue,
     "stylize_method": generator.StudlyCapsToCamel,
-    "union_decode_snippet": JavaScriptUnionDecodeSnippet,
-    "union_encode_snippet": JavaScriptUnionEncodeSnippet,
     "validate_array_params": JavaScriptValidateArrayParams,
-    "validate_enum_params": JavaScriptValidateEnumParams,
+    "validate_handle_params": JavaScriptValidateHandleParams,
+    "validate_interface_params": JavaScriptValidateInterfaceParams,
     "validate_map_params": JavaScriptValidateMapParams,
-    "validate_nullable_params": JavaScriptNullableParam,
+    "validate_string_params": JavaScriptValidateStringParams,
     "validate_struct_params": JavaScriptValidateStructParams,
     "validate_union_params": JavaScriptValidateUnionParams,
   }
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
index 2466636..d6f4f17 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -2,18 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-declare_args() {
-  # Indicates whether typemapping should be supported in this build
-  # configuration. This may be disabled when building external projects which
-  # depend on //mojo but which do not need/want all of the Chromium tree
-  # dependencies that come with typemapping.
-  #
-  # Note that (perhaps obviously) a huge amount of Chromium code will not build
-  # with typemapping disabled, so it is never valid to set this to |false| in
-  # any Chromium build configuration.
-  enable_mojom_typemapping = true
-}
-
 mojom_generator_root = "//mojo/public/tools/bindings"
 mojom_generator_script = "$mojom_generator_root/mojom_bindings_generator.py"
 mojom_generator_sources = [
@@ -24,79 +12,42 @@
   "$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/generate/translate.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 (enable_mojom_typemapping) {
-  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) {
-    # Check that the mojom field of each typemap refers to a mojom that exists.
-    foreach(typemap, configuration.typemaps) {
-      _typemap_config = {
-      }
-      _typemap_config = typemap.config
-      read_file(_typemap_config.mojom, "")
-    }
-    if (is_mac && defined(configuration.typemaps_mac)) {
-      foreach(typemap, configuration.typemaps_mac) {
-        _typemap_config = {
-        }
-        _typemap_config = typemap.config
-        read_file(_typemap_config.mojom, "")
-      }
-    }
-  }
-} else {
-  _bindings_configuration_files = []
-  _bindings_configurations = [
-    {
-      typemaps = []
-    },
-    {
-      variant = "blink"
-      for_blink = true
-      typemaps = []
-    },
+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, "")
+  }
 }
 
-# Generates targets for building C++, JavaScript and Java bindings from mojom
-# files. The output files will go under the generated file directory tree with
-# the same path as each input file.
-#
-# Other targets should depend on one of these generated targets (where "foo"
-# is the target name):
-#
-#   foo
-#       C++ and Javascript bindings. Other mojom targets should also depend on
-#       this target.
-#
-#   foo_blink
-#       C++ bindings using Blink standard types.
-#
-#   foo_java
-#       Java bindings.
+# 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:
 #
@@ -119,78 +70,20 @@
 #
 #   visibility (optional)
 #
-#   visibility_blink (optional)
-#       The value to use for visibility for the blink variant. If unset,
-#       |visibility| is used.
-#
-#   use_once_callback (optional)
-#       If set to true, generated classes will use base::OnceCallback instead of
-#       base::RepeatingCallback.
+#   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(dcheng):
-#           - Convert everything to use OnceCallback.
-#           - Remove support for the old mode.
-#
-#   cpp_only (optional)
-#       If set to true, only the C++ bindings targets will be generated.
-#
-# The following parameters are used to support the component build. They are
-# needed so that bindings which are linked with a component can use the same
-# export settings for classes. The first three are for the chromium variant, and
-# the last three are for the blink variant.
-#   export_class_attribute (optional)
-#       The attribute to add to the class declaration. e.g. "CONTENT_EXPORT"
-#   export_define (optional)
-#       A define to be added to the source_set which is needed by the export
-#       header. e.g. "CONTENT_IMPLEMENTATION=1"
-#   export_header (optional)
-#       A header to be added to the generated bindings to support the component
-#       build. e.g. "content/common/content_export.h"
-#   export_class_attribute_blink (optional)
-#   export_define_blink (optional)
-#   export_header_blink (optional)
-#       These three parameters are the blink variants of the previous 3.
-#
-# The following parameters are used to correct component build dependencies.
-# They are needed so mojom-mojom dependencies follow the rule that dependencies
-# on a source set in another component are replaced by a dependency on the
-# containing component. The first two are for the chromium variant; the other
-# two are for the blink variant.
-#   overridden_deps (optional)
-#       The list of mojom deps to be overridden.
-#   component_deps (optional)
-#       The list of component deps to add to replace overridden_deps.
-#   overridden_deps_blink (optional)
-#   component_deps_blink (optional)
-#       These two parameters are the blink variants of the previous two.
+#       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.")
-  if (defined(invoker.export_class_attribute) ||
-      defined(invoker.export_define) || defined(invoker.export_header)) {
-    assert(defined(invoker.export_class_attribute))
-    assert(defined(invoker.export_define))
-    assert(defined(invoker.export_header))
-  }
-  if (defined(invoker.export_class_attribute_blink) ||
-      defined(invoker.export_define_blink) ||
-      defined(invoker.export_header_blink)) {
-    assert(defined(invoker.export_class_attribute_blink))
-    assert(defined(invoker.export_define_blink))
-    assert(defined(invoker.export_header_blink))
-  }
-  if (defined(invoker.overridden_deps) || defined(invoker.component_deps)) {
-    assert(defined(invoker.overridden_deps))
-    assert(defined(invoker.component_deps))
-  }
-
-  if (defined(invoker.overridden_deps_blink) ||
-      defined(invoker.component_deps_blink)) {
-    assert(defined(invoker.overridden_deps_blink))
-    assert(defined(invoker.component_deps_blink))
-  }
 
   all_deps = []
   if (defined(invoker.deps)) {
@@ -214,89 +107,8 @@
     }
   }
 
-  # Generate code that is shared by different variants.
-  if (defined(invoker.sources)) {
-    common_generator_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) {
-        common_generator_args += [
-          "-I",
-          rebase_path(import_dir, root_build_dir),
-        ]
-      }
-    }
-
-    generator_shared_cpp_outputs = [
-      "{{source_gen_dir}}/{{source_name_part}}.mojom-shared-internal.h",
-      "{{source_gen_dir}}/{{source_name_part}}.mojom-shared.cc",
-      "{{source_gen_dir}}/{{source_name_part}}.mojom-shared.h",
-    ]
-    generator_shared_target_name = "${target_name}_shared__generator"
-    action_foreach(generator_shared_target_name) {
-      script = mojom_generator_script
-      inputs = mojom_generator_sources
-      sources = invoker.sources
-      deps = [
-        "//mojo/public/tools/bindings:precompile_templates",
-      ]
-      outputs = generator_shared_cpp_outputs
-      args = common_generator_args
-      args += [
-        "--generate_non_variant_code",
-        "-g",
-        "c++",
-      ]
-      depfile = "{{source_gen_dir}}/${generator_shared_target_name}_{{source_name_part}}.d"
-      args += [
-        "--depfile",
-        depfile,
-        "--depfile_target",
-        "{{source_gen_dir}}/{{source_name_part}}.mojom-shared-internal.h",
-      ]
-    }
-  }
-
-  shared_cpp_sources_suffix = "shared_cpp_sources"
-  shared_cpp_sources_target_name = "${target_name}_${shared_cpp_sources_suffix}"
-  source_set(shared_cpp_sources_target_name) {
-    if (defined(invoker.testonly)) {
-      testonly = invoker.testonly
-    }
-    deps = []
-    if (defined(invoker.sources)) {
-      sources =
-          process_file_template(invoker.sources, generator_shared_cpp_outputs)
-      deps += [ ":$generator_shared_target_name" ]
-    }
-    public_deps = []
-    foreach(d, all_deps) {
-      # Resolve the name, so that a target //mojo/something becomes
-      # //mojo/something:something and we can append shared_cpp_sources_suffix
-      # to get the cpp dependency name.
-      full_name = get_label_info("$d", "label_no_toolchain")
-      public_deps += [ "${full_name}_${shared_cpp_sources_suffix}" ]
-    }
-  }
-
-  # Generate code for variants.
   foreach(bindings_configuration, _bindings_configurations) {
     cpp_only = false
-    if (defined(invoker.cpp_only)) {
-      cpp_only = invoker.cpp_only
-    }
     variant_suffix = ""
     if (defined(bindings_configuration.variant)) {
       variant = bindings_configuration.variant
@@ -307,6 +119,9 @@
     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 = []
@@ -319,6 +134,7 @@
       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)) {
@@ -339,23 +155,10 @@
       foreach(source, enabled_sources) {
         # TODO(sammc): Use a map instead of a linear scan when GN supports maps.
         foreach(typemap, bindings_configuration.typemaps) {
-          _typemap_config = {
-          }
-          _typemap_config = typemap.config
-          if (get_path_info(source, "abspath") == _typemap_config.mojom) {
+          if (get_path_info(source, "abspath") == typemap.mojom) {
             active_typemaps += [ typemap ]
           }
         }
-        if (is_mac && defined(bindings_configuration.typemaps_mac)) {
-          foreach(typemap, bindings_configuration.typemaps_mac) {
-            _typemap_config = {
-            }
-            _typemap_config = typemap.config
-            if (get_path_info(source, "abspath") == _typemap_config.mojom) {
-              active_typemaps += [ typemap ]
-            }
-          }
-        }
       }
 
       if (!cpp_only) {
@@ -375,7 +178,28 @@
         ]
         outputs = generator_cpp_outputs + generator_java_outputs +
                   generator_js_outputs
-        args = common_generator_args
+        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 += [
@@ -395,14 +219,6 @@
             bindings_configuration.variant,
           ]
         }
-        depfile =
-            "{{source_gen_dir}}/${generator_target_name}_{{source_name_part}}.d"
-        args += [
-          "--depfile",
-          depfile,
-          "--depfile_target",
-          "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.cc",
-        ]
 
         args += [
           "--typemap",
@@ -412,27 +228,11 @@
         if (defined(bindings_configuration.for_blink) &&
             bindings_configuration.for_blink) {
           args += [ "--for_blink" ]
-          if (defined(invoker.export_class_attribute_blink)) {
-            args += [
-              "--export_attribute",
-              invoker.export_class_attribute_blink,
-              "--export_header",
-              invoker.export_header_blink,
-            ]
-          }
-        } else {
-          if (defined(invoker.export_class_attribute)) {
-            args += [
-              "--export_attribute",
-              invoker.export_class_attribute,
-              "--export_header",
-              invoker.export_header,
-            ]
-          }
         }
 
-        if (defined(invoker.use_once_callback) && invoker.use_once_callback) {
-          args += [ "--use_once_callback" ]
+        if (defined(invoker.use_new_wrapper_types) &&
+            invoker.use_new_wrapper_types) {
+          args += [ "--use_new_wrapper_types" ]
         }
       }
     }
@@ -472,39 +272,27 @@
         # line length limitations.
         typemap_description = []
         foreach(typemap, active_typemaps) {
-          _typemap_config = {
-          }
-          _typemap_config = typemap.config
           typemap_description += [ "--start-typemap" ]
-          if (defined(_typemap_config.public_headers)) {
-            foreach(value, _typemap_config.public_headers) {
+          if (defined(typemap.public_headers)) {
+            foreach(value, typemap.public_headers) {
               typemap_description += [ "public_headers=$value" ]
             }
           }
-          if (defined(_typemap_config.traits_headers)) {
-            foreach(value, _typemap_config.traits_headers) {
+          if (defined(typemap.traits_headers)) {
+            foreach(value, typemap.traits_headers) {
               typemap_description += [ "traits_headers=$value" ]
             }
           }
-          foreach(value, _typemap_config.type_mappings) {
+          foreach(value, typemap.type_mappings) {
             typemap_description += [ "type_mappings=$value" ]
           }
-
-          # The typemap configuration files are not actually used as inputs here
-          # but this establishes a necessary build dependency to ensure that
-          # typemap changes force a rebuild of affected targets.
-          inputs += [ typemap.filename ]
         }
         args += typemap_description
       }
     }
 
     source_set("${target_name}${variant_suffix}") {
-      if (defined(bindings_configuration.for_blink) &&
-          bindings_configuration.for_blink &&
-          defined(invoker.visibility_blink)) {
-        visibility = invoker.visibility_blink
-      } else if (defined(invoker.visibility)) {
+      if (defined(invoker.visibility)) {
         visibility = invoker.visibility
       }
       if (defined(invoker.testonly)) {
@@ -513,81 +301,69 @@
       if (defined(invoker.sources) && !defined(bindings_configuration.variant)) {
         data = process_file_template(enabled_sources, generator_js_outputs)
       }
-      defines = []
+
+      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 (defined(invoker.export_define)) {
-        defines += [ invoker.export_define ]
-      }
-      if (defined(invoker.export_define_blink)) {
-        defines += [ invoker.export_define_blink ]
-      }
       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",
-        "//mojo/public/interfaces/bindings:bindings_shared__generator",
-      ]
-      public_deps = [
-        ":$shared_cpp_sources_target_name",
-        "//base",
-        "//mojo/public/cpp/bindings",
       ]
       if (enabled_sources != []) {
-        public_deps += [ ":$generator_target_name" ]
+        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 variant_suffix to
+        # //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}" ]
-      }
-      if (defined(bindings_configuration.for_blink) &&
-          bindings_configuration.for_blink) {
-        if (defined(invoker.overridden_deps_blink)) {
-          foreach(d, invoker.overridden_deps_blink) {
-            # Resolve the name, so that a target //mojo/something becomes
-            # //mojo/something:something and we can append variant_suffix
-            # to get the cpp dependency name.
-            full_name = get_label_info("$d", "label_no_toolchain")
-            public_deps -= [ "${full_name}${variant_suffix}" ]
-          }
-          public_deps += invoker.component_deps_blink
-        }
-      } else {
-        if (defined(invoker.overridden_deps)) {
-          foreach(d, invoker.overridden_deps) {
-            # Resolve the name, so that a target //mojo/something becomes
-            # //mojo/something:something and we can append variant_suffix
-            # to get the cpp dependency name.
-            full_name = get_label_info("$d", "label_no_toolchain")
-            public_deps -= [ "${full_name}${variant_suffix}" ]
-          }
-          public_deps += invoker.component_deps
-        }
+        public_deps += [ "${full_name}${variant_suffix}_${cpp_sources_suffix}" ]
       }
       foreach(typemap, active_typemaps) {
-        _typemap_config = {
+        if (defined(typemap.public_headers)) {
+          sources += typemap.public_headers
         }
-        _typemap_config = typemap.config
-        if (defined(_typemap_config.public_headers)) {
-          sources += _typemap_config.public_headers
+        if (defined(typemap.traits_headers)) {
+          sources += typemap.traits_headers
         }
-        if (defined(_typemap_config.traits_headers)) {
-          sources += _typemap_config.traits_headers
+        if (defined(typemap.sources)) {
+          sources += typemap.sources
         }
-        if (defined(_typemap_config.sources)) {
-          sources += _typemap_config.sources
+        if (defined(typemap.public_deps)) {
+          public_deps += typemap.public_deps
         }
-        if (defined(_typemap_config.public_deps)) {
-          public_deps += _typemap_config.public_deps
-        }
-        if (defined(_typemap_config.deps)) {
-          deps += _typemap_config.deps
+        if (defined(typemap.deps)) {
+          deps += typemap.deps
         }
       }
       if (defined(bindings_configuration.for_blink) &&
@@ -629,8 +405,8 @@
       android_library(java_target_name) {
         deps = [
           "//base:base_java",
-          "//mojo/public/java:bindings_java",
-          "//mojo/public/java:system_java",
+          "//mojo/public/java:bindings",
+          "//mojo/public/java:system",
         ]
 
         foreach(d, all_deps) {
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py
index f90ce01..12196ba 100755
--- a/mojo/public/tools/bindings/mojom_bindings_generator.py
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -37,9 +37,10 @@
 
 from mojom.error import Error
 import mojom.fileutil as fileutil
-from mojom.generate import translate
+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 = {
@@ -101,12 +102,6 @@
 
 
 class MojomProcessor(object):
-  """Parses mojom files and creates ASTs for them.
-
-  Attributes:
-    _processed_files: {Dict[str, mojom.generate.module.Module]} Mapping from
-        relative mojom filename paths to the module AST for that mojom file.
-  """
   def __init__(self, should_generate):
     self._should_generate = should_generate
     self._processed_files = {}
@@ -141,18 +136,20 @@
     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.
-    imports = {}
-    for parsed_imp in tree.import_list:
+    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 = translate.OrderedModule(tree, name, imports)
+    module = OrderedModuleFromData(mojom)
 
     # Set the path as relative to the source root.
     module.path = rel_filename.relative_path()
@@ -166,10 +163,7 @@
             module, args.output_dir, typemap=self._typemap.get(language, {}),
             variant=args.variant, bytecode_path=args.bytecode_path,
             for_blink=args.for_blink,
-            use_once_callback=args.use_once_callback,
-            export_attribute=args.export_attribute,
-            export_header=args.export_header,
-            generate_non_variant_code=args.generate_non_variant_code)
+            use_new_wrapper_types=args.use_new_wrapper_types)
         filtered_args = []
         if hasattr(generator_module, 'GENERATOR_PREFIX'):
           prefix = '--' + generator_module.GENERATOR_PREFIX + '_'
@@ -196,7 +190,7 @@
       with open(rel_filename.path) as f:
         source = f.read()
     except IOError as e:
-      print "%s: Error: %s" % (rel_filename.path, e.strerror) + \
+      print "%s: Error: %s" % (e.rel_filename.path, e.strerror) + \
           MakeImportStackMessage(imported_filename_stack + [rel_filename.path])
       sys.exit(1)
 
@@ -236,12 +230,6 @@
   processor.LoadTypemaps(set(args.typemaps))
   for filename in args.filename:
     processor.ProcessFile(args, remaining_args, generator_modules, filename)
-  if args.depfile:
-    assert args.depfile_target
-    with open(args.depfile, 'w') as f:
-      f.write('%s: %s' % (
-          args.depfile_target,
-          ' '.join(processor._parsed_files.keys())))
 
   return 0
 
@@ -270,6 +258,9 @@
   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",
@@ -295,25 +286,9 @@
                                help="Use WTF types as generated types for mojo "
                                "string/array/map.")
   generate_parser.add_argument(
-      "--use_once_callback", action="store_true",
-      help="Use base::OnceCallback instead of base::RepeatingCallback.")
-  generate_parser.add_argument(
-      "--export_attribute", type=str, default="",
-      help="Optional attribute to specify on class declaration to export it "
-      "for the component build.")
-  generate_parser.add_argument(
-      "--export_header", type=str, default="",
-      help="Optional header to include in the generated headers to support the "
-      "component build.")
-  generate_parser.add_argument(
-      "--generate_non_variant_code", action="store_true",
-      help="Generate code that is shared by different variants.")
-  generate_parser.add_argument(
-      "--depfile", type=str,
-      help="A file into which the list of input files will be written.")
-  generate_parser.add_argument(
-      "--depfile_target", type=str,
-      help="The target name to use in the depfile.")
+      "--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",
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/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
index e4ab373..ccb8363 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -37,19 +37,15 @@
   # 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_once_callback=False,
-               export_attribute=None, export_header=None,
-               generate_non_variant_code=False):
+               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_once_callback = use_once_callback
-    self.export_attribute = export_attribute
-    self.export_header = export_header
-    self.generate_non_variant_code = generate_non_variant_code
+    self.use_new_wrapper_types = use_new_wrapper_types
 
   def GetStructsFromMethods(self):
     result = []
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
index 3a5f188..2775b6c 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -294,20 +294,6 @@
 
 
 class Struct(ReferenceKind):
-  """A struct with typed fields.
-
-  Attributes:
-    name: {str} The name of the struct type.
-    native_only: {bool} Does the struct have a body (i.e. any fields) or is it
-        purely a native struct.
-    module: {Module} The defining module.
-    imported_from: {dict} Information about where this union was
-        imported from.
-    fields: {List[StructField]} The members of the struct.
-    attributes: {dict} Additional information about the struct, such as
-        if it's a native struct.
-  """
-
   ReferenceKind.AddSharedProperty('name')
   ReferenceKind.AddSharedProperty('native_only')
   ReferenceKind.AddSharedProperty('module')
@@ -439,9 +425,14 @@
                              ']')
       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 IsAnyInterfaceKind(key_kind):
+      if IsInterfaceKind(key_kind):
         raise Exception("Interfaces cannot be keys in maps.")
       if IsArrayKind(key_kind):
         raise Exception("Arrays cannot be keys in maps.")
@@ -690,10 +681,6 @@
   return kind.spec == FLOAT.spec
 
 
-def IsDoubleKind(kind):
-  return kind.spec == DOUBLE.spec
-
-
 def IsIntegralKind(kind):
   return (kind.spec == BOOL.spec or
           kind.spec == INT8.spec or
@@ -784,22 +771,20 @@
           IsMapKind(kind))
 
 
-# Please note that it doesn't include any interface 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))
-
-
-def IsAnyInterfaceKind(kind):
-  return (IsInterfaceKind(kind) or IsInterfaceRequestKind(kind) or
-          IsAssociatedKind(kind))
+          IsSharedBufferKind(kind) or
+          IsInterfaceRequestKind(kind))
 
 
 def IsAnyHandleOrInterfaceKind(kind):
-  return IsAnyHandleKind(kind) or IsAnyInterfaceKind(kind)
+  return (IsAnyHandleKind(kind) or IsInterfaceKind(kind) or
+          IsAssociatedKind(kind))
 
 
 def IsAssociatedKind(kind):
@@ -853,39 +838,3 @@
     if method.sync:
       return True
   return False
-
-
-def ContainsHandlesOrInterfaces(kind):
-  """Check if the kind contains any handles.
-
-  This check is recursive so it checks all struct fields, containers elements,
-  etc.
-
-  Args:
-    struct: {Kind} The kind to check.
-
-  Returns:
-    {bool}: True if the kind contains handles.
-  """
-  # We remember the types we already checked to avoid infinite recursion when
-  # checking recursive (or mutually recursive) types:
-  checked = set()
-  def Check(kind):
-    if kind.spec in checked:
-      return False
-    checked.add(kind.spec)
-    if IsStructKind(kind):
-      return any(Check(field.kind) for field in kind.fields)
-    elif IsUnionKind(kind):
-      return any(Check(field.kind) for field in kind.fields)
-    elif IsAnyHandleKind(kind):
-      return True
-    elif IsAnyInterfaceKind(kind):
-      return True
-    elif IsArrayKind(kind):
-      return Check(kind.kind)
-    elif IsMapKind(kind):
-      return Check(kind.key_kind) or Check(kind.value_kind)
-    else:
-      return False
-  return Check(kind)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
index 66f8954..9ea6cf8 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
@@ -2,7 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# Based on third_party/WebKit/Source/build/scripts/template_expander.py.
+# Based on:
+# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py
 
 import imp
 import os.path
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/translate.py b/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
deleted file mode 100644
index ffad744..0000000
--- a/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
+++ /dev/null
@@ -1,639 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Convert parse tree to AST.
-
-This module converts the parse tree to the AST we use for code generation. The
-main entry point is OrderedModule, which gets passed the parser
-representation of a mojom file. When called it's assumed that all imports have
-already been parsed and converted to ASTs before.
-"""
-
-import copy
-import re
-
-import module as mojom
-from mojom.parse 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 _ElemsOfType(elems, elem_type, scope):
-  """Find all elements of the given type.
-
-  Args:
-    elems: {Sequence[Any]} Sequence of elems.
-    elem_type: {Type[C]} Extract all elems of this type.
-    scope: {str} The name of the surrounding scope (e.g. struct
-        definition). Used in error messages.
-
-  Returns:
-    {List[C]} All elems of matching type.
-  """
-  assert isinstance(elem_type, type)
-  result = [elem for elem in elems if isinstance(elem, elem_type)]
-  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 _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])
-
-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 _Kind(kinds, spec, scope):
-  """Convert a type name into a mojom.Kind object.
-
-  As a side-effect this function adds the result to 'kinds'.
-
-  Args:
-    kinds: {Dict[str, mojom.Kind]} All known kinds up to this point, indexed by
-        their names.
-    spec: {str} A name uniquely identifying a type.
-    scope: {Tuple[str, str]} A tuple that looks like (namespace,
-        struct/interface), referring to the location where the type is
-        referenced.
-
-  Returns:
-    {mojom.Kind} The type corresponding to 'spec'.
-  """
-  kind = _LookupKind(kinds, spec, scope)
-  if kind:
-    return kind
-
-  if spec.startswith('?'):
-    kind = _Kind(kinds, spec[1:], scope).MakeNullableKind()
-  elif spec.startswith('a:'):
-    kind = mojom.Array(_Kind(kinds, spec[2:], scope))
-  elif spec.startswith('asso:'):
-    inner_kind = _Kind(kinds, spec[5:], scope)
-    if isinstance(inner_kind, mojom.InterfaceRequest):
-      kind = mojom.AssociatedInterfaceRequest(inner_kind)
-    else:
-      kind = mojom.AssociatedInterface(inner_kind)
-  elif spec.startswith('a'):
-    colon = spec.find(':')
-    length = int(spec[1:colon])
-    kind = mojom.Array(_Kind(kinds, spec[colon+1:], scope), length)
-  elif spec.startswith('r:'):
-    kind = mojom.InterfaceRequest(_Kind(kinds, spec[2:], scope))
-  elif spec.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 = spec.find(']')
-    assert key_end != -1 and key_end < len(spec) - 1
-    assert spec[key_end+1] == '[' and spec[-1] == ']'
-
-    first_kind = spec[2:key_end]
-    second_kind = spec[key_end+2:-1]
-
-    kind = mojom.Map(_Kind(kinds, first_kind, scope),
-                     _Kind(kinds, second_kind, scope))
-  else:
-    kind = mojom.Kind(spec)
-
-  kinds[spec] = 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 _Import(module, import_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 _Struct(module, parsed_struct):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_struct: {ast.Struct} Parsed struct.
-
-  Returns:
-    {mojom.Struct} AST struct.
-  """
-  struct = mojom.Struct(module=module)
-  struct.name = parsed_struct.name
-  struct.native_only = parsed_struct.body is None
-  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: _Enum(module, enum, struct),
-        _ElemsOfType(parsed_struct.body, ast.Enum, parsed_struct.name))
-    struct.constants = map(
-        lambda constant: _Constant(module, constant, struct),
-        _ElemsOfType(parsed_struct.body, ast.Const, parsed_struct.name))
-    # Stash fields parsed_struct here temporarily.
-    struct.fields_data = _ElemsOfType(
-        parsed_struct.body, ast.StructField, parsed_struct.name)
-  struct.attributes = _AttributeListToDict(parsed_struct.attribute_list)
-
-  # 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 _Union(module, parsed_union):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_union: {ast.Union} Parsed union.
-
-  Returns:
-    {mojom.Union} AST union.
-  """
-  union = mojom.Union(module=module)
-  union.name = parsed_union.name
-  union.spec = 'x:' + module.namespace + '.' + union.name
-  module.kinds[union.spec] = union
-  # Stash fields parsed_union here temporarily.
-  union.fields_data = _ElemsOfType(
-      parsed_union.body, ast.UnionField, parsed_union.name)
-  union.attributes = _AttributeListToDict(parsed_union.attribute_list)
-  return union
-
-def _StructField(module, parsed_field, struct):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_field: {ast.StructField} Parsed struct field.
-    struct: {mojom.Struct} Struct this field belongs to.
-
-  Returns:
-    {mojom.StructField} AST struct field.
-  """
-  field = mojom.StructField()
-  field.name = parsed_field.name
-  field.kind = _Kind(
-      module.kinds, _MapKind(parsed_field.typename),
-      (module.namespace, struct.name))
-  field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
-  field.default = _FixupExpression(
-      module, parsed_field.default_value, (module.namespace, struct.name),
-      field.kind)
-  field.attributes = _AttributeListToDict(parsed_field.attribute_list)
-  return field
-
-def _UnionField(module, parsed_field, union):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_field: {ast.UnionField} Parsed union field.
-    union: {mojom.Union} Union this fields belong to.
-
-  Returns:
-    {mojom.UnionField} AST union.
-  """
-  field = mojom.UnionField()
-  field.name = parsed_field.name
-  field.kind = _Kind(
-      module.kinds, _MapKind(parsed_field.typename),
-      (module.namespace, union.name))
-  field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
-  field.default = _FixupExpression(
-      module, None, (module.namespace, union.name), field.kind)
-  field.attributes = _AttributeListToDict(parsed_field.attribute_list)
-  return field
-
-def _Parameter(module, parsed_param, interface):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_param: {ast.Parameter} Parsed parameter.
-    union: {mojom.Interface} Interface this parameter belongs to.
-
-  Returns:
-    {mojom.Parameter} AST parameter.
-  """
-  parameter = mojom.Parameter()
-  parameter.name = parsed_param.name
-  parameter.kind = _Kind(
-      module.kinds, _MapKind(parsed_param.typename),
-      (module.namespace, interface.name))
-  parameter.ordinal = (
-      parsed_param.ordinal.value if parsed_param.ordinal else None)
-  parameter.default = None  # TODO(tibell): We never have these. Remove field?
-  parameter.attributes = _AttributeListToDict(parsed_param.attribute_list)
-  return parameter
-
-def _Method(module, parsed_method, interface):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_method: {ast.Method} Parsed method.
-    interface: {mojom.Interface} Interface this method belongs to.
-
-  Returns:
-    {mojom.Method} AST method.
-  """
-  method = mojom.Method(
-      interface, parsed_method.name,
-      ordinal=parsed_method.ordinal.value if parsed_method.ordinal else None)
-  method.parameters = map(
-      lambda parameter: _Parameter(module, parameter, interface),
-      parsed_method.parameter_list)
-  if parsed_method.response_parameter_list is not None:
-    method.response_parameters = map(
-        lambda parameter: _Parameter(module, parameter, interface),
-                          parsed_method.response_parameter_list)
-  method.attributes = _AttributeListToDict(parsed_method.attribute_list)
-
-  # 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 _Interface(module, parsed_iface):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_iface: {ast.Interface} Parsed interface.
-
-  Returns:
-    {mojom.Interface} AST interface.
-  """
-  interface = mojom.Interface(module=module)
-  interface.name = parsed_iface.name
-  interface.spec = 'x:' + module.namespace + '.' + interface.name
-  module.kinds[interface.spec] = interface
-  interface.enums = map(
-      lambda enum: _Enum(module, enum, interface),
-      _ElemsOfType(parsed_iface.body, ast.Enum, parsed_iface.name))
-  interface.constants = map(
-      lambda constant: _Constant(module, constant, interface),
-      _ElemsOfType(parsed_iface.body, ast.Const, parsed_iface.name))
-  # Stash methods parsed_iface here temporarily.
-  interface.methods_data = _ElemsOfType(
-      parsed_iface.body, ast.Method, parsed_iface.name)
-  interface.attributes = _AttributeListToDict(parsed_iface.attribute_list)
-  return interface
-
-def _EnumField(module, enum, parsed_field, parent_kind):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    enum: {mojom.Enum} Enum this field belongs to.
-    parsed_field: {ast.EnumValue} Parsed enum value.
-    parent_kind: {mojom.Kind} The enclosing type.
-
-  Returns:
-    {mojom.EnumField} AST enum field.
-  """
-  field = mojom.EnumField()
-  field.name = parsed_field.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, parsed_field.value, (module.namespace, parent_kind.name), enum)
-  else:
-    field.value = _FixupExpression(
-        module, parsed_field.value, (module.namespace, ), enum)
-  field.attributes = _AttributeListToDict(parsed_field.attribute_list)
-  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 _Enum(module, parsed_enum, parent_kind):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_enum: {ast.Enum} Parsed enum.
-
-  Returns:
-    {mojom.Enum} AST enum.
-  """
-  enum = mojom.Enum(module=module)
-  enum.name = parsed_enum.name
-  enum.native_only = parsed_enum.enum_value_list is None
-  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 = _AttributeListToDict(parsed_enum.attribute_list)
-  if enum.native_only:
-    enum.fields = []
-  else:
-    enum.fields = map(
-        lambda field: _EnumField(module, enum, field, parent_kind),
-        parsed_enum.enum_value_list)
-    _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 _Constant(module, parsed_const, parent_kind):
-  """
-  Args:
-    module: {mojom.Module} Module currently being constructed.
-    parsed_const: {ast.Const} Parsed constant.
-
-  Returns:
-    {mojom.Constant} AST constant.
-  """
-  constant = mojom.Constant()
-  constant.name = parsed_const.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 = _Kind(module.kinds, _MapKind(parsed_const.typename), scope)
-  constant.parent_kind = parent_kind
-  constant.value = _FixupExpression(module, parsed_const.value, scope, None)
-
-  value = mojom.ConstantValue(module, parent_kind, constant)
-  module.values[value.GetSpec()] = value
-  return constant
-
-def _Module(tree, name, imports):
-  """
-  Args:
-    tree: {ast.Mojom} The parse tree.
-    name: {str} The mojom filename, excluding the path.
-    imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in
-        the import list, to already processed modules. Used to process imports.
-
-  Returns:
-    {mojom.Module} An AST for the mojom.
-  """
-  module = mojom.Module()
-  module.kinds = {}
-  for kind in mojom.PRIMITIVES:
-    module.kinds[kind.spec] = kind
-
-  module.values = {}
-
-  module.name = name
-  module.namespace = tree.module.name[1] if tree.module else ''
-  # Imports must come first, because they add to module.kinds which is used
-  # by by the others.
-  module.imports = [
-      _Import(module, imports[imp.import_filename])
-      for imp in tree.import_list]
-  if tree.module and tree.module.attribute_list:
-    assert isinstance(tree.module.attribute_list, ast.AttributeList)
-    # TODO(vtl): Check for duplicate keys here.
-    module.attributes = dict((attribute.key, attribute.value)
-                             for attribute in tree.module.attribute_list)
-
-  # First pass collects kinds.
-  module.enums = map(
-      lambda enum: _Enum(module, enum, None),
-      _ElemsOfType(tree.definition_list, ast.Enum, name))
-  module.structs = map(
-      lambda struct: _Struct(module, struct),
-      _ElemsOfType(tree.definition_list, ast.Struct, name))
-  module.unions = map(
-      lambda union: _Union(module, union),
-      _ElemsOfType(tree.definition_list, ast.Union, name))
-  module.interfaces = map(
-      lambda interface: _Interface(module, interface),
-      _ElemsOfType(tree.definition_list, ast.Interface, name))
-  module.constants = map(
-      lambda constant: _Constant(module, constant, None),
-      _ElemsOfType(tree.definition_list, ast.Const, name))
-
-  # 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:
-        _StructField(module, field, struct), struct.fields_data)
-    del struct.fields_data
-  for union in module.unions:
-    union.fields = map(lambda field:
-        _UnionField(module, field, union), union.fields_data)
-    del union.fields_data
-  for interface in module.interfaces:
-    interface.methods = map(lambda method:
-        _Method(module, method, interface), interface.methods_data)
-    del interface.methods_data
-
-  return module
-
-def OrderedModule(tree, name, imports):
-  """Convert parse tree to AST module.
-
-  Args:
-    tree: {ast.Mojom} The parse tree.
-    name: {str} The mojom filename, excluding the path.
-    imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in
-        the import list, to already processed modules. Used to process imports.
-
-  Returns:
-    {mojom.Module} An AST for the mojom.
-  """
-  module = _Module(tree, name, imports)
-  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/parse/parser.py b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
index 868fb45..5cf20fe 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -346,7 +346,7 @@
     p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
 
   def p_enum_1(self, p):
-    """enum : attribute_section ENUM NAME LBRACE enum_value_list \
+    """enum : attribute_section ENUM NAME LBRACE nonempty_enum_value_list \
                   RBRACE SEMI
             | attribute_section ENUM NAME LBRACE nonempty_enum_value_list \
                   COMMA RBRACE SEMI"""
@@ -358,14 +358,6 @@
     p[0] = ast.Enum(p[3], p[1], None, filename=self.filename,
                     lineno=p.lineno(2))
 
-  def p_enum_value_list_1(self, p):
-    """enum_value_list : """
-    p[0] = ast.EnumValueList()
-
-  def p_enum_value_list_2(self, p):
-    """enum_value_list : nonempty_enum_value_list"""
-    p[0] = p[1]
-
   def p_nonempty_enum_value_list_1(self, p):
     """nonempty_enum_value_list : enum_value"""
     p[0] = ast.EnumValueList(p[1])
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/gn/zip.py b/mojo/public/tools/gn/zip.py
index adc9cb1..0d4960f 100755
--- a/mojo/public/tools/gn/zip.py
+++ b/mojo/public/tools/gn/zip.py
@@ -20,30 +20,25 @@
                              "build"))
 import gn_helpers
 
-sys.path.append(os.path.join(os.path.dirname(__file__),
-                             os.pardir, os.pardir, os.pardir, os.pardir,
-                             'build', 'android', 'gyp'))
-from util import build_utils
-
-
 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)
-      build_utils.AddToZipHermetic(outfile, file_name, f)
+      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)
-      build_utils.AddToZipHermetic(outfile, file_name, realf)
+      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)
-            build_utils.AddToZipHermetic(outfile, f, data=zf.read(f))
+            with zf.open(f) as zff:
+              outfile.writestr(f, zff.read())
 
 
 def 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())