Update mojo sdk to rev 04a510fb37db10642e156957f9b2c11c2f6442ac

This updates the mojo sdk and updates chromium code to match. The most
significant change is updating references to the mojo JS bindings code,
which reshuffled. This part was reviewed separately by hansmuller@ in
https://codereview.chromium.org/693343003/. This also updates ipc/mojo
to reflect changes in ChannelInfo.

R=morrita@chromium.org for ipc/mojo
R=jam@chromium.org for top-level approval of the rest

Review URL: https://codereview.chromium.org/703273002

Cr-Commit-Position: refs/heads/master@{#303149}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ad9112c..8906815 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -189,7 +189,7 @@
       "//mojo/edk/system",
       "//mojo/environment:chromium",
       "//mojo/public/cpp/bindings",
-      "//mojo/public/js/bindings",
+      "//mojo/public/js",
       "//net:extras",
       "//net:net_with_v8",
       "//storage/browser",
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js
index 89f9dd9..8ce8fe00 100644
--- a/chrome/browser/resources/omnibox/omnibox.js
+++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -17,7 +17,7 @@
  * are available, the Javascript formats them and displays them.
  */
 define('main', [
-    'mojo/public/js/bindings/connection',
+    'mojo/public/js/connection',
     'chrome/browser/ui/webui/omnibox/omnibox.mojom',
     'content/public/renderer/service_provider',
 ], function(connector, browser, serviceProvider) {
diff --git a/content/DEPS b/content/DEPS
index df179ca..d3543fc 100644
--- a/content/DEPS
+++ b/content/DEPS
@@ -32,9 +32,9 @@
 
   "+dbus",
   "+gpu",
-  "+mojo/bindings/js",
   "+mojo/common",
   "+mojo/edk/embedder",
+  "+mojo/edk/js",
   "+mojo/edk/test",
   "+mojo/public",
   "+net",
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 7a09df7c..a420b9e9 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -100,7 +100,7 @@
       "//content/public/common:mojo_bindings",
       "//mojo/public/cpp/bindings",
       "//mojo/public/interfaces/application",
-      "//mojo/public/js/bindings",
+      "//mojo/public/js",
       "//net:http_server",
       "//storage/browser",
       "//storage/common",
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc
index 6fbe577b..caa8f9a 100644
--- a/content/browser/webui/web_ui_data_source_impl.cc
+++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -11,7 +11,7 @@
 #include "base/strings/string_util.h"
 #include "content/grit/content_resources.h"
 #include "content/public/common/content_client.h"
-#include "mojo/public/js/bindings/constants.h"
+#include "mojo/public/js/constants.h"
 #include "ui/base/webui/jstemplate_builder.h"
 #include "ui/base/webui/web_ui_util.h"
 
diff --git a/content/browser/webui/web_ui_mojo_browsertest.cc b/content/browser/webui/web_ui_mojo_browsertest.cc
index 3a2ae3a..9830f1df 100644
--- a/content/browser/webui/web_ui_mojo_browsertest.cc
+++ b/content/browser/webui/web_ui_mojo_browsertest.cc
@@ -28,7 +28,7 @@
 #include "mojo/edk/test/test_utils.h"
 #include "mojo/public/cpp/bindings/interface_impl.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/js/bindings/constants.h"
+#include "mojo/public/js/constants.h"
 
 namespace content {
 namespace {
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index 1342dc90..8e400c3 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -18,6 +18,7 @@
   deps = [
     "//base",
     "//components/tracing",
+    "//mojo/common",
     "//mojo/environment:chromium",
     "//mojo/public/interfaces/application",
     "//skia",
diff --git a/content/content_child.gypi b/content/content_child.gypi
index a374e05aa..c51317f3 100644
--- a/content/content_child.gypi
+++ b/content/content_child.gypi
@@ -7,6 +7,7 @@
     '../base/base.gyp:base',
     '../components/tracing.gyp:tracing',
     '../mojo/mojo_base.gyp:mojo_environment_chromium',
+    '../mojo/mojo_base.gyp:mojo_common_lib',
     '../mojo/public/mojo_public.gyp:mojo_application_bindings',
     '../skia/skia.gyp:skia',
     '../ui/base/ui_base.gyp:ui_base',
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index c64d4be..9885d2e7 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -16,8 +16,8 @@
     '../jingle/jingle.gyp:jingle_glue',
     '../media/blink/media_blink.gyp:media_blink',
     '../media/media.gyp:media',
+    '../mojo/edk/mojo_edk.gyp:mojo_js_lib',
     '../mojo/mojo_base.gyp:mojo_environment_chromium',
-    '../mojo/mojo_base.gyp:mojo_js_bindings_lib',
     '../mojo/public/mojo_public.gyp:mojo_application_bindings',
     '../net/net.gyp:net',
     '../skia/skia.gyp:skia',
diff --git a/content/content_resources.grd b/content/content_resources.grd
index 6b0b7b6..445cc8d9 100644
--- a/content/content_resources.grd
+++ b/content/content_resources.grd
@@ -38,13 +38,13 @@
         <include name="IDR_UTILITY_SANDBOX_PROFILE" file="utility/utility.sb" type="BINDATA" />
       </if>
       <if expr="not is_ios">
-        <include name="IDR_MOJO_BUFFER_JS" file="../mojo/public/js/bindings/buffer.js" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_MOJO_CODEC_JS" file="../mojo/public/js/bindings/codec.js" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_MOJO_CONNECTION_JS" file="../mojo/public/js/bindings/connection.js" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_MOJO_CONNECTOR_JS" file="../mojo/public/js/bindings/connector.js" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_MOJO_ROUTER_JS" file="../mojo/public/js/bindings/router.js" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_MOJO_UNICODE_JS" file="../mojo/public/js/bindings/unicode.js" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_MOJO_VALIDATOR_JS" file="../mojo/public/js/bindings/validator.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_MOJO_BUFFER_JS" file="../mojo/public/js/buffer.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_MOJO_CODEC_JS" file="../mojo/public/js/codec.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_MOJO_CONNECTION_JS" file="../mojo/public/js/connection.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_MOJO_CONNECTOR_JS" file="../mojo/public/js/connector.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_MOJO_ROUTER_JS" file="../mojo/public/js/router.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_MOJO_UNICODE_JS" file="../mojo/public/js/unicode.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_MOJO_VALIDATOR_JS" file="../mojo/public/js/validator.js" flattenhtml="true" type="BINDATA" />
       </if>
     </includes>
   </release>
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 3bbad56..22acf49b 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -39,9 +39,9 @@
     "//jingle:jingle_glue",
     "//media",
     "//media/blink",
-    "//mojo/bindings/js",
+    "//mojo/edk/js",
     "//mojo/environment:chromium",
-    "//mojo/public/js/bindings",
+    "//mojo/public/js",
     "//mojo/public/interfaces/application",
     "//net",
     "//skia",
diff --git a/content/renderer/mojo/service_registry_js_wrapper.cc b/content/renderer/mojo/service_registry_js_wrapper.cc
index ccf2156..73a591e3 100644
--- a/content/renderer/mojo/service_registry_js_wrapper.cc
+++ b/content/renderer/mojo/service_registry_js_wrapper.cc
@@ -6,7 +6,7 @@
 
 #include "content/common/mojo/service_registry_impl.h"
 #include "content/public/common/service_registry.h"
-#include "mojo/bindings/js/handle.h"
+#include "mojo/edk/js/handle.h"
 
 namespace content {
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 561d40d..7837065b 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -101,8 +101,8 @@
 #include "media/blink/webmediaplayer_impl.h"
 #include "media/blink/webmediaplayer_params.h"
 #include "media/filters/gpu_video_accelerator_factories.h"
-#include "mojo/bindings/js/core.h"
-#include "mojo/bindings/js/support.h"
+#include "mojo/edk/js/core.h"
+#include "mojo/edk/js/support.h"
 #include "net/base/data_url.h"
 #include "net/base/net_errors.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
diff --git a/content/test/data/web_ui_mojo.js b/content/test/data/web_ui_mojo.js
index cac2eb7a..643b35d 100644
--- a/content/test/data/web_ui_mojo.js
+++ b/content/test/data/web_ui_mojo.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 define('main', [
-    'mojo/public/js/bindings/connection',
+    'mojo/public/js/connection',
     'content/test/data/web_ui_test_mojo_bindings.mojom',
     'content/public/renderer/service_provider',
 ], function (connection, bindings, serviceProvider) {
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 2a4d374..5808371 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -266,7 +266,7 @@
     "//extensions/common",
     "//extensions/renderer",
     "//extensions/strings",
-    "//mojo/bindings/js",
+    "//mojo/edk/js",
     "//mojo/edk/system",
     "//mojo/environment:chromium",
     "//mojo/public/cpp/bindings",
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index f146669..b4c2680 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -1130,9 +1130,9 @@
         '../device/bluetooth/bluetooth.gyp:device_bluetooth_mocks',
         '../device/serial/serial.gyp:device_serial',
         '../device/serial/serial.gyp:device_serial_test_util',
+        '../mojo/edk/mojo_edk.gyp:mojo_js_lib',
         '../mojo/edk/mojo_edk.gyp:mojo_system_impl',
         '../mojo/mojo_base.gyp:mojo_environment_chromium',
-        '../mojo/mojo_base.gyp:mojo_js_bindings_lib',
         '../mojo/public/mojo_public.gyp:mojo_cpp_bindings',
         '../testing/gmock.gyp:gmock',
         '../testing/gtest.gyp:gtest',
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index 5e02517..f91479d 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -167,7 +167,7 @@
     "//content:resources",
     "//extensions:extensions_resources",
     "//gin",
-    "//mojo/bindings/js",
+    "//mojo/edk/js",
     "//skia",
     "//third_party/WebKit/public:blink",
   ]
diff --git a/extensions/renderer/DEPS b/extensions/renderer/DEPS
index 111fc81..fadfee7 100644
--- a/extensions/renderer/DEPS
+++ b/extensions/renderer/DEPS
@@ -2,7 +2,7 @@
   "+content/public/renderer",
 
   "+gin",
-  "+mojo/bindings/js",
+  "+mojo/edk/js",
 
   "+third_party/skia/include/core",
 
diff --git a/extensions/renderer/api_test_base.cc b/extensions/renderer/api_test_base.cc
index 17e09c2..46a6ac2 100644
--- a/extensions/renderer/api_test_base.cc
+++ b/extensions/renderer/api_test_base.cc
@@ -12,9 +12,9 @@
 #include "extensions/renderer/process_info_native_handler.h"
 #include "gin/converter.h"
 #include "gin/dictionary.h"
-#include "mojo/bindings/js/core.h"
-#include "mojo/bindings/js/handle.h"
-#include "mojo/bindings/js/support.h"
+#include "mojo/edk/js/core.h"
+#include "mojo/edk/js/handle.h"
+#include "mojo/edk/js/support.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/system/core.h"
 
diff --git a/extensions/renderer/api_test_base.h b/extensions/renderer/api_test_base.h
index 3684080..1989727 100644
--- a/extensions/renderer/api_test_base.h
+++ b/extensions/renderer/api_test_base.h
@@ -17,7 +17,7 @@
 #include "gin/modules/module_registry.h"
 #include "gin/object_template_builder.h"
 #include "gin/wrappable.h"
-#include "mojo/bindings/js/handle.h"
+#include "mojo/edk/js/handle.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/system/core.h"
 
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 1de7dcf..cfe247d6 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -80,7 +80,7 @@
 #include "extensions/renderer/utils_native_handler.h"
 #include "extensions/renderer/v8_context_native_handler.h"
 #include "grit/extensions_renderer_resources.h"
-#include "mojo/public/js/bindings/constants.h"
+#include "mojo/public/js/constants.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/web/WebCustomElement.h"
diff --git a/extensions/renderer/resources/async_waiter.js b/extensions/renderer/resources/async_waiter.js
index 3dabeff..6470f64b 100644
--- a/extensions/renderer/resources/async_waiter.js
+++ b/extensions/renderer/resources/async_waiter.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 define('async_waiter', [
-    'mojo/public/js/bindings/support',
+    'mojo/public/js/support',
 ], function(supportModule) {
   /**
    * @module async_waiter
diff --git a/extensions/renderer/resources/data_receiver.js b/extensions/renderer/resources/data_receiver.js
index 70009c2..0f286be 100644
--- a/extensions/renderer/resources/data_receiver.js
+++ b/extensions/renderer/resources/data_receiver.js
@@ -5,8 +5,8 @@
 define('data_receiver', [
     'device/serial/data_stream.mojom',
     'device/serial/data_stream_serialization.mojom',
-    'mojo/public/js/bindings/core',
-    'mojo/public/js/bindings/router',
+    'mojo/public/js/core',
+    'mojo/public/js/router',
 ], function(dataStream, serialization, core, router) {
   /**
    * @module data_receiver
@@ -135,7 +135,7 @@
                                           pendingData,
                                           paused) {
     /**
-     * The [Router]{@link module:mojo/public/js/bindings/router.Router} for the
+     * The [Router]{@link module:mojo/public/js/router.Router} for the
      * connection to the DataSource.
      * @private
      */
diff --git a/extensions/renderer/resources/data_sender.js b/extensions/renderer/resources/data_sender.js
index 99935c48..db259ec 100644
--- a/extensions/renderer/resources/data_sender.js
+++ b/extensions/renderer/resources/data_sender.js
@@ -5,8 +5,8 @@
 define('data_sender', [
     'device/serial/data_stream.mojom',
     'device/serial/data_stream_serialization.mojom',
-    'mojo/public/js/bindings/core',
-    'mojo/public/js/bindings/router',
+    'mojo/public/js/core',
+    'mojo/public/js/router',
 ], function(dataStreamMojom, serialization, core, routerModule) {
   /**
    * @module data_sender
@@ -221,7 +221,7 @@
      */
     this.shutDown_ = false;
     /**
-     * The [Router]{@link module:mojo/public/js/bindings/router.Router} for the
+     * The [Router]{@link module:mojo/public/js/router.Router} for the
      * connection to the DataSink.
      * @private
      */
diff --git a/extensions/renderer/resources/keep_alive.js b/extensions/renderer/resources/keep_alive.js
index 281e8517..aa5f6a0 100644
--- a/extensions/renderer/resources/keep_alive.js
+++ b/extensions/renderer/resources/keep_alive.js
@@ -5,7 +5,7 @@
 define('keep_alive', [
     'content/public/renderer/service_provider',
     'extensions/common/mojo/keep_alive.mojom',
-    'mojo/public/js/bindings/core',
+    'mojo/public/js/core',
 ], function(serviceProvider, mojom, core) {
 
   /**
diff --git a/extensions/renderer/resources/serial_service.js b/extensions/renderer/resources/serial_service.js
index 6d57cee..d0f29761 100644
--- a/extensions/renderer/resources/serial_service.js
+++ b/extensions/renderer/resources/serial_service.js
@@ -8,8 +8,8 @@
     'data_sender',
     'device/serial/serial.mojom',
     'device/serial/serial_serialization.mojom',
-    'mojo/public/js/bindings/core',
-    'mojo/public/js/bindings/router',
+    'mojo/public/js/core',
+    'mojo/public/js/router',
 ], function(serviceProvider,
             dataReceiver,
             dataSender,
diff --git a/extensions/test/data/api_test_base_unittest.js b/extensions/test/data/api_test_base_unittest.js
index c402d59..46641ba5c 100644
--- a/extensions/test/data/api_test_base_unittest.js
+++ b/extensions/test/data/api_test_base_unittest.js
@@ -36,8 +36,8 @@
   },
   function testMojoModulesAreAvailable() {
     Promise.all([
-      requireAsync('mojo/public/js/bindings/connection'),
-      requireAsync('mojo/public/js/bindings/core'),
+      requireAsync('mojo/public/js/connection'),
+      requireAsync('mojo/public/js/core'),
       requireAsync('content/public/renderer/service_provider'),
     ]).then(test.callback(function(modules) {
       var connection = modules[0];
diff --git a/ipc/mojo/ipc_channel_mojo.h b/ipc/mojo/ipc_channel_mojo.h
index 45d2482..29d5f29 100644
--- a/ipc/mojo/ipc_channel_mojo.h
+++ b/ipc/mojo/ipc_channel_mojo.h
@@ -15,14 +15,9 @@
 #include "ipc/ipc_export.h"
 #include "ipc/mojo/ipc_message_pipe_reader.h"
 #include "ipc/mojo/ipc_mojo_bootstrap.h"
+#include "mojo/edk/embedder/channel_info_forward.h"
 #include "mojo/public/cpp/system/core.h"
 
-namespace mojo {
-namespace embedder {
-struct ChannelInfo;
-}
-}
-
 namespace IPC {
 
 namespace internal {
diff --git a/ipc/mojo/ipc_channel_mojo_readers.h b/ipc/mojo/ipc_channel_mojo_readers.h
index cadf453c..ffcc08b 100644
--- a/ipc/mojo/ipc_channel_mojo_readers.h
+++ b/ipc/mojo/ipc_channel_mojo_readers.h
@@ -12,12 +12,6 @@
 #include "ipc/mojo/ipc_message_pipe_reader.h"
 #include "mojo/public/cpp/system/core.h"
 
-namespace mojo {
-namespace embedder {
-struct ChannelInfo;
-}
-}
-
 namespace IPC {
 
 class ChannelMojo;
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index c6c9d77..d9ec502f 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -24,9 +24,9 @@
 group("tests") {
   testonly = true
   deps = [
-    "//mojo/bindings/js/tests:mojo_js_unittests",
     "//mojo/common:mojo_common_unittests",
     "//mojo/converters/surfaces/tests:mojo_surfaces_lib_unittests",
+    "//mojo/edk/js/tests:js_unittests",
     "//mojo/edk/system:mojo_message_pipe_perftests",
     "//mojo/edk/system:mojo_system_unittests",
     "//mojo/public/c/system/tests:perftests",
diff --git a/mojo/bindings/js/tests/DEPS b/mojo/bindings/js/tests/DEPS
deleted file mode 100644
index 2424ea1..0000000
--- a/mojo/bindings/js/tests/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-include_rules = [
-  "+base",
-  "+gin",
-  "+v8",
-  "+mojo/bindings/js/core.h",
-  "+mojo/bindings/js/support.h",
-]
diff --git a/mojo/edk/PRESUBMIT.py b/mojo/edk/PRESUBMIT.py
deleted file mode 100644
index 43d5417..0000000
--- a/mojo/edk/PRESUBMIT.py
+++ /dev/null
@@ -1,16 +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.
-
-"""Presubmit script for mojo/edk.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-def CheckChangeOnUpload(input_api, output_api):
-  results = []
-  results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api,
-                                                              output_api)
-  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
-  return results
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
index 9c2f39fd..50e93b0 100644
--- a/mojo/edk/embedder/BUILD.gn
+++ b/mojo/edk/embedder/BUILD.gn
@@ -17,6 +17,7 @@
   configs += [ "//mojo/edk/system:system_config" ]
 
   sources = [
+    "channel_info_forward.h",
     "channel_init.cc",
     "channel_init.h",
     "embedder.cc",
diff --git a/mojo/edk/embedder/channel_info_forward.h b/mojo/edk/embedder/channel_info_forward.h
new file mode 100644
index 0000000..494d5ef
--- /dev/null
+++ b/mojo/edk/embedder/channel_info_forward.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.
+
+// This file simply (forward) declares |mojo::embedder::ChannelInfo|, which is
+// meant to be opaque to users of the embedder API.
+
+#ifndef MOJO_EDK_EMBEDDER_CHANNEL_INFO_FORWARD_H_
+#define MOJO_EDK_EMBEDDER_CHANNEL_INFO_FORWARD_H_
+
+namespace mojo {
+
+// Forward declare |system::ChannelInfo|, so that we can typedef it to
+// |embedder::ChannelInfo|. Users of the embedder API shouldn't use this
+// directly; instead they should use |embedder::ChannelInfo|.
+namespace system {
+struct ChannelInfo;
+}
+
+namespace embedder {
+
+// This is an opaque type. The embedder API uses (returns and takes as
+// arguments) pointers to this type. (We don't simply use |void*|, so that
+// custom deleters and such can be used without additional wrappers.
+typedef system::ChannelInfo ChannelInfo;
+
+}  // namespace embedder
+
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_CHANNEL_INFO_FORWARD_H_
diff --git a/mojo/edk/embedder/channel_init.h b/mojo/edk/embedder/channel_init.h
index 797cc6d..ce191b4 100644
--- a/mojo/edk/embedder/channel_init.h
+++ b/mojo/edk/embedder/channel_init.h
@@ -8,6 +8,7 @@
 #include "base/files/file.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "mojo/edk/embedder/channel_info_forward.h"
 #include "mojo/edk/system/system_impl_export.h"
 #include "mojo/public/cpp/system/core.h"
 
@@ -18,10 +19,6 @@
 
 namespace mojo {
 namespace embedder {
-struct ChannelInfo;
-}
-
-namespace embedder {
 
 // |ChannelInit| handles creation (and destruction) of the Mojo channel. It is
 // not thread-safe, but may be used on any single thread (with a |MessageLoop|).
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
index 888e883b..ca2169a 100644
--- a/mojo/edk/embedder/embedder.cc
+++ b/mojo/edk/embedder/embedder.cc
@@ -8,9 +8,11 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
 #include "mojo/edk/embedder/platform_support.h"
 #include "mojo/edk/system/channel.h"
 #include "mojo/edk/system/channel_endpoint.h"
+#include "mojo/edk/system/channel_info.h"
 #include "mojo/edk/system/core.h"
 #include "mojo/edk/system/entrypoints.h"
 #include "mojo/edk/system/message_pipe_dispatcher.h"
@@ -20,20 +22,6 @@
 namespace mojo {
 namespace embedder {
 
-// This is defined here (instead of a header file), since it's opaque to the
-// outside world. But we need to define it before our (internal-only) functions
-// that use it.
-struct ChannelInfo {
-  ChannelInfo() {}
-  ~ChannelInfo() {}
-
-  scoped_refptr<system::Channel> channel;
-
-  // May be null, in which case |DestroyChannelOnIOThread()| must be used (from
-  // the IO thread), instead of |DestroyChannel()|.
-  scoped_refptr<base::TaskRunner> io_thread_task_runner;
-};
-
 namespace {
 
 // Helper for |CreateChannel...()|. (Note: May return null for some failures.)
@@ -101,9 +89,9 @@
   ScopedMessagePipeHandle rv(
       MessagePipeHandle(core->AddDispatcher(dispatcher)));
 
-  *channel_info = new ChannelInfo();
-  (*channel_info)->channel =
-      MakeChannel(core, platform_handle.Pass(), channel_endpoint);
+  *channel_info = new ChannelInfo(
+      MakeChannel(core, platform_handle.Pass(), channel_endpoint),
+      base::MessageLoopProxy::current());
 
   return rv.Pass();
 }
@@ -114,6 +102,8 @@
     DidCreateChannelCallback callback,
     scoped_refptr<base::TaskRunner> callback_thread_task_runner) {
   DCHECK(platform_handle.is_valid());
+  DCHECK(io_thread_task_runner.get());
+  DCHECK(!callback.is_null());
 
   scoped_refptr<system::ChannelEndpoint> channel_endpoint;
   scoped_refptr<system::MessagePipeDispatcher> dispatcher =
@@ -125,7 +115,8 @@
       MessagePipeHandle(core->AddDispatcher(dispatcher)));
 
   scoped_ptr<ChannelInfo> channel_info(new ChannelInfo());
-  channel_info->io_thread_task_runner = io_thread_task_runner;
+  // We'll have to set |channel_info->channel| on the I/O thread.
+  channel_info->channel_thread_task_runner = io_thread_task_runner;
 
   if (rv.is_valid()) {
     io_thread_task_runner->PostTask(FROM_HERE,
@@ -159,7 +150,7 @@
 // TODO(vtl): Write tests for this.
 void DestroyChannel(ChannelInfo* channel_info) {
   DCHECK(channel_info);
-  DCHECK(channel_info->io_thread_task_runner.get());
+  DCHECK(channel_info->channel_thread_task_runner.get());
 
   if (!channel_info->channel.get()) {
     // Presumably, |Init()| on the channel failed.
@@ -167,7 +158,7 @@
   }
 
   channel_info->channel->WillShutdownSoon();
-  channel_info->io_thread_task_runner->PostTask(
+  channel_info->channel_thread_task_runner->PostTask(
       FROM_HERE, base::Bind(&DestroyChannelOnIOThread, channel_info));
 }
 
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h
index ce949d0..85bc5832 100644
--- a/mojo/edk/embedder/embedder.h
+++ b/mojo/edk/embedder/embedder.h
@@ -9,6 +9,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/task_runner.h"
+#include "mojo/edk/embedder/channel_info_forward.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/edk/system/system_impl_export.h"
 #include "mojo/public/cpp/system/core.h"
@@ -53,19 +54,15 @@
 // first synchronously and second asynchronously.
 //
 // The destruction functions are similarly synchronous and asynchronous,
-// respectively, and take the |ChannelInfo*| produced by the creation function.
-// (Note: One may call |DestroyChannelOnIOThread()| with the result of
-// |CreateChannel()|, but not |DestroyChannel()| with the result of
-// |CreateChannelOnIOThread()|.)
+// respectively, and take the |ChannelInfo*| produced by the creation functions.
 //
 // TODO(vtl): Figure out channel teardown.
-struct ChannelInfo;
 
 // Creates a channel; must only be called from the I/O thread. |platform_handle|
 // should be a handle to a connected OS "pipe". Eventually (even on failure),
 // the "out" value |*channel_info| should be passed to
-// |DestroyChannelOnIOThread()| to tear down the channel. Returns a handle to
-// the bootstrap message pipe.
+// |DestroyChannelOnIOThread()| (or |DestoryChannel()|) to tear down the
+// channel. Returns a handle to the bootstrap message pipe.
 MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
     CreateChannelOnIOThread(ScopedPlatformHandle platform_handle,
                             ChannelInfo** channel_info);
@@ -93,10 +90,9 @@
 MOJO_SYSTEM_IMPL_EXPORT void DestroyChannelOnIOThread(
     ChannelInfo* channel_info);
 
-// Destroys a channel (asynchronously) that was created using |CreateChannel()|
-// (note: NOT |CreateChannelOnIOThread()|); may be called from any thread.
-// |channel_info| should be the value provided to the callback to
-// |CreateChannel()|.
+// Destroys a channel (asynchronously) that was created using |CreateChannel()|;
+// may be called from any thread. |channel_info| should be the value provided to
+// the callback to |CreateChannel()|.
 MOJO_SYSTEM_IMPL_EXPORT void DestroyChannel(ChannelInfo* channel_info);
 
 // Inform the channel that it will soon be destroyed (doing so is optional).
diff --git a/mojo/bindings/js/BUILD.gn b/mojo/edk/js/BUILD.gn
similarity index 76%
rename from mojo/bindings/js/BUILD.gn
rename to mojo/edk/js/BUILD.gn
index 7e64c0f..b8d60ecc 100644
--- a/mojo/bindings/js/BUILD.gn
+++ b/mojo/edk/js/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# GYP version: mojo/mojo.gyp:mojo_js_bindings
 source_set("js") {
   sources = [
     "core.cc",
@@ -21,7 +20,17 @@
   public_deps = [
     "//base",
     "//gin",
-    "//mojo/common",
     "//v8",
   ]
 }
+
+source_set("js_unittests") {
+  testonly = true
+  deps = [
+    "//mojo/edk/test:test_support",
+  ]
+
+  sources = [
+    "handle_unittest.cc",
+  ]
+}
diff --git a/mojo/bindings/js/DEPS b/mojo/edk/js/DEPS
similarity index 100%
rename from mojo/bindings/js/DEPS
rename to mojo/edk/js/DEPS
diff --git a/mojo/bindings/js/core.cc b/mojo/edk/js/core.cc
similarity index 98%
rename from mojo/bindings/js/core.cc
rename to mojo/edk/js/core.cc
index f9e4359..91a746c2 100644
--- a/mojo/bindings/js/core.cc
+++ b/mojo/edk/js/core.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/bindings/js/core.h"
+#include "mojo/edk/js/core.h"
 
 #include "base/bind.h"
 #include "base/logging.h"
@@ -16,8 +16,8 @@
 #include "gin/per_isolate_data.h"
 #include "gin/public/wrapper_info.h"
 #include "gin/wrappable.h"
-#include "mojo/bindings/js/drain_data.h"
-#include "mojo/bindings/js/handle.h"
+#include "mojo/edk/js/drain_data.h"
+#include "mojo/edk/js/handle.h"
 
 namespace mojo {
 namespace js {
@@ -239,7 +239,7 @@
 
 }  // namespace
 
-const char Core::kModuleName[] = "mojo/public/js/bindings/core";
+const char Core::kModuleName[] = "mojo/public/js/core";
 
 v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) {
   gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
diff --git a/mojo/bindings/js/core.h b/mojo/edk/js/core.h
similarity index 100%
rename from mojo/bindings/js/core.h
rename to mojo/edk/js/core.h
diff --git a/mojo/bindings/js/drain_data.cc b/mojo/edk/js/drain_data.cc
similarity index 98%
rename from mojo/bindings/js/drain_data.cc
rename to mojo/edk/js/drain_data.cc
index a615cd6..0667c64 100644
--- a/mojo/bindings/js/drain_data.cc
+++ b/mojo/edk/js/drain_data.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/bindings/js/drain_data.h"
+#include "mojo/edk/js/drain_data.h"
 
 #include "gin/array_buffer.h"
 #include "gin/converter.h"
diff --git a/mojo/bindings/js/drain_data.h b/mojo/edk/js/drain_data.h
similarity index 100%
rename from mojo/bindings/js/drain_data.h
rename to mojo/edk/js/drain_data.h
diff --git a/mojo/bindings/js/handle.cc b/mojo/edk/js/handle.cc
similarity index 63%
rename from mojo/bindings/js/handle.cc
rename to mojo/edk/js/handle.cc
index baa1bae..ae3415f0 100644
--- a/mojo/bindings/js/handle.cc
+++ b/mojo/edk/js/handle.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/bindings/js/handle.h"
+#include "mojo/edk/js/handle.h"
 
-#include "mojo/bindings/js/handle_close_observer.h"
+#include <sstream>
+#include "mojo/edk/js/handle_close_observer.h"
 
 namespace mojo {
 namespace js {
@@ -19,6 +20,23 @@
   NotifyCloseObservers();
 }
 
+std::string HandleWrapper::ToString() {
+  std::ostringstream oss;
+  oss << "[mojo::Handle ";
+  if (handle_.is_valid())
+    oss << handle_.get().value();
+  else
+    oss << "null";
+  oss << "]";
+  return oss.str();
+}
+
+gin::ObjectTemplateBuilder HandleWrapper::GetObjectTemplateBuilder(
+    v8::Isolate* isolate) {
+  return Wrappable<HandleWrapper>::GetObjectTemplateBuilder(isolate)
+      .SetMethod("toString", &HandleWrapper::ToString);
+}
+
 void HandleWrapper::Close() {
   NotifyCloseObservers();
   handle_.reset();
@@ -68,4 +86,16 @@
   return true;
 }
 
+v8::Handle<v8::Value> Converter<mojo::MessagePipeHandle>::ToV8(
+    v8::Isolate* isolate, mojo::MessagePipeHandle val) {
+  return Converter<mojo::Handle>::ToV8(isolate, val);
+}
+
+bool Converter<mojo::MessagePipeHandle>::FromV8(v8::Isolate* isolate,
+                                                v8::Handle<v8::Value> val,
+                                                mojo::MessagePipeHandle* out) {
+  return Converter<mojo::Handle>::FromV8(isolate, val, out);
+}
+
+
 }  // namespace gin
diff --git a/mojo/bindings/js/handle.h b/mojo/edk/js/handle.h
similarity index 85%
rename from mojo/bindings/js/handle.h
rename to mojo/edk/js/handle.h
index b0f250c..e363b7c4 100644
--- a/mojo/bindings/js/handle.h
+++ b/mojo/edk/js/handle.h
@@ -8,6 +8,7 @@
 #include "base/observer_list.h"
 #include "gin/converter.h"
 #include "gin/handle.h"
+#include "gin/object_template_builder.h"
 #include "gin/wrappable.h"
 #include "mojo/public/cpp/system/core.h"
 
@@ -26,6 +27,11 @@
     return gin::CreateHandle(isolate, new HandleWrapper(handle));
   }
 
+  std::string ToString();
+
+  gin::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate)
+      override;
+
   mojo::Handle get() const { return handle_.get(); }
   mojo::Handle release() { return handle_.release(); }
   void Close();
@@ -59,6 +65,15 @@
                      mojo::Handle* out);
 };
 
+template<>
+struct Converter<mojo::MessagePipeHandle> {
+  static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+                                    mojo::MessagePipeHandle val);
+  static bool FromV8(v8::Isolate* isolate,
+                     v8::Handle<v8::Value> val,
+                     mojo::MessagePipeHandle* out);
+};
+
 // We need to specialize the normal gin::Handle converter in order to handle
 // converting |null| to a wrapper for an empty mojo::Handle.
 template<>
diff --git a/mojo/bindings/js/handle_close_observer.h b/mojo/edk/js/handle_close_observer.h
similarity index 100%
rename from mojo/bindings/js/handle_close_observer.h
rename to mojo/edk/js/handle_close_observer.h
diff --git a/mojo/edk/js/handle_unittest.cc b/mojo/edk/js/handle_unittest.cc
new file mode 100644
index 0000000..53f474e
--- /dev/null
+++ b/mojo/edk/js/handle_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "mojo/edk/js/handle.h"
+#include "mojo/edk/js/handle_close_observer.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace js {
+
+class HandleWrapperTest : public testing::Test,
+                          public HandleCloseObserver {
+ public:
+  HandleWrapperTest() : closes_observed_(0) {}
+
+  void OnWillCloseHandle() override { closes_observed_++; }
+
+ protected:
+  int closes_observed_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HandleWrapperTest);
+};
+
+class TestHandleWrapper : public HandleWrapper {
+ public:
+  explicit TestHandleWrapper(MojoHandle handle) : HandleWrapper(handle) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestHandleWrapper);
+};
+
+// Test that calling Close() on a HandleWrapper for an invalid handle does not
+// notify observers.
+TEST_F(HandleWrapperTest, CloseWithInvalidHandle) {
+  {
+    TestHandleWrapper wrapper(MOJO_HANDLE_INVALID);
+    wrapper.AddCloseObserver(this);
+    ASSERT_EQ(0, closes_observed_);
+    wrapper.Close();
+    EXPECT_EQ(0, closes_observed_);
+  }
+  EXPECT_EQ(0, closes_observed_);
+}
+
+// Test that destroying a HandleWrapper for an invalid handle does not notify
+// observers.
+TEST_F(HandleWrapperTest, DestroyWithInvalidHandle) {
+  {
+    TestHandleWrapper wrapper(MOJO_HANDLE_INVALID);
+    wrapper.AddCloseObserver(this);
+    ASSERT_EQ(0, closes_observed_);
+  }
+  EXPECT_EQ(0, closes_observed_);
+}
+
+// Test that calling Close on a HandleWrapper for a valid handle notifies
+// observers once.
+TEST_F(HandleWrapperTest, CloseWithValidHandle) {
+  {
+    mojo::MessagePipe pipe;
+    TestHandleWrapper wrapper(pipe.handle0.release().value());
+    wrapper.AddCloseObserver(this);
+    ASSERT_EQ(0, closes_observed_);
+    wrapper.Close();
+    EXPECT_EQ(1, closes_observed_);
+    // Check that calling close again doesn't notify observers.
+    wrapper.Close();
+    EXPECT_EQ(1, closes_observed_);
+  }
+  // Check that destroying a closed HandleWrapper doesn't notify observers.
+  EXPECT_EQ(1, closes_observed_);
+}
+
+// Test that destroying a HandleWrapper for a valid handle notifies observers.
+TEST_F(HandleWrapperTest, DestroyWithValidHandle) {
+  {
+    mojo::MessagePipe pipe;
+    TestHandleWrapper wrapper(pipe.handle0.release().value());
+    wrapper.AddCloseObserver(this);
+    ASSERT_EQ(0, closes_observed_);
+  }
+  EXPECT_EQ(1, closes_observed_);
+}
+
+}  // namespace js
+}  // namespace mojo
diff --git a/mojo/bindings/js/support.cc b/mojo/edk/js/support.cc
similarity index 88%
rename from mojo/bindings/js/support.cc
rename to mojo/edk/js/support.cc
index b90953d..7d44024 100644
--- a/mojo/bindings/js/support.cc
+++ b/mojo/edk/js/support.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/bindings/js/support.h"
+#include "mojo/edk/js/support.h"
 
 #include "base/bind.h"
 #include "gin/arguments.h"
@@ -12,8 +12,8 @@
 #include "gin/per_isolate_data.h"
 #include "gin/public/wrapper_info.h"
 #include "gin/wrappable.h"
-#include "mojo/bindings/js/handle.h"
-#include "mojo/bindings/js/waiting_callback.h"
+#include "mojo/edk/js/handle.h"
+#include "mojo/edk/js/waiting_callback.h"
 #include "mojo/public/cpp/system/core.h"
 
 namespace mojo {
@@ -37,7 +37,7 @@
 
 }  // namespace
 
-const char Support::kModuleName[] = "mojo/public/js/bindings/support";
+const char Support::kModuleName[] = "mojo/public/js/support";
 
 v8::Local<v8::Value> Support::GetModule(v8::Isolate* isolate) {
   gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
diff --git a/mojo/bindings/js/support.h b/mojo/edk/js/support.h
similarity index 100%
rename from mojo/bindings/js/support.h
rename to mojo/edk/js/support.h
diff --git a/mojo/bindings/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn
similarity index 79%
rename from mojo/bindings/js/tests/BUILD.gn
rename to mojo/edk/js/tests/BUILD.gn
index 2d13ff4..1555328 100644
--- a/mojo/bindings/js/tests/BUILD.gn
+++ b/mojo/edk/js/tests/BUILD.gn
@@ -2,11 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# GYP version: mojo/mojo_public_tests.gypi:mojo_js_unittests
-test("mojo_js_unittests") {
+test("js_unittests") {
   deps = [
     "//gin:gin_test",
-    "//mojo/bindings/js",
+    "//mojo/edk/js",
+    "//mojo/edk/js:js_unittests",
     "//mojo/edk/test:run_all_unittests",
     "//mojo/edk/test:test_support",
     "//mojo/public/cpp/environment:standalone",
diff --git a/mojo/edk/js/tests/DEPS b/mojo/edk/js/tests/DEPS
new file mode 100644
index 0000000..190ee620
--- /dev/null
+++ b/mojo/edk/js/tests/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+base",
+  "+gin",
+  "+v8",
+  "+mojo/edk/js/core.h",
+  "+mojo/edk/js/support.h",
+]
diff --git a/mojo/bindings/js/tests/run_js_tests.cc b/mojo/edk/js/tests/run_js_tests.cc
similarity index 93%
rename from mojo/bindings/js/tests/run_js_tests.cc
rename to mojo/edk/js/tests/run_js_tests.cc
index 54d98061..4246f8e 100644
--- a/mojo/bindings/js/tests/run_js_tests.cc
+++ b/mojo/edk/js/tests/run_js_tests.cc
@@ -9,8 +9,8 @@
 #include "gin/modules/timer.h"
 #include "gin/test/file_runner.h"
 #include "gin/test/gtest.h"
-#include "mojo/bindings/js/core.h"
-#include "mojo/bindings/js/support.h"
+#include "mojo/edk/js/core.h"
+#include "mojo/edk/js/support.h"
 #include "mojo/public/cpp/environment/environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -37,7 +37,6 @@
   path = path.AppendASCII("mojo")
              .AppendASCII("public")
              .AppendASCII("js")
-             .AppendASCII("bindings")
              .AppendASCII(test);
   TestRunnerDelegate delegate;
   gin::RunTestFromFile(path, &delegate, run_until_idle);
diff --git a/mojo/bindings/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc
similarity index 98%
rename from mojo/bindings/js/waiting_callback.cc
rename to mojo/edk/js/waiting_callback.cc
index c2812489..2b0b07e3 100644
--- a/mojo/bindings/js/waiting_callback.cc
+++ b/mojo/edk/js/waiting_callback.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "mojo/bindings/js/waiting_callback.h"
+#include "mojo/edk/js/waiting_callback.h"
 
 #include "gin/per_context_data.h"
 #include "mojo/public/cpp/environment/environment.h"
diff --git a/mojo/bindings/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h
similarity index 95%
rename from mojo/bindings/js/waiting_callback.h
rename to mojo/edk/js/waiting_callback.h
index 8baa8a0..fdffde5 100644
--- a/mojo/bindings/js/waiting_callback.h
+++ b/mojo/edk/js/waiting_callback.h
@@ -8,8 +8,8 @@
 #include "gin/handle.h"
 #include "gin/runner.h"
 #include "gin/wrappable.h"
-#include "mojo/bindings/js/handle.h"
-#include "mojo/bindings/js/handle_close_observer.h"
+#include "mojo/edk/js/handle.h"
+#include "mojo/edk/js/handle_close_observer.h"
 #include "mojo/public/c/environment/async_waiter.h"
 #include "mojo/public/cpp/system/core.h"
 
diff --git a/mojo/edk/mojo_edk.gyp b/mojo/edk/mojo_edk.gyp
index 58dae524..e826893 100644
--- a/mojo/edk/mojo_edk.gyp
+++ b/mojo/edk/mojo_edk.gyp
@@ -28,6 +28,7 @@
         'mojo_public_utility_unittests',
         'mojo_system_impl',
         'mojo_system_unittests',
+        'mojo_js_unittests',
       ],
     },
     {
@@ -112,6 +113,7 @@
       ],
       'include_dirs': [ '../..' ],
       'sources': [
+        '../public/cpp/environment/tests/async_wait_unittest.cc',
         '../public/cpp/environment/tests/async_waiter_unittest.cc',
         '../public/cpp/environment/tests/logger_unittest.cc',
         '../public/cpp/environment/tests/logging_unittest.cc',
@@ -203,6 +205,7 @@
         'MOJO_USE_SYSTEM_IMPL',
       ],
       'sources': [
+        'embedder/channel_info_forward.h',
         'embedder/channel_init.cc',
         'embedder/channel_init.h',
         'embedder/embedder.cc',
@@ -234,6 +237,8 @@
         'system/channel_endpoint.h',
         'system/channel_endpoint_id.cc',
         'system/channel_endpoint_id.h',
+        'system/channel_info.cc',
+        'system/channel_info.h',
         'system/constants.h',
         'system/core.cc',
         'system/core.h',
@@ -370,6 +375,52 @@
       ],
     },
     {
+      # GN version: //mojo/edk/js
+      'target_name': 'mojo_js_lib',
+      'type': 'static_library',
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../gin/gin.gyp:gin',
+        '../../v8/tools/gyp/v8.gyp:v8',
+      ],
+      'export_dependent_settings': [
+        '../../base/base.gyp:base',
+        '../../gin/gin.gyp:gin',
+      ],
+      'sources': [
+        # Sources list duplicated in GN build.
+        'js/core.cc',
+        'js/core.h',
+        'js/drain_data.cc',
+        'js/drain_data.h',
+        'js/handle.cc',
+        'js/handle.h',
+        'js/handle_close_observer.h',
+        'js/support.cc',
+        'js/support.h',
+        'js/waiting_callback.cc',
+        'js/waiting_callback.h',
+      ],
+    },
+    {
+      # GN version: //mojo/edk/js:js_unittests
+      'target_name': 'mojo_js_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../../gin/gin.gyp:gin_test',
+        'mojo_common_test_support',
+        'mojo_run_all_unittests',
+        'mojo_js_lib',
+        '../public/mojo_public.gyp:mojo_environment_standalone',
+        '../public/mojo_public.gyp:mojo_public_test_interfaces',
+        '../public/mojo_public.gyp:mojo_utility',
+      ],
+      'sources': [
+        'js/handle_unittest.cc',
+        'js/tests/run_js_tests.cc',
+      ],
+    },
+    {
       # GN version: //mojo/common/test:test_support_impl
       'target_name': 'mojo_test_support_impl',
       'type': 'static_library',
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
index 250bcaf..791a192 100644
--- a/mojo/edk/system/BUILD.gn
+++ b/mojo/edk/system/BUILD.gn
@@ -32,6 +32,8 @@
     "channel_endpoint.h",
     "channel_endpoint_id.cc",
     "channel_endpoint_id.h",
+    "channel_info.cc",
+    "channel_info.h",
     "constants.h",
     "core.cc",
     "core.h",
diff --git a/mojo/edk/system/channel_info.cc b/mojo/edk/system/channel_info.cc
new file mode 100644
index 0000000..efc5f040
--- /dev/null
+++ b/mojo/edk/system/channel_info.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/edk/system/channel_info.h"
+
+namespace mojo {
+namespace system {
+
+ChannelInfo::ChannelInfo() {
+}
+
+ChannelInfo::ChannelInfo(
+    scoped_refptr<Channel> channel,
+    scoped_refptr<base::TaskRunner> channel_thread_task_runner)
+    : channel(channel), channel_thread_task_runner(channel_thread_task_runner) {
+}
+
+ChannelInfo::~ChannelInfo() {
+}
+
+}  // namespace system
+}  // namespace mojo
diff --git a/mojo/edk/system/channel_info.h b/mojo/edk/system/channel_info.h
new file mode 100644
index 0000000..bd84e16
--- /dev/null
+++ b/mojo/edk/system/channel_info.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_EDK_SYSTEM_CHANNEL_INFO_H_
+#define MOJO_EDK_SYSTEM_CHANNEL_INFO_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/task_runner.h"
+#include "mojo/edk/system/channel.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+struct MOJO_SYSTEM_IMPL_EXPORT ChannelInfo {
+  ChannelInfo();
+  ChannelInfo(scoped_refptr<Channel> channel,
+              scoped_refptr<base::TaskRunner> channel_thread_task_runner);
+  ~ChannelInfo();
+
+  scoped_refptr<Channel> channel;
+  // The task runner for |channel|'s creation thread (a.k.a. its I/O thread), on
+  // which it must, e.g., be shut down.
+  scoped_refptr<base::TaskRunner> channel_thread_task_runner;
+};
+
+}  // namespace system
+}  // namespace mojo
+
+#endif  // MOJO_EDK_SYSTEM_CHANNEL_INFO_H_
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index 9adde19..e6607df0 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -1084,6 +1084,19 @@
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
 
+  // Peek one character.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = 1u;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->ReadData(
+                ch,
+                UserPointer<void>(elements),
+                MakeUserPointer(&num_bytes),
+                MOJO_READ_DATA_FLAG_NONE | MOJO_READ_DATA_FLAG_PEEK));
+  EXPECT_EQ('A', elements[0]);
+  EXPECT_EQ(-1, elements[1]);
+
   // Read one character.
   elements[0] = -1;
   elements[1] = -1;
@@ -1131,6 +1144,16 @@
                              MOJO_READ_DATA_FLAG_QUERY));
   EXPECT_EQ(4u, num_bytes);
 
+  // Try to query with peek. Should fail.
+  num_bytes = 0;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            core()->ReadData(
+                ch,
+                NullUserPointer(),
+                MakeUserPointer(&num_bytes),
+                MOJO_READ_DATA_FLAG_QUERY | MOJO_READ_DATA_FLAG_PEEK));
+  EXPECT_EQ(0u, num_bytes);
+
   // Try to discard ten characters, in all-or-none mode. Should fail.
   num_bytes = 10;
   EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
@@ -1140,6 +1163,15 @@
                 MakeUserPointer(&num_bytes),
                 MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE));
 
+  // Try to discard two characters, in peek mode. Should fail.
+  num_bytes = 2;
+  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            core()->ReadData(
+                ch,
+                NullUserPointer(),
+                MakeUserPointer(&num_bytes),
+                MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_PEEK));
+
   // Discard two characters.
   num_bytes = 2;
   EXPECT_EQ(MOJO_RESULT_OK,
@@ -1149,9 +1181,17 @@
                 MakeUserPointer(&num_bytes),
                 MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_ALL_OR_NONE));
 
-  // Read the remaining two characters, in two-phase mode (all-or-none).
+  // Try a two-phase read of the remaining two bytes with peek. Should fail.
   const void* read_ptr = nullptr;
   num_bytes = 2;
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            core()->BeginReadData(ch,
+                                  MakeUserPointer(&read_ptr),
+                                  MakeUserPointer(&num_bytes),
+                                  MOJO_READ_DATA_FLAG_PEEK));
+
+  // Read the remaining two characters, in two-phase mode (all-or-none).
+  num_bytes = 2;
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->BeginReadData(ch,
                                   MakeUserPointer(&read_ptr),
diff --git a/mojo/edk/system/data_pipe.cc b/mojo/edk/system/data_pipe.cc
index e9c49035..e9525bc 100644
--- a/mojo/edk/system/data_pipe.cc
+++ b/mojo/edk/system/data_pipe.cc
@@ -260,7 +260,8 @@
 
 MojoResult DataPipe::ConsumerReadData(UserPointer<void> elements,
                                       UserPointer<uint32_t> num_bytes,
-                                      bool all_or_none) {
+                                      bool all_or_none,
+                                      bool peek) {
   base::AutoLock locker(lock_);
   DCHECK(has_local_consumer_no_lock());
 
@@ -279,7 +280,7 @@
   HandleSignalsState old_producer_state =
       ProducerGetHandleSignalsStateImplNoLock();
   MojoResult rv = ConsumerReadDataImplNoLock(
-      elements, num_bytes, max_num_bytes_to_read, min_num_bytes_to_read);
+      elements, num_bytes, max_num_bytes_to_read, min_num_bytes_to_read, peek);
   HandleSignalsState new_producer_state =
       ProducerGetHandleSignalsStateImplNoLock();
   if (!new_producer_state.equals(old_producer_state))
diff --git a/mojo/edk/system/data_pipe.h b/mojo/edk/system/data_pipe.h
index 64b18e2..d4afdda 100644
--- a/mojo/edk/system/data_pipe.h
+++ b/mojo/edk/system/data_pipe.h
@@ -73,7 +73,8 @@
   // a multiple of |element_num_bytes_|.
   MojoResult ConsumerReadData(UserPointer<void> elements,
                               UserPointer<uint32_t> num_bytes,
-                              bool all_or_none);
+                              bool all_or_none,
+                              bool peek);
   MojoResult ConsumerDiscardData(UserPointer<uint32_t> num_bytes,
                                  bool all_or_none);
   MojoResult ConsumerQueryData(UserPointer<uint32_t> num_bytes);
@@ -120,7 +121,8 @@
       UserPointer<void> elements,
       UserPointer<uint32_t> num_bytes,
       uint32_t max_num_bytes_to_read,
-      uint32_t min_num_bytes_to_read) = 0;
+      uint32_t min_num_bytes_to_read,
+      bool peek) = 0;
   virtual MojoResult ConsumerDiscardDataImplNoLock(
       UserPointer<uint32_t> num_bytes,
       uint32_t max_num_bytes_to_discard,
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index 2b3f514..fa103c3 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -58,7 +58,8 @@
 
   if ((flags & MOJO_READ_DATA_FLAG_DISCARD)) {
     // These flags are mutally exclusive.
-    if ((flags & MOJO_READ_DATA_FLAG_QUERY))
+    if ((flags & MOJO_READ_DATA_FLAG_QUERY) ||
+        (flags & MOJO_READ_DATA_FLAG_PEEK))
       return MOJO_RESULT_INVALID_ARGUMENT;
     DVLOG_IF(2, !elements.IsNull())
         << "Discard mode: ignoring non-null |elements|";
@@ -67,6 +68,8 @@
   }
 
   if ((flags & MOJO_READ_DATA_FLAG_QUERY)) {
+    if ((flags & MOJO_READ_DATA_FLAG_PEEK))
+      return MOJO_RESULT_INVALID_ARGUMENT;
     DCHECK(!(flags & MOJO_READ_DATA_FLAG_DISCARD));  // Handled above.
     DVLOG_IF(2, !elements.IsNull())
         << "Query mode: ignoring non-null |elements|";
@@ -74,7 +77,10 @@
   }
 
   return data_pipe_->ConsumerReadData(
-      elements, num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+      elements,
+      num_bytes,
+      !!(flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE),
+      !!(flags & MOJO_READ_DATA_FLAG_PEEK));
 }
 
 MojoResult DataPipeConsumerDispatcher::BeginReadDataImplNoLock(
@@ -85,7 +91,8 @@
 
   // These flags may not be used in two-phase mode.
   if ((flags & MOJO_READ_DATA_FLAG_DISCARD) ||
-      (flags & MOJO_READ_DATA_FLAG_QUERY))
+      (flags & MOJO_READ_DATA_FLAG_QUERY) ||
+      (flags & MOJO_READ_DATA_FLAG_PEEK))
     return MOJO_RESULT_INVALID_ARGUMENT;
 
   return data_pipe_->ConsumerBeginReadData(
diff --git a/mojo/edk/system/local_data_pipe.cc b/mojo/edk/system/local_data_pipe.cc
index 8ee696c..fdfaf28 100644
--- a/mojo/edk/system/local_data_pipe.cc
+++ b/mojo/edk/system/local_data_pipe.cc
@@ -177,7 +177,8 @@
     UserPointer<void> elements,
     UserPointer<uint32_t> num_bytes,
     uint32_t max_num_bytes_to_read,
-    uint32_t min_num_bytes_to_read) {
+    uint32_t min_num_bytes_to_read,
+    bool peek) {
   DCHECK_EQ(max_num_bytes_to_read % element_num_bytes(), 0u);
   DCHECK_EQ(min_num_bytes_to_read % element_num_bytes(), 0u);
   DCHECK_GT(max_num_bytes_to_read, 0u);
@@ -207,7 +208,8 @@
         .PutArray(buffer_.get(), num_bytes_to_read - num_bytes_to_read_first);
   }
 
-  MarkDataAsConsumedNoLock(num_bytes_to_read);
+  if (!peek)
+    MarkDataAsConsumedNoLock(num_bytes_to_read);
   num_bytes.Put(static_cast<uint32_t>(num_bytes_to_read));
   return MOJO_RESULT_OK;
 }
diff --git a/mojo/edk/system/local_data_pipe.h b/mojo/edk/system/local_data_pipe.h
index 8a46c52..c98cb7e 100644
--- a/mojo/edk/system/local_data_pipe.h
+++ b/mojo/edk/system/local_data_pipe.h
@@ -48,7 +48,8 @@
       UserPointer<void> elements,
       UserPointer<uint32_t> num_bytes,
       uint32_t max_num_bytes_to_read,
-      uint32_t min_num_bytes_to_read) override;
+      uint32_t min_num_bytes_to_read,
+      bool peek) override;
   MojoResult ConsumerDiscardDataImplNoLock(
       UserPointer<uint32_t> num_bytes,
       uint32_t max_num_bytes_to_discard,
diff --git a/mojo/edk/system/local_data_pipe_unittest.cc b/mojo/edk/system/local_data_pipe_unittest.cc
index 979b4d2..e8bc716 100644
--- a/mojo/edk/system/local_data_pipe_unittest.cc
+++ b/mojo/edk/system/local_data_pipe_unittest.cc
@@ -118,8 +118,10 @@
   num_bytes = static_cast<uint32_t>(arraysize(elements) * sizeof(elements[0]));
   EXPECT_EQ(
       MOJO_RESULT_SHOULD_WAIT,
-      dp->ConsumerReadData(
-          UserPointer<void>(elements), MakeUserPointer(&num_bytes), false));
+      dp->ConsumerReadData(UserPointer<void>(elements),
+                           MakeUserPointer(&num_bytes),
+                           false,
+                           false));
 
   // Query; nothing there yet.
   num_bytes = 0;
@@ -135,8 +137,10 @@
   num_bytes = sizeof(elements[0]) + 1;
   EXPECT_EQ(
       MOJO_RESULT_INVALID_ARGUMENT,
-      dp->ConsumerReadData(
-          UserPointer<void>(elements), MakeUserPointer(&num_bytes), false));
+      dp->ConsumerReadData(UserPointer<void>(elements),
+                           MakeUserPointer(&num_bytes),
+                           false,
+                           false));
 
   // Write two elements.
   elements[0] = 123;
@@ -160,8 +164,10 @@
   num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
   EXPECT_EQ(
       MOJO_RESULT_OK,
-      dp->ConsumerReadData(
-          UserPointer<void>(elements), MakeUserPointer(&num_bytes), false));
+      dp->ConsumerReadData(UserPointer<void>(elements),
+                           MakeUserPointer(&num_bytes),
+                           false,
+                           false));
   EXPECT_EQ(1u * sizeof(elements[0]), num_bytes);
   EXPECT_EQ(123, elements[0]);
   EXPECT_EQ(-1, elements[1]);
@@ -171,14 +177,35 @@
   EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
   EXPECT_EQ(1 * sizeof(elements[0]), num_bytes);
 
+  // Peek one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerReadData(UserPointer<void>(elements),
+                           MakeUserPointer(&num_bytes),
+                           false,
+                           true));
+  EXPECT_EQ(1u * sizeof(elements[0]), num_bytes);
+  EXPECT_EQ(456, elements[0]);
+  EXPECT_EQ(-1, elements[1]);
+
+  // Query. Still has 1 element remaining.
+  num_bytes = 0;
+  EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(MakeUserPointer(&num_bytes)));
+  EXPECT_EQ(1 * sizeof(elements[0]), num_bytes);
+
   // Try to read two elements, with "all or none".
   elements[0] = -1;
   elements[1] = -1;
   num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
   EXPECT_EQ(
       MOJO_RESULT_OUT_OF_RANGE,
-      dp->ConsumerReadData(
-          UserPointer<void>(elements), MakeUserPointer(&num_bytes), true));
+      dp->ConsumerReadData(UserPointer<void>(elements),
+                           MakeUserPointer(&num_bytes),
+                           true,
+                           false));
   EXPECT_EQ(-1, elements[0]);
   EXPECT_EQ(-1, elements[1]);
 
@@ -188,8 +215,10 @@
   num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
   EXPECT_EQ(
       MOJO_RESULT_OK,
-      dp->ConsumerReadData(
-          UserPointer<void>(elements), MakeUserPointer(&num_bytes), false));
+      dp->ConsumerReadData(UserPointer<void>(elements),
+                           MakeUserPointer(&num_bytes),
+                           false,
+                           false));
   EXPECT_EQ(456, elements[0]);
   EXPECT_EQ(-1, elements[1]);
 
@@ -262,6 +291,32 @@
   EXPECT_EQ(0u, hss.satisfied_signals);
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
 
+  // Peek one element.
+  elements[0] = -1;
+  elements[1] = -1;
+  num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+  EXPECT_EQ(
+      MOJO_RESULT_OK,
+      dp->ConsumerReadData(UserPointer<void>(elements),
+                           MakeUserPointer(&num_bytes),
+                           true,
+                           true));
+  EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+  EXPECT_EQ(123, elements[0]);
+  EXPECT_EQ(-1, elements[1]);
+
+  // Add a waiter.
+  waiter.Init();
+  ASSERT_EQ(
+      MOJO_RESULT_OK,
+      dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 56, nullptr));
+  // And it still shouldn't be writable yet.
+  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, nullptr));
+  hss = HandleSignalsState();
+  dp->ProducerRemoveWaiter(&waiter, &hss);
+  EXPECT_EQ(0u, hss.satisfied_signals);
+  EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals);
+
   // Do it again.
   waiter.Init();
   ASSERT_EQ(
@@ -274,8 +329,10 @@
   num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
   EXPECT_EQ(
       MOJO_RESULT_OK,
-      dp->ConsumerReadData(
-          UserPointer<void>(elements), MakeUserPointer(&num_bytes), true));
+      dp->ConsumerReadData(UserPointer<void>(elements),
+                           MakeUserPointer(&num_bytes),
+                           true,
+                           false));
   EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
   EXPECT_EQ(123, elements[0]);
   EXPECT_EQ(-1, elements[1]);
@@ -432,14 +489,39 @@
     EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
     EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
 
+    // Peek one element.
+    elements[0] = -1;
+    elements[1] = -1;
+    num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        dp->ConsumerReadData(UserPointer<void>(elements),
+                             MakeUserPointer(&num_bytes),
+                             true,
+                             true));
+    EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+    EXPECT_EQ(456, elements[0]);
+    EXPECT_EQ(-1, elements[1]);
+
+    // Should still be readable.
+    waiter.Init();
+    hss = HandleSignalsState();
+    EXPECT_EQ(
+        MOJO_RESULT_ALREADY_EXISTS,
+        dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 78, &hss));
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+    EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
     // Read one element.
     elements[0] = -1;
     elements[1] = -1;
     num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
     EXPECT_EQ(
         MOJO_RESULT_OK,
-        dp->ConsumerReadData(
-            UserPointer<void>(elements), MakeUserPointer(&num_bytes), true));
+        dp->ConsumerReadData(UserPointer<void>(elements),
+                             MakeUserPointer(&num_bytes),
+                             true,
+                             false));
     EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
     EXPECT_EQ(456, elements[0]);
     EXPECT_EQ(-1, elements[1]);
@@ -485,8 +567,10 @@
     num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
     EXPECT_EQ(
         MOJO_RESULT_OK,
-        dp->ConsumerReadData(
-            UserPointer<void>(elements), MakeUserPointer(&num_bytes), true));
+        dp->ConsumerReadData(UserPointer<void>(elements),
+                             MakeUserPointer(&num_bytes),
+                             true,
+                             false));
     EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
     EXPECT_EQ(789, elements[0]);
     EXPECT_EQ(-1, elements[1]);
@@ -843,8 +927,10 @@
   element = 0;
   EXPECT_EQ(
       MOJO_RESULT_OK,
-      dp->ConsumerReadData(
-          UserPointer<void>(&element), MakeUserPointer(&num_bytes), false));
+      dp->ConsumerReadData(UserPointer<void>(&element),
+                           MakeUserPointer(&num_bytes),
+                           false,
+                           false));
   EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes);
   EXPECT_EQ(456, element);
 
@@ -908,8 +994,10 @@
   num_bytes = 5u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 false,
+                                 false));
   EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
   int32_t expected_buffer[100];
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
@@ -933,8 +1021,10 @@
   num_bytes = 5u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 false,
+                                 false));
   EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   expected_buffer[0] = 8;
@@ -972,8 +1062,10 @@
   num_bytes = sizeof(buffer);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 false,
+                                 false));
   EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   expected_buffer[0] = 104;
@@ -1062,8 +1154,10 @@
   num_bytes = sizeof(buffer);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 false,
+                                 false));
   EXPECT_EQ(8u * sizeof(int32_t), num_bytes);
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   expected_buffer[0] = 500;
@@ -1134,8 +1228,10 @@
   num_bytes = 11u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 true,
+                                 false));
   int32_t expected_buffer[100];
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
@@ -1172,8 +1268,10 @@
   num_bytes = 5u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 true,
+                                 false));
   EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   Seq(100, 5, expected_buffer);
@@ -1183,8 +1281,10 @@
   num_bytes = 6u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 true,
+                                 false));
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
 
@@ -1211,8 +1311,10 @@
   num_bytes = 4u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 true,
+                                 false));
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
 
@@ -1225,8 +1327,10 @@
   num_bytes = 2u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 true,
+                                 false));
   EXPECT_EQ(2u * sizeof(int32_t), num_bytes);
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   Seq(400, 2, expected_buffer);
@@ -1291,8 +1395,10 @@
   num_bytes = 1u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 true,
+                                 false));
   EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
   int32_t expected_buffer[100];
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
@@ -1303,8 +1409,10 @@
   num_bytes = 10u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 true,
+                                 false));
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
 
@@ -1336,8 +1444,10 @@
   num_bytes = 10u * sizeof(int32_t);
   memset(buffer, 0xab, sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            dp->ConsumerReadData(
-                UserPointer<void>(buffer), MakeUserPointer(&num_bytes), true));
+            dp->ConsumerReadData(UserPointer<void>(buffer),
+                                 MakeUserPointer(&num_bytes),
+                                 true,
+                                 false));
   memset(expected_buffer, 0xab, sizeof(expected_buffer));
   EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
   Seq(300, 10, expected_buffer);
@@ -1525,8 +1635,10 @@
   num_bytes = 10u;
   EXPECT_EQ(
       MOJO_RESULT_OK,
-      dp->ConsumerReadData(
-          UserPointer<void>(read_buffer), MakeUserPointer(&num_bytes), false));
+      dp->ConsumerReadData(UserPointer<void>(read_buffer),
+                           MakeUserPointer(&num_bytes),
+                           false,
+                           false));
   EXPECT_EQ(10u, num_bytes);
   EXPECT_EQ(0, memcmp(read_buffer, &test_data[0], 10u));
 
@@ -1572,8 +1684,10 @@
   memset(read_buffer, 0, num_bytes);
   EXPECT_EQ(
       MOJO_RESULT_OK,
-      dp->ConsumerReadData(
-          UserPointer<void>(read_buffer), MakeUserPointer(&num_bytes), false));
+      dp->ConsumerReadData(UserPointer<void>(read_buffer),
+                           MakeUserPointer(&num_bytes),
+                           false,
+                           false));
   EXPECT_EQ(100u, num_bytes);
   EXPECT_EQ(0, memcmp(read_buffer, &test_data[10], 100u));
 
@@ -1753,13 +1867,27 @@
     // Close the producer.
     dp->ProducerClose();
 
-    // Read that data.
+    // Peek that data.
     char buffer[1000];
     num_bytes = static_cast<uint32_t>(sizeof(buffer));
     EXPECT_EQ(
         MOJO_RESULT_OK,
-        dp->ConsumerReadData(
-            UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+        dp->ConsumerReadData(UserPointer<void>(buffer),
+                             MakeUserPointer(&num_bytes),
+                             false,
+                             true));
+    EXPECT_EQ(kTestDataSize, num_bytes);
+    EXPECT_EQ(0, memcmp(buffer, kTestData, kTestDataSize));
+
+    // Read that data.
+    memset(buffer, 0, 1000);
+    num_bytes = static_cast<uint32_t>(sizeof(buffer));
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        dp->ConsumerReadData(UserPointer<void>(buffer),
+                             MakeUserPointer(&num_bytes),
+                             false,
+                             false));
     EXPECT_EQ(kTestDataSize, num_bytes);
     EXPECT_EQ(0, memcmp(buffer, kTestData, kTestDataSize));
 
@@ -1767,8 +1895,10 @@
     num_bytes = static_cast<uint32_t>(sizeof(buffer));
     EXPECT_EQ(
         MOJO_RESULT_FAILED_PRECONDITION,
-        dp->ConsumerReadData(
-            UserPointer<void>(buffer), MakeUserPointer(&num_bytes), false));
+        dp->ConsumerReadData(UserPointer<void>(buffer),
+                             MakeUserPointer(&num_bytes),
+                             false,
+                             false));
 
     // A two-phase read should also fail.
     const void* read_buffer_ptr = nullptr;
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp
index 8f8448e4..cba70c9 100644
--- a/mojo/mojo.gyp
+++ b/mojo/mojo.gyp
@@ -16,29 +16,11 @@
         'mojo_base.gyp:mojo_base',
         'mojo_geometry_converters.gyp:mojo_geometry_lib',
         'mojo_input_events_converters.gyp:mojo_input_events_lib',
-        'mojo_js_unittests',
         'mojo_surface_converters.gyp:mojo_surfaces_lib',
         'mojo_surface_converters.gyp:mojo_surfaces_lib_unittests',
         'services/public/mojo_services_public.gyp:mojo_services_public',
         'public/mojo_public.gyp:mojo_public',
       ],
     },
-    {
-      # GN version: //mojo/bindings/js/tests:mojo_js_unittests
-      'target_name': 'mojo_js_unittests',
-      'type': 'executable',
-      'dependencies': [
-        '../gin/gin.gyp:gin_test',
-        'edk/mojo_edk.gyp:mojo_common_test_support',
-        'edk/mojo_edk.gyp:mojo_run_all_unittests',
-        'mojo_base.gyp:mojo_js_bindings_lib',
-        'public/mojo_public.gyp:mojo_environment_standalone',
-        'public/mojo_public.gyp:mojo_public_test_interfaces',
-        'public/mojo_public.gyp:mojo_utility',
-      ],
-      'sources': [
-        'bindings/js/tests/run_js_tests.cc',
-      ],
-    },
-  ],
+  ]
 }
diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp
index 72c89b3..065de09 100644
--- a/mojo/mojo_base.gyp
+++ b/mojo/mojo_base.gyp
@@ -155,36 +155,6 @@
         'public/mojo_public.gyp:mojo_application_base',
        ],
     },
-    {
-      # GN version: //mojo/bindings/js
-      'target_name': 'mojo_js_bindings_lib',
-      'type': 'static_library',
-      'dependencies': [
-        '../base/base.gyp:base',
-        '../gin/gin.gyp:gin',
-        '../v8/tools/gyp/v8.gyp:v8',
-        'mojo_common_lib',
-      ],
-      'export_dependent_settings': [
-        '../base/base.gyp:base',
-        '../gin/gin.gyp:gin',
-        'mojo_common_lib',
-      ],
-      'sources': [
-        # Sources list duplicated in GN build.
-        'bindings/js/core.cc',
-        'bindings/js/core.h',
-        'bindings/js/drain_data.cc',
-        'bindings/js/drain_data.h',
-        'bindings/js/handle.cc',
-        'bindings/js/handle.h',
-        'bindings/js/handle_close_observer.h',
-        'bindings/js/support.cc',
-        'bindings/js/support.h',
-        'bindings/js/waiting_callback.cc',
-        'bindings/js/waiting_callback.h',
-      ],
-    },
   ],
   'conditions': [
     ['OS=="android"', {
diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn
index 89fab8d..6d8077b 100644
--- a/mojo/public/BUILD.gn
+++ b/mojo/public/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//mojo/public/mojo.gni")
+
 group("public") {
   # Meta-target, don't link into production code.
   testonly = true
@@ -22,6 +24,12 @@
     ]
   }
 
+  if (mojo_use_dart) {
+    deps += [
+      "//mojo/public/dart",
+    ]
+  }
+
   if (is_android) {
     deps += [
       "//mojo/public/java:system",
@@ -38,7 +46,7 @@
     "//mojo/public/cpp/environment:standalone",
     "//mojo/public/cpp/utility",
     "//mojo/public/interfaces/application",
-    "//mojo/public/js/bindings",
+    "//mojo/public/js",
   ]
 }
 
diff --git a/mojo/public/VERSION b/mojo/public/VERSION
index b711f1d..11567394 100644
--- a/mojo/public/VERSION
+++ b/mojo/public/VERSION
@@ -1 +1 @@
-cfc99316100efdfa7d53d83f9e07f1d4d3765c21
\ No newline at end of file
+04a510fb37db10642e156957f9b2c11c2f6442ac
\ No newline at end of file
diff --git a/mojo/public/c/PRESUBMIT.py b/mojo/public/c/PRESUBMIT.py
deleted file mode 100644
index 4cba433..0000000
--- a/mojo/public/c/PRESUBMIT.py
+++ /dev/null
@@ -1,16 +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.
-
-"""Presubmit script for mojo/public/c.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-def CheckChangeOnUpload(input_api, output_api):
-  results = []
-  results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api,
-                                                              output_api)
-  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
-  return results
diff --git a/mojo/public/c/system/data_pipe.h b/mojo/public/c/system/data_pipe.h
index a7498e45..089ead3 100644
--- a/mojo/public/c/system/data_pipe.h
+++ b/mojo/public/c/system/data_pipe.h
@@ -83,6 +83,9 @@
 //       read. For use with |MojoReadData()| only. Mutually exclusive with
 //       |MOJO_READ_DATA_FLAG_DISCARD| and |MOJO_READ_DATA_FLAG_ALL_OR_NONE| is
 //       ignored if this flag is set.
+//   |MOJO_READ_DATA_FLAG_PEEK| - Read elements without removing them. For use
+//       with |MojoReadData()| only. Mutually exclusive with
+//       |MOJO_READ_DATA_FLAG_DISCARD| and |MOJO_READ_DATA_FLAG_QUERY|.
 
 typedef uint32_t MojoReadDataFlags;
 
@@ -91,11 +94,13 @@
 const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE = 1 << 0;
 const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD = 1 << 1;
 const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY = 1 << 2;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_PEEK = 1 << 3;
 #else
 #define MOJO_READ_DATA_FLAG_NONE ((MojoReadDataFlags)0)
 #define MOJO_READ_DATA_FLAG_ALL_OR_NONE ((MojoReadDataFlags)1 << 0)
 #define MOJO_READ_DATA_FLAG_DISCARD ((MojoReadDataFlags)1 << 1)
 #define MOJO_READ_DATA_FLAG_QUERY ((MojoReadDataFlags)1 << 2)
+#define MOJO_READ_DATA_FLAG_PEEK ((MojoReadDataFlags)1 << 3)
 #endif
 
 #ifdef __cplusplus
@@ -254,7 +259,9 @@
 // must be a multiple of the data pipe's element size) bytes of data to
 // |elements| and set |*num_bytes| to the amount actually read. If flags has
 // |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, it will either read exactly
-// |*num_bytes| bytes of data or none.
+// |*num_bytes| bytes of data or none. Additionally, if flags has
+// |MOJO_READ_DATA_FLAG_PEEK| set, the data read will remain in the pipe and be
+// available to future reads.
 //
 // If flags has |MOJO_READ_DATA_FLAG_DISCARD| set, it discards up to
 // |*num_bytes| (which again be a multiple of the element size) bytes of data,
@@ -300,7 +307,8 @@
 // |*buffer_num_bytes| will be at least as large as its input value, which must
 // also be a multiple of the element size (if |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
 // is not set, the input value of |*buffer_num_bytes| is ignored). |flags| must
-// not have |MOJO_READ_DATA_FLAG_DISCARD| or |MOJO_READ_DATA_FLAG_QUERY| set.
+// not have |MOJO_READ_DATA_FLAG_DISCARD|, |MOJO_READ_DATA_FLAG_QUERY|, or
+// |MOJO_READ_DATA_FLAG_PEEK| set.
 //
 // During a two-phase read, |data_pipe_consumer_handle| is *not* readable.
 // E.g., if another thread tries to read from it, it will get
diff --git a/mojo/public/cpp/PRESUBMIT.py b/mojo/public/cpp/PRESUBMIT.py
deleted file mode 100644
index 908f7a6c..0000000
--- a/mojo/public/cpp/PRESUBMIT.py
+++ /dev/null
@@ -1,16 +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.
-
-"""Presubmit script for mojo/public/cpp.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-def CheckChangeOnUpload(input_api, output_api):
-  results = []
-  results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api,
-                                                              output_api)
-  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
-  return results
diff --git a/mojo/public/cpp/application/lib/application_test_base.cc b/mojo/public/cpp/application/lib/application_test_base.cc
index 929a5930..42dbda5 100644
--- a/mojo/public/cpp/application/lib/application_test_base.cc
+++ b/mojo/public/cpp/application/lib/application_test_base.cc
@@ -45,8 +45,8 @@
   Environment::InstantiateDefaultRunLoop();
 
   // New applications are constructed for each test to avoid persisting state.
-  application_impl_ =
-      new ApplicationImpl(GetApplicationDelegate(), PassShellHandle());
+  application_impl_ = new ApplicationImpl(GetApplicationDelegate(),
+                                          PassShellHandle());
 
   // Fake application initialization with the given command line arguments.
   application_impl_->Initialize(args_.Clone());
diff --git a/mojo/public/cpp/application/lib/application_test_main.cc b/mojo/public/cpp/application/lib/application_test_main.cc
index 61e3ea3..9b76dad1 100644
--- a/mojo/public/cpp/application/lib/application_test_main.cc
+++ b/mojo/public/cpp/application/lib/application_test_main.cc
@@ -19,7 +19,7 @@
 
     // Construct an ApplicationImpl just for the GTEST commandline arguments.
     // GTEST command line arguments are supported amid application arguments:
-    // $ mojo_shell 'mojo:example_apptest arg1 --gtest_filter=foo arg2'
+    // $ mojo_shell 'mojo:example_apptests arg1 --gtest_filter=foo arg2'
     mojo::ApplicationDelegate dummy_application_delegate;
     mojo::ApplicationImpl app(&dummy_application_delegate, shell_handle);
     MOJO_CHECK(app.WaitForInitialize());
diff --git a/mojo/public/cpp/environment/BUILD.gn b/mojo/public/cpp/environment/BUILD.gn
index a0d6ca7..cc7bb31 100644
--- a/mojo/public/cpp/environment/BUILD.gn
+++ b/mojo/public/cpp/environment/BUILD.gn
@@ -4,17 +4,22 @@
 
 source_set("environment") {
   sources = [
+    "async_waiter.h",
     "logging.h",
     "environment.h",
   ]
 
   public_deps = [ "//mojo/public/c/environment" ]
 
-  deps = [ "//mojo/public/cpp/system" ]
+  deps = [
+    "//mojo/public/cpp/bindings:callback",
+    "//mojo/public/cpp/system",
+  ]
 }
 
 source_set("standalone") {
   sources = [
+    "lib/async_waiter.cc",
     "lib/default_async_waiter.cc",
     "lib/default_async_waiter.h",
     "lib/default_logger.cc",
diff --git a/mojo/public/cpp/environment/DEPS b/mojo/public/cpp/environment/DEPS
new file mode 100644
index 0000000..04346d9
--- /dev/null
+++ b/mojo/public/cpp/environment/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public/cpp/bindings/callback.h",
+]
diff --git a/mojo/public/cpp/environment/async_waiter.h b/mojo/public/cpp/environment/async_waiter.h
new file mode 100644
index 0000000..83b7c8a
--- /dev/null
+++ b/mojo/public/cpp/environment/async_waiter.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_ASYNC_WAITER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_ASYNC_WAITER_H_
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+// A class that waits until a handle is ready and calls |callback| with the
+// result. If the AsyncWaiter is deleted before the handle is ready, the wait is
+// cancelled and the callback will not be called.
+class AsyncWaiter {
+ public:
+  typedef mojo::Callback<void(MojoResult)> Callback;
+
+  AsyncWaiter(Handle handle,
+              MojoHandleSignals signals,
+              const Callback& callback);
+  ~AsyncWaiter();
+
+ private:
+  static void WaitComplete(void* waiter, MojoResult result);
+  void WaitCompleteInternal(MojoResult result);
+
+  const MojoAsyncWaiter* waiter_;
+  MojoAsyncWaitID id_;
+  const Callback callback_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiter);
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_ENVIRONMENT_ASYNC_WAITER_H_
diff --git a/mojo/public/cpp/environment/lib/async_waiter.cc b/mojo/public/cpp/environment/lib/async_waiter.cc
new file mode 100644
index 0000000..599a649
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/async_waiter.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/environment/async_waiter.h"
+
+namespace mojo {
+
+AsyncWaiter::AsyncWaiter(Handle handle,
+                         MojoHandleSignals signals,
+                         const Callback& callback)
+    : waiter_(Environment::GetDefaultAsyncWaiter()),
+      id_(0),
+      callback_(callback) {
+  id_ = waiter_->AsyncWait(handle.value(), signals, MOJO_DEADLINE_INDEFINITE,
+                           &AsyncWaiter::WaitComplete, this);
+}
+
+AsyncWaiter::~AsyncWaiter() {
+  if (id_)
+    waiter_->CancelWait(id_);
+}
+
+// static
+void AsyncWaiter::WaitComplete(void* waiter, MojoResult result) {
+  static_cast<AsyncWaiter*>(waiter)->WaitCompleteInternal(result);
+}
+
+void AsyncWaiter::WaitCompleteInternal(MojoResult result) {
+  id_ = 0;
+  callback_.Run(result);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/environment/tests/BUILD.gn b/mojo/public/cpp/environment/tests/BUILD.gn
index 6fb56f87..c87e2a20 100644
--- a/mojo/public/cpp/environment/tests/BUILD.gn
+++ b/mojo/public/cpp/environment/tests/BUILD.gn
@@ -5,6 +5,7 @@
 # GYP version: mojo/mojo_base.gyp:mojo_public_environment_unittests
 test("mojo_public_environment_unittests") {
   sources = [
+    "async_wait_unittest.cc",
     "async_waiter_unittest.cc",
     "logger_unittest.cc",
     "logging_unittest.cc",
@@ -13,6 +14,7 @@
   deps = [
     "//mojo/edk/test:run_all_unittests",
     "//mojo/public/c/environment",
+    "//mojo/public/cpp/bindings:callback",
     "//mojo/public/cpp/environment:standalone",
     "//mojo/public/cpp/system",
     "//mojo/public/cpp/test_support:test_utils",
diff --git a/mojo/public/cpp/environment/tests/async_wait_unittest.cc b/mojo/public/cpp/environment/tests/async_wait_unittest.cc
new file mode 100644
index 0000000..83c5ca0
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/async_wait_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class TestAsyncWaitCallback {
+ public:
+  TestAsyncWaitCallback() : result_count_(0), last_result_(MOJO_RESULT_OK) {}
+  ~TestAsyncWaitCallback() {}
+
+  int result_count() const { return result_count_; }
+
+  MojoResult last_result() const { return last_result_; }
+
+  // MojoAsyncWaitCallback:
+  static void OnHandleReady(void* closure, MojoResult result) {
+    TestAsyncWaitCallback* self = static_cast<TestAsyncWaitCallback*>(closure);
+    self->result_count_++;
+    self->last_result_ = result;
+  }
+
+ private:
+  int result_count_;
+  MojoResult last_result_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaitCallback);
+};
+
+MojoAsyncWaitID CallAsyncWait(const Handle& handle,
+                              MojoHandleSignals signals,
+                              TestAsyncWaitCallback* callback) {
+  return Environment::GetDefaultAsyncWaiter()->AsyncWait(
+      handle.value(),
+      signals,
+      MOJO_DEADLINE_INDEFINITE,
+      &TestAsyncWaitCallback::OnHandleReady,
+      callback);
+}
+
+void CallCancelWait(MojoAsyncWaitID wait_id) {
+  Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id);
+}
+
+class AsyncWaitTest : public testing::Test {
+ public:
+  AsyncWaitTest() {}
+
+ private:
+  Environment environment_;
+  RunLoop run_loop_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaitTest);
+};
+
+// Verifies AsyncWaitCallback is notified when pipe is ready.
+TEST_F(AsyncWaitTest, CallbackNotified) {
+  TestAsyncWaitCallback callback;
+  MessagePipe test_pipe;
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+  CallAsyncWait(
+      test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback);
+  RunLoop::current()->Run();
+  EXPECT_EQ(1, callback.result_count());
+  EXPECT_EQ(MOJO_RESULT_OK, callback.last_result());
+}
+
+// Verifies 2 AsyncWaitCallbacks are notified when there pipes are ready.
+TEST_F(AsyncWaitTest, TwoCallbacksNotified) {
+  TestAsyncWaitCallback callback1;
+  TestAsyncWaitCallback callback2;
+  MessagePipe test_pipe1;
+  MessagePipe test_pipe2;
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe1.handle1.get(), std::string()));
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe2.handle1.get(), std::string()));
+
+  CallAsyncWait(
+      test_pipe1.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback1);
+  CallAsyncWait(
+      test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback2);
+
+  RunLoop::current()->Run();
+  EXPECT_EQ(1, callback1.result_count());
+  EXPECT_EQ(MOJO_RESULT_OK, callback1.last_result());
+  EXPECT_EQ(1, callback2.result_count());
+  EXPECT_EQ(MOJO_RESULT_OK, callback2.last_result());
+}
+
+// Verifies cancel works.
+TEST_F(AsyncWaitTest, CancelCallback) {
+  TestAsyncWaitCallback callback;
+  MessagePipe test_pipe;
+  EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+  CallCancelWait(CallAsyncWait(
+      test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback));
+  RunLoop::current()->Run();
+  EXPECT_EQ(0, callback.result_count());
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/environment/tests/async_waiter_unittest.cc b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc
index c1876b79..1c1c2bf 100644
--- a/mojo/public/cpp/environment/tests/async_waiter_unittest.cc
+++ b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc
@@ -2,12 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <string>
-
-#include "mojo/public/c/environment/async_waiter.h"
-#include "mojo/public/cpp/environment/environment.h"
-#include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/environment/async_waiter.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "mojo/public/cpp/utility/run_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,11 +20,9 @@
 
   MojoResult last_result() const { return last_result_; }
 
-  // MojoAsyncWaitCallback:
-  static void OnHandleReady(void* closure, MojoResult result) {
-    TestAsyncWaitCallback* self = static_cast<TestAsyncWaitCallback*>(closure);
-    self->result_count_++;
-    self->last_result_ = result;
+  void OnHandleReady(MojoResult result) {
+    result_count_++;
+    last_result_ = result;
   }
 
  private:
@@ -38,20 +32,17 @@
   MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaitCallback);
 };
 
-MojoAsyncWaitID CallAsyncWait(const Handle& handle,
-                              MojoHandleSignals signals,
-                              TestAsyncWaitCallback* callback) {
-  return Environment::GetDefaultAsyncWaiter()->AsyncWait(
-      handle.value(),
-      signals,
-      MOJO_DEADLINE_INDEFINITE,
-      &TestAsyncWaitCallback::OnHandleReady,
-      callback);
-}
+// Manual code to create a callback since we don't have mojo::Bind yet.
+class ManualCallback {
+ public:
+  explicit ManualCallback(TestAsyncWaitCallback* callback)
+      : callback_(callback) {}
 
-void CallCancelWait(MojoAsyncWaitID wait_id) {
-  Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id);
-}
+  void Run(MojoResult result) const { callback_->OnHandleReady(result); }
+
+ private:
+  TestAsyncWaitCallback* callback_;
+};
 
 class AsyncWaiterTest : public testing::Test {
  public:
@@ -70,8 +61,8 @@
   MessagePipe test_pipe;
   EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
 
-  CallAsyncWait(
-      test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback);
+  AsyncWaiter waiter(test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                     ManualCallback(&callback));
   RunLoop::current()->Run();
   EXPECT_EQ(1, callback.result_count());
   EXPECT_EQ(MOJO_RESULT_OK, callback.last_result());
@@ -86,10 +77,10 @@
   EXPECT_TRUE(test::WriteTextMessage(test_pipe1.handle1.get(), std::string()));
   EXPECT_TRUE(test::WriteTextMessage(test_pipe2.handle1.get(), std::string()));
 
-  CallAsyncWait(
-      test_pipe1.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback1);
-  CallAsyncWait(
-      test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback2);
+  AsyncWaiter waiter1(test_pipe1.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                      ManualCallback(&callback1));
+  AsyncWaiter waiter2(test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                      ManualCallback(&callback2));
 
   RunLoop::current()->Run();
   EXPECT_EQ(1, callback1.result_count());
@@ -104,8 +95,10 @@
   MessagePipe test_pipe;
   EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
 
-  CallCancelWait(CallAsyncWait(
-      test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback));
+  {
+    AsyncWaiter waiter(test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+                       ManualCallback(&callback));
+  }
   RunLoop::current()->Run();
   EXPECT_EQ(0, callback.result_count());
 }
diff --git a/mojo/public/dart/BUILD.gn b/mojo/public/dart/BUILD.gn
new file mode 100644
index 0000000..83c5a25
--- /dev/null
+++ b/mojo/public/dart/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("dart") {
+  deps = [
+    ":bindings",
+  ]
+}
+
+
+copy("bindings") {
+  sources = [
+    "bindings.dart",
+    "src/codec.dart",
+  ]
+  outputs = [
+    "{{source_gen_dir}}/{{source_file_part}}"
+  ]
+}
\ No newline at end of file
diff --git a/mojo/public/dart/README b/mojo/public/dart/README
new file mode 100644
index 0000000..aadb348
--- /dev/null
+++ b/mojo/public/dart/README
@@ -0,0 +1,41 @@
+These are interim instructions for building and testing Dart's Mojo bindings.
+These instructions currently only work for Linux, and assume you already have a
+Mojo checkout.
+
+1.) Install the Dart SDK.
+
+- apt-get -
+
+Follow instructions at: https://www.dartlang.org/tools/debian.html
+
+- Debian package -
+
+Download from:
+
+$ wget https://storage.googleapis.com/dart-archive/channels/dev/release/latest/linux_packages/debian_wheezy/dart_1.8.0-dev.2.0-1_amd64.deb
+$ dpkg -i dart_1.8.0-dev.2.0-1_amd64.deb
+
+- From source -
+
+Follow instructions here: https://code.google.com/p/dart/wiki/Building
+
+and build the "create_sdk" target.
+
+With the first two options, dart is on your path and you are ready to go.
+When building from source, you must explicitly add
+e.g. out/ReleaseX64/dart-sdk/bin to your path.
+
+
+2.) Configure Mojo with Dart.
+
+  $ ./mojob.sh --release --with-dart gn
+
+
+3.) Build Mojo with Dart.
+
+  $ ./mojob.sh --release build
+
+
+4.) Run Dart tests.
+
+  $ ./mojob.sh --release darttest
diff --git a/mojo/public/dart/bindings.dart b/mojo/public/dart/bindings.dart
new file mode 100644
index 0000000..6a3be5f
--- /dev/null
+++ b/mojo/public/dart/bindings.dart
@@ -0,0 +1,13 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library bindings;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:core';
+import 'dart:mirrors';
+import 'dart:typed_data';
+
+part 'src/codec.dart';
diff --git a/mojo/public/dart/src/codec.dart b/mojo/public/dart/src/codec.dart
new file mode 100644
index 0000000..003fcc1
--- /dev/null
+++ b/mojo/public/dart/src/codec.dart
@@ -0,0 +1,745 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of bindings;
+
+const int kAlignment = 8;
+const int kArrayHeaderSize = 8;
+const int kStructHeaderSize = 8;
+const int kMessageHeaderSize = 16;
+const int kMessageWithRequestIDHeaderSize = 24;
+const int kMapStructPayloadSize = 16;
+const int kStructHeaderNumBytesOffset = 0;
+const int kStructHeaderNumFieldsOffset = 4;
+const int kEncodedInvalidHandleValue = 0xffffffff;
+const String kErrorUnsigned = "Passing negative value to unsigned encoder";
+
+
+int align(int size) => size + (kAlignment - (size % kAlignment)) % kAlignment;
+bool isAligned(offset) => (offset >= 0) && ((offset % kAlignment) == 0);
+
+
+Uint8List utf8OfString(String s) =>
+  (new Uint8List.fromList((const Utf8Encoder()).convert(s)));
+
+
+String stringOfUtf8(Uint8List bytes) =>
+    (const Utf8Decoder()).convert(bytes.toList());
+
+
+// Given an argument that is either a Type or an instance:
+// Invoke the static method "decode" of the Type, or the instance method
+// "decode" of the instance, on the MojoDecoder.
+Object _callDecode(Object typeOrInstance, MojoDecoder decoder) {
+  if (typeOrInstance is Type) {
+    return reflectClass(typeOrInstance).invoke(#decode, [decoder]).reflectee;
+  } else {
+    return typeOrInstance.decode(decoder);
+  }
+}
+
+
+// Given an argument that is either a Type or an instance:
+// Invoke the static method "encode" of the Type, or the instance method
+// "encode" of the instance, on the MojoEncoder and value to be encoded.
+void _callEncode(Object typeOrInstance, MojoEncoder encoder, Object val) {
+  if (typeOrInstance is Type) {
+    reflectClass(typeOrInstance).invoke(#encode, [encoder, val]);
+  } else {
+    typeOrInstance.encode(encoder, val);
+  }
+}
+
+
+// Given an argument that is either a Type or an instance:
+// Invoke the static getter "encodedSize" of the Type, or the instance getter
+// "encodedSize" of the instance, and return the result.
+int getEncodedSize(Object typeOrInstance) {
+  if (typeOrInstance is Type) {
+    return reflectClass(typeOrInstance).getField(#encodedSize).reflectee;
+  } else {
+    return typeOrInstance.encodedSize;
+  }
+}
+
+
+class MojoDecoder {
+  ByteData buffer;
+  List<int> handles;
+  int base;
+  int next;
+
+  MojoDecoder(this.buffer, this.handles, this.base) {
+    next = base;
+  }
+
+  void skip(int offset) {
+    next += offset;
+  }
+
+  int readInt8() {
+    int result = buffer.getInt8(next);
+    next += 1;
+    return result;
+  }
+
+  int readUint8() {
+    int result = buffer.getUint8(next);
+    next += 1;
+    return result;
+  }
+
+  int readInt16() {
+    int result = buffer.getInt16(next, Endianness.LITTLE_ENDIAN);
+    next += 2;
+    return result;
+  }
+
+  int readUint16() {
+    int result = buffer.getUint16(next, Endianness.LITTLE_ENDIAN);
+    next += 2;
+    return result;
+  }
+
+  int readInt32() {
+    int result = buffer.getInt32(next, Endianness.LITTLE_ENDIAN);
+    next += 4;
+    return result;
+  }
+
+  int readUint32() {
+    int result = buffer.getUint32(next, Endianness.LITTLE_ENDIAN);
+    next += 4;
+    return result;
+  }
+
+  int readInt64() {
+    int result = buffer.getInt64(next, Endianness.LITTLE_ENDIAN);
+    next += 8;
+    return result;
+  }
+
+  int readUint64() {
+    int result = buffer.getUint64(next, Endianness.LITTLE_ENDIAN);
+    next += 8;
+    return result;
+  }
+
+  double readFloat() {
+    double result = buffer.getFloat32(next,Endianness.LITTLE_ENDIAN);
+    next += 4;
+    return result;
+  }
+
+  double readDouble() {
+    double result = buffer.getFloat64(next, Endianness.LITTLE_ENDIAN);
+    next += 8;
+    return result;
+  }
+
+  int decodePointer() {
+    int offsetPointer = next;
+    int offset = readUint64();
+    if (offset == 0) {
+      return 0;
+    }
+    return offsetPointer + offset;
+  }
+
+  MojoDecoder decodeAndCreateDecoder(int offset) {
+    return new MojoDecoder(buffer, handles, offset);
+  }
+
+  int decodeHandle() {
+    return handles[readUint32()];
+  }
+
+  String decodeString() {
+    int numBytes = readUint32();
+    int numElements = readUint32();
+    int base = next;
+    next += numElements;
+    return stringOfUtf8(buffer.buffer.asUint8List(base, numElements));
+  }
+
+  List decodeArray(Object type) {
+    int numBytes = readUint32();
+    int numElements = readUint32();
+    if (type == PackedBool) {
+      int b;
+      List<bool> result = new List<bool>(numElements);
+      for (int i = 0; i < numElements; i++) {
+        if ((i % 8) == 0) {
+          b = readUint8();
+        }
+        result[i] = ((b & (1 << (i % 8)) != 0) ? true : false);
+      }
+      return result;
+    } else {
+      List result = new List(numElements);
+      for (int i = 0; i < numElements; i++) {
+        result[i] = _callDecode(type, this);
+      }
+      return result;
+    }
+  }
+
+  Object decodeStruct(Object t) {
+    return _callDecode(t, this);
+  }
+
+  Object decodeStructPointer(Object t) {
+    int pointer = decodePointer();
+    if (pointer == 0) {
+      return null;
+    }
+    return _callDecode(t, decodeAndCreateDecoder(pointer));
+  }
+
+  List decodeArrayPointer(Object type) {
+    int pointer = decodePointer();
+    if (pointer == 0) {
+      return null;
+    }
+    return decodeAndCreateDecoder(pointer).decodeArray(type);
+  }
+
+  String decodeStringPointer() {
+    int pointer = decodePointer();
+    if (pointer == 0) {
+      return null;
+    }
+    return decodeAndCreateDecoder(pointer).decodeString();
+  }
+
+  Map decodeMap(Object keyType, Object valType) {
+    skip(4);  // number of bytes.
+    skip(4);  // number of fields.
+    List keys = decodeArrayPointer(keyType);
+    List values = decodeArrayPointer(valType);
+    return new Map.fromIterables(keys, values);
+  }
+
+  Map decodeMapPointer(Object keyType, Object valType) {
+    int pointer = this.decodePointer();
+    if (pointer == 0) {
+      return null;
+    }
+    MojoDecoder decoder = decodeAndCreateDecoder(pointer);
+    return decoder.decodeMap(keyType, valType);
+  }
+}
+
+
+class MojoEncoder {
+  ByteData buffer;
+  List<int> handles;
+  int base;
+  int next;
+  int extent;
+
+  MojoEncoder(this.buffer, this.handles, this.base, this.extent) {
+    next = base;
+  }
+
+  void skip(int offset) {
+    next += offset;
+  }
+
+  void writeInt8(int val) {
+    buffer.setInt8(next, val);
+    next += 1;
+  }
+
+  void writeUint8(int val) {
+    if (val < 0) {
+      throw new ArgumentError("$kErrorUnsigned: $val");
+    }
+    buffer.setUint8(next, val);
+    next += 1;
+  }
+
+  void writeInt16(int val) {
+    buffer.setInt16(next, val, Endianness.LITTLE_ENDIAN);
+    next += 2;
+  }
+
+  void writeUint16(int val) {
+    if (val < 0) {
+      throw new ArgumentError("$kErrorUnsigned: $val");
+    }
+    buffer.setUint16(next, val, Endianness.LITTLE_ENDIAN);
+    next += 2;
+  }
+
+  void writeInt32(int val) {
+    buffer.setInt32(next, val, Endianness.LITTLE_ENDIAN);
+    next += 4;
+  }
+
+  void writeUint32(int val) {
+    if (val < 0) {
+      throw new ArgumentError("$kErrorUnsigned: $val");
+    }
+    buffer.setUint32(next, val, Endianness.LITTLE_ENDIAN);
+    next += 4;
+  }
+
+  void writeInt64(int val) {
+    buffer.setInt64(next, val, Endianness.LITTLE_ENDIAN);
+    next += 8;
+  }
+
+  void writeUint64(int val) {
+    if (val < 0) {
+      throw new ArgumentError("$kErrorUnsigned: $val");
+    }
+    buffer.setUint64(next, val, Endianness.LITTLE_ENDIAN);
+    next += 8;
+  }
+
+  void writeFloat(double val) {
+    buffer.setFloat32(next, val, Endianness.LITTLE_ENDIAN);
+    next += 4;
+  }
+
+  void writeDouble(double val) {
+    buffer.setFloat64(next, val, Endianness.LITTLE_ENDIAN);
+    next += 8;
+  }
+
+  void encodePointer(int pointer) {
+    if (pointer == null) {
+      writeUint64(0);
+      return;
+    }
+    int offset = pointer - next;
+    writeUint64(offset);
+  }
+
+  void grow(int new_size) {
+    Uint8List new_buffer = new Uint8List(new_size);
+    new_buffer.setRange(0, next, buffer.buffer.asUint8List());
+    buffer = new_buffer.buffer.asByteData();
+  }
+
+  int alloc(int size_request) {
+    int pointer = extent;
+    extent += size_request;
+    if (extent > buffer.lengthInBytes) {
+      int new_size = buffer.lengthInBytes + size_request;
+      new_size += new_size ~/ 2;
+      grow(new_size);
+    }
+    return pointer;
+  }
+
+  MojoEncoder createAndEncodeEncoder(int size) {
+    int pointer = alloc(align(size));
+    encodePointer(pointer);
+    return new MojoEncoder(buffer, handles, pointer, extent);
+  }
+
+  void encodeHandle(int handle) {
+    handles.add(handle);
+    writeUint32(handles.length - 1);
+  }
+
+  void encodeString(String val) {
+    Uint8List utf8string = utf8OfString(val);
+    int numElements = utf8string.lengthInBytes;
+    int numBytes = kArrayHeaderSize + numElements;
+    writeUint32(numBytes);
+    writeUint32(numElements);
+    buffer.buffer.asUint8List().setRange(next, next + numElements, utf8string);
+    next += numElements;
+  }
+
+  void encodeArray(Object t, List val, [int numElements, int encodedSize]) {
+    if (numElements == null) {
+      numElements = val.length;
+    }
+    if (encodedSize == null) {
+      encodedSize = kArrayHeaderSize + (getEncodedSize(t) * numElements);
+    }
+
+    writeUint32(encodedSize);
+    writeUint32(numElements);
+
+    if (t == PackedBool) {
+      int b = 0;
+      for (int i = 0; i < numElements; i++) {
+        if (val[i]) {
+          b |= (1 << (i % 8));
+        }
+        if (((i % 8) == 7) || (i == (numElements - 1))) {
+          Uint8.encode(this, b);
+        }
+      }
+    } else {
+      for (int i = 0; i < numElements; i++) {
+        _callEncode(t, this, val[i]);
+      }
+    }
+  }
+
+  void encodeStruct(Object t, Object val) {
+    _callEncode(t, this, val);
+  }
+
+  void encodeStructPointer(Object t, Object val) {
+    if (val == null) {
+      encodePointer(val);
+      return;
+    }
+    MojoEncoder encoder = createAndEncodeEncoder(getEncodedSize(t));
+    _callEncode(t, encoder, val);
+    extent = encoder.extent;
+    buffer = encoder.buffer;
+  }
+
+  void encodeArrayPointer(Object t, List val) {
+    if (val == null) {
+      encodePointer(val);
+      return;
+    }
+    int numElements = val.length;
+    int encodedSize = kArrayHeaderSize + ((t == PackedBool) ?
+        (numElements / 8).ceil() : (getEncodedSize(t) * numElements));
+    MojoEncoder encoder = createAndEncodeEncoder(encodedSize);
+    encoder.encodeArray(t, val, numElements, encodedSize);
+    extent = encoder.extent;
+    buffer = encoder.buffer;
+  }
+
+  void encodeStringPointer(String val) {
+    if (val == null) {
+      encodePointer(val);
+      return;
+    }
+    int encodedSize = kArrayHeaderSize + utf8OfString(val).lengthInBytes;
+    MojoEncoder encoder = createAndEncodeEncoder(encodedSize);
+    encoder.encodeString(val);
+    extent = encoder.extent;
+    buffer = encoder.buffer;
+  }
+
+  void encodeMap(Object keyType, Object valType, Map val) {
+    List keys = val.keys;
+    List vals = val.values;
+    writeUint32(kStructHeaderSize + kMapStructPayloadSize);
+    writeUint32(2);
+    encodeArrayPointer(keyType, keys);
+    encodeArrayPointer(valType, vals);
+  }
+
+  void encodeMapPointer(Object keyTYpe, Object valType, Map val) {
+    if (val == null) {
+      encodePointer(val);
+      return;
+    }
+    int encodedSize = kStructHeaderSize + kMapStructPayloadSize;
+    MojoEncoder encoder = createAndEncodeEncoder(encodedSize);
+    encoder.encodeMap(keyType, valType, val);
+    extent = encoder.extent;
+    buffer = encoder.buffer;
+  }
+}
+
+
+const int kMessageNameOffset = kStructHeaderSize;
+const int kMessageFlagsOffset = kMessageNameOffset + 4;
+const int kMessageRequestIDOffset = kMessageFlagsOffset + 4;
+const int kMessageExpectsResponse = 1 << 0;
+const int kMessageIsResponse = 1 << 1;
+
+class Message {
+  ByteData buffer;
+  List<int> handles;
+
+  Message(this.buffer, this.handles);
+
+  int getHeaderNumBytes() => buffer.getUint32(kStructHeaderNumBytesOffset);
+  int getHeaderNumFields() => buffer.getUint32(kStructHeaderNumFieldsOffset);
+  int getName() => buffer.getUint32(kMessageNameOffset);
+  int getFlags() => buffer.getUint32(kMessageFlagsOffset);
+  bool isResponse() => (getFlags() & kMessageIsResponse) != 0;
+  bool expectsResponse() => (getFlags() & kMessageExpectsResponse) != 0;
+
+  void setRequestID(int id) {
+    buffer.setUint64(kMessageRequestIDOffset, id);
+  }
+}
+
+
+class MessageBuilder {
+  MojoEncoder encoder;
+  List<int> handles;
+
+  MessageBuilder(int name, int payloadSize) {
+    int numBytes = kMessageHeaderSize + payloadSize;
+    var buffer = new ByteData(numBytes);
+    handles = [];
+
+    encoder = new MojoEncoder(buffer, handles, 0, kMessageHeaderSize);
+    encoder.writeUint32(kMessageHeaderSize);
+    encoder.writeUint32(2);  // num_fields;
+    encoder.writeUint32(name);
+    encoder.writeUint32(0);  // flags.
+  }
+
+  MojoEncoder createEncoder(int size) {
+    encoder = new MojoEncoder(encoder.buffer,
+                              handles,
+                              encoder.next,
+                              encoder.next + size);
+    return encoder;
+  }
+
+  void encodeStruct(Object t, Object val) {
+    encoder = createEncoder(getEncodedSize(t));
+    _callEncode(t, encoder, val);
+  }
+
+  ByteData _trimBuffer() {
+    return new ByteData.view(encoder.buffer.buffer, 0, encoder.extent);
+  }
+
+  Message finish() {
+    Message message = new Message(_trimBuffer(), handles);
+    encoder = null;
+    handles = null;
+    return message;
+  }
+}
+
+
+class MessageWithRequestIDBuilder extends MessageBuilder {
+  MessageWithRequestIDBuilder(
+      int name, int payloadSize, int flags, int requestID) {
+    int numBytes = kMessageWithRequestIDHeaderSize + payloadSize;
+    buffer = new ByteData(numBytes);
+    handles = [];
+    base = 0;
+
+    encoder = createEncoder(0, kMessageWithRequestIDHeaderSize);
+    encoder.writeUint32(kMessageWithRequestIDHeaderSize);
+    encoder.writeUint32(3);  // num_fields.
+    encoder.writeUint32(name);
+    encoder.writeUint32(flags);
+    encoder.writeUint64(requestID);
+    base = encoder.next;
+    buffer = encoder.buffer;
+  }
+}
+
+
+class MessageReader {
+  MojoDecoder decoder;
+  int payloadSize;
+  int name;
+  int flags;
+  int requestID;
+
+  MessageReader(Message message) {
+    decoder = new MojoDecoder(message.buffer, message.handles, 0);
+
+    int messageHeaderSize = decoder.readUint32();
+    payloadSize = message.buffer.lengthInBytes - messageHeaderSize;
+
+    int num_fields = decoder.readUint32();
+    name = decoder.readUint32();
+    flags = decoder.readUint32();
+
+    if (num_fields >= 3) {
+      requestID = decoder.readUint64();
+    }
+    decoder.skip(messageHeaderSize - decoder.next);
+  }
+
+  Object decodeStruct(Object t) => _callDecode(t, decoder);
+}
+
+
+abstract class MojoType<T> {
+  static const int encodedSize = 0;
+  static T decode(MojoDecoder decoder) { return null }
+  static void encode(MojoEncoder encoder, T val) {}
+}
+
+
+class PackedBool {}
+
+
+class Int8 implements MojoType<int> {
+  static const int encodedSize = 1;
+  static int decode(MojoDecoder decoder) => decoder.readInt8();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.writeInt8(val);
+  }
+}
+
+
+class Uint8 implements MojoType<int> {
+  static const int encodedSize = 1;
+  static int decode(MojoDecoder decoder) => decoder.readUint8();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.writeUint8(val);
+  }
+}
+
+
+class Int16 implements MojoType<int> {
+  static const int encodedSize = 2;
+  static int decode(MojoDecoder decoder) => decoder.readInt16();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.writeInt16(val);
+  }
+}
+
+
+class Uint16 implements MojoType<int> {
+  static const int encodedSize = 2;
+  static int decode(MojoDecoder decoder) => decoder.readUint16();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.writeUint16(val);
+  }
+}
+
+
+class Int32 implements MojoType<int> {
+  static const int encodedSize = 4;
+  static int decode(MojoDecoder decoder) => decoder.readInt32();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.writeInt32(val);
+  }
+}
+
+
+class Uint32 implements MojoType<int> {
+  static const int encodedSize = 4;
+  static int decode(MojoDecoder decoder) => decoder.readUint32();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.writeUint32(val);
+  }
+}
+
+
+class Int64 implements MojoType<int> {
+  static const int encodedSize = 8;
+  static int decode(MojoDecoder decoder) => decoder.readInt64();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.writeInt64(val);
+  }
+}
+
+
+class Uint64 implements MojoType<int> {
+  static const int encodedSize = 8;
+  static int decode(MojoDecoder decoder) => decoder.readUint64();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.writeUint64(val);
+  }
+}
+
+
+class MojoString implements MojoType<String> {
+  static const int encodedSize = 8;
+  static String decode(MojoDecoder decoder) => decoder.decodeStringPointer();
+  static void encode(MojoEncoder encoder, String val) {
+    encoder.encodeStringPointer(val);
+  }
+}
+
+
+class NullableMojoString implements MojoType<String> {
+  static const int encodedSize = MojoString.encodedSize;
+  static var decode = MojoString.decode;
+  static var encode = MojoString.encode;
+}
+
+
+class Float implements MojoType<double> {
+  static const int encodedSize = 4;
+  static double decode(MojoDecoder decoder) => decoder.readFloat();
+  static void encode(MojoEncoder encoder, double val) {
+    encoder.writeFloat(val);
+  }
+}
+
+
+class Double implements MojoType<double> {
+  static const int encodedSize = 8;
+  static double decode(MojoDecoder decoder) => decoder.readDouble();
+  static void encode(MojoEncoder encoder, double val) {
+    encoder.writeDouble(val);
+  }
+}
+
+
+class PointerTo {
+  Object val;
+
+  PointerTo(this.val);
+
+  int encodedSize = 8;
+  Object decode(MojoDecoder decoder) {
+    int pointer = decoder.decodePointer();
+    if (pointer == 0) {
+      return null;
+    }
+    return _callDecode(val, decoder.decodeAndCreateDecoder(pointer));
+  }
+  void encode(MojoEncoder encoder, Object val) {
+    if (val == null) {
+      encoder.encodePointer(val);
+      return;
+    }
+    MojoEncoder obj_encoder =
+        encoder.createAndEncodeEncoder(getEncodedSize(this.val));
+    _callEncode(this.val, obj_encoder, val);
+  }
+}
+
+
+class NullablePointerTo extends PointerTo {
+  static const int encodedSize = PointerTo.encodedSize;
+}
+
+
+class ArrayOf {
+  Object val;
+  int length;
+
+  ArrayOf(this.val, [this.length = 0]);
+
+  int dimensions() => [length].addAll((val is ArrayOf) ? val.dimensions() : []);
+
+  int encodedSize = 8;
+  List decode(MojoDecoder decoder) => decoder.decodeArrayPointer(val);
+  void encode(MojoEncoder encoder, List val) {
+    encoder.encodeArrayPointer(this.val, val);
+  }
+}
+
+
+class NullableArrayOf extends ArrayOf {
+  static const int encodedSize = ArrayOf.encodedSize;
+}
+
+
+class Handle implements MojoType<int> {
+  static const int encodedSize = 4;
+  static int decode(MojoDecoder decoder) => decoder.decodeHandle();
+  static void encode(MojoEncoder encoder, int val) {
+    encoder.encodeHandle(val);
+  }
+}
+
+
+class NullableHandle implements MojoType<int> {
+  static const int encodedSize = Handle.encodedSize;
+  static const decode = Handle.decode;
+  static const encode = Handle.encode;
+}
diff --git a/mojo/public/js/bindings/BUILD.gn b/mojo/public/js/BUILD.gn
similarity index 89%
rename from mojo/public/js/bindings/BUILD.gn
rename to mojo/public/js/BUILD.gn
index 0f305e9..4b86e6eb 100644
--- a/mojo/public/js/bindings/BUILD.gn
+++ b/mojo/public/js/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("bindings") {
+source_set("js") {
   sources = [
     "constants.cc",
     "constants.h",
diff --git a/mojo/public/js/bindings/constants.cc b/mojo/public/js/bindings/constants.cc
deleted file mode 100644
index 547279da..0000000
--- a/mojo/public/js/bindings/constants.cc
+++ /dev/null
@@ -1,17 +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.
-
-#include "mojo/public/js/bindings/constants.h"
-
-namespace mojo {
-
-const char kBufferModuleName[] = "mojo/public/js/bindings/buffer";
-const char kCodecModuleName[] = "mojo/public/js/bindings/codec";
-const char kConnectionModuleName[] = "mojo/public/js/bindings/connection";
-const char kConnectorModuleName[] = "mojo/public/js/bindings/connector";
-const char kUnicodeModuleName[] = "mojo/public/js/bindings/unicode";
-const char kRouterModuleName[] = "mojo/public/js/bindings/router";
-const char kValidatorModuleName[] = "mojo/public/js/bindings/validator";
-
-}  // namespace mojo
diff --git a/mojo/public/js/bindings/buffer.js b/mojo/public/js/buffer.js
similarity index 98%
rename from mojo/public/js/bindings/buffer.js
rename to mojo/public/js/buffer.js
index d74fef6..e35f6951 100644
--- a/mojo/public/js/bindings/buffer.js
+++ b/mojo/public/js/buffer.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-define("mojo/public/js/bindings/buffer", function() {
+define("mojo/public/js/buffer", function() {
 
   var kHostIsLittleEndian = (function () {
     var endianArrayBuffer = new ArrayBuffer(2);
diff --git a/mojo/public/js/bindings/codec.js b/mojo/public/js/codec.js
similarity index 99%
rename from mojo/public/js/bindings/codec.js
rename to mojo/public/js/codec.js
index b84dc28..285c069 100644
--- a/mojo/public/js/bindings/codec.js
+++ b/mojo/public/js/codec.js
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-define("mojo/public/js/bindings/codec", [
-  "mojo/public/js/bindings/unicode",
-  "mojo/public/js/bindings/buffer",
+define("mojo/public/js/codec", [
+  "mojo/public/js/unicode",
+  "mojo/public/js/buffer",
 ], function(unicode, buffer) {
 
   var kErrorUnsigned = "Passing negative value to unsigned";
diff --git a/mojo/public/js/bindings/codec_unittests.js b/mojo/public/js/codec_unittests.js
similarity index 99%
rename from mojo/public/js/bindings/codec_unittests.js
rename to mojo/public/js/codec_unittests.js
index 0276db2..5d69507 100644
--- a/mojo/public/js/bindings/codec_unittests.js
+++ b/mojo/public/js/codec_unittests.js
@@ -4,7 +4,7 @@
 
 define([
     "gin/test/expect",
-    "mojo/public/js/bindings/codec",
+    "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",
diff --git a/mojo/public/js/bindings/connection.js b/mojo/public/js/connection.js
similarity index 93%
rename from mojo/public/js/bindings/connection.js
rename to mojo/public/js/connection.js
index 4cfb0cd..d9b64aa 100644
--- a/mojo/public/js/bindings/connection.js
+++ b/mojo/public/js/connection.js
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-define("mojo/public/js/bindings/connection", [
-  "mojo/public/js/bindings/connector",
-  "mojo/public/js/bindings/router",
+define("mojo/public/js/connection", [
+  "mojo/public/js/connector",
+  "mojo/public/js/router",
 ], function(connector, router) {
 
   function Connection(
diff --git a/mojo/public/js/bindings/connector.js b/mojo/public/js/connector.js
similarity index 94%
rename from mojo/public/js/bindings/connector.js
rename to mojo/public/js/connector.js
index 495896d3..badd12a 100644
--- a/mojo/public/js/bindings/connector.js
+++ b/mojo/public/js/connector.js
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-define("mojo/public/js/bindings/connector", [
-  "mojo/public/js/bindings/buffer",
-  "mojo/public/js/bindings/codec",
-  "mojo/public/js/bindings/core",
-  "mojo/public/js/bindings/support",
+define("mojo/public/js/connector", [
+  "mojo/public/js/buffer",
+  "mojo/public/js/codec",
+  "mojo/public/js/core",
+  "mojo/public/js/support",
 ], function(buffer, codec, core, support) {
 
   function Connector(handle) {
@@ -86,6 +86,8 @@
     for (;;) {
       var read = core.readMessage(this.handle_,
                                   core.READ_MESSAGE_FLAG_NONE);
+      if (this.handle_ == null) // The connector has been closed.
+        return;
       if (read.result == core.RESULT_SHOULD_WAIT) {
         this.waitToReadMore_();
         return;
diff --git a/mojo/public/js/constants.cc b/mojo/public/js/constants.cc
new file mode 100644
index 0000000..4f98cc8
--- /dev/null
+++ b/mojo/public/js/constants.cc
@@ -0,0 +1,17 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/js/constants.h"
+
+namespace mojo {
+
+const char kBufferModuleName[] = "mojo/public/js/buffer";
+const char kCodecModuleName[] = "mojo/public/js/codec";
+const char kConnectionModuleName[] = "mojo/public/js/connection";
+const char kConnectorModuleName[] = "mojo/public/js/connector";
+const char kUnicodeModuleName[] = "mojo/public/js/unicode";
+const char kRouterModuleName[] = "mojo/public/js/router";
+const char kValidatorModuleName[] = "mojo/public/js/validator";
+
+}  // namespace mojo
diff --git a/mojo/public/js/bindings/constants.h b/mojo/public/js/constants.h
similarity index 100%
rename from mojo/public/js/bindings/constants.h
rename to mojo/public/js/constants.h
diff --git a/mojo/public/js/bindings/core.js b/mojo/public/js/core.js
similarity index 99%
rename from mojo/public/js/bindings/core.js
rename to mojo/public/js/core.js
index 95d49a5..8727833 100644
--- a/mojo/public/js/bindings/core.js
+++ b/mojo/public/js/core.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Module "mojo/public/js/bindings/core"
+// Module "mojo/public/js/core"
 //
 // Note: This file is for documentation purposes only. The code here is not
 // actually executed. The real module is implemented natively in Mojo.
diff --git a/mojo/public/js/bindings/core_unittests.js b/mojo/public/js/core_unittests.js
similarity index 98%
rename from mojo/public/js/bindings/core_unittests.js
rename to mojo/public/js/core_unittests.js
index 5fed10d0..34cd4c0 100644
--- a/mojo/public/js/bindings/core_unittests.js
+++ b/mojo/public/js/core_unittests.js
@@ -4,7 +4,7 @@
 
 define([
     "gin/test/expect",
-    "mojo/public/js/bindings/core",
+    "mojo/public/js/core",
     "gc",
   ], function(expect, core, gc) {
   runWithMessagePipe(testNop);
diff --git a/mojo/public/js/bindings/router.js b/mojo/public/js/router.js
similarity index 82%
rename from mojo/public/js/bindings/router.js
rename to mojo/public/js/router.js
index 3682392..1d9793bf 100644
--- a/mojo/public/js/bindings/router.js
+++ b/mojo/public/js/router.js
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-define("mojo/public/js/bindings/router", [
-  "mojo/public/js/bindings/codec",
-  "mojo/public/js/bindings/connector",
-  "mojo/public/js/bindings/validator",
+define("mojo/public/js/router", [
+  "mojo/public/js/codec",
+  "mojo/public/js/connector",
+  "mojo/public/js/validator",
 ], function(codec, connector, validator) {
 
   function Router(handle, connectorFactory) {
@@ -14,7 +14,7 @@
     this.connector_ = new connectorFactory(handle);
     this.incomingReceiver_ = null;
     this.nextRequestID_ = 0;
-    this.responders_ = {};
+    this.completers_ = new Map();
     this.payloadValidators_ = [];
 
     this.connector_.setIncomingReceiver({
@@ -26,7 +26,7 @@
   }
 
   Router.prototype.close = function() {
-    this.responders_ = {};  // Drop any responders.
+    this.completers_.clear();  // Drop any responders.
     this.connector_.close();
   };
 
@@ -38,7 +38,7 @@
     // TODO(mpcomplete): no way to trasmit errors over a Connection.
   };
 
-  Router.prototype.acceptWithResponder = function(message, responder) {
+  Router.prototype.acceptAndExpectResponse = function(message) {
     // Reserve 0 in case we want it to convey special meaning in the future.
     var requestID = this.nextRequestID_++;
     if (requestID == 0)
@@ -46,13 +46,15 @@
 
     message.setRequestID(requestID);
     var result = this.connector_.accept(message);
+    if (!result)
+      return Promise.reject(Error("Connection error"));
 
-    this.responders_[requestID] = responder;
-
-    // TODO(mpcomplete): accept should return a Promise too, maybe?
-    if (result)
-      return Promise.resolve();
-    return Promise.reject(Error("Connection error"));
+    var completer = {};
+    this.completers_.set(requestID, completer);
+    return new Promise(function(resolve, reject) {
+      completer.resolve = resolve;
+      completer.reject = reject;
+    });
   };
 
   Router.prototype.setIncomingReceiver = function(receiver) {
@@ -92,9 +94,9 @@
     } else if (message.isResponse()) {
       var reader = new codec.MessageReader(message);
       var requestID = reader.requestID;
-      var responder = this.responders_[requestID];
-      delete this.responders_[requestID];
-      responder.accept(message);
+      var completer = this.completers_.get(requestID);
+      this.completers_.delete(requestID);
+      completer.resolve(message);
     } else {
       if (this.incomingReceiver_)
         this.incomingReceiver_.accept(message);
@@ -106,8 +108,9 @@
   }
 
   Router.prototype.handleConnectionError_ = function(result) {
-    for (var each in this.responders_)
-      this.responders_[each].reject(result);
+    this.completers_.forEach(function(value) {
+      value.reject(result);
+    });
     this.close();
   };
 
diff --git a/mojo/public/js/bindings/struct_unittests.js b/mojo/public/js/struct_unittests.js
similarity index 98%
rename from mojo/public/js/bindings/struct_unittests.js
rename to mojo/public/js/struct_unittests.js
index b32b63a..092d239 100644
--- a/mojo/public/js/bindings/struct_unittests.js
+++ b/mojo/public/js/struct_unittests.js
@@ -6,8 +6,8 @@
     "gin/test/expect",
     "mojo/public/interfaces/bindings/tests/rect.mojom",
     "mojo/public/interfaces/bindings/tests/test_structs.mojom",
-    "mojo/public/js/bindings/codec",
-    "mojo/public/js/bindings/validator",
+    "mojo/public/js/codec",
+    "mojo/public/js/validator",
 ], function(expect,
             rect,
             testStructs,
diff --git a/mojo/public/js/bindings/support.js b/mojo/public/js/support.js
similarity index 95%
rename from mojo/public/js/bindings/support.js
rename to mojo/public/js/support.js
index 58df6bd..025da6c6 100644
--- a/mojo/public/js/bindings/support.js
+++ b/mojo/public/js/support.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Module "mojo/public/js/bindings/support"
+// Module "mojo/public/js/support"
 //
 // Note: This file is for documentation purposes only. The code here is not
 // actually executed. The real module is implemented natively in Mojo.
diff --git a/mojo/public/js/bindings/tests/validation_test_input_parser.js b/mojo/public/js/test/validation_test_input_parser.js
similarity index 99%
rename from mojo/public/js/bindings/tests/validation_test_input_parser.js
rename to mojo/public/js/test/validation_test_input_parser.js
index 98b1c19..f5a57f91 100644
--- a/mojo/public/js/bindings/tests/validation_test_input_parser.js
+++ b/mojo/public/js/test/validation_test_input_parser.js
@@ -7,7 +7,7 @@
 // mojo/public/cpp/bindings/tests/validation_test_input_parser.h
 
 define([
-    "mojo/public/js/bindings/buffer"
+    "mojo/public/js/buffer"
   ], function(buffer) {
 
   // Files and Lines represent the raw text from an input string
diff --git a/mojo/public/js/bindings/unicode.js b/mojo/public/js/unicode.js
similarity index 96%
rename from mojo/public/js/bindings/unicode.js
rename to mojo/public/js/unicode.js
index ba0f00f9..be2ba0e 100644
--- a/mojo/public/js/bindings/unicode.js
+++ b/mojo/public/js/unicode.js
@@ -7,7 +7,7 @@
  * stored in ArrayBuffers. There is much room for optimization in this code if
  * it proves necessary.
  */
-define("mojo/public/js/bindings/unicode", function() {
+define("mojo/public/js/unicode", function() {
   /**
    * Decodes the UTF8 string from the given buffer.
    * @param {ArrayBufferView} buffer The buffer containing UTF8 string data.
diff --git a/mojo/public/js/bindings/validation_unittests.js b/mojo/public/js/validation_unittests.js
similarity index 96%
rename from mojo/public/js/bindings/validation_unittests.js
rename to mojo/public/js/validation_unittests.js
index 59aeb59..05988f3 100644
--- a/mojo/public/js/bindings/validation_unittests.js
+++ b/mojo/public/js/validation_unittests.js
@@ -7,14 +7,14 @@
     "file",
     "gin/test/expect",
     "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom",
-    "mojo/public/js/bindings/buffer",
-    "mojo/public/js/bindings/codec",
-    "mojo/public/js/bindings/connection",
-    "mojo/public/js/bindings/connector",
-    "mojo/public/js/bindings/core",
-    "mojo/public/js/bindings/tests/validation_test_input_parser",
-    "mojo/public/js/bindings/router",
-    "mojo/public/js/bindings/validator",
+    "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,
diff --git a/mojo/public/js/bindings/validator.js b/mojo/public/js/validator.js
similarity index 99%
rename from mojo/public/js/bindings/validator.js
rename to mojo/public/js/validator.js
index 26759fd..a6be692 100644
--- a/mojo/public/js/bindings/validator.js
+++ b/mojo/public/js/validator.js
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-define("mojo/public/js/bindings/validator", [
-  "mojo/public/js/bindings/codec",
+define("mojo/public/js/validator", [
+  "mojo/public/js/codec",
 ], function(codec) {
 
   var validationError = {
diff --git a/mojo/public/mojo.gni b/mojo/public/mojo.gni
index 5ff2989..5cff440f 100644
--- a/mojo/public/mojo.gni
+++ b/mojo/public/mojo.gni
@@ -6,4 +6,7 @@
   # Whether to use a prebuilt mojo_shell binary instead of one built from
   # source.
   use_prebuilt_mojo_shell = false
+
+  # Whether to build the dart bindings.
+  mojo_use_dart = false
 }
diff --git a/mojo/public/mojo_public.gyp b/mojo/public/mojo_public.gyp
index 92fa394..46f22f7 100644
--- a/mojo/public/mojo_public.gyp
+++ b/mojo/public/mojo_public.gyp
@@ -130,8 +130,8 @@
         '../..'
       ],
       'sources': [
-        'js/bindings/constants.cc',
-        'js/bindings/constants.h',
+        'js/constants.cc',
+        'js/constants.h',
       ],
     },
     {
@@ -141,7 +141,9 @@
       'sources': [
         'c/environment/async_waiter.h',
         'c/environment/logger.h',
+        'cpp/environment/async_waiter.h',
         'cpp/environment/environment.h',
+        'cpp/environment/lib/async_waiter.cc',
         'cpp/environment/lib/default_async_waiter.cc',
         'cpp/environment/lib/default_async_waiter.h',
         'cpp/environment/lib/default_logger.cc',
diff --git a/mojo/public/python/mojo/bindings/descriptor.py b/mojo/public/python/mojo/bindings/descriptor.py
index 44c0f39..f566d47 100644
--- a/mojo/public/python/mojo/bindings/descriptor.py
+++ b/mojo/public/python/mojo/bindings/descriptor.py
@@ -210,13 +210,41 @@
     return unicode(string_array.tostring(), 'utf8')
 
 
-class HandleType(SerializableType):
+class BaseHandleType(SerializableType):
   """Type object for handles."""
 
   def __init__(self, nullable=False):
     SerializableType.__init__(self, 'i')
     self.nullable = nullable
 
+  def Serialize(self, value, data_offset, data, handle_offset):
+    handle = self.ToHandle(value)
+    if not handle.IsValid() and not self.nullable:
+      raise serialization.SerializationException(
+          'Trying to serialize null for non nullable type.')
+    if not handle.IsValid():
+      return (-1, [])
+    return (handle_offset, [handle])
+
+  def Deserialize(self, value, data, handles):
+    if value == -1:
+      if not self.nullable:
+        raise serialization.DeserializationException(
+            'Trying to deserialize null for non nullable type.')
+      return self.FromHandle(mojo.system.Handle())
+    # TODO(qsr) validate handle order
+    return self.FromHandle(handles[value])
+
+  def FromHandle(self, handle):
+    raise NotImplementedError()
+
+  def ToHandle(self, value):
+    raise NotImplementedError()
+
+
+class HandleType(BaseHandleType):
+  """Type object for handles."""
+
   def Convert(self, value):
     if value is None:
       return mojo.system.Handle()
@@ -224,22 +252,62 @@
       raise TypeError('%r is not a handle' % value)
     return value
 
-  def Serialize(self, value, data_offset, data, handle_offset):
-    if not value.IsValid() and not self.nullable:
-      raise serialization.SerializationException(
-          'Trying to serialize null for non nullable type.')
-    if not value.IsValid():
-      return (-1, [])
-    return (handle_offset, [value])
+  def FromHandle(self, handle):
+    return handle
 
-  def Deserialize(self, value, data, handles):
-    if value == -1:
-      if not self.nullable:
-        raise serialization.DeserializationException(
-            'Trying to deserialize null for non nullable type.')
+  def ToHandle(self, value):
+    return value
+
+
+class InterfaceRequestType(BaseHandleType):
+  """Type object for interface requests."""
+
+  def Convert(self, value):
+    if value is None:
+      return reflection.InterfaceRequest(mojo.system.Handle())
+    if not isinstance(value, reflection.InterfaceRequest):
+      raise TypeError('%r is not an interface request' % value)
+    return value
+
+  def FromHandle(self, handle):
+    return reflection.InterfaceRequest(handle)
+
+  def ToHandle(self, value):
+    return value.PassMessagePipe()
+
+
+class InterfaceType(BaseHandleType):
+  """Type object for interfaces."""
+
+  def __init__(self, interface_getter, nullable=False):
+    BaseHandleType.__init__(self, nullable)
+    self._interface_getter = interface_getter
+    self._interface = None
+
+  def Convert(self, value):
+    if value is None or isinstance(value, self.interface):
+      return value
+    raise TypeError('%r is not an instance of ' % self.interface)
+
+  @property
+  def interface(self):
+    if not self._interface:
+      self._interface = self._interface_getter()
+    return self._interface
+
+  def FromHandle(self, handle):
+    if handle.IsValid():
+      return self.interface.manager.Proxy(handle)
+    return None
+
+  def ToHandle(self, value):
+    if not value:
       return mojo.system.Handle()
-    # TODO(qsr) validate handle order
-    return handles[value]
+    if isinstance(value, reflection.InterfaceProxy):
+      return value.manager.PassMessagePipe()
+    pipe = mojo.system.MessagePipe()
+    self.interface.manager.Bind(value, pipe.handle0)
+    return pipe.handle1
 
 
 class BaseArrayType(PointerType):
@@ -460,24 +528,6 @@
       return GenericArrayType(t)
 
 
-class NoneType(SerializableType):
-  """Placeholder type, used temporarily until all mojo types are handled."""
-
-  def __init__(self):
-    SerializableType.__init__(self, 'B')
-
-  def Convert(self, value):
-    return None
-
-  def Serialize(self, value, data_offset, data, handle_offset):
-    return (0, [])
-
-  def Deserialize(self, value, data, handles):
-    return None
-
-
-TYPE_NONE = NoneType()
-
 TYPE_BOOL = BooleanType()
 
 TYPE_INT8 = IntegerType('b')
@@ -499,6 +549,9 @@
 TYPE_HANDLE = HandleType()
 TYPE_NULLABLE_HANDLE = HandleType(True)
 
+TYPE_INTERFACE_REQUEST = InterfaceRequestType()
+TYPE_NULLABLE_INTERFACE_REQUEST = InterfaceRequestType(True)
+
 
 class FieldDescriptor(object):
   """Describes a field in a generated struct."""
diff --git a/mojo/public/python/mojo/bindings/messaging.py b/mojo/public/python/mojo/bindings/messaging.py
index fb1263e..f9061fb85 100644
--- a/mojo/public/python/mojo/bindings/messaging.py
+++ b/mojo/public/python/mojo/bindings/messaging.py
@@ -247,6 +247,14 @@
       self._cancellable = None
     self._handle.Close()
 
+  def PassMessagePipe(self):
+    if self._cancellable:
+      self._cancellable()
+      self._cancellable = None
+    result = self._handle
+    self._handle = system.Handle()
+    return result
+
   def _OnAsyncWaiterResult(self, result):
     self._cancellable = None
     if result == system.RESULT_OK:
@@ -319,7 +327,7 @@
     # The message must have a header.
     header = message.header
     assert header.expects_response
-    request_id = self.NextRequestId()
+    request_id = self._NextRequestId()
     header.request_id = request_id
     if not self._connector.Accept(message):
       return False
@@ -329,6 +337,9 @@
   def Close(self):
     self._connector.Close()
 
+  def PassMessagePipe(self):
+    return self._connector.PassMessagePipe()
+
   def _HandleIncomingMessage(self, message):
     header = message.header
     if header.expects_response:
@@ -350,7 +361,7 @@
     # Ok to drop the message
     return False
 
-  def NextRequestId(self):
+  def _NextRequestId(self):
     request_id = self._next_request_id
     while request_id == 0 or request_id in self._responders:
       request_id = (request_id + 1) % (1 << 64)
diff --git a/mojo/public/python/mojo/bindings/reflection.py b/mojo/public/python/mojo/bindings/reflection.py
index 49c911ca..3193f6e3 100644
--- a/mojo/public/python/mojo/bindings/reflection.py
+++ b/mojo/public/python/mojo/bindings/reflection.py
@@ -181,9 +181,9 @@
     methods = [_MethodDescriptor(x) for x in descriptor.get('methods', [])]
     for method in methods:
       dictionary[method.name] = _NotImplemented
-    client_class = descriptor.get('client', None)
+    client_class_getter = descriptor.get('client', None)
 
-    interface_manager = InterfaceManager(name, methods, client_class)
+    interface_manager = InterfaceManager(name, methods, client_class_getter)
     dictionary.update({
         'client': None,
         'manager': None,
@@ -207,6 +207,31 @@
     raise AttributeError, 'can\'t delete attribute'
 
 
+class InterfaceProxy(object):
+  """
+  A proxy allows to access a remote interface through a message pipe.
+  """
+  pass
+
+
+class InterfaceRequest(object):
+  """
+  An interface request allows to send a request for an interface to a remote
+  object and start using it immediately.
+  """
+
+  def __init__(self, handle):
+    self._handle = handle
+
+  def IsPending(self):
+    return self._handle.IsValid()
+
+  def PassMessagePipe(self):
+    result = self._handle
+    self._handle = None
+    return result
+
+
 class InterfaceManager(object):
   """
   Manager for an interface class. The manager contains the operation that allows
@@ -214,17 +239,24 @@
   over a pipe.
   """
 
-  def __init__(self, name, methods, client_class):
+  def __init__(self, name, methods, client_class_getter):
     self.name = name
     self.methods = methods
-    if client_class:
-      self.client_manager = client_class.manager
-    else:
-      self.client_manager = None
     self.interface_class = None
+    self._client_class_getter = client_class_getter
+    self._client_manager = None
+    self._client_manager_computed = False
     self._proxy_class = None
     self._stub_class = None
 
+  @property
+  def client_manager(self):
+    if not self._client_manager_computed:
+      self._client_manager_computed = True
+      if self._client_class_getter:
+        self._client_manager = self._client_class_getter().manager
+    return self._client_manager
+
   def Proxy(self, handle):
     router = messaging.Router(handle)
     error_handler = _ProxyErrorHandler()
@@ -267,7 +299,7 @@
       for method in self.methods:
         dictionary[method.name] = _ProxyMethodCall(method)
       self._proxy_class = type('%sProxy' % self.name,
-                               (self.interface_class,),
+                               (self.interface_class, InterfaceProxy),
                                dictionary)
 
     proxy = self._proxy_class(router, error_handler)
@@ -301,6 +333,9 @@
   def Close(self):
     self.router.Close()
 
+  def PassMessagePipe(self):
+    return self.router.PassMessagePipe()
+
 
 class _MethodDescriptor(object):
   def __init__(self, descriptor):
diff --git a/mojo/public/sky/BUILD.gn b/mojo/public/sky/BUILD.gn
index 23b1492..2d34c93 100644
--- a/mojo/public/sky/BUILD.gn
+++ b/mojo/public/sky/BUILD.gn
@@ -5,13 +5,13 @@
 action_foreach("sky") {
   script = "convert_amd_modules_to_sky.py"
   sources = [
-    "//mojo/public/js/bindings/buffer.js",
-    "//mojo/public/js/bindings/codec.js",
-    "//mojo/public/js/bindings/connection.js",
-    "//mojo/public/js/bindings/connector.js",
-    "//mojo/public/js/bindings/router.js",
-    "//mojo/public/js/bindings/unicode.js",
-    "//mojo/public/js/bindings/validator.js",
+    "//mojo/public/js/buffer.js",
+    "//mojo/public/js/codec.js",
+    "//mojo/public/js/connection.js",
+    "//mojo/public/js/connector.js",
+    "//mojo/public/js/router.js",
+    "//mojo/public/js/unicode.js",
+    "//mojo/public/js/validator.js",
   ]
   outputs = [
     "$target_gen_dir/{{source_name_part}}.sky",
diff --git a/mojo/public/sky/convert_amd_modules_to_sky.py b/mojo/public/sky/convert_amd_modules_to_sky.py
index 1f254384..8712784 100755
--- a/mojo/public/sky/convert_amd_modules_to_sky.py
+++ b/mojo/public/sky/convert_amd_modules_to_sky.py
@@ -42,7 +42,7 @@
     module.imports[i].name = names[i]
 
 def RewritePathNames(path):
-  return path.replace("mojo/public/js/bindings", "mojo/public/sky")
+  return path.replace("mojo/public/js", "mojo/public/sky")
 
 def Parse(amd_module):
   module = Module()
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 afdaf1ae..fdbe8ca 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -230,6 +230,9 @@
 
       params->DecodePointersAndHandles(message->mutable_handles());
       {{alloc_params(method.parameters)|indent(6)}}
+      // A null |sink_| typically means there is a missing call to
+      // InterfacePtr::set_client().
+      assert(sink_);
       sink_->{{method.name}}({{pass_params(method.parameters)}});
       return true;
 {%-     else %}
@@ -259,6 +262,9 @@
               message->request_id(), responder);
       {{interface_macros.declare_callback(method)}} callback(runnable);
       {{alloc_params(method.parameters)|indent(6)}}
+      // A null |sink_| typically means there is a missing call to
+      // InterfacePtr::set_client().
+      assert(sink_);
       sink_->{{method.name}}(
 {%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback);
       return true;
@@ -305,6 +311,9 @@
   }
 {%- endif %}
 
+  // A null |sink_| typically means there is a missing call to
+  // InterfacePtr::set_client().
+  assert(sink_);
   return sink_->Accept(message);
 }
 
@@ -334,6 +343,9 @@
   }
 {%- endif %}
 
+  // A null |sink_| typically means there is a missing call to
+  // InterfacePtr::set_client().
+  assert(sink_);
   return sink_->Accept(message);
 }
 {%- endif -%}
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 1212dae..27c7cf3 100644
--- a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -32,17 +32,14 @@
           codec.kMessageExpectsResponse, 0);
       builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params);
       var message = builder.finish();
-      this.receiver_.acceptWithResponder(message, {
-          accept: function(message) {
-            var reader = new codec.MessageReader(message);
-            var responseParams =
-                reader.decodeStruct({{interface.name}}_{{method.name}}_ResponseParams);
-            resolve(responseParams);
-          },
-          reject: function(result) {
-            reject(Error("Connection error: " + result));
-          },
-      }).catch(reject);
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct({{interface.name}}_{{method.name}}_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
     }.bind(this));
 {%- endif %}
   };
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 e830a84..697eabe3 100644
--- a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 define("{{module.path}}", [
-    "mojo/public/js/bindings/codec",
-    "mojo/public/js/bindings/validator",
+    "mojo/public/js/codec",
+    "mojo/public/js/validator",
 {%- for import in imports %}
     "{{import.module.path}}",
 {%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/mojom_python_generator.py b/mojo/public/tools/bindings/generators/mojom_python_generator.py
index 295c656f4..f93da091 100644
--- a/mojo/public/tools/bindings/generators/mojom_python_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_python_generator.py
@@ -51,22 +51,6 @@
   mojom.DOUBLE: 'd',
 }
 
-_kind_to_typecode = dict(_kind_to_typecode_for_native_array)
-_kind_to_typecode.update({
-  mojom.INT64:                 'q',
-  mojom.UINT64:                'Q',
-  mojom.HANDLE:                'i',
-  mojom.DCPIPE:                'i',
-  mojom.DPPIPE:                'i',
-  mojom.MSGPIPE:               'i',
-  mojom.SHAREDBUFFER:          'i',
-  mojom.NULLABLE_HANDLE:       'i',
-  mojom.NULLABLE_DCPIPE:       'i',
-  mojom.NULLABLE_DPPIPE:       'i',
-  mojom.NULLABLE_MSGPIPE:      'i',
-  mojom.NULLABLE_SHAREDBUFFER: 'i',
-})
-
 
 def NameToComponent(name):
   # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
@@ -137,7 +121,7 @@
   if mojom.IsArrayKind(kind):
     arguments = []
     if kind.kind in _kind_to_typecode_for_native_array:
-      arguments.append('%r' %_kind_to_typecode_for_native_array[kind.kind])
+      arguments.append('%r' % _kind_to_typecode_for_native_array[kind.kind])
     elif kind.kind != mojom.BOOL:
       arguments.append(GetFieldType(kind.kind))
     if mojom.IsNullableKind(kind):
@@ -169,7 +153,19 @@
   if mojom.IsEnumKind(kind):
     return GetFieldType(mojom.INT32)
 
-  return _kind_to_type.get(kind, '_descriptor.TYPE_NONE')
+  if mojom.IsInterfaceKind(kind):
+    arguments = [ 'lambda: %s' % GetFullyQualifiedName(kind) ]
+    if mojom.IsNullableKind(kind):
+      arguments.append('nullable=True')
+    return '_descriptor.InterfaceType(%s)' % ', '.join(arguments)
+
+  if mojom.IsInterfaceRequestKind(kind):
+    arguments = []
+    if mojom.IsNullableKind(kind):
+      arguments.append('nullable=True')
+    return '_descriptor.InterfaceRequestType(%s)' % ', '.join(arguments)
+
+  return _kind_to_type[kind]
 
 def GetFieldDescriptor(packed_field):
   field = packed_field.field
diff --git a/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl b/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl
index e0dd27f..9560c29 100644
--- a/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl
+++ b/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl
@@ -37,7 +37,7 @@
   __metaclass__ = _reflection.MojoInterfaceType
   DESCRIPTOR = {
 {%  if interface.client %}
-    'client': {{interface.qualified_client|fully_qualified_name}},
+    'client': lambda: {{interface.qualified_client|fully_qualified_name}},
 {%  endif %}
     'methods': [
 {%  for method in interface.methods %}
diff --git a/mojo/services/public/cpp/network/udp_socket_wrapper.cc b/mojo/services/public/cpp/network/udp_socket_wrapper.cc
index 3a7f39b5..b594f726 100644
--- a/mojo/services/public/cpp/network/udp_socket_wrapper.cc
+++ b/mojo/services/public/cpp/network/udp_socket_wrapper.cc
@@ -124,7 +124,7 @@
   MOJO_DCHECK(send_requests_.empty());
   current_pending_sends_++;
   socket_->SendTo(dest_addr.Pass(), data.Pass(),
-                  ErrorCallback(static_cast<typename ErrorCallback::Runnable*>(
+                  ErrorCallback(static_cast<ErrorCallback::Runnable*>(
                       new SendCallbackHandler(this, callback))));
 }
 
@@ -157,7 +157,7 @@
   socket_->NegotiateMaxPendingSendRequests(
       requested_max_pending_sends,
       Callback<void(uint32_t)>(
-          static_cast<typename Callback<void(uint32_t)>::Runnable*>(
+          static_cast< Callback<void(uint32_t)>::Runnable*>(
               new NegotiateCallbackHandler(this))));
   socket_->ReceiveMore(max_receive_queue_size_);
 }
@@ -196,7 +196,7 @@
 
   socket_->SendTo(
       request->dest_addr.Pass(), request->data.Pass(),
-      ErrorCallback(static_cast<typename ErrorCallback::Runnable*>(
+      ErrorCallback(static_cast<ErrorCallback::Runnable*>(
           new SendCallbackHandler(this, request->callback))));
 
   delete request;
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
index 79bde29..860beb5 100644
--- a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
@@ -275,6 +275,14 @@
   delegate_->OnEmbed(this, root, exported_services, remote.Pass());
 }
 
+void ViewManagerClientImpl::OnEmbeddedAppDisconnected(Id view_id) {
+  View* view = GetViewById(view_id);
+  if (view) {
+    FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view).observers(),
+                      OnViewEmbeddedAppDisconnected(view));
+  }
+}
+
 void ViewManagerClientImpl::OnViewBoundsChanged(Id view_id,
                                                 RectPtr old_bounds,
                                                 RectPtr new_bounds) {
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
index 001626ed..f40e3a8 100644
--- a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
@@ -92,6 +92,7 @@
                ViewDataPtr root,
                InterfaceRequest<ServiceProvider> parent_services,
                ScopedMessagePipeHandle window_manager_pipe) override;
+  void OnEmbeddedAppDisconnected(Id view_id) override;
   void OnViewBoundsChanged(Id view_id,
                            RectPtr old_bounds,
                            RectPtr new_bounds) override;
diff --git a/mojo/services/public/cpp/view_manager/view_observer.h b/mojo/services/public/cpp/view_manager/view_observer.h
index ec4e79d..480f6601 100644
--- a/mojo/services/public/cpp/view_manager/view_observer.h
+++ b/mojo/services/public/cpp/view_manager/view_observer.h
@@ -68,6 +68,8 @@
                                      const std::vector<uint8_t>* old_data,
                                      const std::vector<uint8_t>* new_data) {}
 
+  virtual void OnViewEmbeddedAppDisconnected(View* view) {}
+
   // Sent when the drawn state changes. This is only sent for the root nodes
   // when embedded.
   virtual void OnViewDrawnChanging(View* view) {}
diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom
index b0f512f4..2a1a17841 100644
--- a/mojo/services/public/interfaces/view_manager/view_manager.mojom
+++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom
@@ -111,9 +111,12 @@
   //
   // A view may only be a root of one connection at a time. Subsequent calls to
   // Embed() for the same view result in the view being removed from the
-  // current connection. The current connection is told this by way of
+  // currently embedded app. The embedded app is told this by way of
   // OnViewDeleted().
   //
+  // The embedder can detect when the embedded app disconnects by way of
+  // OnEmbeddedAppDisconnected().
+  //
   // When a connection embeds an app the connection no longer has priviledges
   // to access or see any of the children of the view. If the view had existing
   // children the children are removed. The one exception is the root
@@ -144,6 +147,9 @@
           ServiceProvider&? parent_service_provider,
           handle<message_pipe> window_manager_pipe);
 
+  // Invoked when the application embedded at |view| is disconnected.
+  OnEmbeddedAppDisconnected(uint32 view);
+
   // Invoked when a view's bounds have changed.
   OnViewBoundsChanged(uint32 view,
                       mojo.Rect old_bounds,
diff --git a/ui/keyboard/BUILD.gn b/ui/keyboard/BUILD.gn
index 2cfa1e9..2217b691 100644
--- a/ui/keyboard/BUILD.gn
+++ b/ui/keyboard/BUILD.gn
@@ -40,7 +40,7 @@
     "//base/third_party/dynamic_annotations",
     "//content/public/browser",
     "//content/public/common",
-    "//mojo/bindings/js",
+    "//mojo/edk/js",
     "//mojo/edk/system",
     "//mojo/environment:chromium",
     "//mojo/public/cpp/bindings",
diff --git a/ui/keyboard/resources/keyboard_mojo.js b/ui/keyboard/resources/keyboard_mojo.js
index ea681c7..88e32e3c3 100644
--- a/ui/keyboard/resources/keyboard_mojo.js
+++ b/ui/keyboard/resources/keyboard_mojo.js
@@ -7,7 +7,7 @@
 
 if (!chrome.virtualKeyboardPrivate) {
   define('main', [
-      'mojo/public/js/bindings/connection',
+      'mojo/public/js/connection',
       'ui/keyboard/webui/keyboard.mojom',
       'content/public/renderer/service_provider',
   ], function(connector, keyboard, serviceProvider) {