Move //mojo/shell to //shell

//shell is unambiguous in this repository and shorter.

R=ben@chromium.org

Review URL: https://codereview.chromium.org/775343004
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
new file mode 100644
index 0000000..405b423
--- /dev/null
+++ b/shell/BUILD.gn
@@ -0,0 +1,443 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ui.gni")
+import("//mojo/public/mojo.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# We don't support building in the component build since mojo apps are
+# inherently components.
+assert(!is_component_build)
+
+group("shell") {
+  testonly = true
+
+  deps = [
+    ":mojo_shell",
+    ":tests",
+  ]
+
+  if (!is_win) {
+    deps += [ ":mojo_launcher" ]
+  }
+}
+
+group("tests") {
+  testonly = true
+  deps = [
+    ":external_application_unittests",
+    ":mojo_shell_tests",
+  ]
+}
+
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
+if (!use_prebuilt_mojo_shell) {
+  executable("mojo_shell") {
+    sources = [
+      "desktop/mojo_main.cc",
+    ]
+
+    deps = [
+      ":init",
+      ":lib",
+      "//base",
+      "//build/config/sanitizers:deps",
+      "//mojo/common",
+      "//mojo/environment:chromium",
+    ]
+  }
+}  # !use_prebuilt_mojo_shell
+
+executable("mojo_launcher") {
+  sources = [
+    "launcher_main.cc",
+  ]
+
+  deps = [
+    ":external_application_registrar_bindings",
+    ":external_application_registrar_connection",
+    ":init",
+    ":in_process_dynamic_service_runner",
+    "//base",
+    "//build/config/sanitizers:deps",
+    "//mojo/common",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//url",
+  ]
+}
+
+source_set("init") {
+  sources = [
+    "init.cc",
+    "init.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
+source_set("in_process_dynamic_service_runner") {
+  sources = [
+    "dynamic_service_runner.cc",
+    "dynamic_service_runner.h",
+    "in_process_dynamic_service_runner.cc",
+    "in_process_dynamic_service_runner.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/gles2",
+    "//mojo/public/cpp/system",
+  ]
+
+  # This target has to include the public thunk headers, which generally
+  # shouldn't be included without picking an implementation. We are providing
+  # the implementation but the thunk header target cannot declare that we are
+  # permitted to include it since it's in the public SDK and we are not.
+  # Suppress include checking so we can still check the rest of the targets in
+  # this file.
+  check_includes = false
+}
+
+source_set("lib") {
+  sources = [
+    "app_child_process.cc",
+    "app_child_process.h",
+    "app_child_process_host.cc",
+    "app_child_process_host.h",
+    "child_process.cc",
+    "child_process.h",
+    "child_process_host.cc",
+    "child_process_host.h",
+    "context.cc",
+    "context.h",
+    "data_pipe_peek.cc",
+    "data_pipe_peek.h",
+    "dynamic_application_loader.cc",
+    "dynamic_application_loader.h",
+    "external_application_listener.h",
+    "external_application_listener_posix.cc",
+    "external_application_listener_win.cc",
+    "filename_util.cc",
+    "filename_util.h",
+    "incoming_connection_listener_posix.cc",
+    "incoming_connection_listener_posix.h",
+    "mojo_url_resolver.cc",
+    "mojo_url_resolver.h",
+    "out_of_process_dynamic_service_runner.cc",
+    "out_of_process_dynamic_service_runner.h",
+    "switches.cc",
+    "switches.h",
+    "task_runners.cc",
+    "task_runners.h",
+    "test_child_process.cc",
+    "test_child_process.h",
+    "ui_application_loader_android.cc",
+    "ui_application_loader_android.h",
+  ]
+
+  deps = [
+    ":app_child_process_bindings",
+    ":external_application_registrar_bindings",
+    ":init",
+    ":in_process_dynamic_service_runner",
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//base:base_static",
+    "//mojo/application",
+    "//mojo/application_manager",
+    "//mojo/common",
+    "//mojo/common:tracing_impl",
+    "//mojo/edk/system",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/public/interfaces/network",
+    "//shell/domain_socket",
+    "//mojo/spy",
+    "//services/tracing:bindings",
+    "//url",
+  ]
+
+  if (is_win) {
+    deps -= [ "//shell/domain_socket" ]
+  }
+
+  if (is_android) {
+    sources += [
+      "android/android_handler.h",
+      "android/android_handler.cc",
+      "android/android_handler_loader.h",
+      "android/android_handler_loader.cc",
+      "network_application_loader.cc",
+      "network_application_loader.h",
+    ]
+
+    deps += [
+      ":jni_headers",
+      ":run_android_application_function",
+      "//mojo/application:content_handler",
+      "//mojo/services/network:lib",
+      "//services/gles2",
+      "//services/native_viewport:lib",
+    ]
+  }
+
+  # This target includes some files behind #ifdef OS... guards. Since gn is not
+  # smart enough to understand preprocess includes, it does complains about
+  # these includes when not using the build files for that OS. Suppress checking
+  # so we can enable checking for the rest of the targets in this file.
+  # TODO: Might be better to split the files with OS-specific includes out to a
+  # separate source_set so we can leave checking on for the rest of the target.
+  check_includes = false
+}
+
+if (is_android) {
+  generate_jni("jni_headers") {
+    sources = [
+      "android/apk/src/org/chromium/mojo_shell_apk/AndroidHandler.java",
+      "android/apk/src/org/chromium/mojo_shell_apk/Bootstrap.java",
+      "android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java",
+    ]
+    jni_package = "mojo"
+  }
+
+  android_library("bootstrap_java") {
+    java_files =
+        [ "android/apk/src/org/chromium/mojo_shell_apk/Bootstrap.java" ]
+
+    deps = [
+      "//base:base_java",
+    ]
+
+    dex_path = "$target_out_dir/bootstrap_java.dex.jar"
+  }
+
+  shared_library("bootstrap") {
+    sources = [
+      "android/bootstrap.cc",
+    ]
+    deps = [
+      ":jni_headers",
+      ":lib",
+      ":run_android_application_function",
+      "//base",
+    ]
+  }
+
+  # Shared header between the bootstrap and the main shell .so.
+  source_set("run_android_application_function") {
+    sources = [
+      "android/run_android_application_function.h",
+    ]
+  }
+
+  android_library("java") {
+    java_files = [
+      "android/apk/src/org/chromium/mojo_shell_apk/AndroidHandler.java",
+      "android/apk/src/org/chromium/mojo_shell_apk/FileHelper.java",
+      "android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java",
+      "android/apk/src/org/chromium/mojo_shell_apk/MojoShellActivity.java",
+      "android/apk/src/org/chromium/mojo_shell_apk/MojoShellApplication.java",
+    ]
+
+    deps = [
+      "//base:base_java",
+      "//net/android:net_java",
+    ]
+  }
+
+  android_resources("resources") {
+    resource_dirs = [ "android/apk/res" ]
+    custom_package = "org.chromium.mojo_shell_apk"
+  }
+
+  shared_library("libmojo_shell") {
+    sources = [
+      "android/library_loader.cc",
+      "android/mojo_main.cc",
+      "android/mojo_main.h",
+    ]
+    deps = [
+      ":jni_headers",
+      ":lib",
+      "//mojo/application_manager",
+      "//net",
+      "//services/native_viewport:lib",
+      "//ui/gl",
+    ]
+  }
+
+  mojo_shell_assets_dir = "$root_build_dir/mojo_shell_assets"
+
+  copy_ex("copy_mojo_shell_assets") {
+    clear_dir = true
+    dest = mojo_shell_assets_dir
+    sources = [
+      "$root_out_dir/lib.stripped/libbootstrap.so",
+      "$root_out_dir/obj/mojo/shell/bootstrap_java.dex.jar",
+    ]
+  }
+
+  android_apk("mojo_shell_apk") {
+    apk_name = "MojoShell"
+
+    android_manifest = "android/apk/AndroidManifest.xml"
+
+    native_libs = [ "libmojo_shell.so" ]
+
+    asset_location = mojo_shell_assets_dir
+
+    deps = [
+      ":copy_mojo_shell_assets",
+      ":java",
+      ":libmojo_shell",
+      ":resources",
+      "//services/native_viewport:native_viewport_java",
+    ]
+  }
+}
+
+mojom("app_child_process_bindings") {
+  sources = [
+    "app_child_process.mojom",
+  ]
+}
+
+mojom("external_application_registrar_bindings") {
+  sources = [
+    "external_application_registrar.mojom",
+  ]
+
+  deps = [
+    "//mojo/public/interfaces/application",
+  ]
+}
+
+source_set("external_application_registrar_connection") {
+  sources = [
+    "external_application_registrar_connection.cc",
+    "external_application_registrar_connection.h",
+  ]
+
+  deps = [
+    ":external_application_registrar_bindings",
+    "//base",
+    "//mojo/common",
+    "//mojo/edk/system",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/interfaces/application",
+    "//shell/domain_socket",
+    "//url",
+  ]
+
+  if (is_win) {
+    deps -= [ "//shell/domain_socket" ]
+  }
+}
+
+# GYP version: mojo/mojo.gyp:mojo_shell_tests
+test("mojo_shell_tests") {
+  sources = [
+    "child_process_host_unittest.cc",
+    "data_pipe_peek_unittest.cc",
+    "dynamic_application_loader_unittest.cc",
+    "in_process_dynamic_service_runner_unittest.cc",
+    "mojo_url_resolver_unittest.cc",
+    "shell_test_base.cc",
+    "shell_test_base.h",
+    "shell_test_base_unittest.cc",
+    "shell_test_main.cc",
+  ]
+
+  deps = [
+    ":in_process_dynamic_service_runner",
+    ":lib",
+    "//base",
+    "//base:i18n",
+    "//base/test:test_support",
+    "//testing/gtest",
+    "//net:test_support",
+    "//url",
+    "//mojo/application_manager",
+    "//mojo/common",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings",
+    "//services/test_service:bindings",
+  ]
+
+  datadeps = [
+    "//services/test_service:test_app",
+    "//services/test_service:test_request_tracker_app",
+  ]
+
+  if (is_android) {
+    deps += [
+      # TODO(GYP):
+      #'../testing/android/native_test.gyp:native_test_native_code',
+    ]
+  }
+}
+
+# GYP version: mojo/mojo.gyp:mojo_shell_test_support
+source_set("test_support") {
+  sources = [
+    "shell_test_helper.cc",
+    "shell_test_helper.h",
+  ]
+
+  deps = [
+    ":init",
+    ":lib",
+    "//base",
+    "//mojo/application_manager",
+    "//mojo/edk/system",
+  ]
+}
+
+test("external_application_unittests") {
+  sources = [
+    "incoming_connection_listener_unittest.cc",
+    "external_application_listener_unittest.cc",
+    "external_application_test_main.cc",
+  ]
+
+  deps = [
+    ":lib",
+    ":external_application_registrar_connection",
+    ":external_application_registrar_bindings",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+    "//url",
+    "//mojo/application",
+    "//mojo/application_manager",
+    "//mojo/common",
+    "//mojo/edk/system",
+    "//mojo/environment:chromium",
+    "//shell/domain_socket",
+    "//shell/domain_socket:tests",
+  ]
+
+  if (is_win) {
+    sources -= [
+      "incoming_connection_listener_unittest.cc",
+      "external_application_listener_unittest.cc",
+    ]
+
+    deps -= [
+      ":lib",
+      ":external_application_registrar_connection",
+      ":external_application_registrar_bindings",
+      "//shell/domain_socket",
+      "//shell/domain_socket:tests",
+    ]
+  }
+}
diff --git a/shell/android/android_handler.cc b/shell/android/android_handler.cc
new file mode 100644
index 0000000..073aa01
--- /dev/null
+++ b/shell/android/android_handler.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/android/android_handler.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/scoped_native_library.h"
+#include "jni/AndroidHandler_jni.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "shell/android/run_android_application_function.h"
+#include "shell/dynamic_service_runner.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::GetApplicationContext;
+
+namespace mojo {
+
+namespace {
+// This function loads the application library, sets the application context and
+// thunks and calls into the application MojoMain. To ensure that the thunks are
+// set correctly we keep it in the Mojo shell .so and pass the function pointer
+// to the helper libbootstrap.so.
+void RunAndroidApplication(JNIEnv* env,
+                           jobject j_context,
+                           const base::FilePath& app_path,
+                           jint j_handle) {
+  ScopedMessagePipeHandle handle((mojo::MessagePipeHandle(j_handle)));
+
+  // Load the library, so that we can set the application context there if
+  // needed.
+  base::NativeLibraryLoadError error;
+  base::ScopedNativeLibrary app_library(
+      base::LoadNativeLibrary(app_path, &error));
+  if (!app_library.is_valid()) {
+    LOG(ERROR) << "Failed to load app library (error: " << error.ToString()
+               << ")";
+    return;
+  }
+
+  // Set the application context if needed. Most applications will need to
+  // access the Android ApplicationContext in which they are run. If the
+  // application library exports the InitApplicationContext function, we will
+  // set it there.
+  const char* init_application_context_name = "InitApplicationContext";
+  typedef void (*InitApplicationContextFn)(
+      const base::android::JavaRef<jobject>&);
+  InitApplicationContextFn init_application_context =
+      reinterpret_cast<InitApplicationContextFn>(
+          app_library.GetFunctionPointer(init_application_context_name));
+  if (init_application_context) {
+    base::android::ScopedJavaLocalRef<jobject> scoped_context(env, j_context);
+    init_application_context(scoped_context);
+  }
+
+  // Run the application.
+  base::ScopedNativeLibrary app_library_from_runner(
+      shell::DynamicServiceRunner::LoadAndRunService(app_path, handle.Pass()));
+}
+}  // namespace
+
+void AndroidHandler::RunApplication(ShellPtr shell, URLResponsePtr response) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> j_archive_path =
+      Java_AndroidHandler_getNewTempArchivePath(env, GetApplicationContext());
+  base::FilePath archive_path(
+      ConvertJavaStringToUTF8(env, j_archive_path.obj()));
+
+  mojo::common::BlockingCopyToFile(response->body.Pass(), archive_path);
+  RunAndroidApplicationFn run_android_application_fn = &RunAndroidApplication;
+  Java_AndroidHandler_bootstrap(
+      env, GetApplicationContext(), j_archive_path.obj(),
+      shell.PassMessagePipe().release().value(),
+      reinterpret_cast<jlong>(run_android_application_fn));
+}
+
+void AndroidHandler::Initialize(ApplicationImpl* app) {
+}
+
+bool AndroidHandler::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  connection->AddService(&content_handler_factory_);
+  return true;
+}
+
+bool RegisterAndroidHandlerJni(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace mojo
diff --git a/shell/android/android_handler.h b/shell/android/android_handler.h
new file mode 100644
index 0000000..17107af
--- /dev/null
+++ b/shell/android/android_handler.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_ANDROID_CONTENT_HANDLER_H_
+#define MOJO_SHELL_ANDROID_CONTENT_HANDLER_H_
+
+#include <jni.h>
+
+#include "mojo/application/content_handler_factory.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace mojo {
+
+class AndroidHandler : public ApplicationDelegate,
+                       public ContentHandlerFactory::Delegate {
+ public:
+  AndroidHandler() : content_handler_factory_(this) {}
+  virtual ~AndroidHandler() {}
+
+ private:
+  // ApplicationDelegate:
+  void Initialize(ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(ApplicationConnection* connection) override;
+
+  // ContentHandlerFactory::Delegate:
+  void RunApplication(ShellPtr shell, URLResponsePtr response) override;
+
+  ContentHandlerFactory content_handler_factory_;
+  MOJO_DISALLOW_COPY_AND_ASSIGN(AndroidHandler);
+};
+
+bool RegisterAndroidHandlerJni(JNIEnv* env);
+
+}  // namespace mojo
+
+#endif  // MOJO_SHELL_ANDROID_CONTENT_HANDLER_H_
diff --git a/shell/android/android_handler_loader.cc b/shell/android/android_handler_loader.cc
new file mode 100644
index 0000000..a6eab42
--- /dev/null
+++ b/shell/android/android_handler_loader.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/android/android_handler_loader.h"
+
+namespace mojo {
+namespace shell {
+
+AndroidHandlerLoader::AndroidHandlerLoader() {
+}
+
+AndroidHandlerLoader::~AndroidHandlerLoader() {
+}
+
+void AndroidHandlerLoader::Load(ApplicationManager* manager,
+                                const GURL& url,
+                                ScopedMessagePipeHandle shell_handle,
+                                LoadCallback callback) {
+  DCHECK(shell_handle.is_valid());
+  application_.reset(
+      new ApplicationImpl(&android_handler_, shell_handle.Pass()));
+}
+
+void AndroidHandlerLoader::OnApplicationError(ApplicationManager* manager,
+                                              const GURL& url) {
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/android/android_handler_loader.h b/shell/android/android_handler_loader.h
new file mode 100644
index 0000000..19e7fa6
--- /dev/null
+++ b/shell/android/android_handler_loader.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_ANDROID_ANDROID_HANDLER_LOADER_H_
+#define SHELL_ANDROID_ANDROID_HANDLER_LOADER_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "shell/android/android_handler.h"
+
+namespace mojo {
+namespace shell {
+
+class AndroidHandlerLoader : public ApplicationLoader {
+ public:
+  AndroidHandlerLoader();
+  virtual ~AndroidHandlerLoader();
+
+ private:
+  // ApplicationLoader overrides:
+  void Load(ApplicationManager* manager,
+            const GURL& url,
+            ScopedMessagePipeHandle shell_handle,
+            LoadCallback callback) override;
+  void OnApplicationError(ApplicationManager* manager,
+                          const GURL& url) override;
+
+  AndroidHandler android_handler_;
+  scoped_ptr<ApplicationImpl> application_;
+
+  DISALLOW_COPY_AND_ASSIGN(AndroidHandlerLoader);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_ANDROID_ANDROID_HANDLER_LOADER_H_
diff --git a/shell/android/apk/AndroidManifest.xml b/shell/android/apk/AndroidManifest.xml
new file mode 100644
index 0000000..3c4c3ae
--- /dev/null
+++ b/shell/android/apk/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2013 The Chromium Authors. All rights reserved.
+
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.chromium.mojo_shell_apk">
+
+    <application android:name="MojoShellApplication"
+            android:label="Mojo Shell">
+        <activity android:name="MojoShellActivity"
+                  android:launchMode="singleTask"
+                  android:theme="@android:style/Theme.Holo.Light.NoActionBar"
+                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
+                  android:hardwareAccelerated="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/shell/android/apk/res/layout/mojo_shell_activity.xml b/shell/android/apk/res/layout/mojo_shell_activity.xml
new file mode 100644
index 0000000..d319941
--- /dev/null
+++ b/shell/android/apk/res/layout/mojo_shell_activity.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2013 The Chromium Authors. All rights reserved.
+
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+</LinearLayout>
diff --git a/shell/android/apk/res/values/strings.xml b/shell/android/apk/res/values/strings.xml
new file mode 100644
index 0000000..ff3f8bb
--- /dev/null
+++ b/shell/android/apk/res/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2013 The Chromium Authors. All rights reserved.
+
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+ -->
+
+<resources>
+</resources>
diff --git a/shell/android/apk/src/org/chromium/mojo_shell_apk/AndroidHandler.java b/shell/android/apk/src/org/chromium/mojo_shell_apk/AndroidHandler.java
new file mode 100644
index 0000000..9b7838d
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo_shell_apk/AndroidHandler.java
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo_shell_apk;
+
+import android.content.Context;
+import android.util.Log;
+
+import dalvik.system.DexClassLoader;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+
+/**
+ * Content handler for archives containing native libraries bundled with Java code.
+ * <p>
+ * TODO(ppi): create a seperate instance for each application being bootstrapped to keep track of
+ * the temporary files and clean them up once the execution finishes.
+ */
+@JNINamespace("mojo")
+public class AndroidHandler {
+    private static final String TAG = "AndroidHandler";
+
+    // Bootstrap native and java libraries are packaged with the MojoShell APK as assets.
+    private static final String BOOTSTRAP_JAVA_LIBRARY = "bootstrap_java.dex.jar";
+    private static final String BOOTSTRAP_NATIVE_LIBRARY = "libbootstrap.so";
+    // Name of the bootstrapping runnable shipped in the packaged Java library.
+    private static final String BOOTSTRAP_CLASS = "org.chromium.mojo_shell_apk.Bootstrap";
+
+    // File extensions used to identify application libraries in the provided archive.
+    private static final String JAVA_LIBRARY_SUFFIX = ".dex.jar";
+    private static final String NATIVE_LIBRARY_SUFFIX = ".so";
+    // Filename sections used for naming temporary files holding application files.
+    private static final String ARCHIVE_PREFIX = "archive";
+    private static final String ARCHIVE_SUFFIX = ".zip";
+
+    // Directories used to hold temporary files. These are cleared when clearTemporaryFiles() is
+    // called.
+    private static final String DEX_OUTPUT_DIRECTORY = "dex_output";
+    private static final String APP_DIRECTORY = "applications";
+    private static final String ASSET_DIRECTORY = "assets";
+
+    /**
+     * Deletes directories holding the temporary files. This should be called early on shell startup
+     * to clean up after the previous run.
+     */
+    static void clearTemporaryFiles(Context context) {
+        FileHelper.deleteRecursively(getDexOutputDir(context));
+        FileHelper.deleteRecursively(getAppDir(context));
+        FileHelper.deleteRecursively(getAssetDir(context));
+    }
+
+    /**
+     * Returns the path at which the native part should save the application archive.
+     */
+    @CalledByNative
+    private static String getNewTempArchivePath(Context context) throws IOException {
+        return File.createTempFile(ARCHIVE_PREFIX, ARCHIVE_SUFFIX,
+                getAppDir(context)).getAbsolutePath();
+    }
+
+    /**
+     * Extracts and runs the application libraries contained by the indicated archive.
+     * @param context the application context
+     * @param archivePath the path of the archive containing the application to be run
+     * @param handle handle to the shell to be passed to the native application. On the Java side
+     *               this is opaque payload.
+     * @param runApplicationPtr pointer to the function that will set the native thunks and call
+     *                          into the application MojoMain. On the Java side this is opaque
+     *                          payload.
+     */
+    @CalledByNative
+    private static boolean bootstrap(Context context, String archivePath, int handle,
+            long runApplicationPtr) {
+        File bootstrap_java_library;
+        File bootstrap_native_library;
+        try {
+            bootstrap_java_library = FileHelper.extractFromAssets(context, BOOTSTRAP_JAVA_LIBRARY,
+                    getAssetDir(context));
+            bootstrap_native_library = FileHelper.extractFromAssets(context,
+                    BOOTSTRAP_NATIVE_LIBRARY, getAssetDir(context));
+        } catch (Exception e) {
+            Log.e(TAG, "Extraction of bootstrap files from assets failed.", e);
+            return false;
+        }
+
+        File application_java_library;
+        File application_native_library;
+        try {
+            File archive = new File(archivePath);
+            application_java_library = FileHelper.extractFromArchive(archive, JAVA_LIBRARY_SUFFIX,
+                    getAppDir(context));
+            application_native_library = FileHelper.extractFromArchive(archive,
+                    NATIVE_LIBRARY_SUFFIX, getAppDir(context));
+        } catch (Exception e) {
+            Log.e(TAG, "Extraction of application files from the archive failed.",  e);
+            return false;
+        }
+
+        String dexPath = bootstrap_java_library.getAbsolutePath() + File.pathSeparator
+                + application_java_library.getAbsolutePath();
+        DexClassLoader bootstrapLoader = new DexClassLoader(dexPath,
+                getDexOutputDir(context).getAbsolutePath(), null,
+                ClassLoader.getSystemClassLoader());
+
+        try {
+            Class<?> loadedClass = bootstrapLoader.loadClass(BOOTSTRAP_CLASS);
+            Class<? extends Runnable> bootstrapClass = loadedClass.asSubclass(Runnable.class);
+            Constructor<? extends Runnable> constructor = bootstrapClass.getConstructor(
+                    Context.class, File.class, File.class, Integer.class, Long.class);
+            Runnable bootstrapRunnable = constructor.newInstance(context, bootstrap_native_library,
+                    application_native_library, Integer.valueOf(handle),
+                    Long.valueOf(runApplicationPtr));
+            bootstrapRunnable.run();
+        } catch (Throwable t) {
+            Log.e(TAG, "Running Bootstrap failed.", t);
+            return false;
+        }
+        return true;
+    }
+
+    private static File getDexOutputDir(Context context) {
+        return context.getDir(DEX_OUTPUT_DIRECTORY, Context.MODE_PRIVATE);
+    }
+
+    private static File getAppDir(Context context) {
+        return context.getDir(APP_DIRECTORY, Context.MODE_PRIVATE);
+    }
+
+    private static File getAssetDir(Context context) {
+        return context.getDir(ASSET_DIRECTORY, Context.MODE_PRIVATE);
+    }
+}
diff --git a/shell/android/apk/src/org/chromium/mojo_shell_apk/Bootstrap.java b/shell/android/apk/src/org/chromium/mojo_shell_apk/Bootstrap.java
new file mode 100644
index 0000000..b74f03a
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo_shell_apk/Bootstrap.java
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo_shell_apk;
+
+import android.content.Context;
+
+import org.chromium.base.JNINamespace;
+
+import java.io.File;
+
+/**
+ * Runnable used to bootstrap execution of Android Mojo application. For the JNI to work, we need a
+ * Java class with the application classloader in the call stack. We load this class in the
+ * application classloader and call into native from it to achieve that.
+ */
+@JNINamespace("mojo")
+public class Bootstrap implements Runnable {
+
+    private final Context mContext;
+    private final File mBootstrapNativeLibrary;
+    private final File mApplicationNativeLibrary;
+    private final int mHandle;
+    private final long mRunApplicationPtr;
+
+    public Bootstrap(Context context, File bootstrapNativeLibrary, File applicationNativeLibrary,
+            Integer handle, Long runApplicationPtr) {
+        mContext = context;
+        mBootstrapNativeLibrary = bootstrapNativeLibrary;
+        mApplicationNativeLibrary = applicationNativeLibrary;
+        mHandle = handle;
+        mRunApplicationPtr = runApplicationPtr;
+    }
+
+    @Override
+    public void run() {
+        System.load(mBootstrapNativeLibrary.getAbsolutePath());
+        System.load(mApplicationNativeLibrary.getAbsolutePath());
+        nativeBootstrap(mContext, mApplicationNativeLibrary.getAbsolutePath(), mHandle,
+                mRunApplicationPtr);
+    }
+
+    native void nativeBootstrap(Context context, String libraryPath, int handle,
+            long runApplicationPtr);
+}
diff --git a/shell/android/apk/src/org/chromium/mojo_shell_apk/FileHelper.java b/shell/android/apk/src/org/chromium/mojo_shell_apk/FileHelper.java
new file mode 100644
index 0000000..70f95e1
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo_shell_apk/FileHelper.java
@@ -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.
+
+package org.chromium.mojo_shell_apk;
+
+import android.content.Context;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Helper methods for file extraction from APK assets and zip archives.
+ */
+class FileHelper {
+    // Size of the buffer used in streaming file operations.
+    private static final int BUFFER_SIZE = 1024 * 1024;
+    // Prefix used when naming temporary files.
+    private static final String TEMP_FILE_PREFIX = "temp-";
+
+    static File extractFromAssets(Context context, String assetName, File outputDirectory)
+            throws IOException, FileNotFoundException {
+        // Make the original filename part of the temp file name.
+        // TODO(ppi): do we need to sanitize the suffix?
+        String suffix = "-" + assetName;
+        File outputFile = File.createTempFile(TEMP_FILE_PREFIX, suffix, outputDirectory);
+        BufferedInputStream inputStream = new BufferedInputStream(
+                context.getAssets().open(assetName));
+        writeStreamToFile(inputStream, outputFile);
+        inputStream.close();
+        return outputFile;
+    }
+
+    /**
+     * Extracts the file of the given extension from the archive. Throws FileNotFoundException if no
+     * matching file is found.
+     */
+    static File extractFromArchive(File archive, String suffixToMatch,
+            File outputDirectory) throws IOException, FileNotFoundException {
+        ZipInputStream zip = new ZipInputStream(new BufferedInputStream(new FileInputStream(
+                archive)));
+        ZipEntry entry;
+        while ((entry = zip.getNextEntry()) != null) {
+            if (entry.getName().endsWith(suffixToMatch)) {
+                // Make the original filename part of the temp file name.
+                // TODO(ppi): do we need to sanitize the suffix?
+                String suffix = "-" + new File(entry.getName()).getName();
+                File extractedFile = File.createTempFile(TEMP_FILE_PREFIX, suffix,
+                        outputDirectory);
+                writeStreamToFile(zip, extractedFile);
+                zip.close();
+                return extractedFile;
+            }
+        }
+        zip.close();
+        throw new FileNotFoundException();
+    }
+
+    /**
+     * Deletes a file or directory. Directory will be deleted even if not empty.
+     */
+    static void deleteRecursively(File file) {
+        if (file.isDirectory()) {
+            for (File child : file.listFiles()) {
+                deleteRecursively(child);
+            }
+        }
+        file.delete();
+    }
+
+    private static void writeStreamToFile(InputStream inputStream, File outputFile)
+            throws IOException {
+        byte[] buffer = new byte[BUFFER_SIZE];
+        OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
+        int read;
+        while ((read = inputStream.read(buffer, 0, BUFFER_SIZE)) > 0) {
+            outputStream.write(buffer, 0, read);
+        }
+        outputStream.close();
+    }
+}
diff --git a/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java b/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java
new file mode 100644
index 0000000..2503eb0
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java
@@ -0,0 +1,54 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo_shell_apk;
+
+import android.content.Context;
+
+import org.chromium.base.JNINamespace;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A placeholder class to call native functions.
+ **/
+@JNINamespace("mojo")
+public class MojoMain {
+    /**
+     * A guard flag for calling nativeInit() only once.
+     **/
+    private static boolean sInitialized = false;
+
+    /**
+     * Initializes the native system.
+     **/
+    static void ensureInitialized(Context applicationContext, String[] parameters) {
+        if (sInitialized)
+            return;
+        List<String> parametersList = new ArrayList<String>();
+        // Program name.
+        parametersList.add("mojo_shell");
+        if (parameters != null) {
+            parametersList.addAll(Arrays.asList(parameters));
+        }
+        nativeInit(applicationContext, parametersList.toArray(new String[parametersList.size()]));
+        sInitialized = true;
+    }
+
+    /**
+     * Starts the specified application in the specified context.
+     **/
+    static void start(final String appUrl) {
+        nativeStart(appUrl);
+    }
+
+    /**
+     * Initializes the native system. This API should be called only once per process.
+     **/
+    private static native void nativeInit(Context context, String[] parameters);
+
+    private static native void nativeStart(String appUrl);
+}
diff --git a/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellActivity.java b/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellActivity.java
new file mode 100644
index 0000000..66e8565
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellActivity.java
@@ -0,0 +1,66 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo_shell_apk;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.EditText;
+
+/**
+ * Activity for managing the Mojo Shell.
+ */
+public class MojoShellActivity extends Activity {
+    private static final String TAG = "MojoShellActivity";
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        String appUrl = getUrlFromIntent(getIntent());
+        if (appUrl == null) {
+            Log.i(TAG, "No URL provided via intent, prompting user...");
+            AlertDialog.Builder alert = new AlertDialog.Builder(this);
+            alert.setTitle("Enter a URL");
+            alert.setMessage("Enter a URL");
+            final EditText input = new EditText(this);
+            alert.setView(input);
+            alert.setPositiveButton("Load", new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int button) {
+                    String url = input.getText().toString();
+                    startWithURL(url);
+                }
+            });
+            alert.show();
+        } else {
+            startWithURL(appUrl);
+        }
+    }
+
+    private static String getUrlFromIntent(Intent intent) {
+        return intent != null ? intent.getDataString() : null;
+    }
+
+    private static String[] getParametersFromIntent(Intent intent) {
+        return intent != null ? intent.getStringArrayExtra("parameters") : null;
+    }
+
+    private void startWithURL(String url) {
+        // TODO(ppi): Gotcha - the call below will work only once per process lifetime, but the OS
+        // has no obligation to kill the application process between destroying and restarting the
+        // activity. If the application process is kept alive, initialization parameters sent with
+        // the intent will be stale.
+        // TODO(qsr): We should be passing application context here as required by
+        // InitApplicationContext on the native side. Currently we can't, as PlatformViewportAndroid
+        // relies on this being the activity context.
+        MojoMain.ensureInitialized(this, getParametersFromIntent(getIntent()));
+        MojoMain.start(url);
+        Log.i(TAG, "Mojo started: " + url);
+    }
+}
diff --git a/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellApplication.java b/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellApplication.java
new file mode 100644
index 0000000..c90fd5e
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo_shell_apk/MojoShellApplication.java
@@ -0,0 +1,57 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo_shell_apk;
+
+import android.util.Log;
+
+import org.chromium.base.BaseChromiumApplication;
+import org.chromium.base.PathUtils;
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.library_loader.ProcessInitException;
+
+/**
+ * MojoShell implementation of {@link android.app.Application}, managing application-level global
+ * state and initializations.
+ */
+public class MojoShellApplication extends BaseChromiumApplication {
+    private static final String TAG = "MojoShellApplication";
+    private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "mojo_shell";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        clearTemporaryFiles();
+        initializeJavaUtils();
+        initializeNative();
+    }
+
+    /**
+     * Deletes the temporary files and directories created in the previous run of the application.
+     * This is important regardless of cleanups on exit, as the previous run could have crashed.
+     */
+    private void clearTemporaryFiles() {
+        AndroidHandler.clearTemporaryFiles(this);
+    }
+
+    /**
+     * Initializes Java-side utils.
+     */
+    private void initializeJavaUtils() {
+        PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
+    }
+
+    /**
+     * Loads the native library.
+     */
+    private void initializeNative() {
+        try {
+            LibraryLoader.ensureInitialized();
+        } catch (ProcessInitException e) {
+            Log.e(TAG, "libmojo_shell initialization failed.", e);
+            System.exit(-1);
+            return;
+        }
+    }
+}
diff --git a/shell/android/bootstrap.cc b/shell/android/bootstrap.cc
new file mode 100644
index 0000000..823258c
--- /dev/null
+++ b/shell/android/bootstrap.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "jni/Bootstrap_jni.h"
+#include "shell/android/run_android_application_function.h"
+
+namespace mojo {
+
+void Bootstrap(JNIEnv* env,
+               jobject,
+               jobject j_context,
+               jstring j_native_library_path,
+               jint j_handle,
+               jlong j_run_application_ptr) {
+  base::FilePath app_path(
+      base::android::ConvertJavaStringToUTF8(env, j_native_library_path));
+  RunAndroidApplicationFn run_android_application_fn =
+      reinterpret_cast<RunAndroidApplicationFn>(j_run_application_ptr);
+  run_android_application_fn(env, j_context, app_path, j_handle);
+}
+
+bool RegisterBootstrapJni(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace mojo
+
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  base::android::InitVM(vm);
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  if (!mojo::RegisterBootstrapJni(env))
+    return -1;
+
+  return JNI_VERSION_1_4;
+}
diff --git a/shell/android/library_loader.cc b/shell/android/library_loader.cc
new file mode 100644
index 0000000..8db9250
--- /dev/null
+++ b/shell/android/library_loader.cc
@@ -0,0 +1,51 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/logging.h"
+#include "net/android/net_jni_registrar.h"
+#include "services/native_viewport/platform_viewport_android.h"
+#include "shell/android/android_handler.h"
+#include "shell/android/mojo_main.h"
+
+namespace {
+
+base::android::RegistrationMethod kMojoRegisteredMethods[] = {
+    {"AndroidHandler", mojo::RegisterAndroidHandlerJni},
+    {"MojoMain", mojo::RegisterMojoMain},
+    {"PlatformViewportAndroid", mojo::PlatformViewportAndroid::Register},
+};
+
+bool RegisterMojoJni(JNIEnv* env) {
+  return RegisterNativeMethods(env, kMojoRegisteredMethods,
+                               arraysize(kMojoRegisteredMethods));
+}
+
+}  // namespace
+
+// This is called by the VM when the shared library is first loaded.
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  base::android::InitVM(vm);
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  if (!base::android::RegisterLibraryLoaderEntryHook(env))
+    return -1;
+
+  if (!base::android::RegisterJni(env))
+    return -1;
+
+  if (!net::android::RegisterJni(env))
+    return -1;
+
+  if (!RegisterMojoJni(env))
+    return -1;
+
+  if (!mojo::RegisterAndroidHandlerJni(env))
+    return -1;
+
+  return JNI_VERSION_1_4;
+}
diff --git a/shell/android/mojo_main.cc b/shell/android/mojo_main.cc
new file mode 100644
index 0000000..491b59e
--- /dev/null
+++ b/shell/android/mojo_main.cc
@@ -0,0 +1,97 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/android/mojo_main.h"
+
+#include "base/android/command_line_android.h"
+#include "base/android/java_handler_thread.h"
+#include "base/android/jni_string.h"
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "jni/MojoMain_jni.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/application_manager/application_manager.h"
+#include "shell/context.h"
+#include "shell/init.h"
+#include "ui/gl/gl_surface_egl.h"
+
+using base::LazyInstance;
+
+namespace mojo {
+
+namespace {
+
+LazyInstance<scoped_ptr<base::MessageLoop>> g_java_message_loop =
+    LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<scoped_ptr<shell::Context>> g_context = LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<scoped_ptr<base::android::JavaHandlerThread>> g_shell_thread =
+    LAZY_INSTANCE_INITIALIZER;
+
+void RunShell(std::vector<GURL> app_urls) {
+  shell::Context* context = g_context.Pointer()->get();
+  context->Init();
+  context->set_ui_loop(g_java_message_loop.Get().get());
+  for (std::vector<GURL>::const_iterator it = app_urls.begin();
+       it != app_urls.end(); ++it) {
+    context->Run(*it);
+  }
+}
+
+}  // namespace
+
+static void Init(JNIEnv* env,
+                 jclass clazz,
+                 jobject context,
+                 jobjectArray jparameters) {
+  base::android::ScopedJavaLocalRef<jobject> scoped_context(env, context);
+
+  base::android::InitApplicationContext(env, scoped_context);
+
+  base::android::InitNativeCommandLineFromJavaArray(env, jparameters);
+  mojo::shell::InitializeLogging();
+
+  // We want ~MessageLoop to happen prior to ~Context. Initializing
+  // LazyInstances is akin to stack-allocating objects; their destructors
+  // will be invoked first-in-last-out.
+  shell::Context* shell_context = new shell::Context();
+  g_context.Get().reset(shell_context);
+  g_java_message_loop.Get().reset(new base::MessageLoopForUI);
+  base::MessageLoopForUI::current()->Start();
+
+  // TODO(abarth): At which point should we switch to cross-platform
+  // initialization?
+
+  gfx::GLSurface::InitializeOneOff();
+}
+
+static void Start(JNIEnv* env, jclass clazz, jstring jurl) {
+  std::vector<GURL> app_urls;
+#if defined(MOJO_SHELL_DEBUG_URL)
+  app_urls.push_back(GURL(MOJO_SHELL_DEBUG_URL));
+  // Sleep for 5 seconds to give the debugger a chance to attach.
+  sleep(5);
+#else
+  if (jurl)
+    app_urls.push_back(GURL(base::android::ConvertJavaStringToUTF8(env, jurl)));
+#endif
+
+  g_shell_thread.Get().reset(
+      new base::android::JavaHandlerThread("shell_thread"));
+  g_shell_thread.Get()->Start();
+  g_shell_thread.Get()->message_loop()->PostTask(
+      FROM_HERE, base::Bind(&RunShell, app_urls));
+}
+
+bool RegisterMojoMain(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace mojo
diff --git a/shell/android/mojo_main.h b/shell/android/mojo_main.h
new file mode 100644
index 0000000..65fec20
--- /dev/null
+++ b/shell/android/mojo_main.h
@@ -0,0 +1,16 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_ANDROID_MOJO_MAIN_H_
+#define SHELL_ANDROID_MOJO_MAIN_H_
+
+#include <jni.h>
+
+namespace mojo {
+
+bool RegisterMojoMain(JNIEnv* env);
+
+}  // namespace mojo
+
+#endif  // SHELL_ANDROID_MOJO_MAIN_H_
diff --git a/shell/android/run_android_application_function.h b/shell/android/run_android_application_function.h
new file mode 100644
index 0000000..490d724
--- /dev/null
+++ b/shell/android/run_android_application_function.h
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_ANDROID_RUN_ANDROID_APPLICATION_FUNCTION_H_
+#define SHELL_ANDROID_RUN_ANDROID_APPLICATION_FUNCTION_H_
+
+#include "base/android/jni_android.h"
+#include "base/files/file_path.h"
+
+// Type of the function that we inject from the main .so of the Mojo shell to
+// the helper libbootstrap.so. This function will set the thunks in the
+// application .so and call into application MojoMain. Injecting the function
+// from the main .so ensures that the thunks are set correctly.
+
+namespace mojo {
+typedef void (*RunAndroidApplicationFn)(JNIEnv* env,
+                                        jobject j_context,
+                                        const base::FilePath& app_path,
+                                        jint j_handle);
+}
+
+#endif  // SHELL_ANDROID_RUN_ANDROID_APPLICATION_FUNCTION_H_
diff --git a/shell/app_child_process.cc b/shell/app_child_process.cc
new file mode 100644
index 0000000..3a00843
--- /dev/null
+++ b/shell/app_child_process.cc
@@ -0,0 +1,256 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/app_child_process.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/public/cpp/system/core.h"
+#include "shell/app_child_process.mojom.h"
+#include "shell/dynamic_service_runner.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+// Blocker ---------------------------------------------------------------------
+
+// Blocks a thread until another thread unblocks it, at which point it unblocks
+// and runs a closure provided by that thread.
+class Blocker {
+ public:
+  class Unblocker {
+   public:
+    ~Unblocker() {}
+
+    void Unblock(base::Closure run_after) {
+      DCHECK(blocker_);
+      DCHECK(blocker_->run_after_.is_null());
+      blocker_->run_after_ = run_after;
+      blocker_->event_.Signal();
+      blocker_ = NULL;
+    }
+
+   private:
+    friend class Blocker;
+    Unblocker(Blocker* blocker) : blocker_(blocker) { DCHECK(blocker_); }
+
+    Blocker* blocker_;
+
+    // Copy and assign allowed.
+  };
+
+  Blocker() : event_(true, false) {}
+  ~Blocker() {}
+
+  void Block() {
+    DCHECK(run_after_.is_null());
+    event_.Wait();
+    run_after_.Run();
+  }
+
+  Unblocker GetUnblocker() { return Unblocker(this); }
+
+ private:
+  base::WaitableEvent event_;
+  base::Closure run_after_;
+
+  DISALLOW_COPY_AND_ASSIGN(Blocker);
+};
+
+// AppContext ------------------------------------------------------------------
+
+class AppChildControllerImpl;
+
+static void DestroyController(scoped_ptr<AppChildControllerImpl> controller) {
+}
+
+// Should be created and initialized on the main thread.
+class AppContext {
+ public:
+  AppContext()
+      : io_thread_("io_thread"), controller_thread_("controller_thread") {}
+  ~AppContext() {}
+
+  void Init() {
+    // Initialize Mojo before starting any threads.
+    embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+        new mojo::embedder::SimplePlatformSupport()));
+
+    // Create and start our I/O thread.
+    base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
+    CHECK(io_thread_.StartWithOptions(io_thread_options));
+    io_runner_ = io_thread_.message_loop_proxy().get();
+    CHECK(io_runner_.get());
+
+    // Create and start our controller thread.
+    base::Thread::Options controller_thread_options;
+    controller_thread_options.message_loop_type =
+        base::MessageLoop::TYPE_CUSTOM;
+    controller_thread_options.message_pump_factory =
+        base::Bind(&common::MessagePumpMojo::Create);
+    CHECK(controller_thread_.StartWithOptions(controller_thread_options));
+    controller_runner_ = controller_thread_.message_loop_proxy().get();
+    CHECK(controller_runner_.get());
+  }
+
+  void Shutdown() {
+    controller_runner_->PostTask(
+        FROM_HERE, base::Bind(&DestroyController, base::Passed(&controller_)));
+  }
+
+  base::SingleThreadTaskRunner* io_runner() const { return io_runner_.get(); }
+
+  base::SingleThreadTaskRunner* controller_runner() const {
+    return controller_runner_.get();
+  }
+
+  AppChildControllerImpl* controller() const { return controller_.get(); }
+
+  void set_controller(scoped_ptr<AppChildControllerImpl> controller) {
+    controller_ = controller.Pass();
+  }
+
+ private:
+  // Accessed only on the controller thread.
+  // IMPORTANT: This must be BEFORE |controller_thread_|, so that the controller
+  // thread gets joined (and thus |controller_| reset) before |controller_| is
+  // destroyed.
+  scoped_ptr<AppChildControllerImpl> controller_;
+
+  base::Thread io_thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
+
+  base::Thread controller_thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> controller_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppContext);
+};
+
+// AppChildControllerImpl ------------------------------------------------------
+
+class AppChildControllerImpl : public InterfaceImpl<AppChildController> {
+ public:
+  ~AppChildControllerImpl() override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    // TODO(vtl): Pass in the result from |MainMain()|.
+    client()->AppCompleted(MOJO_RESULT_UNIMPLEMENTED);
+  }
+
+  // To be executed on the controller thread. Creates the |AppChildController|,
+  // etc.
+  static void Init(AppContext* app_context,
+                   embedder::ScopedPlatformHandle platform_channel,
+                   const Blocker::Unblocker& unblocker) {
+    DCHECK(app_context);
+    DCHECK(platform_channel.is_valid());
+
+    DCHECK(!app_context->controller());
+
+    scoped_ptr<AppChildControllerImpl> impl(
+        new AppChildControllerImpl(app_context, unblocker));
+
+    ScopedMessagePipeHandle host_message_pipe(embedder::CreateChannel(
+        platform_channel.Pass(), app_context->io_runner(),
+        base::Bind(&AppChildControllerImpl::DidCreateChannel,
+                   base::Unretained(impl.get())),
+        base::MessageLoopProxy::current()));
+
+    BindToPipe(impl.get(), host_message_pipe.Pass());
+
+    app_context->set_controller(impl.Pass());
+  }
+
+  void OnConnectionError() override {
+    // TODO(darin): How should we handle a connection error here?
+  }
+
+  // |AppChildController| methods:
+  void StartApp(const String& app_path,
+                ScopedMessagePipeHandle service) override {
+    DVLOG(2) << "AppChildControllerImpl::StartApp(" << app_path << ", ...)";
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    unblocker_.Unblock(base::Bind(&AppChildControllerImpl::StartAppOnMainThread,
+                                  base::FilePath::FromUTF8Unsafe(app_path),
+                                  base::Passed(&service)));
+  }
+
+ private:
+  AppChildControllerImpl(AppContext* app_context,
+                         const Blocker::Unblocker& unblocker)
+      : app_context_(app_context), unblocker_(unblocker), channel_info_(NULL) {}
+
+  // Callback for |embedder::CreateChannel()|.
+  void DidCreateChannel(embedder::ChannelInfo* channel_info) {
+    DVLOG(2) << "AppChildControllerImpl::DidCreateChannel()";
+    DCHECK(thread_checker_.CalledOnValidThread());
+    channel_info_ = channel_info;
+  }
+
+  static void StartAppOnMainThread(const base::FilePath& app_path,
+                                   ScopedMessagePipeHandle service) {
+    // TODO(vtl): This is copied from in_process_dynamic_service_runner.cc.
+    DVLOG(2) << "Loading/running Mojo app from " << app_path.value()
+             << " out of process";
+
+    // We intentionally don't unload the native library as its lifetime is the
+    // same as that of the process.
+    DynamicServiceRunner::LoadAndRunService(app_path, service.Pass());
+  }
+
+  base::ThreadChecker thread_checker_;
+  AppContext* const app_context_;
+  Blocker::Unblocker unblocker_;
+
+  embedder::ChannelInfo* channel_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppChildControllerImpl);
+};
+
+}  // namespace
+
+// AppChildProcess -------------------------------------------------------------
+
+AppChildProcess::AppChildProcess() {
+}
+
+AppChildProcess::~AppChildProcess() {
+}
+
+void AppChildProcess::Main() {
+  DVLOG(2) << "AppChildProcess::Main()";
+
+  AppContext app_context;
+  app_context.Init();
+
+  Blocker blocker;
+  app_context.controller_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&AppChildControllerImpl::Init, base::Unretained(&app_context),
+                 base::Passed(platform_channel()), blocker.GetUnblocker()));
+  // This will block, then run whatever the controller wants.
+  blocker.Block();
+
+  app_context.Shutdown();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/app_child_process.h b/shell/app_child_process.h
new file mode 100644
index 0000000..9f0a071
--- /dev/null
+++ b/shell/app_child_process.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_APP_CHILD_PROCESS_H_
+#define SHELL_APP_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "shell/child_process.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |ChildProcess| for a |TYPE_APP| child process, which
+// runs a single app (loaded from the file system) on its main thread.
+class AppChildProcess : public ChildProcess {
+ public:
+  AppChildProcess();
+  ~AppChildProcess() override;
+
+  void Main() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppChildProcess);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_APP_CHILD_PROCESS_H_
diff --git a/shell/app_child_process.mojom b/shell/app_child_process.mojom
new file mode 100644
index 0000000..a0d72ca
--- /dev/null
+++ b/shell/app_child_process.mojom
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.shell;
+
+[Client=AppChildControllerClient]
+interface AppChildController {
+  // TODO(vtl): |service| should be of a more specific type.
+  StartApp(string? app_path, handle<message_pipe>? service);
+};
+
+interface AppChildControllerClient {
+  AppCompleted(int32 result);
+};
diff --git a/shell/app_child_process_host.cc b/shell/app_child_process_host.cc
new file mode 100644
index 0000000..d6cf7f0
--- /dev/null
+++ b/shell/app_child_process_host.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/app_child_process_host.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/public/cpp/system/core.h"
+#include "shell/context.h"
+#include "shell/task_runners.h"
+
+namespace mojo {
+namespace shell {
+
+AppChildProcessHost::AppChildProcessHost(
+    Context* context,
+    AppChildControllerClient* controller_client)
+    : ChildProcessHost(context, this, ChildProcess::TYPE_APP),
+      controller_client_(controller_client),
+      channel_info_(NULL) {
+}
+
+AppChildProcessHost::~AppChildProcessHost() {
+}
+
+void AppChildProcessHost::WillStart() {
+  DCHECK(platform_channel()->is_valid());
+
+  mojo::ScopedMessagePipeHandle handle(embedder::CreateChannel(
+      platform_channel()->Pass(), context()->task_runners()->io_runner(),
+      base::Bind(&AppChildProcessHost::DidCreateChannel,
+                 base::Unretained(this)),
+      base::MessageLoop::current()->message_loop_proxy()));
+
+  controller_.Bind(handle.Pass());
+  controller_.set_client(controller_client_);
+}
+
+void AppChildProcessHost::DidStart(bool success) {
+  DVLOG(2) << "AppChildProcessHost::DidStart()";
+
+  if (!success) {
+    LOG(ERROR) << "Failed to start app child process";
+    controller_client_->AppCompleted(MOJO_RESULT_UNKNOWN);
+    return;
+  }
+}
+
+// Callback for |embedder::CreateChannel()|.
+void AppChildProcessHost::DidCreateChannel(
+    embedder::ChannelInfo* channel_info) {
+  DVLOG(2) << "AppChildProcessHost::DidCreateChannel()";
+
+  CHECK(channel_info);
+  channel_info_ = channel_info;
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/app_child_process_host.h b/shell/app_child_process_host.h
new file mode 100644
index 0000000..4a00d3f
--- /dev/null
+++ b/shell/app_child_process_host.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_APP_CHILD_PROCESS_HOST_H_
+#define SHELL_APP_CHILD_PROCESS_HOST_H_
+
+#include "base/macros.h"
+#include "mojo/edk/embedder/channel_info_forward.h"
+#include "shell/app_child_process.mojom.h"
+#include "shell/child_process_host.h"
+
+namespace mojo {
+namespace shell {
+
+// A subclass of |ChildProcessHost| to host a |TYPE_APP| child process, which
+// runs a single app (loaded from the file system).
+//
+// Note: After |Start()|, this object must remain alive until the controller
+// client's |AppCompleted()| is called.
+class AppChildProcessHost : public ChildProcessHost,
+                            public ChildProcessHost::Delegate {
+ public:
+  AppChildProcessHost(Context* context,
+                      AppChildControllerClient* controller_client);
+  ~AppChildProcessHost() override;
+
+  AppChildController* controller() { return controller_.get(); }
+
+ private:
+  // |ChildProcessHost::Delegate| methods:
+  void WillStart() override;
+  void DidStart(bool success) override;
+
+  // Callback for |embedder::CreateChannel()|.
+  void DidCreateChannel(embedder::ChannelInfo* channel_info);
+
+  AppChildControllerClient* const controller_client_;
+
+  AppChildControllerPtr controller_;
+  embedder::ChannelInfo* channel_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppChildProcessHost);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_APP_CHILD_PROCESS_HOST_H_
diff --git a/shell/child_process.cc b/shell/child_process.cc
new file mode 100644
index 0000000..e53ac0a
--- /dev/null
+++ b/shell/child_process.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/child_process.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "shell/app_child_process.h"
+#include "shell/switches.h"
+#include "shell/test_child_process.h"
+
+namespace mojo {
+namespace shell {
+
+ChildProcess::~ChildProcess() {
+}
+
+// static
+scoped_ptr<ChildProcess> ChildProcess::Create(
+    const base::CommandLine& command_line) {
+  if (!command_line.HasSwitch(switches::kChildProcessType))
+    return scoped_ptr<ChildProcess>();
+
+  int type_as_int;
+  CHECK(base::StringToInt(
+      command_line.GetSwitchValueASCII(switches::kChildProcessType),
+      &type_as_int));
+
+  scoped_ptr<ChildProcess> rv;
+  switch (type_as_int) {
+    case TYPE_TEST:
+      rv.reset(new TestChildProcess());
+      break;
+    case TYPE_APP:
+      rv.reset(new AppChildProcess());
+      break;
+    default:
+      CHECK(false) << "Invalid child process type";
+      break;
+  }
+
+  if (rv) {
+    rv->platform_channel_ =
+        embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
+            command_line);
+    CHECK(rv->platform_channel_.is_valid());
+  }
+
+  return rv.Pass();
+}
+
+ChildProcess::ChildProcess() {
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/child_process.h b/shell/child_process.h
new file mode 100644
index 0000000..2394d5c
--- /dev/null
+++ b/shell/child_process.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_CHILD_PROCESS_H_
+#define SHELL_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace mojo {
+namespace shell {
+
+// A base class for child processes -- i.e., code that is actually run within
+// the child process. (Instances are manufactured by |Create()|.)
+class ChildProcess {
+ public:
+  enum Type {
+    TYPE_TEST,
+    // Hosts a single app (see app_child_process(_host).*).
+    TYPE_APP
+  };
+
+  virtual ~ChildProcess();
+
+  // Returns null if the command line doesn't indicate that this is a child
+  // process. |main()| should call this, and if it returns non-null it should
+  // call |Main()| (without a message loop on the current thread).
+  static scoped_ptr<ChildProcess> Create(const base::CommandLine& command_line);
+
+  // To be implemented by subclasses. This is the "entrypoint" for a child
+  // process. Run with no message loop for the main thread.
+  virtual void Main() = 0;
+
+ protected:
+  ChildProcess();
+
+  embedder::ScopedPlatformHandle* platform_channel() {
+    return &platform_channel_;
+  }
+
+ private:
+  // Available in |Main()| (after a successful |Create()|).
+  embedder::ScopedPlatformHandle platform_channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildProcess);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_CHILD_PROCESS_H_
diff --git a/shell/child_process_host.cc b/shell/child_process_host.cc
new file mode 100644
index 0000000..9a68ec4
--- /dev/null
+++ b/shell/child_process_host.cc
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/child_process_host.h"
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task_runner.h"
+#include "base/task_runner_util.h"
+#include "shell/context.h"
+#include "shell/switches.h"
+
+namespace mojo {
+namespace shell {
+
+ChildProcessHost::ChildProcessHost(Context* context,
+                                   Delegate* delegate,
+                                   ChildProcess::Type type)
+    : context_(context),
+      delegate_(delegate),
+      type_(type),
+      child_process_handle_(base::kNullProcessHandle) {
+  DCHECK(delegate);
+  platform_channel_ = platform_channel_pair_.PassServerHandle();
+  CHECK(platform_channel_.is_valid());
+}
+
+ChildProcessHost::~ChildProcessHost() {
+  if (child_process_handle_ != base::kNullProcessHandle) {
+    LOG(WARNING) << "Destroying ChildProcessHost with unjoined child";
+    base::CloseProcessHandle(child_process_handle_);
+    child_process_handle_ = base::kNullProcessHandle;
+  }
+}
+
+void ChildProcessHost::Start() {
+  DCHECK_EQ(child_process_handle_, base::kNullProcessHandle);
+
+  delegate_->WillStart();
+
+  CHECK(base::PostTaskAndReplyWithResult(
+      context_->task_runners()->blocking_pool(), FROM_HERE,
+      base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)),
+      base::Bind(&ChildProcessHost::DidLaunch, base::Unretained(this))));
+}
+
+int ChildProcessHost::Join() {
+  DCHECK_NE(child_process_handle_, base::kNullProcessHandle);
+  int rv = -1;
+  // Note: |WaitForExitCode()| closes the process handle.
+  LOG_IF(ERROR, !base::WaitForExitCode(child_process_handle_, &rv))
+      << "Failed to wait for child process";
+  child_process_handle_ = base::kNullProcessHandle;
+  return rv;
+}
+
+bool ChildProcessHost::DoLaunch() {
+  static const char* kForwardSwitches[] = {
+      switches::kTraceToConsole, switches::kV, switches::kVModule,
+  };
+
+  const base::CommandLine* parent_command_line =
+      base::CommandLine::ForCurrentProcess();
+  base::CommandLine child_command_line(parent_command_line->GetProgram());
+  child_command_line.CopySwitchesFrom(*parent_command_line, kForwardSwitches,
+                                      arraysize(kForwardSwitches));
+  child_command_line.AppendSwitchASCII(
+      switches::kChildProcessType, base::IntToString(static_cast<int>(type_)));
+
+  embedder::HandlePassingInformation handle_passing_info;
+  platform_channel_pair_.PrepareToPassClientHandleToChildProcess(
+      &child_command_line, &handle_passing_info);
+
+  base::LaunchOptions options;
+#if defined(OS_WIN)
+  options.start_hidden = true;
+  options.handles_to_inherit = &handle_passing_info;
+#elif defined(OS_POSIX)
+  options.fds_to_remap = &handle_passing_info;
+#endif
+
+  if (!base::LaunchProcess(child_command_line, options, &child_process_handle_))
+    return false;
+
+  platform_channel_pair_.ChildProcessLaunched();
+  return true;
+}
+
+void ChildProcessHost::DidLaunch(bool success) {
+  delegate_->DidStart(success);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/child_process_host.h b/shell/child_process_host.h
new file mode 100644
index 0000000..8ed37e6
--- /dev/null
+++ b/shell/child_process_host.h
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_CHILD_PROCESS_HOST_H_
+#define SHELL_CHILD_PROCESS_HOST_H_
+
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "shell/child_process.h"  // For |ChildProcess::Type|.
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+// (Base) class for a "child process host". Handles launching and connecting a
+// platform-specific "pipe" to the child, and supports joining the child
+// process. Intended for use as a base class, but may be used on its own in
+// simple cases.
+//
+// This class is not thread-safe. It should be created/used/destroyed on a
+// single thread.
+//
+// Note: Does not currently work on Windows before Vista.
+class ChildProcessHost {
+ public:
+  class Delegate {
+   public:
+    virtual void WillStart() = 0;
+    virtual void DidStart(bool success) = 0;
+  };
+
+  ChildProcessHost(Context* context,
+                   Delegate* delegate,
+                   ChildProcess::Type type);
+  virtual ~ChildProcessHost();
+
+  // |Start()|s the child process; calls the delegate's |DidStart()| (on the
+  // thread on which |Start()| was called) when the child has been started (or
+  // failed to start). After calling |Start()|, this object must not be
+  // destroyed until |DidStart()| has been called.
+  // TODO(vtl): Consider using weak pointers and removing this requirement.
+  void Start();
+
+  // Waits for the child process to terminate, and returns its exit code.
+  // Note: If |Start()| has been called, this must not be called until the
+  // callback has been called.
+  int Join();
+
+  embedder::ScopedPlatformHandle* platform_channel() {
+    return &platform_channel_;
+  }
+
+ protected:
+  Context* context() const { return context_; }
+
+ private:
+  bool DoLaunch();
+  void DidLaunch(bool success);
+
+  Context* const context_;
+  Delegate* const delegate_;
+  const ChildProcess::Type type_;
+
+  base::ProcessHandle child_process_handle_;
+
+  embedder::PlatformChannelPair platform_channel_pair_;
+
+  // Platform-specific "pipe" to the child process. Valid immediately after
+  // creation.
+  embedder::ScopedPlatformHandle platform_channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildProcessHost);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_CHILD_PROCESS_HOST_H_
diff --git a/shell/child_process_host_unittest.cc b/shell/child_process_host_unittest.cc
new file mode 100644
index 0000000..88aaba7
--- /dev/null
+++ b/shell/child_process_host_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: This file also tests child_process.*.
+
+#include "shell/child_process_host.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "shell/context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace test {
+namespace {
+
+class TestChildProcessHostDelegate : public ChildProcessHost::Delegate {
+ public:
+  TestChildProcessHostDelegate() {}
+  ~TestChildProcessHostDelegate() {}
+  void WillStart() override {
+    VLOG(2) << "TestChildProcessHostDelegate::WillStart()";
+  }
+  void DidStart(bool success) override {
+    VLOG(2) << "TestChildProcessHostDelegate::DidStart(" << success << ")";
+    base::MessageLoop::current()->QuitWhenIdle();
+  }
+};
+
+typedef testing::Test ChildProcessHostTest;
+
+TEST_F(ChildProcessHostTest, Basic) {
+  Context context;
+  base::MessageLoop message_loop(
+      scoped_ptr<base::MessagePump>(new common::MessagePumpMojo()));
+  context.Init();
+  TestChildProcessHostDelegate child_process_host_delegate;
+  ChildProcessHost child_process_host(&context, &child_process_host_delegate,
+                                      ChildProcess::TYPE_TEST);
+  child_process_host.Start();
+  message_loop.Run();
+  int exit_code = child_process_host.Join();
+  VLOG(2) << "Joined child: exit_code = " << exit_code;
+  EXPECT_EQ(0, exit_code);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/context.cc b/shell/context.cc
new file mode 100644
index 0000000..7cb4449
--- /dev/null
+++ b/shell/context.cc
@@ -0,0 +1,316 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/context.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string_split.h"
+#include "build/build_config.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/application_manager/background_shell_application_loader.h"
+#include "mojo/common/tracing_impl.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/spy/spy.h"
+#include "services/tracing/tracing.mojom.h"
+#include "shell/dynamic_application_loader.h"
+#include "shell/external_application_listener.h"
+#include "shell/in_process_dynamic_service_runner.h"
+#include "shell/out_of_process_dynamic_service_runner.h"
+#include "shell/switches.h"
+#include "shell/ui_application_loader_android.h"
+#include "url/gurl.h"
+
+#if defined(OS_ANDROID)
+#include "services/gles2/gpu_impl.h"
+#include "services/native_viewport/native_viewport_impl.h"
+#include "shell/android/android_handler_loader.h"
+#include "shell/network_application_loader.h"
+#endif  // defined(OS_ANDROID)
+
+namespace mojo {
+namespace shell {
+namespace {
+
+// These mojo: URLs are loaded directly from the local filesystem. They
+// correspond to shared libraries bundled alongside the mojo_shell.
+const char* kLocalMojoURLs[] = {
+    "mojo:network_service",
+};
+
+// Used to ensure we only init once.
+class Setup {
+ public:
+  Setup() {
+    embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+        new mojo::embedder::SimplePlatformSupport()));
+  }
+
+  ~Setup() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Setup);
+};
+
+static base::LazyInstance<Setup>::Leaky setup = LAZY_INSTANCE_INITIALIZER;
+
+void InitContentHandlers(DynamicApplicationLoader* loader,
+                         base::CommandLine* command_line) {
+  // Default content handlers.
+  loader->RegisterContentHandler("application/pdf", GURL("mojo:pdf_viewer"));
+  loader->RegisterContentHandler("image/png", GURL("mojo:png_viewer"));
+  loader->RegisterContentHandler("text/html", GURL("mojo:html_viewer"));
+
+  // Command-line-specified content handlers.
+  std::string handlers_spec =
+      command_line->GetSwitchValueASCII(switches::kContentHandlers);
+  if (handlers_spec.empty())
+    return;
+
+  std::vector<std::string> parts;
+  base::SplitString(handlers_spec, ',', &parts);
+  if (parts.size() % 2 != 0) {
+    LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers
+               << ": must be a comma-separated list of mimetype/url pairs.";
+    return;
+  }
+
+  for (size_t i = 0; i < parts.size(); i += 2) {
+    GURL url(parts[i + 1]);
+    if (!url.is_valid()) {
+      LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers
+                 << ": '" << parts[i + 1] << "' is not a valid URL.";
+      return;
+    }
+    loader->RegisterContentHandler(parts[i], url);
+  }
+}
+
+class EmptyServiceProvider : public InterfaceImpl<ServiceProvider> {
+ private:
+  void ConnectToService(const mojo::String& service_name,
+                        ScopedMessagePipeHandle client_handle) override {}
+};
+
+bool ConfigureURLMappings(const std::string& mappings,
+                          mojo::shell::MojoURLResolver* resolver) {
+  base::StringPairs pairs;
+  if (!base::SplitStringIntoKeyValuePairs(mappings, '=', ',', &pairs))
+    return false;
+  using StringPair = std::pair<std::string, std::string>;
+  for (const StringPair& pair : pairs) {
+    const GURL from(pair.first);
+    const GURL to(pair.second);
+    if (!from.is_valid() || !to.is_valid())
+      return false;
+    resolver->AddCustomMapping(from, to);
+  }
+  return true;
+}
+
+}  // namespace
+
+#if defined(OS_ANDROID)
+class Context::NativeViewportApplicationLoader
+    : public ApplicationLoader,
+      public ApplicationDelegate,
+      public InterfaceFactory<NativeViewport>,
+      public InterfaceFactory<Gpu> {
+ public:
+  NativeViewportApplicationLoader() : gpu_state_(new gles2::GpuImpl::State) {}
+  virtual ~NativeViewportApplicationLoader() {}
+
+ private:
+  // ApplicationLoader implementation.
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    ScopedMessagePipeHandle shell_handle,
+                    LoadCallback callback) override {
+    DCHECK(shell_handle.is_valid());
+    app_.reset(new ApplicationImpl(this, shell_handle.Pass()));
+  }
+
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override {}
+
+  // ApplicationDelegate implementation.
+  virtual bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    connection->AddService<NativeViewport>(this);
+    connection->AddService<Gpu>(this);
+    return true;
+  }
+
+  // InterfaceFactory<NativeViewport> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<NativeViewport> request) override {
+    BindToRequest(new NativeViewportImpl(app_.get(), false), &request);
+  }
+
+  // InterfaceFactory<Gpu> implementation.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<Gpu> request) override {
+    new gles2::GpuImpl(request.Pass(), gpu_state_);
+  }
+
+  scoped_refptr<gles2::GpuImpl::State> gpu_state_;
+  scoped_ptr<ApplicationImpl> app_;
+  DISALLOW_COPY_AND_ASSIGN(NativeViewportApplicationLoader);
+};
+#endif
+
+Context::Context() : application_manager_(this) {
+  DCHECK(!base::MessageLoop::current());
+}
+
+Context::~Context() {
+  DCHECK(!base::MessageLoop::current());
+}
+
+void Context::EnsureEmbedderIsInitialized() {
+  setup.Get();
+}
+
+bool Context::Init() {
+  EnsureEmbedderIsInitialized();
+  task_runners_.reset(
+      new TaskRunners(base::MessageLoop::current()->message_loop_proxy()));
+
+  for (size_t i = 0; i < arraysize(kLocalMojoURLs); ++i)
+    mojo_url_resolver_.AddLocalFileMapping(GURL(kLocalMojoURLs[i]));
+
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+  if (command_line->HasSwitch(switches::kEnableExternalApplications)) {
+    listener_ = ExternalApplicationListener::Create(
+        task_runners_->shell_runner(), task_runners_->io_runner());
+
+    base::FilePath socket_path =
+        command_line->GetSwitchValuePath(switches::kEnableExternalApplications);
+    if (socket_path.empty())
+      socket_path = ExternalApplicationListener::ConstructDefaultSocketPath();
+
+    listener_->ListenInBackground(
+        socket_path,
+        base::Bind(&ApplicationManager::RegisterExternalApplication,
+                   base::Unretained(&application_manager_)));
+  }
+  if (command_line->HasSwitch(switches::kOrigin)) {
+    mojo_url_resolver()->SetBaseURL(
+        GURL(command_line->GetSwitchValueASCII(switches::kOrigin)));
+  }
+  if (command_line->HasSwitch(switches::kURLMappings) &&
+      !ConfigureURLMappings(
+          command_line->GetSwitchValueASCII(switches::kURLMappings),
+          mojo_url_resolver())) {
+    return false;
+  }
+
+  scoped_ptr<DynamicServiceRunnerFactory> runner_factory;
+  if (command_line->HasSwitch(switches::kEnableMultiprocess))
+    runner_factory.reset(new OutOfProcessDynamicServiceRunnerFactory());
+  else
+    runner_factory.reset(new InProcessDynamicServiceRunnerFactory());
+
+  DynamicApplicationLoader* dynamic_application_loader =
+      new DynamicApplicationLoader(this, runner_factory.Pass());
+  InitContentHandlers(dynamic_application_loader, command_line);
+  application_manager_.set_default_loader(
+      scoped_ptr<ApplicationLoader>(dynamic_application_loader));
+
+// The native viewport service synchronously waits for certain messages. If we
+// don't run it on its own thread we can easily deadlock. Long term native
+// viewport should run its own process so that this isn't an issue.
+#if defined(OS_ANDROID)
+  application_manager_.SetLoaderForURL(
+      scoped_ptr<ApplicationLoader>(new UIApplicationLoader(
+          scoped_ptr<ApplicationLoader>(new NativeViewportApplicationLoader()),
+          this)),
+      GURL("mojo:native_viewport_service"));
+#endif
+
+  if (command_line->HasSwitch(switches::kSpy)) {
+    spy_.reset(
+        new mojo::Spy(&application_manager_,
+                      command_line->GetSwitchValueASCII(switches::kSpy)));
+    // TODO(cpu): the spy can snoop, but can't tell anybody until
+    // the Spy::WebSocketDelegate is implemented. In the original repo this
+    // was implemented by src\mojo\spy\websocket_server.h and .cc.
+  }
+
+#if defined(OS_ANDROID)
+  // On android, the network service is bundled with the shell because the
+  // network stack depends on the android runtime.
+  {
+    scoped_ptr<BackgroundShellApplicationLoader> loader(
+        new BackgroundShellApplicationLoader(
+            scoped_ptr<ApplicationLoader>(new NetworkApplicationLoader()),
+            "network_service", base::MessageLoop::TYPE_IO));
+    application_manager_.SetLoaderForURL(loader.Pass(),
+                                         GURL("mojo:network_service"));
+  }
+  {
+    scoped_ptr<BackgroundShellApplicationLoader> loader(
+        new BackgroundShellApplicationLoader(
+            scoped_ptr<ApplicationLoader>(new AndroidHandlerLoader()),
+            "android_handler", base::MessageLoop::TYPE_DEFAULT));
+    application_manager_.SetLoaderForURL(loader.Pass(),
+                                         GURL("mojo:android_handler"));
+  }
+#endif
+
+  tracing::TraceDataCollectorPtr trace_data_collector_ptr;
+  application_manager_.ConnectToService(GURL("mojo:tracing"),
+                                        &trace_data_collector_ptr);
+  TracingImpl::Create(trace_data_collector_ptr.Pass());
+
+  if (listener_)
+    listener_->WaitForListening();
+
+  return true;
+}
+
+void Context::OnApplicationError(const GURL& url) {
+  if (app_urls_.find(url) != app_urls_.end()) {
+    app_urls_.erase(url);
+    if (app_urls_.empty() && base::MessageLoop::current()->is_running())
+      base::MessageLoop::current()->Quit();
+  }
+}
+
+GURL Context::ResolveURL(const GURL& url) {
+  return mojo_url_resolver_.Resolve(url);
+}
+
+void Context::Run(const GURL& url) {
+  EmptyServiceProvider* sp = new EmptyServiceProvider;
+  ServiceProviderPtr spp;
+  BindToProxy(sp, &spp);
+
+  app_urls_.insert(url);
+  application_manager_.ConnectToApplication(url, GURL(), spp.Pass());
+}
+
+ScopedMessagePipeHandle Context::ConnectToServiceByName(
+    const GURL& application_url,
+    const std::string& service_name) {
+  app_urls_.insert(application_url);
+  return application_manager_.ConnectToServiceByName(application_url,
+                                                     service_name).Pass();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/context.h b/shell/context.h
new file mode 100644
index 0000000..71ef68b
--- /dev/null
+++ b/shell/context.h
@@ -0,0 +1,74 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_CONTEXT_H_
+#define SHELL_CONTEXT_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "mojo/application_manager/application_manager.h"
+#include "shell/mojo_url_resolver.h"
+#include "shell/task_runners.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/scoped_java_ref.h"
+#endif  // defined(OS_ANDROID)
+
+namespace mojo {
+
+class Spy;
+
+namespace shell {
+
+class DynamicApplicationLoader;
+class ExternalApplicationListener;
+
+// The "global" context for the shell's main process.
+class Context : ApplicationManager::Delegate {
+ public:
+  Context();
+  ~Context() override;
+
+  static void EnsureEmbedderIsInitialized();
+  bool Init();
+
+  void Run(const GURL& url);
+  ScopedMessagePipeHandle ConnectToServiceByName(
+      const GURL& application_url,
+      const std::string& service_name);
+
+  TaskRunners* task_runners() { return task_runners_.get(); }
+  ApplicationManager* application_manager() { return &application_manager_; }
+  MojoURLResolver* mojo_url_resolver() { return &mojo_url_resolver_; }
+
+#if defined(OS_ANDROID)
+  base::MessageLoop* ui_loop() const { return ui_loop_; }
+  void set_ui_loop(base::MessageLoop* ui_loop) { ui_loop_ = ui_loop; }
+#endif  // defined(OS_ANDROID)
+
+ private:
+  class NativeViewportApplicationLoader;
+
+  // ApplicationManager::Delegate override.
+  void OnApplicationError(const GURL& url) override;
+  GURL ResolveURL(const GURL& url) override;
+
+  std::set<GURL> app_urls_;
+  scoped_ptr<TaskRunners> task_runners_;
+  scoped_ptr<ExternalApplicationListener> listener_;
+  ApplicationManager application_manager_;
+  MojoURLResolver mojo_url_resolver_;
+  scoped_ptr<Spy> spy_;
+#if defined(OS_ANDROID)
+  base::MessageLoop* ui_loop_;
+#endif  // defined(OS_ANDROID)
+
+  DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_CONTEXT_H_
diff --git a/shell/data_pipe_peek.cc b/shell/data_pipe_peek.cc
new file mode 100644
index 0000000..82e72a9
--- /dev/null
+++ b/shell/data_pipe_peek.cc
@@ -0,0 +1,155 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/data_pipe_peek.h"
+
+#include "base/bind.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+// Sleep for as long as max_sleep_micros if the deadline hasn't been reached
+// and the number of bytes read is still increasing. Returns true if sleep
+// was actually called.
+//
+// This class is a substitute for being able to wait until N bytes are available
+// from a data pipe. The MaybeSleep method is called when num_bytes_read are
+// available but more are needed by the Peek operation. If a second
+// Peek operation finds the same number of bytes after sleeping we assume
+// that there's no point in trying again.
+// TODO(hansmuller): this heuristic is weak. crbug.com/429377
+class PeekSleeper {
+ public:
+  explicit PeekSleeper(MojoTimeTicks deadline)
+      : deadline_(deadline),
+        kMaxSleepMicros_(1000 * 10),  // 10ms
+        last_number_bytes_read_(0) {}
+
+  bool MaybeSleep(uint32 num_bytes_read) {
+    if (num_bytes_read > 0 && last_number_bytes_read_ >= num_bytes_read)
+      return false;
+    last_number_bytes_read_ = num_bytes_read;
+
+    MojoTimeTicks now(GetTimeTicksNow());
+    if (now > deadline_)
+      return false;
+
+    MojoTimeTicks sleep_time =
+        (deadline_ == 0)
+            ? kMaxSleepMicros_
+            : std::min<int64>(deadline_ - now, PeekSleeper::kMaxSleepMicros_);
+    base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(sleep_time));
+    return true;
+  }
+
+ private:
+  const MojoTimeTicks deadline_;  // 0 => MOJO_DEADLINE_INDEFINITE
+  const MojoTimeTicks kMaxSleepMicros_;
+  uint32 last_number_bytes_read_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(PeekSleeper);
+};
+
+enum PeekStatus { kSuccess, kFail, kKeepReading };
+typedef const base::Callback<PeekStatus(const void*, uint32_t, std::string*)>&
+    PeekFunc;
+
+// When data is available on source, call peek_func and then either return true
+// and value, continue waiting for enough data to satisfy peek_func, or fail
+// and return false. Fail if the timeout is exceeded.
+// This function is not guaranteed to work correctly if applied to a data pipe
+// that's already been read from.
+bool BlockingPeekHelper(DataPipeConsumerHandle source,
+                        std::string* value,
+                        MojoDeadline timeout,
+                        PeekFunc peek_func) {
+  DCHECK(value);
+  value->clear();
+
+  MojoTimeTicks deadline =
+      (timeout == MOJO_DEADLINE_INDEFINITE)
+          ? 0
+          : 1 + GetTimeTicksNow() + static_cast<MojoTimeTicks>(timeout);
+  PeekSleeper sleeper(deadline);
+
+  MojoResult result;
+  do {
+    const void* buffer;
+    uint32_t num_bytes;
+    result =
+        BeginReadDataRaw(source, &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
+
+    if (result == MOJO_RESULT_OK) {
+      PeekStatus status = peek_func.Run(buffer, num_bytes, value);
+      CHECK_EQ(EndReadDataRaw(source, 0), MOJO_RESULT_OK);
+      switch (status) {
+        case PeekStatus::kSuccess:
+          return true;
+        case PeekStatus::kFail:
+          return false;
+        case PeekStatus::kKeepReading:
+          break;
+      }
+      if (!sleeper.MaybeSleep(num_bytes))
+        return false;
+    } else if (result == MOJO_RESULT_SHOULD_WAIT) {
+      MojoTimeTicks now(GetTimeTicksNow());
+      if (timeout == MOJO_DEADLINE_INDEFINITE || now < deadline)
+        result = Wait(source, MOJO_HANDLE_SIGNAL_READABLE, deadline - now);
+    }
+  } while (result == MOJO_RESULT_OK);
+
+  return false;
+}
+
+PeekStatus PeekLine(size_t max_line_length,
+                    const void* buffer,
+                    uint32 buffer_num_bytes,
+                    std::string* line) {
+  const char* p = static_cast<const char*>(buffer);
+  size_t max_p_index = std::min<size_t>(buffer_num_bytes, max_line_length);
+  for (size_t i = 0; i < max_p_index; i++) {
+    if (p[i] == '\n') {
+      *line = std::string(p, i + 1);  // Include the trailing newline.
+      return PeekStatus::kSuccess;
+    }
+  }
+  return (buffer_num_bytes >= max_line_length) ? PeekStatus::kFail
+                                               : PeekStatus::kKeepReading;
+}
+
+PeekStatus PeekNBytes(size_t bytes_length,
+                      const void* buffer,
+                      uint32 buffer_num_bytes,
+                      std::string* bytes) {
+  if (buffer_num_bytes >= bytes_length) {
+    const char* p = static_cast<const char*>(buffer);
+    *bytes = std::string(p, bytes_length);
+    return PeekStatus::kSuccess;
+  }
+  return PeekStatus::kKeepReading;
+}
+
+}  // namespace
+
+bool BlockingPeekNBytes(DataPipeConsumerHandle source,
+                        std::string* bytes,
+                        size_t bytes_length,
+                        MojoDeadline timeout) {
+  PeekFunc peek_nbytes = base::Bind(PeekNBytes, bytes_length);
+  return BlockingPeekHelper(source, bytes, timeout, peek_nbytes);
+}
+
+bool BlockingPeekLine(DataPipeConsumerHandle source,
+                      std::string* line,
+                      size_t max_line_length,
+                      MojoDeadline timeout) {
+  PeekFunc peek_line = base::Bind(PeekLine, max_line_length);
+  return BlockingPeekHelper(source, line, timeout, peek_line);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/data_pipe_peek.h b/shell/data_pipe_peek.h
new file mode 100644
index 0000000..adaabce
--- /dev/null
+++ b/shell/data_pipe_peek.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DATA_PIPE_SEEK_H_
+#define SHELL_DATA_PIPE_SEEK_H_
+
+#include <string>
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace shell {
+
+// The Peek functions are only intended to be used by the
+// DyanmicApplicationLoader class for discovering the type of a
+// URL response. They are a stopgap to be replaced by real support
+// in the DataPipe classes.
+
+// Return true and the first newline terminated line from source. Return false
+// if more than max_line_length bytes are scanned without seeing a newline, or
+// if the timeout is exceeded.
+bool BlockingPeekLine(DataPipeConsumerHandle source,
+                      std::string* line,
+                      size_t max_line_length,
+                      MojoDeadline timeout);
+
+// Return true and the first bytes_length bytes from source. Return false
+// if the timeout is exceeded.
+bool BlockingPeekNBytes(DataPipeConsumerHandle source,
+                        std::string* bytes,
+                        size_t bytes_length,
+                        MojoDeadline timeout);
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DATA_PIPE_SEEK_H_
diff --git a/shell/data_pipe_peek_unittest.cc b/shell/data_pipe_peek_unittest.cc
new file mode 100644
index 0000000..ec96490
--- /dev/null
+++ b/shell/data_pipe_peek_unittest.cc
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/data_pipe_peek.h"
+
+#include "shell/context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+TEST(DataPipePeek, PeekNBytes) {
+  Context::EnsureEmbedderIsInitialized();
+
+  DataPipe data_pipe;
+  DataPipeConsumerHandle consumer(data_pipe.consumer_handle.get());
+  DataPipeProducerHandle producer(data_pipe.producer_handle.get());
+
+  // Inialize the pipe with 4 bytes.
+
+  const char* s4 = "1234";
+  uint32_t num_bytes4 = 4;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WriteDataRaw(producer, s4, &num_bytes4, MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(4u, num_bytes4);
+
+  // We're not consuming data, so peeking for 4 bytes should always succeed.
+
+  std::string bytes;
+  MojoDeadline timeout = 0;
+  EXPECT_TRUE(BlockingPeekNBytes(consumer, &bytes, num_bytes4, timeout));
+  EXPECT_EQ(bytes, std::string(s4));
+
+  timeout = 1000;  // 1ms
+  EXPECT_TRUE(BlockingPeekNBytes(consumer, &bytes, num_bytes4, timeout));
+  EXPECT_EQ(bytes, std::string(s4));
+
+  timeout = MOJO_DEADLINE_INDEFINITE;
+  EXPECT_TRUE(BlockingPeekNBytes(consumer, &bytes, num_bytes4, timeout));
+  EXPECT_EQ(bytes, std::string(s4));
+
+  // Peeking for 5 bytes should fail, until another byte is written.
+
+  uint32_t bytes1 = 1;
+  uint32_t num_bytes5 = 5;
+  const char* s1 = "5";
+  const char* s5 = "12345";
+
+  timeout = 0;
+  EXPECT_FALSE(BlockingPeekNBytes(consumer, &bytes, num_bytes5, timeout));
+
+  timeout = 500;  // Should cause peek to timeout after about 0.5ms.
+  EXPECT_FALSE(BlockingPeekNBytes(consumer, &bytes, num_bytes5, timeout));
+
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WriteDataRaw(producer, s1, &bytes1, MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(1u, bytes1);
+
+  EXPECT_TRUE(BlockingPeekNBytes(consumer, &bytes, num_bytes5, timeout));
+  EXPECT_EQ(bytes, std::string(s5));
+
+  // If the consumer side of the pipe is closed, peek should fail.
+
+  data_pipe.consumer_handle.reset();
+  timeout = 0;
+  EXPECT_FALSE(BlockingPeekNBytes(consumer, &bytes, num_bytes5, timeout));
+}
+
+TEST(DataPipePeek, PeekLine) {
+  Context::EnsureEmbedderIsInitialized();
+
+  DataPipe data_pipe;
+  DataPipeConsumerHandle consumer(data_pipe.consumer_handle.get());
+  DataPipeProducerHandle producer(data_pipe.producer_handle.get());
+
+  // Inialize the pipe with 4 bytes and no newline.
+
+  const char* s4 = "1234";
+  uint32_t num_bytes4 = 4;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WriteDataRaw(producer, s4, &num_bytes4, MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(4u, num_bytes4);
+
+  // Peeking for a line should fail.
+
+  std::string str;
+  size_t max_str_length = 5;
+  MojoDeadline timeout = 0;
+  EXPECT_FALSE(BlockingPeekLine(consumer, &str, max_str_length, timeout));
+
+  // Writing a newline should cause PeekLine to succeed.
+
+  uint32_t bytes1 = 1;
+  const char* s1 = "\n";
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WriteDataRaw(producer, s1, &bytes1, MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(1u, bytes1);
+
+  EXPECT_TRUE(BlockingPeekLine(consumer, &str, max_str_length, timeout));
+  EXPECT_EQ(str, std::string(s4) + "\n");
+
+  // If the max_line_length parameter is less than the length of the
+  // newline terminated string, then peek should fail.
+
+  max_str_length = 3;
+  EXPECT_FALSE(BlockingPeekLine(consumer, &str, max_str_length, timeout));
+}
+
+}  // namespace
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/desktop/mojo_main.cc b/shell/desktop/mojo_main.cc
new file mode 100644
index 0000000..6d3e1ea
--- /dev/null
+++ b/shell/desktop/mojo_main.cc
@@ -0,0 +1,173 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <iostream>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "shell/child_process.h"
+#include "shell/context.h"
+#include "shell/init.h"
+#include "shell/mojo_url_resolver.h"
+#include "shell/switches.h"
+
+namespace {
+
+#if defined(OS_LINUX)
+// Copied from ui/gfx/switches.cc to avoid a dependency on //ui/gfx
+const char kEnableHarfBuzzRenderText[] = "enable-harfbuzz-rendertext";
+#endif
+
+bool IsEmpty(const std::string& s) {
+  return s.empty();
+}
+
+// The value of app_url_and_args is "<mojo_app_url> [<args>...]", where args
+// is a list of "configuration" arguments separated by spaces. If one or more
+// arguments are specified they will be available when the Mojo application
+// is initialized. See ApplicationImpl::args().
+GURL GetAppURLAndSetArgs(const std::string& app_url_and_args,
+                         mojo::shell::Context* context) {
+  // SplitString() returns empty strings for extra delimeter characters (' ').
+  std::vector<std::string> argv;
+  base::SplitString(app_url_and_args, ' ', &argv);
+  argv.erase(std::remove_if(argv.begin(), argv.end(), IsEmpty), argv.end());
+
+  if (argv.empty())
+    return GURL::EmptyGURL();
+  GURL app_url(argv[0]);
+  if (!app_url.is_valid()) {
+    LOG(ERROR) << "Error: invalid URL: " << argv[0];
+    return app_url;
+  }
+  if (argv.size() > 1)
+    context->application_manager()->SetArgsForURL(argv, app_url);
+  return app_url;
+}
+
+void RunApps(mojo::shell::Context* context) {
+  const auto& command_line = *base::CommandLine::ForCurrentProcess();
+  for (const auto& arg : command_line.GetArgs()) {
+    std::string arg2;
+#if defined(OS_WIN)
+    arg2 = base::UTF16ToUTF8(arg);
+#else
+    arg2 = arg;
+#endif
+    GURL url = GetAppURLAndSetArgs(arg2, context);
+    if (!url.is_valid())
+      return;
+    context->Run(GetAppURLAndSetArgs(arg2, context));
+  }
+}
+
+void Usage() {
+  std::cerr << "Launch Mojo applications.\n";
+  std::cerr
+      << "Usage: mojo_shell"
+      << " [--" << switches::kArgsFor << "=<mojo-app>]"
+      << " [--" << switches::kContentHandlers << "=<handlers>]"
+      << " [--" << switches::kEnableExternalApplications << "]"
+      << " [--" << switches::kDisableCache << "]"
+      << " [--" << switches::kEnableMultiprocess << "]"
+      << " [--" << switches::kOrigin << "=<url-lib-path>]"
+      << " [--" << switches::kURLMappings << "=from1=to1,from2=to2]"
+      << " <mojo-app> ...\n\n"
+      << "A <mojo-app> is a Mojo URL or a Mojo URL and arguments within "
+      << "quotes.\n"
+      << "Example: mojo_shell \"mojo:js_standalone test.js\".\n"
+      << "<url-lib-path> is searched for shared libraries named by mojo URLs.\n"
+      << "The value of <handlers> is a comma separated list like:\n"
+      << "text/html,mojo:html_viewer,"
+      << "application/javascript,mojo:js_content_handler\n";
+}
+
+bool IsArgsFor(const std::string& arg, std::string* value) {
+  const std::string kArgsForSwitches[] = {
+      "-" + std::string(switches::kArgsFor),
+      "--" + std::string(switches::kArgsFor),
+  };
+  for (size_t i = 0; i < arraysize(kArgsForSwitches); i++) {
+    std::string argsfor_switch(kArgsForSwitches[i]);
+    if (arg.compare(0, argsfor_switch.size(), argsfor_switch) == 0) {
+      *value = arg.substr(argsfor_switch.size() + 1, std::string::npos);
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  base::AtExitManager at_exit;
+  base::CommandLine::Init(argc, argv);
+
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  const std::set<std::string> all_switches = switches::GetAllSwitches();
+  const base::CommandLine::SwitchMap switches = command_line.GetSwitches();
+  bool found_unknown_switch = false;
+  for (const auto& s : switches) {
+    if (all_switches.find(s.first) == all_switches.end()) {
+      std::cerr << "unknown switch: " << s.first << std::endl;
+      found_unknown_switch = true;
+    }
+  }
+
+  if (found_unknown_switch ||
+      (!command_line.HasSwitch(switches::kEnableExternalApplications) &&
+       (command_line.HasSwitch(switches::kHelp) ||
+        command_line.GetArgs().empty()))) {
+    Usage();
+    return 0;
+  }
+
+#if defined(OS_LINUX)
+  // We use gfx::RenderText from multiple threads concurrently and the pango
+  // backend (currently the default on linux) is not close to threadsafe. Force
+  // use of the harfbuzz backend for now.
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      kEnableHarfBuzzRenderText);
+#endif
+  mojo::shell::InitializeLogging();
+
+  // TODO(vtl): Unify parent and child process cases to the extent possible.
+  if (scoped_ptr<mojo::shell::ChildProcess> child_process =
+          mojo::shell::ChildProcess::Create(
+              *base::CommandLine::ForCurrentProcess())) {
+    child_process->Main();
+  } else {
+    // We want the shell::Context to outlive the MessageLoop so that pipes are
+    // all gracefully closed / error-out before we try to shut the Context down.
+    mojo::shell::Context shell_context;
+    {
+      base::MessageLoop message_loop;
+      if (!shell_context.Init()) {
+        Usage();
+        return 0;
+      }
+
+      // The mojo_shell --args-for command-line switch is handled specially
+      // because it can appear more than once. The base::CommandLine class
+      // collapses multiple occurrences of the same switch.
+      for (int i = 1; i < argc; i++) {
+        std::string args_for_value;
+        if (IsArgsFor(argv[i], &args_for_value))
+          GetAppURLAndSetArgs(args_for_value, &shell_context);
+      }
+
+      message_loop.PostTask(FROM_HERE, base::Bind(RunApps, &shell_context));
+      message_loop.Run();
+    }
+  }
+  return 0;
+}
diff --git a/shell/domain_socket/BUILD.gn b/shell/domain_socket/BUILD.gn
new file mode 100644
index 0000000..601e612
--- /dev/null
+++ b/shell/domain_socket/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("domain_socket") {
+  sources = [
+    "completion_callback.h",
+    "net_errors.cc",
+    "net_errors.h",
+    "net_error_list.h",
+    "socket_descriptor.h",
+    "socket_libevent.cc",
+    "socket_libevent.h",
+    "unix_domain_client_socket_posix.cc",
+    "unix_domain_client_socket_posix.h",
+    "unix_domain_server_socket_posix.cc",
+    "unix_domain_server_socket_posix.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
+source_set("tests") {
+  testonly = true
+
+  sources = [
+    "test_completion_callback.cc",
+    "test_completion_callback.h",
+    "unix_domain_client_socket_posix_unittest.cc",
+    "unix_domain_server_socket_posix_unittest.cc",
+  ]
+
+  deps = [
+    ":domain_socket",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/shell/domain_socket/completion_callback.h b/shell/domain_socket/completion_callback.h
new file mode 100644
index 0000000..453e48d
--- /dev/null
+++ b/shell/domain_socket/completion_callback.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DOMAIN_SOCKET_COMPLETION_CALLBACK_H__
+#define SHELL_DOMAIN_SOCKET_COMPLETION_CALLBACK_H__
+
+#include "base/callback.h"
+#include "base/cancelable_callback.h"
+
+namespace mojo {
+namespace shell {
+
+// A callback specialization that takes a single int parameter. Usually this is
+// used to report a byte count or network error code.
+typedef base::Callback<void(int)> CompletionCallback;
+
+// 64bit version of callback specialization that takes a single int64 parameter.
+// Usually this is used to report a file offset, size or network error code.
+typedef base::Callback<void(int64)> Int64CompletionCallback;
+
+typedef base::CancelableCallback<void(int)> CancelableCompletionCallback;
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DOMAIN_SOCKET_COMPLETION_CALLBACK_H__
diff --git a/shell/domain_socket/net_error_list.h b/shell/domain_socket/net_error_list.h
new file mode 100644
index 0000000..bd6ccb3
--- /dev/null
+++ b/shell/domain_socket/net_error_list.h
@@ -0,0 +1,338 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file intentionally does not have header guards, it's included
+// inside a macro to generate enum values.
+
+// This file contains the list of network errors.
+
+//
+// Ranges:
+//     0- 99 System related errors
+//   100-199 Connection related errors
+
+// An asynchronous IO operation is not yet complete.  This usually does not
+// indicate a fatal error.  Typically this error will be generated as a
+// notification to wait for some external notification that the IO operation
+// finally completed.
+NET_ERROR(IO_PENDING, -1)
+
+// A generic failure occurred.
+NET_ERROR(FAILED, -2)
+
+// An operation was aborted (due to user action).
+NET_ERROR(ABORTED, -3)
+
+// An argument to the function is incorrect.
+NET_ERROR(INVALID_ARGUMENT, -4)
+
+// The handle or file descriptor is invalid.
+NET_ERROR(INVALID_HANDLE, -5)
+
+// The file or directory cannot be found.
+NET_ERROR(FILE_NOT_FOUND, -6)
+
+// An operation timed out.
+NET_ERROR(TIMED_OUT, -7)
+
+// The file is too large.
+NET_ERROR(FILE_TOO_BIG, -8)
+
+// An unexpected error.  This may be caused by a programming mistake or an
+// invalid assumption.
+NET_ERROR(UNEXPECTED, -9)
+
+// Permission to access a resource, other than the network, was denied.
+NET_ERROR(ACCESS_DENIED, -10)
+
+// The operation failed because of unimplemented functionality.
+NET_ERROR(NOT_IMPLEMENTED, -11)
+
+// There were not enough resources to complete the operation.
+NET_ERROR(INSUFFICIENT_RESOURCES, -12)
+
+// Memory allocation failed.
+NET_ERROR(OUT_OF_MEMORY, -13)
+
+// The file upload failed because the file's modification time was different
+// from the expectation.
+NET_ERROR(UPLOAD_FILE_CHANGED, -14)
+
+// The socket is not connected.
+NET_ERROR(SOCKET_NOT_CONNECTED, -15)
+
+// The file already exists.
+NET_ERROR(FILE_EXISTS, -16)
+
+// The path or file name is too long.
+NET_ERROR(FILE_PATH_TOO_LONG, -17)
+
+// Not enough room left on the disk.
+NET_ERROR(FILE_NO_SPACE, -18)
+
+// The file has a virus.
+NET_ERROR(FILE_VIRUS_INFECTED, -19)
+
+// The client chose to block the request.
+NET_ERROR(BLOCKED_BY_CLIENT, -20)
+
+// The network changed.
+NET_ERROR(NETWORK_CHANGED, -21)
+
+// The request was blocked by the URL blacklist configured by the domain
+// administrator.
+NET_ERROR(BLOCKED_BY_ADMINISTRATOR, -22)
+
+// The socket is already connected.
+NET_ERROR(SOCKET_IS_CONNECTED, -23)
+
+// The request was blocked because the forced reenrollment check is still
+// pending. This error can only occur on ChromeOS.
+// The error can be emitted by code in chrome/browser/policy/policy_helpers.cc.
+NET_ERROR(BLOCKED_ENROLLMENT_CHECK_PENDING, -24)
+
+// The upload failed because the upload stream needed to be re-read, due to a
+// retry or a redirect, but the upload stream doesn't support that operation.
+NET_ERROR(UPLOAD_STREAM_REWIND_NOT_SUPPORTED, -25)
+
+// A connection was closed (corresponding to a TCP FIN).
+NET_ERROR(CONNECTION_CLOSED, -100)
+
+// A connection was reset (corresponding to a TCP RST).
+NET_ERROR(CONNECTION_RESET, -101)
+
+// A connection attempt was refused.
+NET_ERROR(CONNECTION_REFUSED, -102)
+
+// A connection timed out as a result of not receiving an ACK for data sent.
+// This can include a FIN packet that did not get ACK'd.
+NET_ERROR(CONNECTION_ABORTED, -103)
+
+// A connection attempt failed.
+NET_ERROR(CONNECTION_FAILED, -104)
+
+// The host name could not be resolved.
+NET_ERROR(NAME_NOT_RESOLVED, -105)
+
+// The Internet connection has been lost.
+NET_ERROR(INTERNET_DISCONNECTED, -106)
+
+// An SSL protocol error occurred.
+NET_ERROR(SSL_PROTOCOL_ERROR, -107)
+
+// The IP address or port number is invalid (e.g., cannot connect to the IP
+// address 0 or the port 0).
+NET_ERROR(ADDRESS_INVALID, -108)
+
+// The IP address is unreachable.  This usually means that there is no route to
+// the specified host or network.
+NET_ERROR(ADDRESS_UNREACHABLE, -109)
+
+// The server requested a client certificate for SSL client authentication.
+NET_ERROR(SSL_CLIENT_AUTH_CERT_NEEDED, -110)
+
+// A tunnel connection through the proxy could not be established.
+NET_ERROR(TUNNEL_CONNECTION_FAILED, -111)
+
+// No SSL protocol versions are enabled.
+NET_ERROR(NO_SSL_VERSIONS_ENABLED, -112)
+
+// The client and server don't support a common SSL protocol version or
+// cipher suite.
+NET_ERROR(SSL_VERSION_OR_CIPHER_MISMATCH, -113)
+
+// The server requested a renegotiation (rehandshake).
+NET_ERROR(SSL_RENEGOTIATION_REQUESTED, -114)
+
+// The proxy requested authentication (for tunnel establishment) with an
+// unsupported method.
+NET_ERROR(PROXY_AUTH_UNSUPPORTED, -115)
+
+// During SSL renegotiation (rehandshake), the server sent a certificate with
+// an error.
+//
+// Note: this error is not in the -2xx range so that it won't be handled as a
+// certificate error.
+NET_ERROR(CERT_ERROR_IN_SSL_RENEGOTIATION, -116)
+
+// The SSL handshake failed because of a bad or missing client certificate.
+NET_ERROR(BAD_SSL_CLIENT_AUTH_CERT, -117)
+
+// A connection attempt timed out.
+NET_ERROR(CONNECTION_TIMED_OUT, -118)
+
+// There are too many pending DNS resolves, so a request in the queue was
+// aborted.
+NET_ERROR(HOST_RESOLVER_QUEUE_TOO_LARGE, -119)
+
+// Failed establishing a connection to the SOCKS proxy server for a target host.
+NET_ERROR(SOCKS_CONNECTION_FAILED, -120)
+
+// The SOCKS proxy server failed establishing connection to the target host
+// because that host is unreachable.
+NET_ERROR(SOCKS_CONNECTION_HOST_UNREACHABLE, -121)
+
+// The request to negotiate an alternate protocol failed.
+NET_ERROR(NPN_NEGOTIATION_FAILED, -122)
+
+// The peer sent an SSL no_renegotiation alert message.
+NET_ERROR(SSL_NO_RENEGOTIATION, -123)
+
+// Winsock sometimes reports more data written than passed.  This is probably
+// due to a broken LSP.
+NET_ERROR(WINSOCK_UNEXPECTED_WRITTEN_BYTES, -124)
+
+// An SSL peer sent us a fatal decompression_failure alert. This typically
+// occurs when a peer selects DEFLATE compression in the mistaken belief that
+// it supports it.
+NET_ERROR(SSL_DECOMPRESSION_FAILURE_ALERT, -125)
+
+// An SSL peer sent us a fatal bad_record_mac alert. This has been observed
+// from servers with buggy DEFLATE support.
+NET_ERROR(SSL_BAD_RECORD_MAC_ALERT, -126)
+
+// The proxy requested authentication (for tunnel establishment).
+NET_ERROR(PROXY_AUTH_REQUESTED, -127)
+
+// A known TLS strict server didn't offer the renegotiation extension.
+NET_ERROR(SSL_UNSAFE_NEGOTIATION, -128)
+
+// The SSL server attempted to use a weak ephemeral Diffie-Hellman key.
+NET_ERROR(SSL_WEAK_SERVER_EPHEMERAL_DH_KEY, -129)
+
+// Could not create a connection to the proxy server. An error occurred
+// either in resolving its name, or in connecting a socket to it.
+// Note that this does NOT include failures during the actual "CONNECT" method
+// of an HTTP proxy.
+NET_ERROR(PROXY_CONNECTION_FAILED, -130)
+
+// A mandatory proxy configuration could not be used. Currently this means
+// that a mandatory PAC script could not be fetched, parsed or executed.
+NET_ERROR(MANDATORY_PROXY_CONFIGURATION_FAILED, -131)
+
+// -132 was formerly ERR_ESET_ANTI_VIRUS_SSL_INTERCEPTION
+
+// We've hit the max socket limit for the socket pool while preconnecting.  We
+// don't bother trying to preconnect more sockets.
+NET_ERROR(PRECONNECT_MAX_SOCKET_LIMIT, -133)
+
+// The permission to use the SSL client certificate's private key was denied.
+NET_ERROR(SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED, -134)
+
+// The SSL client certificate has no private key.
+NET_ERROR(SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY, -135)
+
+// The certificate presented by the HTTPS Proxy was invalid.
+NET_ERROR(PROXY_CERTIFICATE_INVALID, -136)
+
+// An error occurred when trying to do a name resolution (DNS).
+NET_ERROR(NAME_RESOLUTION_FAILED, -137)
+
+// Permission to access the network was denied. This is used to distinguish
+// errors that were most likely caused by a firewall from other access denied
+// errors. See also ERR_ACCESS_DENIED.
+NET_ERROR(NETWORK_ACCESS_DENIED, -138)
+
+// The request throttler module cancelled this request to avoid DDOS.
+NET_ERROR(TEMPORARILY_THROTTLED, -139)
+
+// A request to create an SSL tunnel connection through the HTTPS proxy
+// received a non-200 (OK) and non-407 (Proxy Auth) response.  The response
+// body might include a description of why the request failed.
+NET_ERROR(HTTPS_PROXY_TUNNEL_RESPONSE, -140)
+
+// We were unable to sign the CertificateVerify data of an SSL client auth
+// handshake with the client certificate's private key.
+//
+// Possible causes for this include the user implicitly or explicitly
+// denying access to the private key, the private key may not be valid for
+// signing, the key may be relying on a cached handle which is no longer
+// valid, or the CSP won't allow arbitrary data to be signed.
+NET_ERROR(SSL_CLIENT_AUTH_SIGNATURE_FAILED, -141)
+
+// The message was too large for the transport.  (for example a UDP message
+// which exceeds size threshold).
+NET_ERROR(MSG_TOO_BIG, -142)
+
+// A SPDY session already exists, and should be used instead of this connection.
+NET_ERROR(SPDY_SESSION_ALREADY_EXISTS, -143)
+
+// Error -144 was removed (LIMIT_VIOLATION).
+
+// Websocket protocol error. Indicates that we are terminating the connection
+// due to a malformed frame or other protocol violation.
+NET_ERROR(WS_PROTOCOL_ERROR, -145)
+
+// Connection was aborted for switching to another ptotocol.
+// WebSocket abort SocketStream connection when alternate protocol is found.
+NET_ERROR(PROTOCOL_SWITCHED, -146)
+
+// Returned when attempting to bind an address that is already in use.
+NET_ERROR(ADDRESS_IN_USE, -147)
+
+// An operation failed because the SSL handshake has not completed.
+NET_ERROR(SSL_HANDSHAKE_NOT_COMPLETED, -148)
+
+// SSL peer's public key is invalid.
+NET_ERROR(SSL_BAD_PEER_PUBLIC_KEY, -149)
+
+// The certificate didn't match the built-in public key pins for the host name.
+// The pins are set in net/http/transport_security_state.cc and require that
+// one of a set of public keys exist on the path from the leaf to the root.
+NET_ERROR(SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, -150)
+
+// Server request for client certificate did not contain any types we support.
+NET_ERROR(CLIENT_AUTH_CERT_TYPE_UNSUPPORTED, -151)
+
+// Server requested one type of cert, then requested a different type while the
+// first was still being generated.
+NET_ERROR(ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH, -152)
+
+// An SSL peer sent us a fatal decrypt_error alert. This typically occurs when
+// a peer could not correctly verify a signature (in CertificateVerify or
+// ServerKeyExchange) or validate a Finished message.
+NET_ERROR(SSL_DECRYPT_ERROR_ALERT, -153)
+
+// There are too many pending WebSocketJob instances, so the new job was not
+// pushed to the queue.
+NET_ERROR(WS_THROTTLE_QUEUE_TOO_LARGE, -154)
+
+// There are too many active SocketStream instances, so the new connect request
+// was rejected.
+NET_ERROR(TOO_MANY_SOCKET_STREAMS, -155)
+
+// The SSL server certificate changed in a renegotiation.
+NET_ERROR(SSL_SERVER_CERT_CHANGED, -156)
+
+// The SSL server indicated that an unnecessary TLS version fallback was
+// performed.
+NET_ERROR(SSL_INAPPROPRIATE_FALLBACK, -157)
+
+// Certificate Transparency: All Signed Certificate Timestamps failed to verify.
+NET_ERROR(CT_NO_SCTS_VERIFIED_OK, -158)
+
+// The SSL server sent us a fatal unrecognized_name alert.
+NET_ERROR(SSL_UNRECOGNIZED_NAME_ALERT, -159)
+
+// Failed to set the socket's receive buffer size as requested.
+NET_ERROR(SOCKET_SET_RECEIVE_BUFFER_SIZE_ERROR, -160)
+
+// Failed to set the socket's send buffer size as requested.
+NET_ERROR(SOCKET_SET_SEND_BUFFER_SIZE_ERROR, -161)
+
+// Failed to set the socket's receive buffer size as requested, despite success
+// return code from setsockopt.
+NET_ERROR(SOCKET_RECEIVE_BUFFER_SIZE_UNCHANGEABLE, -162)
+
+// Failed to set the socket's send buffer size as requested, despite success
+// return code from setsockopt.
+NET_ERROR(SOCKET_SEND_BUFFER_SIZE_UNCHANGEABLE, -163)
+
+// Failed to import a client certificate from the platform store into the SSL
+// library.
+NET_ERROR(SSL_CLIENT_AUTH_CERT_BAD_FORMAT, -164)
+
+// The SSL server requires falling back to a version older than the configured
+// minimum fallback version, and thus fallback failed.
+NET_ERROR(SSL_FALLBACK_BEYOND_MINIMUM_VERSION, -165)
diff --git a/shell/domain_socket/net_errors.cc b/shell/domain_socket/net_errors.cc
new file mode 100644
index 0000000..2216579
--- /dev/null
+++ b/shell/domain_socket/net_errors.cc
@@ -0,0 +1,168 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/domain_socket/net_errors.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#if defined(OS_POSIX)
+#include <unistd.h>
+#endif
+#if defined(OS_WIN)
+#include <winsock2.h>
+#define EDQUOT WSAEDQUOT
+#define EHOSTDOWN WSAEHOSTDOWN
+#define EUSERS WSAEUSERS
+#endif
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file.h"
+#include "base/logging.h"
+
+namespace mojo {
+namespace shell {
+namespace net {
+
+std::string ErrorToString(int error) {
+  if (error == 0)
+    return "OK";
+
+  const char* error_string;
+  switch (error) {
+#define NET_ERROR(label, value) \
+  case ERR_##label:             \
+    error_string = #label;      \
+    break;
+#include "shell/domain_socket/net_error_list.h"
+#undef NET_ERROR
+    default:
+      NOTREACHED();
+      error_string = "<unknown>";
+  }
+  return std::string("ERR_") + error_string;
+}
+
+Error FileErrorToNetError(base::File::Error file_error) {
+  switch (file_error) {
+    case base::File::FILE_OK:
+      return net::OK;
+    case base::File::FILE_ERROR_ACCESS_DENIED:
+      return net::ERR_ACCESS_DENIED;
+    case base::File::FILE_ERROR_NOT_FOUND:
+      return net::ERR_FILE_NOT_FOUND;
+    default:
+      return net::ERR_FAILED;
+  }
+}
+
+Error MapSystemError(int os_error) {
+  if (os_error != 0)
+    DVLOG(2) << "Error " << os_error;
+
+  // There are numerous posix error codes, but these are the ones we thus far
+  // find interesting.
+  switch (os_error) {
+    case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+    case EWOULDBLOCK:
+#endif
+      return ERR_IO_PENDING;
+    case EACCES:
+      return ERR_ACCESS_DENIED;
+    case ENETDOWN:
+      return ERR_INTERNET_DISCONNECTED;
+    case ETIMEDOUT:
+      return ERR_TIMED_OUT;
+    case ECONNRESET:
+    case ENETRESET:  // Related to keep-alive.
+    case EPIPE:
+      return ERR_CONNECTION_RESET;
+    case ECONNABORTED:
+      return ERR_CONNECTION_ABORTED;
+    case ECONNREFUSED:
+      return ERR_CONNECTION_REFUSED;
+    case EHOSTUNREACH:
+    case EHOSTDOWN:
+    case ENETUNREACH:
+    case EAFNOSUPPORT:
+      return ERR_ADDRESS_UNREACHABLE;
+    case EADDRNOTAVAIL:
+      return ERR_ADDRESS_INVALID;
+    case EMSGSIZE:
+      return ERR_MSG_TOO_BIG;
+    case ENOTCONN:
+      return ERR_SOCKET_NOT_CONNECTED;
+    case EISCONN:
+      return ERR_SOCKET_IS_CONNECTED;
+    case EINVAL:
+      return ERR_INVALID_ARGUMENT;
+    case EADDRINUSE:
+      return ERR_ADDRESS_IN_USE;
+    case E2BIG:  // Argument list too long.
+      return ERR_INVALID_ARGUMENT;
+    case EBADF:  // Bad file descriptor.
+      return ERR_INVALID_HANDLE;
+    case EBUSY:  // Device or resource busy.
+      return ERR_INSUFFICIENT_RESOURCES;
+    case ECANCELED:  // Operation canceled.
+      return ERR_ABORTED;
+    case EDEADLK:  // Resource deadlock avoided.
+      return ERR_INSUFFICIENT_RESOURCES;
+    case EDQUOT:  // Disk quota exceeded.
+      return ERR_FILE_NO_SPACE;
+    case EEXIST:  // File exists.
+      return ERR_FILE_EXISTS;
+    case EFAULT:  // Bad address.
+      return ERR_INVALID_ARGUMENT;
+    case EFBIG:  // File too large.
+      return ERR_FILE_TOO_BIG;
+    case EISDIR:  // Operation not allowed for a directory.
+      return ERR_ACCESS_DENIED;
+    case ENAMETOOLONG:  // Filename too long.
+      return ERR_FILE_PATH_TOO_LONG;
+    case ENFILE:  // Too many open files in system.
+      return ERR_INSUFFICIENT_RESOURCES;
+    case ENOBUFS:  // No buffer space available.
+      return ERR_OUT_OF_MEMORY;
+    case ENODEV:  // No such device.
+      return ERR_INVALID_ARGUMENT;
+    case ENOENT:  // No such file or directory.
+      return ERR_FILE_NOT_FOUND;
+    case ENOLCK:  // No locks available.
+      return ERR_INSUFFICIENT_RESOURCES;
+    case ENOMEM:  // Not enough space.
+      return ERR_OUT_OF_MEMORY;
+    case ENOSPC:  // No space left on device.
+      return ERR_FILE_NO_SPACE;
+    case ENOSYS:  // Function not implemented.
+      return ERR_NOT_IMPLEMENTED;
+    case ENOTDIR:  // Not a directory.
+      return ERR_FILE_NOT_FOUND;
+    case ENOTSUP:  // Operation not supported.
+      return ERR_NOT_IMPLEMENTED;
+    case EPERM:  // Operation not permitted.
+      return ERR_ACCESS_DENIED;
+    case EROFS:  // Read-only file system.
+      return ERR_ACCESS_DENIED;
+    case ETXTBSY:  // Text file busy.
+      return ERR_ACCESS_DENIED;
+    case EUSERS:  // Too many users.
+      return ERR_INSUFFICIENT_RESOURCES;
+    case EMFILE:  // Too many open files.
+      return ERR_INSUFFICIENT_RESOURCES;
+
+    case 0:
+      return OK;
+    default:
+      LOG(WARNING) << "Unknown error " << os_error
+                   << " mapped to net::ERR_FAILED";
+      return ERR_FAILED;
+  }
+}
+
+}  // namespace net
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/domain_socket/net_errors.h b/shell/domain_socket/net_errors.h
new file mode 100644
index 0000000..997705d
--- /dev/null
+++ b/shell/domain_socket/net_errors.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DOMAIN_SOCKET_NET_ERRORS_H__
+#define SHELL_DOMAIN_SOCKET_NET_ERRORS_H__
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file.h"
+
+namespace mojo {
+namespace shell {
+namespace net {
+
+// Error values are negative.
+enum Error {
+  // No error.
+  OK = 0,
+
+#define NET_ERROR(label, value) ERR_##label = value,
+#include "shell/domain_socket/net_error_list.h"
+#undef NET_ERROR
+
+};
+
+// Returns a textual representation of the error code for logging purposes.
+std::string ErrorToString(int error);
+
+// A convenient function to translate file error to net error code.
+Error FileErrorToNetError(base::File::Error file_error);
+
+// Map system error code to Error.
+Error MapSystemError(int os_error);
+
+}  // namespace net
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DOMAIN_SOCKET_NET_ERRORS_H__
diff --git a/shell/domain_socket/socket_descriptor.h b/shell/domain_socket/socket_descriptor.h
new file mode 100644
index 0000000..b636bdf
--- /dev/null
+++ b/shell/domain_socket/socket_descriptor.h
@@ -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.
+
+#ifndef SHELL_DOMAIN_SOCKET_SOCKET_DESCRIPTOR_H_
+#define SHELL_DOMAIN_SOCKET_SOCKET_DESCRIPTOR_H_
+
+namespace mojo {
+namespace shell {
+
+typedef int SocketDescriptor;
+const SocketDescriptor kInvalidSocket = -1;
+
+}  // namespace mojo
+}  // namespace shell
+
+#endif  // SHELL_DOMAIN_SOCKET_SOCKET_DESCRIPTOR_H_
diff --git a/shell/domain_socket/socket_libevent.cc b/shell/domain_socket/socket_libevent.cc
new file mode 100644
index 0000000..21fc924
--- /dev/null
+++ b/shell/domain_socket/socket_libevent.cc
@@ -0,0 +1,380 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/domain_socket/socket_libevent.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "shell/domain_socket/net_errors.h"
+
+namespace mojo {
+namespace shell {
+
+SockaddrStorage::SockaddrStorage(const SockaddrStorage& other)
+    : addr_len(other.addr_len),
+      addr(reinterpret_cast<struct sockaddr*>(&addr_storage)) {
+  memcpy(addr, other.addr, addr_len);
+}
+
+void SockaddrStorage::operator=(const SockaddrStorage& other) {
+  addr_len = other.addr_len;
+  // addr is already set to &this->addr_storage by default ctor.
+  memcpy(addr, other.addr, addr_len);
+}
+
+namespace {
+
+int MapAcceptError(int os_error) {
+  switch (os_error) {
+    // If the client aborts the connection before the server calls accept,
+    // POSIX specifies accept should fail with ECONNABORTED. The server can
+    // ignore the error and just call accept again, so we map the error to
+    // ERR_IO_PENDING. See UNIX Network Programming, Vol. 1, 3rd Ed., Sec.
+    // 5.11, "Connection Abort before accept Returns".
+    case ECONNABORTED:
+      return net::ERR_IO_PENDING;
+    default:
+      return net::MapSystemError(os_error);
+  }
+}
+
+int MapConnectError(int os_error) {
+  switch (os_error) {
+    case EINPROGRESS:
+      return net::ERR_IO_PENDING;
+    case EACCES:
+      return net::ERR_NETWORK_ACCESS_DENIED;
+    case ETIMEDOUT:
+      return net::ERR_CONNECTION_TIMED_OUT;
+    default: {
+      int net_error = net::MapSystemError(os_error);
+      if (net_error == net::ERR_FAILED)
+        return net::ERR_CONNECTION_FAILED;  // More specific than ERR_FAILED.
+      return net_error;
+    }
+  }
+}
+
+int SetNonBlocking(int fd) {
+  int flags = fcntl(fd, F_GETFL, 0);
+  if (-1 == flags)
+    return flags;
+  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+}  // namespace
+
+SocketLibevent::SocketLibevent()
+    : socket_fd_(kInvalidSocket), waiting_connect_(false) {
+}
+
+SocketLibevent::~SocketLibevent() {
+  Close();
+}
+
+int SocketLibevent::Open(int address_family) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(kInvalidSocket, socket_fd_);
+  DCHECK(address_family == AF_INET || address_family == AF_INET6 ||
+         address_family == AF_UNIX);
+
+  int socket_type = SOCK_STREAM;
+#ifdef SOCK_NONBLOCK
+  socket_type |= SOCK_NONBLOCK;
+#endif
+  socket_fd_ = ::socket(address_family, socket_type,
+                        address_family == AF_UNIX ? 0 : IPPROTO_TCP);
+#ifndef SOCK_NONBLOCK
+  if (SetNonBlocking(socket_fd_) != 0) {
+    PLOG(ERROR) << "SetNonBlocking() returned an error, errno=" << errno;
+    return net::MapSystemError(errno);
+  }
+#endif
+  if (socket_fd_ < 0) {
+    PLOG(ERROR) << "CreatePlatformSocket() returned an error, errno=" << errno;
+    return net::MapSystemError(errno);
+  }
+
+  return net::OK;
+}
+
+int SocketLibevent::AdoptConnectedSocket(SocketDescriptor socket,
+                                         const SockaddrStorage& address) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(kInvalidSocket, socket_fd_);
+
+  socket_fd_ = socket;
+
+  if (SetNonBlocking(socket_fd_)) {
+    int rv = net::MapSystemError(errno);
+    Close();
+    return rv;
+  }
+
+  SetPeerAddress(address);
+  return net::OK;
+}
+
+SocketDescriptor SocketLibevent::ReleaseConnectedSocket() {
+  StopWatchingAndCleanUp();
+  SocketDescriptor socket_fd = socket_fd_;
+  socket_fd_ = kInvalidSocket;
+  return socket_fd;
+}
+
+int SocketLibevent::Bind(const SockaddrStorage& address) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_NE(kInvalidSocket, socket_fd_);
+
+  int rv = bind(socket_fd_, address.addr, address.addr_len);
+  if (rv < 0) {
+    PLOG(ERROR) << "bind() returned an error, errno=" << errno;
+    return net::MapSystemError(errno);
+  }
+
+  return net::OK;
+}
+
+int SocketLibevent::Listen(int backlog) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_NE(kInvalidSocket, socket_fd_);
+  DCHECK_LT(0, backlog);
+
+  int rv = listen(socket_fd_, backlog);
+  if (rv < 0) {
+    PLOG(ERROR) << "listen() returned an error, errno=" << errno;
+    return net::MapSystemError(errno);
+  }
+
+  return net::OK;
+}
+
+int SocketLibevent::Accept(scoped_ptr<SocketLibevent>* socket,
+                           const CompletionCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_NE(kInvalidSocket, socket_fd_);
+  DCHECK(accept_callback_.is_null());
+  DCHECK(socket);
+  DCHECK(!callback.is_null());
+
+  int rv = DoAccept(socket);
+  if (rv != net::ERR_IO_PENDING)
+    return rv;
+
+  if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+          socket_fd_, true, base::MessageLoopForIO::WATCH_READ,
+          &accept_socket_watcher_, this)) {
+    PLOG(ERROR) << "WatchFileDescriptor failed on accept, errno " << errno;
+    return net::MapSystemError(errno);
+  }
+
+  accept_socket_ = socket;
+  accept_callback_ = callback;
+  return net::ERR_IO_PENDING;
+}
+
+int SocketLibevent::Connect(const SockaddrStorage& address,
+                            const CompletionCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_NE(kInvalidSocket, socket_fd_);
+  DCHECK(!waiting_connect_);
+  DCHECK(!callback.is_null());
+
+  SetPeerAddress(address);
+
+  int rv = DoConnect();
+  if (rv != net::ERR_IO_PENDING)
+    return rv;
+
+  if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+          socket_fd_, true, base::MessageLoopForIO::WATCH_WRITE,
+          &write_socket_watcher_, this)) {
+    PLOG(ERROR) << "WatchFileDescriptor failed on connect, errno " << errno;
+    return net::MapSystemError(errno);
+  }
+
+  write_callback_ = callback;
+  waiting_connect_ = true;
+  return net::ERR_IO_PENDING;
+}
+
+bool SocketLibevent::IsConnected() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (socket_fd_ == kInvalidSocket || waiting_connect_)
+    return false;
+
+  // Checks if connection is alive.
+  char c;
+  int rv = HANDLE_EINTR(recv(socket_fd_, &c, 1, MSG_PEEK));
+  if (rv == 0)
+    return false;
+  if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
+    return false;
+
+  return true;
+}
+
+bool SocketLibevent::IsConnectedAndIdle() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (socket_fd_ == kInvalidSocket || waiting_connect_)
+    return false;
+
+  // Check if connection is alive and we haven't received any data
+  // unexpectedly.
+  char c;
+  int rv = HANDLE_EINTR(recv(socket_fd_, &c, 1, MSG_PEEK));
+  if (rv >= 0)
+    return false;
+  if (errno != EAGAIN && errno != EWOULDBLOCK)
+    return false;
+
+  return true;
+}
+
+int SocketLibevent::GetLocalAddress(SockaddrStorage* address) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(address);
+
+  if (getsockname(socket_fd_, address->addr, &address->addr_len) < 0)
+    return net::MapSystemError(errno);
+  return net::OK;
+}
+
+int SocketLibevent::GetPeerAddress(SockaddrStorage* address) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(address);
+
+  if (!HasPeerAddress())
+    return net::ERR_SOCKET_NOT_CONNECTED;
+
+  *address = *peer_address_;
+  return net::OK;
+}
+
+void SocketLibevent::SetPeerAddress(const SockaddrStorage& address) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // |peer_address_| will be non-NULL if Connect() has been called. Unless
+  // Close() is called to reset the internal state, a second call to Connect()
+  // is not allowed.
+  // Please note that we don't allow a second Connect() even if the previous
+  // Connect() has failed. Connecting the same |socket_| again after a
+  // connection attempt failed results in unspecified behavior according to
+  // POSIX.
+  DCHECK(!peer_address_);
+  peer_address_.reset(new SockaddrStorage(address));
+}
+
+bool SocketLibevent::HasPeerAddress() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return peer_address_ != NULL;
+}
+
+void SocketLibevent::Close() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  StopWatchingAndCleanUp();
+
+  if (socket_fd_ != kInvalidSocket) {
+    if (IGNORE_EINTR(close(socket_fd_)) < 0)
+      PLOG(ERROR) << "close() returned an error, errno=" << errno;
+    socket_fd_ = kInvalidSocket;
+  }
+}
+
+void SocketLibevent::OnFileCanReadWithoutBlocking(int fd) {
+  DCHECK(!accept_callback_.is_null() || !read_callback_.is_null());
+  if (!accept_callback_.is_null()) {
+    AcceptCompleted();
+  } else {  // !read_callback_.is_null()
+    NOTREACHED();
+  }
+}
+
+void SocketLibevent::OnFileCanWriteWithoutBlocking(int fd) {
+  DCHECK(!write_callback_.is_null());
+  if (waiting_connect_) {
+    ConnectCompleted();
+  } else {
+    NOTREACHED();
+  }
+}
+
+int SocketLibevent::DoAccept(scoped_ptr<SocketLibevent>* socket) {
+  SockaddrStorage new_peer_address;
+  int new_socket = HANDLE_EINTR(
+      accept(socket_fd_, new_peer_address.addr, &new_peer_address.addr_len));
+  if (new_socket < 0)
+    return MapAcceptError(errno);
+
+  scoped_ptr<SocketLibevent> accepted_socket(new SocketLibevent);
+  int rv = accepted_socket->AdoptConnectedSocket(new_socket, new_peer_address);
+  if (rv != net::OK)
+    return rv;
+
+  *socket = accepted_socket.Pass();
+  return net::OK;
+}
+
+void SocketLibevent::AcceptCompleted() {
+  DCHECK(accept_socket_);
+  int rv = DoAccept(accept_socket_);
+  if (rv == net::ERR_IO_PENDING)
+    return;
+
+  bool ok = accept_socket_watcher_.StopWatchingFileDescriptor();
+  DCHECK(ok);
+  accept_socket_ = NULL;
+  base::ResetAndReturn(&accept_callback_).Run(rv);
+}
+
+int SocketLibevent::DoConnect() {
+  int rv = HANDLE_EINTR(
+      connect(socket_fd_, peer_address_->addr, peer_address_->addr_len));
+  DCHECK_GE(0, rv);
+  return rv == 0 ? net::OK : MapConnectError(errno);
+}
+
+void SocketLibevent::ConnectCompleted() {
+  // Get the error that connect() completed with.
+  int os_error = 0;
+  socklen_t len = sizeof(os_error);
+  if (getsockopt(socket_fd_, SOL_SOCKET, SO_ERROR, &os_error, &len) == 0) {
+    // TCPSocketLibevent expects errno to be set.
+    errno = os_error;
+  }
+
+  int rv = MapConnectError(errno);
+  if (rv == net::ERR_IO_PENDING)
+    return;
+
+  bool ok = write_socket_watcher_.StopWatchingFileDescriptor();
+  DCHECK(ok);
+  waiting_connect_ = false;
+  base::ResetAndReturn(&write_callback_).Run(rv);
+}
+
+void SocketLibevent::StopWatchingAndCleanUp() {
+  bool ok = accept_socket_watcher_.StopWatchingFileDescriptor();
+  DCHECK(ok);
+
+  if (!accept_callback_.is_null()) {
+    accept_socket_ = NULL;
+    accept_callback_.Reset();
+  }
+
+  waiting_connect_ = false;
+  peer_address_.reset();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/domain_socket/socket_libevent.h b/shell/domain_socket/socket_libevent.h
new file mode 100644
index 0000000..4a84f40
--- /dev/null
+++ b/shell/domain_socket/socket_libevent.h
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DOMAIN_SOCKET_SOCKET_LIBEVENT_H_
+#define SHELL_DOMAIN_SOCKET_SOCKET_LIBEVENT_H_
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_checker.h"
+#include "shell/domain_socket/completion_callback.h"
+#include "shell/domain_socket/socket_descriptor.h"
+
+namespace mojo {
+namespace shell {
+
+// Convenience struct for when you need a |struct sockaddr|.
+struct SockaddrStorage {
+  SockaddrStorage()
+      : addr_len(sizeof(addr_storage)),
+        addr(reinterpret_cast<struct sockaddr*>(&addr_storage)) {}
+  SockaddrStorage(const SockaddrStorage& other);
+  void operator=(const SockaddrStorage& other);
+
+  struct sockaddr_storage addr_storage;
+  socklen_t addr_len;
+  struct sockaddr* const addr;
+};
+
+// Socket class to provide asynchronous read/write operations on top of the
+// posix socket api. It supports AF_INET, AF_INET6, and AF_UNIX addresses.
+class SocketLibevent : public base::MessageLoopForIO::Watcher {
+ public:
+  SocketLibevent();
+  ~SocketLibevent() override;
+
+  // Opens a socket and returns net::OK if |address_family| is AF_INET, AF_INET6
+  // or AF_UNIX. Otherwise, it does DCHECK() and returns a net error.
+  int Open(int address_family);
+  // Takes ownership of |socket|.
+  int AdoptConnectedSocket(SocketDescriptor socket,
+                           const SockaddrStorage& peer_address);
+  // Releases ownership of |socket_fd_| to caller.
+  SocketDescriptor ReleaseConnectedSocket();
+
+  int Bind(const SockaddrStorage& address);
+
+  int Listen(int backlog);
+  int Accept(scoped_ptr<SocketLibevent>* socket,
+             const CompletionCallback& callback);
+
+  // Connects socket. On non-ERR_IO_PENDING error, sets errno and returns a net
+  // error code. On ERR_IO_PENDING, |callback| is called with a net error code,
+  // not errno, though errno is set if connect event happens with error.
+  // TODO(byungchul): Need more robust way to pass system errno.
+  int Connect(const SockaddrStorage& address,
+              const CompletionCallback& callback);
+  bool IsConnected() const;
+  bool IsConnectedAndIdle() const;
+
+  int GetLocalAddress(SockaddrStorage* address) const;
+  int GetPeerAddress(SockaddrStorage* address) const;
+  void SetPeerAddress(const SockaddrStorage& address);
+  // Returns true if peer address has been set regardless of socket state.
+  bool HasPeerAddress() const;
+
+  void Close();
+
+  SocketDescriptor socket_fd() const { return socket_fd_; }
+
+ private:
+  // base::MessageLoopForIO::Watcher methods.
+  void OnFileCanReadWithoutBlocking(int fd) override;
+  void OnFileCanWriteWithoutBlocking(int fd) override;
+
+  int DoAccept(scoped_ptr<SocketLibevent>* socket);
+  void AcceptCompleted();
+
+  int DoConnect();
+  void ConnectCompleted();
+
+  void StopWatchingAndCleanUp();
+
+  SocketDescriptor socket_fd_;
+
+  base::MessageLoopForIO::FileDescriptorWatcher accept_socket_watcher_;
+  scoped_ptr<SocketLibevent>* accept_socket_;
+  CompletionCallback accept_callback_;
+
+  base::MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_;
+  // External callback; called when read is complete.
+  CompletionCallback read_callback_;
+
+  base::MessageLoopForIO::FileDescriptorWatcher write_socket_watcher_;
+  // External callback; called when write or connect is complete.
+  CompletionCallback write_callback_;
+
+  // A connect operation is pending. In this case, |write_callback_| needs to be
+  // called when connect is complete.
+  bool waiting_connect_;
+
+  scoped_ptr<SockaddrStorage> peer_address_;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(SocketLibevent);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DOMAIN_SOCKET_SOCKET_LIBEVENT_H_
diff --git a/shell/domain_socket/test_completion_callback.cc b/shell/domain_socket/test_completion_callback.cc
new file mode 100644
index 0000000..566f806
--- /dev/null
+++ b/shell/domain_socket/test_completion_callback.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/domain_socket/test_completion_callback.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+
+namespace mojo {
+namespace shell {
+
+namespace internal {
+
+void TestCompletionCallbackBaseInternal::DidSetResult() {
+  have_result_ = true;
+  if (waiting_for_result_)
+    base::MessageLoop::current()->Quit();
+}
+
+void TestCompletionCallbackBaseInternal::WaitForResult() {
+  DCHECK(!waiting_for_result_);
+  while (!have_result_) {
+    waiting_for_result_ = true;
+    base::MessageLoop::current()->Run();
+    waiting_for_result_ = false;
+  }
+  have_result_ = false;  // Auto-reset for next callback.
+}
+
+TestCompletionCallbackBaseInternal::TestCompletionCallbackBaseInternal()
+    : have_result_(false), waiting_for_result_(false) {
+}
+
+}  // namespace internal
+
+TestCompletionCallback::TestCompletionCallback()
+    : callback_(base::Bind(&TestCompletionCallback::SetResult,
+                           base::Unretained(this))) {
+}
+
+TestCompletionCallback::~TestCompletionCallback() {
+}
+
+TestInt64CompletionCallback::TestInt64CompletionCallback()
+    : callback_(base::Bind(&TestInt64CompletionCallback::SetResult,
+                           base::Unretained(this))) {
+}
+
+TestInt64CompletionCallback::~TestInt64CompletionCallback() {
+}
+
+}  // namespace net
+}  // namespace net
diff --git a/shell/domain_socket/test_completion_callback.h b/shell/domain_socket/test_completion_callback.h
new file mode 100644
index 0000000..5c3a5f1
--- /dev/null
+++ b/shell/domain_socket/test_completion_callback.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DOMAIN_SOCKET_TEST_COMPLETION_CALLBACK_H_
+#define SHELL_DOMAIN_SOCKET_TEST_COMPLETION_CALLBACK_H_
+
+#include "base/compiler_specific.h"
+#include "base/tuple.h"
+#include "shell/domain_socket/completion_callback.h"
+#include "shell/domain_socket/net_errors.h"
+
+//-----------------------------------------------------------------------------
+// completion callback helper
+
+// A helper class for completion callbacks, designed to make it easy to run
+// tests involving asynchronous operations.  Just call WaitForResult to wait
+// for the asynchronous operation to complete.
+//
+// NOTE: Since this runs a message loop to wait for the completion callback,
+// there could be other side-effects resulting from WaitForResult.  For this
+// reason, this class is probably not ideal for a general application.
+//
+
+namespace mojo {
+namespace shell {
+
+namespace internal {
+
+class TestCompletionCallbackBaseInternal {
+ public:
+  bool have_result() const { return have_result_; }
+
+ protected:
+  TestCompletionCallbackBaseInternal();
+  void DidSetResult();
+  void WaitForResult();
+
+  bool have_result_;
+  bool waiting_for_result_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestCompletionCallbackBaseInternal);
+};
+
+template <typename R>
+class TestCompletionCallbackTemplate
+    : public TestCompletionCallbackBaseInternal {
+ public:
+  virtual ~TestCompletionCallbackTemplate() {}
+
+  R WaitForResult() {
+    TestCompletionCallbackBaseInternal::WaitForResult();
+    return result_;
+  }
+
+  R GetResult(R result) {
+    if (net::ERR_IO_PENDING != result)
+      return result;
+    return WaitForResult();
+  }
+
+ protected:
+  // Override this method to gain control as the callback is running.
+  virtual void SetResult(R result) {
+    result_ = result;
+    DidSetResult();
+  }
+
+  TestCompletionCallbackTemplate() : result_(R()) {}
+  R result_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestCompletionCallbackTemplate);
+};
+
+}  // namespace internal
+
+// Base class overridden by custom implementations of TestCompletionCallback.
+typedef internal::TestCompletionCallbackTemplate<int>
+    TestCompletionCallbackBase;
+
+typedef internal::TestCompletionCallbackTemplate<int64>
+    TestInt64CompletionCallbackBase;
+
+class TestCompletionCallback : public TestCompletionCallbackBase {
+ public:
+  TestCompletionCallback();
+  ~TestCompletionCallback() override;
+
+  const CompletionCallback& callback() const { return callback_; }
+
+ private:
+  const CompletionCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestCompletionCallback);
+};
+
+class TestInt64CompletionCallback : public TestInt64CompletionCallbackBase {
+ public:
+  TestInt64CompletionCallback();
+  ~TestInt64CompletionCallback() override;
+
+  const Int64CompletionCallback& callback() const { return callback_; }
+
+ private:
+  const Int64CompletionCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestInt64CompletionCallback);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DOMAIN_SOCKET_TEST_COMPLETION_CALLBACK_H_
diff --git a/shell/domain_socket/unix_domain_client_socket_posix.cc b/shell/domain_socket/unix_domain_client_socket_posix.cc
new file mode 100644
index 0000000..1733950
--- /dev/null
+++ b/shell/domain_socket/unix_domain_client_socket_posix.cc
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/domain_socket/unix_domain_client_socket_posix.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/socket_libevent.h"
+
+namespace mojo {
+namespace shell {
+
+UnixDomainClientSocket::UnixDomainClientSocket(const std::string& socket_path,
+                                               bool use_abstract_namespace)
+    : socket_path_(socket_path),
+      use_abstract_namespace_(use_abstract_namespace) {
+}
+
+UnixDomainClientSocket::UnixDomainClientSocket(
+    scoped_ptr<SocketLibevent> socket)
+    : use_abstract_namespace_(false), socket_(socket.Pass()) {
+}
+
+UnixDomainClientSocket::~UnixDomainClientSocket() {
+  Disconnect();
+}
+
+// static
+bool UnixDomainClientSocket::FillAddress(const std::string& socket_path,
+                                         bool use_abstract_namespace,
+                                         SockaddrStorage* address) {
+  struct sockaddr_un* socket_addr =
+      reinterpret_cast<struct sockaddr_un*>(address->addr);
+  size_t path_max = address->addr_len - offsetof(struct sockaddr_un, sun_path);
+  // Non abstract namespace pathname should be null-terminated. Abstract
+  // namespace pathname must start with '\0'. So, the size is always greater
+  // than socket_path size by 1.
+  size_t path_size = socket_path.size() + 1;
+  if (path_size > path_max)
+    return false;
+
+  memset(socket_addr, 0, address->addr_len);
+  socket_addr->sun_family = AF_UNIX;
+  address->addr_len = path_size + offsetof(struct sockaddr_un, sun_path);
+  if (!use_abstract_namespace) {
+    memcpy(socket_addr->sun_path, socket_path.c_str(), socket_path.size());
+    return true;
+  }
+
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+  // Convert the path given into abstract socket name. It must start with
+  // the '\0' character, so we are adding it. |addr_len| must specify the
+  // length of the structure exactly, as potentially the socket name may
+  // have '\0' characters embedded (although we don't support this).
+  // Note that addr.sun_path is already zero initialized.
+  memcpy(socket_addr->sun_path + 1, socket_path.c_str(), socket_path.size());
+  return true;
+#else
+  return false;
+#endif
+}
+
+int UnixDomainClientSocket::Connect(const CompletionCallback& callback) {
+  DCHECK(!socket_);
+
+  if (socket_path_.empty())
+    return net::ERR_ADDRESS_INVALID;
+
+  SockaddrStorage address;
+  if (!FillAddress(socket_path_, use_abstract_namespace_, &address))
+    return net::ERR_ADDRESS_INVALID;
+
+  socket_.reset(new SocketLibevent);
+  int rv = socket_->Open(AF_UNIX);
+  DCHECK_NE(net::ERR_IO_PENDING, rv);
+  if (rv != net::OK)
+    return rv;
+
+  return socket_->Connect(address, callback);
+}
+
+void UnixDomainClientSocket::Disconnect() {
+  socket_.reset();
+}
+
+bool UnixDomainClientSocket::IsConnected() const {
+  return socket_ && socket_->IsConnected();
+}
+
+bool UnixDomainClientSocket::IsConnectedAndIdle() const {
+  return socket_ && socket_->IsConnectedAndIdle();
+}
+
+SocketDescriptor UnixDomainClientSocket::ReleaseConnectedSocket() {
+  DCHECK(socket_);
+  DCHECK(socket_->IsConnected());
+
+  SocketDescriptor socket_fd = socket_->ReleaseConnectedSocket();
+  socket_.reset();
+  return socket_fd;
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/domain_socket/unix_domain_client_socket_posix.h b/shell/domain_socket/unix_domain_client_socket_posix.h
new file mode 100644
index 0000000..719f9e4
--- /dev/null
+++ b/shell/domain_socket/unix_domain_client_socket_posix.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DOMAIN_SOCKET_UNIX_DOMAIN_CLIENT_SOCKET_POSIX_H_
+#define SHELL_DOMAIN_SOCKET_UNIX_DOMAIN_CLIENT_SOCKET_POSIX_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "shell/domain_socket/completion_callback.h"
+#include "shell/domain_socket/socket_descriptor.h"
+
+namespace mojo {
+namespace shell {
+
+class SocketLibevent;
+struct SockaddrStorage;
+
+// A client socket that uses unix domain socket as the transport layer.
+class UnixDomainClientSocket {
+ public:
+  // Builds a client socket with |socket_path|. The caller should call Connect()
+  // to connect to a server socket.
+  UnixDomainClientSocket(const std::string& socket_path,
+                         bool use_abstract_namespace);
+  // Builds a client socket with socket libevent which is already connected.
+  // UnixDomainServerSocket uses this after it accepts a connection.
+  explicit UnixDomainClientSocket(scoped_ptr<SocketLibevent> socket);
+
+  ~UnixDomainClientSocket();
+
+  // Fills |address| with |socket_path| and its length. For Android or Linux
+  // platform, this supports abstract namespaces.
+  static bool FillAddress(const std::string& socket_path,
+                          bool use_abstract_namespace,
+                          SockaddrStorage* address);
+
+  int Connect(const CompletionCallback& callback);
+  void Disconnect();
+  bool IsConnected() const;
+  bool IsConnectedAndIdle() const;
+
+  // Releases ownership of underlying SocketDescriptor to caller.
+  // Internal state is reset so that this object can be used again.
+  // Socket must be connected in order to release it.
+  SocketDescriptor ReleaseConnectedSocket();
+
+ private:
+  const std::string socket_path_;
+  const bool use_abstract_namespace_;
+  scoped_ptr<SocketLibevent> socket_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnixDomainClientSocket);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DOMAIN_SOCKET_UNIX_DOMAIN_CLIENT_SOCKET_POSIX_H_
diff --git a/shell/domain_socket/unix_domain_client_socket_posix_unittest.cc b/shell/domain_socket/unix_domain_client_socket_posix_unittest.cc
new file mode 100644
index 0000000..69e8bf7
--- /dev/null
+++ b/shell/domain_socket/unix_domain_client_socket_posix_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/domain_socket/unix_domain_client_socket_posix.h"
+
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/socket_libevent.h"
+#include "shell/domain_socket/test_completion_callback.h"
+#include "shell/domain_socket/unix_domain_server_socket_posix.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+const char kSocketFilename[] = "socket_for_testing";
+
+bool UserCanConnectCallback(
+    bool allow_user,
+    const UnixDomainServerSocket::Credentials& credentials) {
+  // Here peers are running in same process.
+  EXPECT_EQ(getpid(), credentials.process_id);
+  EXPECT_EQ(getuid(), credentials.user_id);
+  EXPECT_EQ(getgid(), credentials.group_id);
+  return allow_user;
+}
+
+UnixDomainServerSocket::AuthCallback CreateAuthCallback(bool allow_user) {
+  return base::Bind(&UserCanConnectCallback, allow_user);
+}
+
+// Connects socket synchronously.
+int ConnectSynchronously(UnixDomainClientSocket* socket) {
+  TestCompletionCallback connect_callback;
+  int rv = socket->Connect(connect_callback.callback());
+  if (rv == net::ERR_IO_PENDING)
+    rv = connect_callback.WaitForResult();
+  return rv;
+}
+}  // namespace
+
+class UnixDomainClientSocketTest : public testing::Test {
+ protected:
+  UnixDomainClientSocketTest() {
+    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+    socket_path_ = temp_dir_.path().Append(kSocketFilename).value();
+  }
+
+  base::ScopedTempDir temp_dir_;
+  std::string socket_path_;
+  base::MessageLoopForIO loop_;
+};
+
+TEST_F(UnixDomainClientSocketTest, ConnectWithSocketDescriptor) {
+  const bool kUseAbstractNamespace = false;
+
+  UnixDomainServerSocket server_socket(CreateAuthCallback(true),
+                                       kUseAbstractNamespace);
+  EXPECT_EQ(net::OK, server_socket.ListenWithPath(socket_path_, 1));
+
+  SocketDescriptor accepted_socket_fd = kInvalidSocket;
+  TestCompletionCallback accept_callback;
+  EXPECT_EQ(
+      net::ERR_IO_PENDING,
+      server_socket.Accept(&accepted_socket_fd, accept_callback.callback()));
+  EXPECT_EQ(kInvalidSocket, accepted_socket_fd);
+
+  UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace);
+  EXPECT_FALSE(client_socket.IsConnected());
+
+  EXPECT_EQ(net::OK, ConnectSynchronously(&client_socket));
+  EXPECT_TRUE(client_socket.IsConnected());
+  // Server has not yet been notified of the connection.
+  EXPECT_EQ(kInvalidSocket, accepted_socket_fd);
+
+  EXPECT_EQ(net::OK, accept_callback.WaitForResult());
+  EXPECT_NE(kInvalidSocket, accepted_socket_fd);
+
+  SocketDescriptor client_socket_fd = client_socket.ReleaseConnectedSocket();
+  EXPECT_NE(kInvalidSocket, client_socket_fd);
+
+  // Now, re-wrap client_socket_fd in a UnixDomainClientSocket and check that
+  // it hasn't gotten accidentally closed.
+  SockaddrStorage addr;
+  ASSERT_TRUE(UnixDomainClientSocket::FillAddress(socket_path_, false, &addr));
+  scoped_ptr<SocketLibevent> adopter(new SocketLibevent);
+  adopter->AdoptConnectedSocket(client_socket_fd, addr);
+  UnixDomainClientSocket rewrapped_socket(adopter.Pass());
+  EXPECT_TRUE(rewrapped_socket.IsConnected());
+
+  EXPECT_EQ(0, IGNORE_EINTR(close(accepted_socket_fd)));
+}
+
+TEST_F(UnixDomainClientSocketTest, ConnectWithAbstractNamespace) {
+  const bool kUseAbstractNamespace = true;
+
+  UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace);
+  EXPECT_FALSE(client_socket.IsConnected());
+
+  UnixDomainServerSocket server_socket(CreateAuthCallback(true),
+                                       kUseAbstractNamespace);
+  EXPECT_EQ(net::OK, server_socket.ListenWithPath(socket_path_, 1));
+
+  SocketDescriptor accepted_socket_fd = kInvalidSocket;
+  TestCompletionCallback accept_callback;
+  EXPECT_EQ(
+      net::ERR_IO_PENDING,
+      server_socket.Accept(&accepted_socket_fd, accept_callback.callback()));
+  EXPECT_EQ(kInvalidSocket, accepted_socket_fd);
+
+  EXPECT_EQ(net::OK, ConnectSynchronously(&client_socket));
+  EXPECT_TRUE(client_socket.IsConnected());
+  // Server has not yet been notified of the connection.
+  EXPECT_EQ(kInvalidSocket, accepted_socket_fd);
+
+  EXPECT_EQ(net::OK, accept_callback.WaitForResult());
+  EXPECT_NE(kInvalidSocket, accepted_socket_fd);
+
+  EXPECT_EQ(0, IGNORE_EINTR(close(accepted_socket_fd)));
+}
+
+TEST_F(UnixDomainClientSocketTest, ConnectToNonExistentSocket) {
+  const bool kUseAbstractNamespace = false;
+
+  UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace);
+  EXPECT_FALSE(client_socket.IsConnected());
+  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, ConnectSynchronously(&client_socket));
+}
+
+TEST_F(UnixDomainClientSocketTest,
+       ConnectToNonExistentSocketWithAbstractNamespace) {
+  const bool kUseAbstractNamespace = true;
+
+  UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace);
+  EXPECT_FALSE(client_socket.IsConnected());
+
+  TestCompletionCallback connect_callback;
+  EXPECT_EQ(net::ERR_CONNECTION_REFUSED, ConnectSynchronously(&client_socket));
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/domain_socket/unix_domain_server_socket_posix.cc b/shell/domain_socket/unix_domain_server_socket_posix.cc
new file mode 100644
index 0000000..778e41f
--- /dev/null
+++ b/shell/domain_socket/unix_domain_server_socket_posix.cc
@@ -0,0 +1,156 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/domain_socket/unix_domain_server_socket_posix.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "shell/domain_socket/completion_callback.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "shell/domain_socket/socket_libevent.h"
+#include "shell/domain_socket/unix_domain_client_socket_posix.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+// Intended for use as SetterCallbacks in Accept() helper methods.
+void SetSocketDescriptor(SocketDescriptor* socket,
+                         scoped_ptr<SocketLibevent> accepted_socket) {
+  *socket = accepted_socket->ReleaseConnectedSocket();
+}
+
+}  // anonymous namespace
+
+UnixDomainServerSocket::UnixDomainServerSocket(
+    const AuthCallback& auth_callback,
+    bool use_abstract_namespace)
+    : auth_callback_(auth_callback),
+      use_abstract_namespace_(use_abstract_namespace) {
+  DCHECK(!auth_callback_.is_null());
+}
+
+UnixDomainServerSocket::~UnixDomainServerSocket() {
+}
+
+// static
+bool UnixDomainServerSocket::GetPeerCredentials(SocketDescriptor socket,
+                                                Credentials* credentials) {
+  struct ucred user_cred;
+  socklen_t len = sizeof(user_cred);
+  if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &user_cred, &len) < 0)
+    return false;
+  credentials->process_id = user_cred.pid;
+  credentials->user_id = user_cred.uid;
+  credentials->group_id = user_cred.gid;
+  return true;
+}
+
+int UnixDomainServerSocket::ListenWithPath(const std::string& unix_domain_path,
+                                           int backlog) {
+  DCHECK(!listen_socket_);
+
+  SockaddrStorage address;
+  if (!UnixDomainClientSocket::FillAddress(unix_domain_path,
+                                           use_abstract_namespace_, &address)) {
+    return net::ERR_ADDRESS_INVALID;
+  }
+
+  scoped_ptr<SocketLibevent> socket(new SocketLibevent);
+  int rv = socket->Open(AF_UNIX);
+  DCHECK_NE(net::ERR_IO_PENDING, rv);
+  if (rv != net::OK)
+    return rv;
+
+  rv = socket->Bind(address);
+  DCHECK_NE(net::ERR_IO_PENDING, rv);
+  if (rv != net::OK) {
+    PLOG(ERROR) << "Could not bind unix domain socket to " << unix_domain_path
+                << (use_abstract_namespace_ ? " (with abstract namespace)"
+                                            : "");
+    return rv;
+  }
+
+  rv = socket->Listen(backlog);
+  DCHECK_NE(net::ERR_IO_PENDING, rv);
+  if (rv != net::OK)
+    return rv;
+
+  listen_socket_.swap(socket);
+  return rv;
+}
+
+int UnixDomainServerSocket::Accept(SocketDescriptor* socket,
+                                   const CompletionCallback& callback) {
+  DCHECK(socket);
+
+  SetterCallback setter_callback = base::Bind(&SetSocketDescriptor, socket);
+  return DoAccept(setter_callback, callback);
+}
+
+int UnixDomainServerSocket::DoAccept(const SetterCallback& setter_callback,
+                                     const CompletionCallback& callback) {
+  DCHECK(!setter_callback.is_null());
+  DCHECK(!callback.is_null());
+  DCHECK(listen_socket_);
+  DCHECK(!accept_socket_);
+
+  while (true) {
+    int rv = listen_socket_->Accept(
+        &accept_socket_,
+        base::Bind(&UnixDomainServerSocket::AcceptCompleted,
+                   base::Unretained(this), setter_callback, callback));
+    if (rv != net::OK)
+      return rv;
+    if (AuthenticateAndGetStreamSocket(setter_callback))
+      return net::OK;
+    // Accept another socket because authentication error should be transparent
+    // to the caller.
+  }
+}
+
+void UnixDomainServerSocket::AcceptCompleted(
+    const SetterCallback& setter_callback,
+    const CompletionCallback& callback,
+    int rv) {
+  if (rv != net::OK) {
+    callback.Run(rv);
+    return;
+  }
+
+  if (AuthenticateAndGetStreamSocket(setter_callback)) {
+    callback.Run(net::OK);
+    return;
+  }
+
+  // Accept another socket because authentication error should be transparent
+  // to the caller.
+  rv = DoAccept(setter_callback, callback);
+  if (rv != net::ERR_IO_PENDING)
+    callback.Run(rv);
+}
+
+bool UnixDomainServerSocket::AuthenticateAndGetStreamSocket(
+    const SetterCallback& setter_callback) {
+  DCHECK(accept_socket_);
+
+  Credentials credentials;
+  if (!GetPeerCredentials(accept_socket_->socket_fd(), &credentials) ||
+      !auth_callback_.Run(credentials)) {
+    accept_socket_.reset();
+    return false;
+  }
+
+  setter_callback.Run(accept_socket_.Pass());
+  return true;
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/domain_socket/unix_domain_server_socket_posix.h b/shell/domain_socket/unix_domain_server_socket_posix.h
new file mode 100644
index 0000000..ebb1f9f
--- /dev/null
+++ b/shell/domain_socket/unix_domain_server_socket_posix.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DOMAIN_SOCKET_UNIX_DOMAIN_SERVER_SOCKET_POSIX_H_
+#define SHELL_DOMAIN_SOCKET_UNIX_DOMAIN_SERVER_SOCKET_POSIX_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "shell/domain_socket/completion_callback.h"
+#include "shell/domain_socket/socket_descriptor.h"
+
+namespace mojo {
+namespace shell {
+
+class SocketLibevent;
+
+// Unix Domain Server Socket Implementation. Supports abstract namespaces on
+// Linux and Android.
+class UnixDomainServerSocket {
+ public:
+  // Credentials of a peer process connected to the socket.
+  struct Credentials {
+    pid_t process_id;
+    uid_t user_id;
+    gid_t group_id;
+  };
+
+  // Callback that returns whether the already connected client, identified by
+  // its credentials, is allowed to keep the connection open. Note that
+  // the socket is closed immediately in case the callback returns false.
+  typedef base::Callback<bool(const Credentials&)> AuthCallback;
+
+  UnixDomainServerSocket(const AuthCallback& auth_callack,
+                         bool use_abstract_namespace);
+  ~UnixDomainServerSocket();
+
+  // Gets credentials of peer to check permissions.
+  static bool GetPeerCredentials(SocketDescriptor socket_fd,
+                                 Credentials* credentials);
+
+  int ListenWithPath(const std::string& unix_domain_path, int backlog);
+
+  // Accepts an incoming connection on |listen_socket_|, but passes back
+  // a raw SocketDescriptor instead of a StreamSocket.
+  int Accept(SocketDescriptor* socket_descriptor,
+             const CompletionCallback& callback);
+
+ private:
+  // A callback to wrap the setting of the out-parameter to Accept().
+  // This allows the internal machinery of that call to be implemented in
+  // a manner that's agnostic to the caller's desired output.
+  typedef base::Callback<void(scoped_ptr<SocketLibevent>)> SetterCallback;
+
+  int DoAccept(const SetterCallback& setter_callback,
+               const CompletionCallback& callback);
+  void AcceptCompleted(const SetterCallback& setter_callback,
+                       const CompletionCallback& callback,
+                       int rv);
+  bool AuthenticateAndGetStreamSocket(const SetterCallback& setter_callback);
+
+  scoped_ptr<SocketLibevent> listen_socket_;
+  const AuthCallback auth_callback_;
+  const bool use_abstract_namespace_;
+
+  scoped_ptr<SocketLibevent> accept_socket_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnixDomainServerSocket);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DOMAIN_SOCKET_UNIX_DOMAIN_SERVER_SOCKET_POSIX_H_
diff --git a/shell/domain_socket/unix_domain_server_socket_posix_unittest.cc b/shell/domain_socket/unix_domain_server_socket_posix_unittest.cc
new file mode 100644
index 0000000..96760f2
--- /dev/null
+++ b/shell/domain_socket/unix_domain_server_socket_posix_unittest.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/domain_socket/unix_domain_server_socket_posix.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/test_completion_callback.h"
+#include "shell/domain_socket/unix_domain_client_socket_posix.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+const char kSocketFilename[] = "socket_for_testing";
+const char kInvalidSocketPath[] = "/invalid/path";
+
+bool UserCanConnectCallback(
+    bool allow_user,
+    const UnixDomainServerSocket::Credentials& credentials) {
+  // Here peers are running in same process.
+  EXPECT_EQ(getpid(), credentials.process_id);
+  EXPECT_EQ(getuid(), credentials.user_id);
+  EXPECT_EQ(getgid(), credentials.group_id);
+  return allow_user;
+}
+
+UnixDomainServerSocket::AuthCallback CreateAuthCallback(bool allow_user) {
+  return base::Bind(&UserCanConnectCallback, allow_user);
+}
+}  // namespace
+
+class UnixDomainServerSocketTest : public testing::Test {
+ protected:
+  UnixDomainServerSocketTest() {
+    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+    socket_path_ = temp_dir_.path().Append(kSocketFilename).value();
+  }
+
+  base::ScopedTempDir temp_dir_;
+  std::string socket_path_;
+  base::MessageLoopForIO loop_;
+};
+
+TEST_F(UnixDomainServerSocketTest, ListenWithInvalidPath) {
+  const bool kUseAbstractNamespace = false;
+  UnixDomainServerSocket server_socket(CreateAuthCallback(true),
+                                       kUseAbstractNamespace);
+  EXPECT_EQ(net::ERR_FILE_NOT_FOUND,
+            server_socket.ListenWithPath(kInvalidSocketPath, 1));
+}
+
+TEST_F(UnixDomainServerSocketTest, ListenWithInvalidPathWithAbstractNamespace) {
+  const bool kUseAbstractNamespace = true;
+  UnixDomainServerSocket server_socket(CreateAuthCallback(true),
+                                       kUseAbstractNamespace);
+  EXPECT_EQ(net::OK, server_socket.ListenWithPath(kInvalidSocketPath, 1));
+}
+
+TEST_F(UnixDomainServerSocketTest, ListenAgainAfterFailureWithInvalidPath) {
+  const bool kUseAbstractNamespace = false;
+  UnixDomainServerSocket server_socket(CreateAuthCallback(true),
+                                       kUseAbstractNamespace);
+  EXPECT_EQ(net::ERR_FILE_NOT_FOUND,
+            server_socket.ListenWithPath(kInvalidSocketPath, 1));
+  EXPECT_EQ(net::OK, server_socket.ListenWithPath(socket_path_, 1));
+}
+
+TEST_F(UnixDomainServerSocketTest, AcceptWithForbiddenUser) {
+  const bool kUseAbstractNamespace = false;
+
+  UnixDomainServerSocket server_socket(CreateAuthCallback(false),
+                                       kUseAbstractNamespace);
+  EXPECT_EQ(net::OK, server_socket.ListenWithPath(socket_path_, 1));
+
+  SocketDescriptor accepted_socket = kInvalidSocket;
+  TestCompletionCallback accept_callback;
+  EXPECT_EQ(net::ERR_IO_PENDING,
+            server_socket.Accept(&accepted_socket, accept_callback.callback()));
+  EXPECT_EQ(accepted_socket, kInvalidSocket);
+
+  UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace);
+  EXPECT_FALSE(client_socket.IsConnected());
+
+  // Connect() will return net::OK before the server rejects the connection.
+  TestCompletionCallback connect_callback;
+  int rv = connect_callback.GetResult(
+      client_socket.Connect(connect_callback.callback()));
+  ASSERT_EQ(net::OK, rv);
+
+  // Run message loop so server can process incoming connection attempt.
+  {
+    base::RunLoop run_loop;
+    run_loop.RunUntilIdle();
+  }
+  EXPECT_FALSE(client_socket.IsConnected());
+
+  // The server socket should not have called |accept_callback| or modified
+  // |accepted_socket|.
+  EXPECT_FALSE(accept_callback.have_result());
+  EXPECT_EQ(accepted_socket, kInvalidSocket);
+}
+
+// Normal cases including read/write are tested by UnixDomainClientSocketTest.
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/dynamic_application_loader.cc b/shell/dynamic_application_loader.cc
new file mode 100644
index 0000000..e8a4fb2
--- /dev/null
+++ b/shell/dynamic_application_loader.cc
@@ -0,0 +1,406 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/dynamic_application_loader.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "shell/context.h"
+#include "shell/data_pipe_peek.h"
+#include "shell/filename_util.h"
+#include "shell/switches.h"
+#include "url/url_util.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+static const char kMojoMagic[] = "#!mojo:";
+static const size_t kMaxShebangLength = 2048;
+
+void IgnoreResult(bool result) {
+}
+
+}  // namespace
+
+// Encapsulates loading and running one individual application.
+//
+// Loaders are owned by DynamicApplicationLoader. DynamicApplicationLoader must
+// ensure that all the parameters passed to Loader subclasses stay valid through
+// Loader's lifetime.
+//
+// Async operations are done with WeakPtr to protect against
+// DynamicApplicationLoader going away (and taking all the Loaders with it)
+// while the async operation is outstanding.
+class DynamicApplicationLoader::Loader {
+ public:
+  Loader(MimeTypeToURLMap* mime_type_to_url,
+         Context* context,
+         DynamicServiceRunnerFactory* runner_factory,
+         ScopedMessagePipeHandle shell_handle,
+         ApplicationLoader::LoadCallback load_callback,
+         const LoaderCompleteCallback& loader_complete_callback)
+      : shell_handle_(shell_handle.Pass()),
+        load_callback_(load_callback),
+        loader_complete_callback_(loader_complete_callback),
+        context_(context),
+        mime_type_to_url_(mime_type_to_url),
+        runner_factory_(runner_factory),
+        weak_ptr_factory_(this) {}
+
+  virtual ~Loader() {}
+
+ protected:
+  virtual URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
+                                       uint32_t skip) = 0;
+
+  virtual void AsPath(
+      base::TaskRunner* task_runner,
+      base::Callback<void(const base::FilePath&, bool)> callback) = 0;
+
+  virtual std::string MimeType() = 0;
+
+  virtual bool HasMojoMagic() = 0;
+
+  virtual bool PeekFirstLine(std::string* line) = 0;
+
+  void Load() {
+    // If the response begins with a #!mojo:<content-handler-url>, use it.
+    GURL url;
+    std::string shebang;
+    if (PeekContentHandler(&shebang, &url)) {
+      load_callback_.Run(
+          url, shell_handle_.Pass(),
+          AsURLResponse(context_->task_runners()->blocking_pool(),
+                        static_cast<int>(shebang.size())));
+      return;
+    }
+
+    MimeTypeToURLMap::iterator iter = mime_type_to_url_->find(MimeType());
+    if (iter != mime_type_to_url_->end()) {
+      load_callback_.Run(
+          iter->second, shell_handle_.Pass(),
+          AsURLResponse(context_->task_runners()->blocking_pool(), 0));
+      return;
+    }
+
+    // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
+    // application. That could either mean looking for the platform-specific dll
+    // header, or looking for some specific mojo signature prepended to the
+    // library.
+
+    AsPath(context_->task_runners()->blocking_pool(),
+           base::Bind(&Loader::RunLibrary, weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void ReportComplete() { loader_complete_callback_.Run(this); }
+
+ private:
+  bool PeekContentHandler(std::string* mojo_shebang,
+                          GURL* mojo_content_handler_url) {
+    std::string shebang;
+    if (HasMojoMagic() && PeekFirstLine(&shebang)) {
+      GURL url(shebang.substr(2, std::string::npos));
+      if (url.is_valid()) {
+        *mojo_shebang = shebang;
+        *mojo_content_handler_url = url;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void RunLibrary(const base::FilePath& path, bool path_exists) {
+    DCHECK(shell_handle_.is_valid());
+
+    if (!path_exists) {
+      LOG(ERROR) << "Library not started because library path '" << path.value()
+                 << "' does not exist.";
+      ReportComplete();
+      return;
+    }
+
+    runner_ = runner_factory_->Create(context_);
+    runner_->Start(
+        path, shell_handle_.Pass(),
+        base::Bind(&Loader::ReportComplete, weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  ScopedMessagePipeHandle shell_handle_;
+  ApplicationLoader::LoadCallback load_callback_;
+  LoaderCompleteCallback loader_complete_callback_;
+  Context* context_;
+  MimeTypeToURLMap* mime_type_to_url_;
+  DynamicServiceRunnerFactory* runner_factory_;
+  scoped_ptr<DynamicServiceRunner> runner_;
+  base::WeakPtrFactory<Loader> weak_ptr_factory_;
+};
+
+// A loader for local files.
+class DynamicApplicationLoader::LocalLoader : public Loader {
+ public:
+  LocalLoader(const GURL& url,
+              MimeTypeToURLMap* mime_type_to_url,
+              Context* context,
+              DynamicServiceRunnerFactory* runner_factory,
+              ScopedMessagePipeHandle shell_handle,
+              ApplicationLoader::LoadCallback load_callback,
+              const LoaderCompleteCallback& loader_complete_callback)
+      : Loader(mime_type_to_url,
+               context,
+               runner_factory,
+               shell_handle.Pass(),
+               load_callback,
+               loader_complete_callback),
+        url_(url),
+        path_(UrlToFile(url)) {
+    Load();
+  }
+
+ private:
+  static base::FilePath UrlToFile(const GURL& url) {
+    DCHECK(url.SchemeIsFile());
+    url::RawCanonOutputW<1024> output;
+    url::DecodeURLEscapeSequences(
+        url.path().data(), static_cast<int>(url.path().length()), &output);
+    base::string16 decoded_path =
+        base::string16(output.data(), output.length());
+#if defined(OS_WIN)
+    base::TrimString(decoded_path, L"/", &decoded_path);
+    base::FilePath path(decoded_path);
+#else
+    base::FilePath path(base::UTF16ToUTF8(decoded_path));
+#endif
+    return path;
+  }
+
+  URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
+                               uint32_t skip) override {
+    URLResponsePtr response(URLResponse::New());
+    response->url = String::From(url_);
+    DataPipe data_pipe;
+    response->body = data_pipe.consumer_handle.Pass();
+    int64 file_size;
+    if (base::GetFileSize(path_, &file_size)) {
+      response->headers = Array<String>(1);
+      response->headers[0] =
+          base::StringPrintf("Content-Length: %" PRId64, file_size);
+    }
+    common::CopyFromFile(path_, data_pipe.producer_handle.Pass(), skip,
+                         task_runner, base::Bind(&IgnoreResult));
+    return response.Pass();
+  }
+
+  void AsPath(
+      base::TaskRunner* task_runner,
+      base::Callback<void(const base::FilePath&, bool)> callback) override {
+    // Async for consistency with network case.
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
+  }
+
+  std::string MimeType() override { return ""; }
+
+  bool HasMojoMagic() override {
+    std::string magic;
+    ReadFileToString(path_, &magic, strlen(kMojoMagic));
+    return magic == kMojoMagic;
+  }
+
+  bool PeekFirstLine(std::string* line) override {
+    std::string start_of_file;
+    ReadFileToString(path_, &start_of_file, kMaxShebangLength);
+    size_t return_position = start_of_file.find('\n');
+    if (return_position == std::string::npos)
+      return false;
+    *line = start_of_file.substr(0, return_position + 1);
+    return true;
+  }
+
+  GURL url_;
+  base::FilePath path_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalLoader);
+};
+
+// A loader for network files.
+class DynamicApplicationLoader::NetworkLoader : public Loader {
+ public:
+  NetworkLoader(const GURL& url,
+                NetworkService* network_service,
+                MimeTypeToURLMap* mime_type_to_url,
+                Context* context,
+                DynamicServiceRunnerFactory* runner_factory,
+                ScopedMessagePipeHandle shell_handle,
+                ApplicationLoader::LoadCallback load_callback,
+                const LoaderCompleteCallback& loader_complete_callback)
+      : Loader(mime_type_to_url,
+               context,
+               runner_factory,
+               shell_handle.Pass(),
+               load_callback,
+               loader_complete_callback),
+        weak_ptr_factory_(this) {
+    StartNetworkRequest(url, network_service);
+  }
+
+  ~NetworkLoader() override {
+    if (!path_.empty())
+      base::DeleteFile(path_, false);
+  }
+
+ private:
+  // TODO(hansmuller): Revisit this when a real peek operation is available.
+  static const MojoDeadline kPeekTimeout = MOJO_DEADLINE_INDEFINITE;
+
+  URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
+                               uint32_t skip) override {
+    if (skip != 0) {
+      MojoResult result = ReadDataRaw(
+          response_->body.get(), nullptr, &skip,
+          MOJO_READ_DATA_FLAG_ALL_OR_NONE | MOJO_READ_DATA_FLAG_DISCARD);
+      DCHECK_EQ(result, MOJO_RESULT_OK);
+    }
+    return response_.Pass();
+  }
+
+  void AsPath(
+      base::TaskRunner* task_runner,
+      base::Callback<void(const base::FilePath&, bool)> callback) override {
+    if (!path_.empty() || !response_) {
+      base::MessageLoop::current()->PostTask(
+          FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
+      return;
+    }
+    base::CreateTemporaryFile(&path_);
+    common::CopyToFile(response_->body.Pass(), path_, task_runner,
+                       base::Bind(callback, path_));
+  }
+
+  std::string MimeType() override {
+    DCHECK(response_);
+    return response_->mime_type;
+  }
+
+  bool HasMojoMagic() override {
+    std::string magic;
+    return BlockingPeekNBytes(response_->body.get(), &magic, strlen(kMojoMagic),
+                              kPeekTimeout) &&
+           magic == kMojoMagic;
+  }
+
+  bool PeekFirstLine(std::string* line) override {
+    return BlockingPeekLine(response_->body.get(), line, kMaxShebangLength,
+                            kPeekTimeout);
+  }
+
+  void StartNetworkRequest(const GURL& url, NetworkService* network_service) {
+    URLRequestPtr request(URLRequest::New());
+    request->url = String::From(url);
+    request->auto_follow_redirects = true;
+
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kDisableCache)) {
+      request->bypass_cache = true;
+    }
+
+    network_service->CreateURLLoader(GetProxy(&url_loader_));
+    url_loader_->Start(request.Pass(),
+                       base::Bind(&NetworkLoader::OnLoadComplete,
+                                  weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void OnLoadComplete(URLResponsePtr response) {
+    if (response->error) {
+      LOG(ERROR) << "Error (" << response->error->code << ": "
+                 << response->error->description << ") while fetching "
+                 << response->url;
+      ReportComplete();
+      return;
+    }
+    response_ = response.Pass();
+    Load();
+  }
+
+  URLLoaderPtr url_loader_;
+  URLResponsePtr response_;
+  base::FilePath path_;
+  base::WeakPtrFactory<NetworkLoader> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkLoader);
+};
+
+DynamicApplicationLoader::DynamicApplicationLoader(
+    Context* context,
+    scoped_ptr<DynamicServiceRunnerFactory> runner_factory)
+    : context_(context),
+      runner_factory_(runner_factory.Pass()),
+
+      // Unretained() is correct here because DynamicApplicationLoader owns the
+      // loaders that we pass this callback to.
+      loader_complete_callback_(
+          base::Bind(&DynamicApplicationLoader::LoaderComplete,
+                     base::Unretained(this))) {
+}
+
+DynamicApplicationLoader::~DynamicApplicationLoader() {
+}
+
+void DynamicApplicationLoader::RegisterContentHandler(
+    const std::string& mime_type,
+    const GURL& content_handler_url) {
+  DCHECK(content_handler_url.is_valid())
+      << "Content handler URL is invalid for mime type " << mime_type;
+  mime_type_to_url_[mime_type] = content_handler_url;
+}
+
+void DynamicApplicationLoader::Load(ApplicationManager* manager,
+                                    const GURL& url,
+                                    ScopedMessagePipeHandle shell_handle,
+                                    LoadCallback load_callback) {
+  if (url.SchemeIsFile()) {
+    loaders_.push_back(new LocalLoader(
+        url, &mime_type_to_url_, context_, runner_factory_.get(),
+        shell_handle.Pass(), load_callback, loader_complete_callback_));
+    return;
+  }
+
+  if (!network_service_) {
+    context_->application_manager()->ConnectToService(
+        GURL("mojo:network_service"), &network_service_);
+  }
+
+  loaders_.push_back(
+      new NetworkLoader(url, network_service_.get(), &mime_type_to_url_,
+                        context_, runner_factory_.get(), shell_handle.Pass(),
+                        load_callback, loader_complete_callback_));
+}
+
+void DynamicApplicationLoader::OnApplicationError(ApplicationManager* manager,
+                                                  const GURL& url) {
+  // TODO(darin): What should we do about service errors? This implies that
+  // the app closed its handle to the service manager. Maybe we don't care?
+}
+
+void DynamicApplicationLoader::LoaderComplete(Loader* loader) {
+  loaders_.erase(std::find(loaders_.begin(), loaders_.end(), loader));
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/dynamic_application_loader.h b/shell/dynamic_application_loader.h
new file mode 100644
index 0000000..1dc9a8a
--- /dev/null
+++ b/shell/dynamic_application_loader.h
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DYNAMIC_APPLICATION_LOADER_H_
+#define SHELL_DYNAMIC_APPLICATION_LOADER_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "shell/dynamic_service_runner.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+class Context;
+class DynamicServiceRunnerFactory;
+class DynamicServiceRunner;
+
+// An implementation of ApplicationLoader that retrieves a dynamic library
+// containing the implementation of the service and loads/runs it (via a
+// DynamicServiceRunner).
+class DynamicApplicationLoader : public ApplicationLoader {
+ public:
+  DynamicApplicationLoader(
+      Context* context,
+      scoped_ptr<DynamicServiceRunnerFactory> runner_factory);
+  ~DynamicApplicationLoader() override;
+
+  void RegisterContentHandler(const std::string& mime_type,
+                              const GURL& content_handler_url);
+
+  // ApplicationLoader methods:
+  void Load(ApplicationManager* manager,
+            const GURL& url,
+            ScopedMessagePipeHandle shell_handle,
+            LoadCallback callback) override;
+  void OnApplicationError(ApplicationManager* manager,
+                          const GURL& url) override;
+
+ private:
+  class Loader;
+  class LocalLoader;
+  class NetworkLoader;
+
+  typedef std::map<std::string, GURL> MimeTypeToURLMap;
+  typedef base::Callback<void(Loader*)> LoaderCompleteCallback;
+
+  void LoaderComplete(Loader* loader);
+
+  Context* const context_;
+  scoped_ptr<DynamicServiceRunnerFactory> runner_factory_;
+  NetworkServicePtr network_service_;
+  MimeTypeToURLMap mime_type_to_url_;
+  ScopedVector<Loader> loaders_;
+  LoaderCompleteCallback loader_complete_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(DynamicApplicationLoader);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DYNAMIC_APPLICATION_LOADER_H_
diff --git a/shell/dynamic_application_loader_unittest.cc b/shell/dynamic_application_loader_unittest.cc
new file mode 100644
index 0000000..ad4726d
--- /dev/null
+++ b/shell/dynamic_application_loader_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/scoped_temp_dir.h"
+#include "shell/context.h"
+#include "shell/dynamic_application_loader.h"
+#include "shell/dynamic_service_runner.h"
+#include "shell/filename_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+struct TestState {
+  TestState()
+      : runner_was_created(false),
+        runner_was_started(false),
+        runner_was_destroyed(false) {}
+
+  bool runner_was_created;
+  bool runner_was_started;
+  bool runner_was_destroyed;
+};
+
+class TestDynamicServiceRunner : public DynamicServiceRunner {
+ public:
+  explicit TestDynamicServiceRunner(TestState* state) : state_(state) {
+    state_->runner_was_created = true;
+  }
+  ~TestDynamicServiceRunner() override {
+    state_->runner_was_destroyed = true;
+    base::MessageLoop::current()->Quit();
+  }
+  void Start(const base::FilePath& app_path,
+             ScopedMessagePipeHandle service_handle,
+             const base::Closure& app_completed_callback) override {
+    state_->runner_was_started = true;
+  }
+
+ private:
+  TestState* state_;
+};
+
+class TestDynamicServiceRunnerFactory : public DynamicServiceRunnerFactory {
+ public:
+  explicit TestDynamicServiceRunnerFactory(TestState* state) : state_(state) {}
+  ~TestDynamicServiceRunnerFactory() override {}
+  scoped_ptr<DynamicServiceRunner> Create(Context* context) override {
+    return scoped_ptr<DynamicServiceRunner>(
+        new TestDynamicServiceRunner(state_));
+  }
+
+ private:
+  TestState* state_;
+};
+
+}  // namespace
+
+class DynamicApplicationLoaderTest : public testing::Test {
+ public:
+  DynamicApplicationLoaderTest() {}
+  ~DynamicApplicationLoaderTest() override {}
+  void SetUp() override {
+    context_.Init();
+    scoped_ptr<DynamicServiceRunnerFactory> factory(
+        new TestDynamicServiceRunnerFactory(&state_));
+    loader_.reset(new DynamicApplicationLoader(&context_, factory.Pass()));
+  }
+
+ protected:
+  Context context_;
+  base::MessageLoop loop_;
+  scoped_ptr<DynamicApplicationLoader> loader_;
+  TestState state_;
+};
+
+TEST_F(DynamicApplicationLoaderTest, DoesNotExist) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath nonexistent_file(FILE_PATH_LITERAL("nonexistent.txt"));
+  GURL url(FilePathToFileURL(temp_dir.path().Append(nonexistent_file)));
+  MessagePipe pipe;
+  loader_->Load(context_.application_manager(), url, pipe.handle0.Pass(),
+                ApplicationLoader::SimpleLoadCallback());
+  EXPECT_FALSE(state_.runner_was_created);
+  EXPECT_FALSE(state_.runner_was_started);
+  EXPECT_FALSE(state_.runner_was_destroyed);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/dynamic_service_runner.cc b/shell/dynamic_service_runner.cc
new file mode 100644
index 0000000..fc0a0ab
--- /dev/null
+++ b/shell/dynamic_service_runner.cc
@@ -0,0 +1,112 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/dynamic_service_runner.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h"
+#include "mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h"
+#include "mojo/public/platform/native/gles2_impl_thunks.h"
+#include "mojo/public/platform/native/gles2_thunks.h"
+#include "mojo/public/platform/native/system_thunks.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+template <typename Thunks>
+bool SetThunks(Thunks (*make_thunks)(),
+               const char* function_name,
+               base::NativeLibrary library) {
+  typedef size_t (*SetThunksFn)(const Thunks* thunks);
+  SetThunksFn set_thunks = reinterpret_cast<SetThunksFn>(
+      base::GetFunctionPointerFromNativeLibrary(library, function_name));
+  if (!set_thunks)
+    return false;
+  Thunks thunks = make_thunks();
+  size_t expected_size = set_thunks(&thunks);
+  if (expected_size > sizeof(Thunks)) {
+    LOG(ERROR) << "Invalid app library: expected " << function_name
+               << " to return thunks of size: " << expected_size;
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+base::NativeLibrary DynamicServiceRunner::LoadAndRunService(
+    const base::FilePath& app_path,
+    ScopedMessagePipeHandle service_handle) {
+  DVLOG(2) << "Loading/running Mojo app in process from library: "
+           << app_path.value();
+  base::NativeLibraryLoadError error;
+  base::NativeLibrary app_library = base::LoadNativeLibrary(app_path, &error);
+  do {
+    if (!app_library) {
+      LOG(ERROR) << "Failed to load app library (error: " << error.ToString()
+                 << ")";
+      break;
+    }
+    // Go shared library support requires us to initialize the runtime before we
+    // start running any go code. This is a temporary patch.
+    typedef void (*InitGoRuntimeFn)();
+    InitGoRuntimeFn init_go_runtime = reinterpret_cast<InitGoRuntimeFn>(
+        base::GetFunctionPointerFromNativeLibrary(app_library,
+                                                  "InitGoRuntime"));
+    if (init_go_runtime) {
+      DVLOG(2) << "InitGoRuntime: Initializing Go Runtime found in app";
+      init_go_runtime();
+    }
+
+    if (!SetThunks(&MojoMakeSystemThunks, "MojoSetSystemThunks", app_library)) {
+      LOG(ERROR) << app_path.value() << " MojoSetSystemThunks not found";
+      break;
+    }
+
+    if (SetThunks(&MojoMakeGLES2ControlThunks, "MojoSetGLES2ControlThunks",
+                  app_library)) {
+      // If we have the control thunks, we should also have the GLES2
+      // implementation thunks.
+      if (!SetThunks(&MojoMakeGLES2ImplThunks, "MojoSetGLES2ImplThunks",
+                     app_library)) {
+        LOG(ERROR) << app_path.value()
+                   << " has MojoSetGLES2ControlThunks, "
+                      "but doesn't have MojoSetGLES2ImplThunks.";
+        break;
+      }
+
+      // If the application is using GLES2 extension points, register those
+      // thunks. Applications may use or not use any of these, so don't warn if
+      // they are missing.
+      SetThunks(MojoMakeGLES2ImplChromiumTextureMailboxThunks,
+                "MojoSetGLES2ImplChromiumTextureMailboxThunks", app_library);
+      SetThunks(MojoMakeGLES2ImplChromiumSyncPointThunks,
+                "MojoSetGLES2ImplChromiumSyncPointThunks", app_library);
+    }
+    // Unlike system thunks, we don't warn on a lack of GLES2 thunks because
+    // not everything is a visual app.
+
+    typedef MojoResult (*MojoMainFunction)(MojoHandle);
+    MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>(
+        base::GetFunctionPointerFromNativeLibrary(app_library, "MojoMain"));
+    if (!main_function) {
+      LOG(ERROR) << app_path.value() << " MojoMain not found";
+      break;
+    }
+    // |MojoMain()| takes ownership of the service handle.
+    MojoResult result = main_function(service_handle.release().value());
+    if (result < MOJO_RESULT_OK) {
+      LOG(ERROR) << app_path.value() << " MojoMain returned error(" << result
+                 << ")";
+    }
+  } while (false);
+
+  return app_library;
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/dynamic_service_runner.h b/shell/dynamic_service_runner.h
new file mode 100644
index 0000000..7408789
--- /dev/null
+++ b/shell/dynamic_service_runner.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_DYNAMIC_SERVICE_RUNNER_H_
+#define SHELL_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/native_library.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+// Abstraction for loading a service (from the file system) and running it (on
+// another thread or in a separate process).
+class DynamicServiceRunner {
+ public:
+  virtual ~DynamicServiceRunner() {}
+
+  // Takes ownership of the file at |app_path|. Loads the app in that file and
+  // runs it on some other thread/process. |app_completed_callback| is posted
+  // (to the thread on which |Start()| was called) after |MojoMain()| completes.
+  virtual void Start(const base::FilePath& app_path,
+                     ScopedMessagePipeHandle service_handle,
+                     const base::Closure& app_completed_callback) = 0;
+
+  // Loads the service in the DSO specificed by |app_path| and prepares it for
+  // execution. Runs the DSO's exported function MojoMain().
+  // The NativeLibrary is returned and ownership transferred to the caller.
+  // This is so if it is unloaded at all, this can be done safely after this
+  // thread is destroyed and any thread-local destructors have been executed.
+  static base::NativeLibrary LoadAndRunService(
+      const base::FilePath& app_path,
+      ScopedMessagePipeHandle service_handle);
+};
+
+class DynamicServiceRunnerFactory {
+ public:
+  virtual ~DynamicServiceRunnerFactory() {}
+  virtual scoped_ptr<DynamicServiceRunner> Create(Context* context) = 0;
+};
+
+// A generic factory.
+template <class DynamicServiceRunnerImpl>
+class DynamicServiceRunnerFactoryImpl : public DynamicServiceRunnerFactory {
+ public:
+  DynamicServiceRunnerFactoryImpl() {}
+  virtual ~DynamicServiceRunnerFactoryImpl() {}
+  virtual scoped_ptr<DynamicServiceRunner> Create(Context* context) override {
+    return scoped_ptr<DynamicServiceRunner>(
+        new DynamicServiceRunnerImpl(context));
+  }
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/shell/external_application_listener.h b/shell/external_application_listener.h
new file mode 100644
index 0000000..15b930e
--- /dev/null
+++ b/shell/external_application_listener.h
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_EXTERNAL_APPLICATION_LISTENER_H_
+#define SHELL_EXTERNAL_APPLICATION_LISTENER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// In order to support Mojo apps whose lifetime is managed by
+// something other than mojo_shell, mojo_shell needs to support a
+// mechanism by which such an application can discover a running shell
+// instance, connect to it, and ask to be "registered" at a given
+// URL. Registration implies that the app can be connected to at that
+// URL from then on out, and that the app has received a usable ShellPtr.
+//
+// External applications can connect to the shell using the
+// ExternalApplicationRegistrarConnection class.
+class ExternalApplicationListener {
+ public:
+  // When run, a RegisterCallback should note that an app has asked to be
+  // registered at app_url and Bind the provided pipe handle to a ShellImpl.
+  typedef base::Callback<void(const GURL& app_url,
+                              ScopedMessagePipeHandle shell)> RegisterCallback;
+  typedef base::Callback<void(int rv)> ErrorCallback;
+
+  virtual ~ExternalApplicationListener() {}
+
+  // Implementations of this class may use two threads, an IO thread for
+  // listening and accepting incoming sockets, and a "main" thread
+  // where all Mojo traffic is processed and provided callbacks are run.
+  static scoped_ptr<ExternalApplicationListener> Create(
+      const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+      const scoped_refptr<base::SequencedTaskRunner>& io_runner);
+
+  static base::FilePath ConstructDefaultSocketPath();
+
+  // Begin listening (on io_runner) to a socket at listen_socket_path.
+  // Incoming registration requests will be forwarded to register_callback.
+  // Errors are ignored.
+  virtual void ListenInBackground(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback) = 0;
+
+  // Begin listening (on io_runner) to a socket at listen_socket_path.
+  // Incoming registration requests will be forwarded to register_callback.
+  // Errors are reported via error_callback.
+  virtual void ListenInBackgroundWithErrorCallback(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback,
+      const ErrorCallback& error_callback) = 0;
+
+  // Block the current thread until listening has started on io_runner.
+  // If listening has already started, returns immediately.
+  virtual void WaitForListening() = 0;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_EXTERNAL_APPLICATION_LISTENER_H_
diff --git a/shell/external_application_listener_posix.cc b/shell/external_application_listener_posix.cc
new file mode 100644
index 0000000..adc6725
--- /dev/null
+++ b/shell/external_application_listener_posix.cc
@@ -0,0 +1,194 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/external_application_listener_posix.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_checker.h"
+#include "base/tracked_objects.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "shell/external_application_registrar.mojom.h"
+#include "shell/incoming_connection_listener_posix.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+const char kDefaultListenSocketPath[] = "/var/run/mojo/system_socket";
+}  // namespace
+
+// static
+base::FilePath ExternalApplicationListener::ConstructDefaultSocketPath() {
+  return base::FilePath(FILE_PATH_LITERAL(kDefaultListenSocketPath));
+}
+
+// static
+scoped_ptr<ExternalApplicationListener> ExternalApplicationListener::Create(
+    const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& io_runner) {
+  return make_scoped_ptr(
+      new ExternalApplicationListenerPosix(shell_runner, io_runner));
+}
+
+class ExternalApplicationListenerPosix::RegistrarImpl
+    : public InterfaceImpl<ExternalApplicationRegistrar> {
+ public:
+  explicit RegistrarImpl(const RegisterCallback& callback);
+  ~RegistrarImpl() override;
+
+  void OnConnectionError() override;
+
+  embedder::ChannelInit channel_init;
+
+ private:
+  virtual void Register(
+      const String& app_url,
+      const mojo::Callback<void(ShellPtr)>& callback) override;
+
+  const RegisterCallback register_callback_;
+};
+
+ExternalApplicationListenerPosix::ExternalApplicationListenerPosix(
+    const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& io_runner)
+    : shell_runner_(shell_runner),
+      io_runner_(io_runner),
+      signal_on_listening_(true, false),
+      weak_ptr_factory_(this) {
+  DCHECK(shell_runner_.get() && shell_runner_->RunsTasksOnCurrentThread());
+  DCHECK(io_runner_.get());
+  listener_thread_checker_.DetachFromThread();  // Will attach in StartListener.
+}
+
+ExternalApplicationListenerPosix::~ExternalApplicationListenerPosix() {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  weak_ptr_factory_.InvalidateWeakPtrs();
+
+  // listener_ needs to be destroyed on io_runner_, and it has to die before
+  // this object does, as it holds a pointer back to this instance.
+  base::WaitableEvent stop_listening_event(true, false);
+  io_runner_->PostTask(
+      FROM_HERE, base::Bind(&ExternalApplicationListenerPosix::StopListening,
+                            base::Unretained(this), &stop_listening_event));
+  stop_listening_event.Wait();
+}
+
+void ExternalApplicationListenerPosix::ListenInBackground(
+    const base::FilePath& listen_socket_path,
+    const RegisterCallback& register_callback) {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  ListenInBackgroundWithErrorCallback(listen_socket_path, register_callback,
+                                      ErrorCallback());
+}
+
+void ExternalApplicationListenerPosix::ListenInBackgroundWithErrorCallback(
+    const base::FilePath& listen_socket_path,
+    const RegisterCallback& register_callback,
+    const ErrorCallback& error_callback) {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  register_callback_ = register_callback;
+  error_callback_ = error_callback;
+
+  io_runner_->PostTask(
+      FROM_HERE, base::Bind(&ExternalApplicationListenerPosix::StartListening,
+                            base::Unretained(this), listen_socket_path));
+}
+
+void ExternalApplicationListenerPosix::WaitForListening() {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  signal_on_listening_.Wait();
+}
+
+void ExternalApplicationListenerPosix::StartListening(
+    const base::FilePath& listen_socket_path) {
+  CHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_IO);
+  DCHECK(listener_thread_checker_.CalledOnValidThread());
+  listener_.reset(
+      new IncomingConnectionListenerPosix(listen_socket_path, this));
+  listener_->StartListening();
+}
+
+void ExternalApplicationListenerPosix::StopListening(
+    base::WaitableEvent* event) {
+  DCHECK(listener_thread_checker_.CalledOnValidThread());
+  listener_.reset();
+  event->Signal();
+}
+
+void ExternalApplicationListenerPosix::OnListening(int rv) {
+  DCHECK(listener_thread_checker_.CalledOnValidThread());
+  signal_on_listening_.Signal();
+  shell_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &ExternalApplicationListenerPosix::RunErrorCallbackIfListeningFailed,
+          weak_ptr_factory_.GetWeakPtr(), rv));
+}
+
+void ExternalApplicationListenerPosix::OnConnection(SocketDescriptor incoming) {
+  DCHECK(listener_thread_checker_.CalledOnValidThread());
+  shell_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(
+          &ExternalApplicationListenerPosix::CreatePipeAndBindToRegistrarImpl,
+          weak_ptr_factory_.GetWeakPtr(), incoming));
+}
+
+void ExternalApplicationListenerPosix::RunErrorCallbackIfListeningFailed(
+    int rv) {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+  if (rv != net::OK && !error_callback_.is_null()) {
+    error_callback_.Run(rv);
+  }
+}
+
+void ExternalApplicationListenerPosix::CreatePipeAndBindToRegistrarImpl(
+    SocketDescriptor incoming_socket) {
+  DCHECK(register_thread_checker_.CalledOnValidThread());
+
+  DVLOG(1) << "Accepted incoming connection";
+  scoped_ptr<RegistrarImpl> registrar(new RegistrarImpl(register_callback_));
+  // Passes ownership of incoming_socket to registrar->channel_init.
+  mojo::ScopedMessagePipeHandle message_pipe =
+      registrar->channel_init.Init(incoming_socket, io_runner_);
+  CHECK(message_pipe.is_valid());
+
+  BindToPipe(registrar.release(), message_pipe.Pass());
+}
+
+ExternalApplicationListenerPosix::RegistrarImpl::RegistrarImpl(
+    const RegisterCallback& callback)
+    : register_callback_(callback) {
+}
+
+ExternalApplicationListenerPosix::RegistrarImpl::~RegistrarImpl() {
+}
+
+void ExternalApplicationListenerPosix::RegistrarImpl::OnConnectionError() {
+  channel_init.WillDestroySoon();
+}
+
+void ExternalApplicationListenerPosix::RegistrarImpl::Register(
+    const String& app_url,
+    const mojo::Callback<void(ShellPtr)>& callback) {
+  MessagePipe pipe;
+  register_callback_.Run(app_url.To<GURL>(), pipe.handle0.Pass());
+  callback.Run(MakeProxy<Shell>(pipe.handle1.Pass()));
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/external_application_listener_posix.h b/shell/external_application_listener_posix.h
new file mode 100644
index 0000000..13e671a
--- /dev/null
+++ b/shell/external_application_listener_posix.h
@@ -0,0 +1,129 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_EXTERNAL_APPLICATION_LISTENER_POSIX_H_
+#define SHELL_EXTERNAL_APPLICATION_LISTENER_POSIX_H_
+
+#include "shell/external_application_listener.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "shell/external_application_registrar.mojom.h"
+#include "shell/incoming_connection_listener_posix.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// In order to support Mojo apps whose lifetime is managed by
+// something other than mojo_shell, mojo_shell needs to support a
+// mechanism by which such an application can discover a running shell
+// instance, connect to it, and ask to be "registered" at a given
+// URL. Registration implies that the app can be connected to at that
+// URL from then on out, and that the app has received a usable ShellPtr.
+//
+// This class implements most of the mojo_shell side of external application
+// registration. It handles:
+//  1) discoverability - sets up a unix domain socket at a well-known location,
+//  2) incoming connections - listens for and accepts incoming connections on
+//     that socket, and
+//  3) registration requests - forwarded to a RegisterCallback that implements
+//     the actual registration logic.
+//
+// External applications can connect to the shell using the
+// ExternalApplicationRegistrarConnection class.
+class ExternalApplicationListenerPosix
+    : public ExternalApplicationListener,
+      public IncomingConnectionListenerPosix::Delegate {
+ public:
+  // This class uses two threads, an IO thread for listening and accepting
+  // incoming sockets, and a "main" thread where all Mojo traffic is processed
+  // and provided callbacks are run.
+  ExternalApplicationListenerPosix(
+      const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+      const scoped_refptr<base::SequencedTaskRunner>& io_runner);
+
+  // Some of this class' internal state needs to be destroyed on io_runner_,
+  // so the destructor will post a task to that thread to call StopListening()
+  // and then wait for it to complete.
+  ~ExternalApplicationListenerPosix() override;
+
+  // Begin listening (on io_runner) to a socket at listen_socket_path.
+  // Incoming registration requests will be forwarded to register_callback.
+  // Errors are ignored.
+  void ListenInBackground(const base::FilePath& listen_socket_path,
+                          const RegisterCallback& register_callback) override;
+
+  // Begin listening (on io_runner) to a socket at listen_socket_path.
+  // Incoming registration requests will be forwarded to register_callback.
+  // Errors are reported via error_callback.
+  void ListenInBackgroundWithErrorCallback(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback,
+      const ErrorCallback& error_callback) override;
+
+  // Block the current thread until listening has started on io_runner.
+  // If listening has already started, returns immediately.
+  void WaitForListening() override;
+
+ private:
+  class RegistrarImpl;
+
+  // MUST be called on io_runner.
+  // Creates listener_ and tells it to StartListening() on a socket it creates
+  // at listen_socket_path.
+  void StartListening(const base::FilePath& listen_socket_path);
+
+  // MUST be called on io_runner.
+  // Destroys listener_ and signals event when done.
+  void StopListening(base::WaitableEvent* event);
+
+  // Implementation of IncomingConnectionListener::Delegate
+  void OnListening(int rv) override;
+  void OnConnection(SocketDescriptor incoming) override;
+
+  // If listener_ fails to start listening, this method is run on shell_runner_
+  // to report the error.
+  void RunErrorCallbackIfListeningFailed(int rv);
+
+  // When a connection succeeds, it is passed to this method running
+  // on shell_runner_, where it is "promoted" to a Mojo MessagePipe and
+  // bound to a RegistrarImpl.
+  void CreatePipeAndBindToRegistrarImpl(SocketDescriptor incoming_socket);
+
+  scoped_refptr<base::SequencedTaskRunner> shell_runner_;
+  scoped_refptr<base::SequencedTaskRunner> io_runner_;
+
+  // MUST be created, used, and destroyed on io_runner_.
+  scoped_ptr<IncomingConnectionListenerPosix> listener_;
+
+  // Callers can wait on this event, which will be signalled once listening
+  // has either successfully begun or definitively failed.
+  base::WaitableEvent signal_on_listening_;
+
+  // Locked to thread on which StartListening() is run (should be io_runner_).
+  // All methods that touch listener_ check that they're on that same thread.
+  base::ThreadChecker listener_thread_checker_;
+
+  ErrorCallback error_callback_;
+  RegisterCallback register_callback_;
+  base::ThreadChecker register_thread_checker_;
+
+  // Used on shell_runner_.
+  base::WeakPtrFactory<ExternalApplicationListenerPosix> weak_ptr_factory_;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_EXTERNAL_APPLICATION_LISTENER_POSIX_H_
diff --git a/shell/external_application_listener_unittest.cc b/shell/external_application_listener_unittest.cc
new file mode 100644
index 0000000..5a76f60
--- /dev/null
+++ b/shell/external_application_listener_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/application_manager/application_manager.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/test_completion_callback.h"
+#include "shell/domain_socket/unix_domain_client_socket_posix.h"
+#include "shell/external_application_listener_posix.h"
+#include "shell/external_application_registrar.mojom.h"
+#include "shell/external_application_registrar_connection.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+class NotAnApplicationLoader : public ApplicationLoader {
+ public:
+  NotAnApplicationLoader() {}
+  ~NotAnApplicationLoader() override {}
+
+  void Load(ApplicationManager* application_manager,
+            const GURL& url,
+            ScopedMessagePipeHandle shell_handle,
+            LoadCallback callback) override {
+    NOTREACHED();
+  }
+
+  void OnApplicationError(ApplicationManager* manager,
+                          const GURL& url) override {
+    NOTREACHED();
+  }
+};
+
+class ExternalApplicationListenerTest : public testing::Test {
+ public:
+  ExternalApplicationListenerTest() : io_thread_("io thread") {}
+  ~ExternalApplicationListenerTest() override {}
+
+  void SetUp() override {
+    base::Thread::Options options;
+    options.message_loop_type = base::MessageLoop::TYPE_IO;
+    io_thread_.StartWithOptions(options);
+
+    listener_.reset(new ExternalApplicationListenerPosix(
+        loop_.task_runner(), io_thread_.task_runner()));
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    socket_path_ = temp_dir_.path().Append(FILE_PATH_LITERAL("socket"));
+  }
+
+ protected:
+  base::MessageLoop loop_;
+  base::RunLoop run_loop_;
+  base::Thread io_thread_;
+
+  base::ScopedTempDir temp_dir_;
+  base::FilePath socket_path_;
+  scoped_ptr<ExternalApplicationListener> listener_;
+};
+
+namespace {
+
+class StubShellImpl : public InterfaceImpl<Shell> {
+ private:
+  void ConnectToApplication(
+      const String& requestor_url,
+      InterfaceRequest<ServiceProvider> in_service_provider) override {
+    ServiceProviderPtr out_service_provider;
+    out_service_provider.Bind(in_service_provider.PassMessagePipe());
+    client()->AcceptConnection(requestor_url, out_service_provider.Pass());
+  }
+};
+
+void DoLocalRegister(const GURL& app_url, ScopedMessagePipeHandle shell) {
+  BindToPipe(new StubShellImpl, shell.Pass());
+}
+
+void QuitLoopOnConnect(scoped_refptr<base::TaskRunner> loop,
+                       base::Closure quit_callback,
+                       int rv) {
+  EXPECT_EQ(net::OK, rv);
+  loop->PostTask(FROM_HERE, quit_callback);
+}
+
+void ConnectOnIOThread(const base::FilePath& socket_path,
+                       scoped_refptr<base::TaskRunner> to_quit,
+                       base::Closure quit_callback) {
+  ExternalApplicationRegistrarConnection connection(socket_path);
+  connection.Connect(base::Bind(&QuitLoopOnConnect, to_quit, quit_callback));
+}
+
+}  // namespace
+
+TEST_F(ExternalApplicationListenerTest, ConnectConnection) {
+  listener_->ListenInBackground(socket_path_, base::Bind(&DoLocalRegister));
+  listener_->WaitForListening();
+  io_thread_.task_runner()->PostTask(
+      FROM_HERE, base::Bind(&ConnectOnIOThread, socket_path_,
+                            loop_.task_runner(), run_loop_.QuitClosure()));
+  run_loop_.Run();
+}
+
+namespace {
+
+class QuitLoopOnConnectApplicationImpl : public InterfaceImpl<Application> {
+ public:
+  QuitLoopOnConnectApplicationImpl(const std::string& url,
+                                   scoped_refptr<base::TaskRunner> loop,
+                                   base::Closure quit_callback)
+      : url_(url), to_quit_(loop), quit_callback_(quit_callback) {}
+
+ private:
+  void Initialize(Array<String> args) override {}
+
+  void AcceptConnection(const String& requestor_url,
+                        ServiceProviderPtr p) override {
+    DVLOG(1) << url_ << " accepting connection from " << requestor_url;
+    to_quit_->PostTask(FROM_HERE, quit_callback_);
+  }
+
+  const std::string url_;
+  scoped_refptr<base::TaskRunner> to_quit_;
+  base::Closure quit_callback_;
+};
+
+class FakeExternalApplication {
+ public:
+  FakeExternalApplication(const std::string& url) : url_(url) {}
+
+  void ConnectSynchronously(const base::FilePath& socket_path) {
+    connection_.reset(new ExternalApplicationRegistrarConnection(socket_path));
+    TestCompletionCallback connect_callback;
+    connection_->Connect(connect_callback.callback());
+    connect_callback.WaitForResult();
+  }
+
+  // application_impl is the the actual implementation to be registered.
+  void Register(scoped_ptr<InterfaceImpl<Application>> application_impl,
+                base::Closure register_complete_callback) {
+    connection_->Register(
+        GURL(url_),
+        base::Bind(&FakeExternalApplication::OnRegister, base::Unretained(this),
+                   register_complete_callback));
+    application_impl_ = application_impl.Pass();
+  }
+
+  void ConnectToAppByUrl(std::string app_url) {
+    ServiceProviderPtr sp;
+    ptr_->ConnectToApplication(app_url, GetProxy(&sp));
+  }
+
+  const std::string& url() { return url_; }
+
+ private:
+  void OnRegister(base::Closure complete_callback, ShellPtr shell) {
+    ptr_ = shell.Pass();
+    ptr_.set_client(application_impl_.get());
+    complete_callback.Run();
+  }
+
+  const std::string url_;
+  scoped_ptr<InterfaceImpl<Application>> application_impl_;
+  ShellPtr ptr_;
+
+  scoped_ptr<ExternalApplicationRegistrarConnection> connection_;
+};
+
+void ConnectToApp(FakeExternalApplication* connector,
+                  FakeExternalApplication* connectee) {
+  connector->ConnectToAppByUrl(connectee->url());
+}
+
+void NoOp() {
+}
+
+void ConnectAndRegisterOnIOThread(const base::FilePath& socket_path,
+                                  scoped_refptr<base::TaskRunner> loop,
+                                  base::Closure quit_callback,
+                                  FakeExternalApplication* connector,
+                                  FakeExternalApplication* connectee) {
+  // Connect the first app to the registrar.
+  connector->ConnectSynchronously(socket_path);
+  // connector will use this implementation of the Mojo Application interface
+  // once registration complete.
+  scoped_ptr<QuitLoopOnConnectApplicationImpl> connector_app_impl(
+      new QuitLoopOnConnectApplicationImpl(connector->url(), loop,
+                                           quit_callback));
+  // Since connectee won't be ready when connector is done registering, pass
+  // in a do-nothing callback.
+  connector->Register(connector_app_impl.Pass(), base::Bind(&NoOp));
+
+  // Connect the second app to the registrar.
+  connectee->ConnectSynchronously(socket_path);
+  scoped_ptr<QuitLoopOnConnectApplicationImpl> connectee_app_impl(
+      new QuitLoopOnConnectApplicationImpl(connectee->url(), loop,
+                                           quit_callback));
+  // After connectee is successfully registered, connector should be
+  // able to connect to is by URL. Pass in a callback to attempt the
+  // app -> app connection.
+  connectee->Register(connectee_app_impl.Pass(),
+                      base::Bind(&ConnectToApp, connector, connectee));
+}
+
+void DestroyOnIOThread(scoped_ptr<FakeExternalApplication> doomed1,
+                       scoped_ptr<FakeExternalApplication> doomed2) {
+}
+
+}  // namespace
+
+// Create two external applications, have them discover and connect to
+// the registrar, and then have one app connect to the other by URL.
+TEST_F(ExternalApplicationListenerTest, ConnectTwoExternalApplications) {
+  ApplicationManager::Delegate delegate;
+  ApplicationManager application_manager(&delegate);
+  application_manager.set_default_loader(
+      scoped_ptr<ApplicationLoader>(new NotAnApplicationLoader));
+
+  listener_->ListenInBackground(
+      socket_path_, base::Bind(&ApplicationManager::RegisterExternalApplication,
+                               base::Unretained(&application_manager)));
+  listener_->WaitForListening();
+
+  // Create two external apps.
+  scoped_ptr<FakeExternalApplication> supersweet_app(
+      new FakeExternalApplication("http://my.supersweet.app"));
+  scoped_ptr<FakeExternalApplication> awesome_app(
+      new FakeExternalApplication("http://my.awesome.app"));
+
+  // Connecting and talking to the registrar has to happen on the IO thread.
+  io_thread_.task_runner()->PostTask(
+      FROM_HERE, base::Bind(&ConnectAndRegisterOnIOThread, socket_path_,
+                            loop_.task_runner(), run_loop_.QuitClosure(),
+                            supersweet_app.get(), awesome_app.get()));
+  run_loop_.Run();
+
+  // The apps need to be destroyed on the thread where they did socket stuff.
+  io_thread_.task_runner()->PostTask(
+      FROM_HERE, base::Bind(&DestroyOnIOThread, base::Passed(&supersweet_app),
+                            base::Passed(&awesome_app)));
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/external_application_listener_win.cc b/shell/external_application_listener_win.cc
new file mode 100644
index 0000000..af34598
--- /dev/null
+++ b/shell/external_application_listener_win.cc
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/external_application_listener_win.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+
+namespace mojo {
+namespace shell {
+
+// static
+base::FilePath ExternalApplicationListener::ConstructDefaultSocketPath() {
+  return base::FilePath(FILE_PATH_LITERAL(""));
+}
+
+// static
+scoped_ptr<ExternalApplicationListener> ExternalApplicationListener::Create(
+    const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& io_runner) {
+  return make_scoped_ptr(new ExternalApplicationListenerStub);
+}
+
+ExternalApplicationListenerStub::ExternalApplicationListenerStub() {
+}
+
+ExternalApplicationListenerStub::~ExternalApplicationListenerStub() {
+}
+
+void ExternalApplicationListenerStub::ListenInBackground(
+    const base::FilePath& listen_socket_path,
+    const RegisterCallback& register_callback) {
+}
+
+void ExternalApplicationListenerStub::ListenInBackgroundWithErrorCallback(
+    const base::FilePath& listen_socket_path,
+    const RegisterCallback& register_callback,
+    const ErrorCallback& error_callback) {
+}
+
+void ExternalApplicationListenerStub::WaitForListening() {
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/external_application_listener_win.h b/shell/external_application_listener_win.h
new file mode 100644
index 0000000..522d3f2
--- /dev/null
+++ b/shell/external_application_listener_win.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_EXTERNAL_APPLICATION_LISTENER_WIN_H_
+#define SHELL_EXTERNAL_APPLICATION_LISTENER_WIN_H_
+
+#include "shell/external_application_listener.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+
+namespace mojo {
+namespace shell {
+
+// External application registration is not supported on Windows, hence this
+// stub.
+class ExternalApplicationListenerStub : public ExternalApplicationListener {
+ public:
+  ExternalApplicationListenerStub();
+  virtual ~ExternalApplicationListenerStub() override;
+
+  void ListenInBackground(const base::FilePath& listen_socket_path,
+                          const RegisterCallback& register_callback) override;
+  void ListenInBackgroundWithErrorCallback(
+      const base::FilePath& listen_socket_path,
+      const RegisterCallback& register_callback,
+      const ErrorCallback& error_callback) override;
+  void WaitForListening() override;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_EXTERNAL_APPLICATION_LISTENER_WIN_H_
diff --git a/shell/external_application_registrar.mojom b/shell/external_application_registrar.mojom
new file mode 100644
index 0000000..a523f33
--- /dev/null
+++ b/shell/external_application_registrar.mojom
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo;
+
+import "mojo/public/interfaces/application/shell.mojom";
+
+// This interface allows applications running outside the auspices of a Shell
+// to request registration at the given application_url.
+// The shell implementing this interface will make the calling application
+// available to other apps at that URL and pass back a Shell bound to
+// an impl capable of servicing the external application's connection requests.
+interface ExternalApplicationRegistrar {
+  Register(string application_url) => (Shell shell);
+};
diff --git a/shell/external_application_registrar_connection.cc b/shell/external_application_registrar_connection.cc
new file mode 100644
index 0000000..2abcccc
--- /dev/null
+++ b/shell/external_application_registrar_connection.cc
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/external_application_registrar_connection.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "shell/domain_socket/unix_domain_client_socket_posix.h"
+#include "shell/external_application_registrar.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+ExternalApplicationRegistrarConnection::ExternalApplicationRegistrarConnection(
+    const base::FilePath& socket_path)
+    : client_socket_(new UnixDomainClientSocket(socket_path.value(), false)) {
+}
+
+ExternalApplicationRegistrarConnection::
+    ~ExternalApplicationRegistrarConnection() {
+  channel_init_.WillDestroySoon();
+}
+
+void ExternalApplicationRegistrarConnection::OnConnectionError() {
+  channel_init_.WillDestroySoon();
+}
+
+void ExternalApplicationRegistrarConnection::Connect(
+    const CompletionCallback& callback) {
+  DCHECK(client_socket_) << "Single use only.";
+  int rv = client_socket_->Connect(
+      base::Bind(&ExternalApplicationRegistrarConnection::OnConnect,
+                 base::Unretained(this), callback));
+  if (rv != net::ERR_IO_PENDING) {
+    DVLOG(1) << "Connect returning immediately: " << net::ErrorToString(rv);
+    OnConnect(callback, rv);
+    return;
+  }
+  DVLOG(1) << "Waiting for connection.";
+}
+
+void ExternalApplicationRegistrarConnection::Register(
+    const GURL& app_url,
+    base::Callback<void(ShellPtr)> register_complete_callback) {
+  DCHECK(!client_socket_);
+  registrar_->Register(String::From(app_url), register_complete_callback);
+}
+
+void ExternalApplicationRegistrarConnection::OnConnect(
+    CompletionCallback callback,
+    int rv) {
+  DVLOG(1) << "OnConnect called: " << net::ErrorToString(rv);
+  if (rv != net::OK) {
+    callback.Run(rv);
+    return;
+  }
+
+  mojo::ScopedMessagePipeHandle ptr_message_pipe_handle =
+      channel_init_.Init(client_socket_->ReleaseConnectedSocket(),
+                         base::MessageLoopProxy::current());
+  CHECK(ptr_message_pipe_handle.is_valid());
+  client_socket_.reset();  // This is dead now, ensure it can't be reused.
+
+  registrar_.Bind(ptr_message_pipe_handle.Pass());
+  callback.Run(rv);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/external_application_registrar_connection.h b/shell/external_application_registrar_connection.h
new file mode 100644
index 0000000..28780e4
--- /dev/null
+++ b/shell/external_application_registrar_connection.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_EXTERNAL_APPLICATION_REGISTRAR_CONNECTION_H_
+#define SHELL_EXTERNAL_APPLICATION_REGISTRAR_CONNECTION_H_
+
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "shell/domain_socket/unix_domain_client_socket_posix.h"
+#include "shell/external_application_registrar.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// Externally-running applications can use this class to discover and register
+// with a running mojo_shell instance.
+// MUST be run on an IO thread
+class ExternalApplicationRegistrarConnection : public ErrorHandler {
+ public:
+  // Configures client_socket_ to point at socket_path.
+  explicit ExternalApplicationRegistrarConnection(
+      const base::FilePath& socket_path);
+  ~ExternalApplicationRegistrarConnection() override;
+
+  // Implementation of ErrorHandler
+  void OnConnectionError() override;
+
+  // Connects client_socket_ and binds it to registrar_.
+  // Status code is passed to callback upon success or failure.
+  // May return either synchronously or asynchronously, depending on the
+  // status of the underlying socket.
+  void Connect(const CompletionCallback& callback);
+
+  // Registers this app with the shell at the provided URL.
+  void Register(const GURL& app_url,
+                base::Callback<void(ShellPtr)> register_complete_callback);
+
+ private:
+  // Handles the result of Connect(). If it was successful, promotes the socket
+  // to a MessagePipe and binds it to registrar_.
+  // Hands rv to callback regardless.
+  void OnConnect(CompletionCallback callback, int rv);
+
+  scoped_ptr<UnixDomainClientSocket> client_socket_;
+  mojo::embedder::ChannelInit channel_init_;
+  ExternalApplicationRegistrarPtr registrar_;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_EXTERNAL_APPLICATION_REGISTRAR_CONNECTION_H_
diff --git a/shell/external_application_test_main.cc b/shell/external_application_test_main.cc
new file mode 100644
index 0000000..0373185
--- /dev/null
+++ b/shell/external_application_test_main.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+  mojo::embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+      new mojo::embedder::SimplePlatformSupport()));
+
+  base::TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/shell/filename_util.cc b/shell/filename_util.cc
new file mode 100644
index 0000000..9ef0358
--- /dev/null
+++ b/shell/filename_util.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/filename_util.h"
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "url/gurl.h"
+#include "url/url_canon_internal.h"
+#include "url/url_util.h"
+
+namespace mojo {
+
+// Prefix to prepend to get a file URL.
+static const base::FilePath::CharType kFileURLPrefix[] =
+    FILE_PATH_LITERAL("file://");
+
+GURL FilePathToFileURL(const base::FilePath& path) {
+  // Produce a URL like "file:///C:/foo" for a regular file, or
+  // "file://///server/path" for UNC. The URL canonicalizer will fix up the
+  // latter case to be the canonical UNC form: "file://server/path"
+  base::FilePath::StringType url_string(kFileURLPrefix);
+  if (!path.IsAbsolute()) {
+    base::FilePath current_dir;
+    PathService::Get(base::DIR_CURRENT, &current_dir);
+    url_string.append(current_dir.value());
+    url_string.push_back(base::FilePath::kSeparators[0]);
+  }
+  url_string.append(path.value());
+
+  // Now do replacement of some characters. Since we assume the input is a
+  // literal filename, anything the URL parser might consider special should
+  // be escaped here.
+
+  // This must be the first substitution since others will introduce percents as
+  // the escape character
+  ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("%"),
+                               FILE_PATH_LITERAL("%25"));
+
+  // A semicolon is supposed to be some kind of separator according to RFC 2396.
+  ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL(";"),
+                               FILE_PATH_LITERAL("%3B"));
+
+  ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("#"),
+                               FILE_PATH_LITERAL("%23"));
+
+  ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("?"),
+                               FILE_PATH_LITERAL("%3F"));
+
+#if defined(OS_POSIX)
+  ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("\\"),
+                               FILE_PATH_LITERAL("%5C"));
+#endif
+
+  return GURL(url_string);
+}
+
+}  // namespace mojo
diff --git a/shell/filename_util.h b/shell/filename_util.h
new file mode 100644
index 0000000..10fa8fe
--- /dev/null
+++ b/shell/filename_util.h
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_FILENAME_UTIL_H_
+#define SHELL_FILENAME_UTIL_H_
+
+class GURL;
+
+namespace base {
+class FilePath;
+}
+
+namespace mojo {
+
+// Given the full path to a file name, creates a file: URL. The returned URL
+// may not be valid if the input is malformed.
+GURL FilePathToFileURL(const base::FilePath& path);
+
+}  // namespace mojo
+
+#endif  // SHELL_FILENAME_UTIL_H_
diff --git a/shell/in_process_dynamic_service_runner.cc b/shell/in_process_dynamic_service_runner.cc
new file mode 100644
index 0000000..0c0a20f
--- /dev/null
+++ b/shell/in_process_dynamic_service_runner.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/in_process_dynamic_service_runner.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/platform_thread.h"
+
+namespace mojo {
+namespace shell {
+
+InProcessDynamicServiceRunner::InProcessDynamicServiceRunner(Context* context)
+    : app_library_(nullptr) {
+}
+
+InProcessDynamicServiceRunner::~InProcessDynamicServiceRunner() {
+  if (thread_) {
+    DCHECK(thread_->HasBeenStarted());
+    DCHECK(!thread_->HasBeenJoined());
+    thread_->Join();
+  }
+
+  // It is important to let the thread exit before unloading the DSO because
+  // the library may have registered thread-local data and destructors to run
+  // on thread termination.
+  if (app_library_)
+    base::UnloadNativeLibrary(app_library_);
+}
+
+void InProcessDynamicServiceRunner::Start(
+    const base::FilePath& app_path,
+    ScopedMessagePipeHandle service_handle,
+    const base::Closure& app_completed_callback) {
+  app_path_ = app_path;
+
+  DCHECK(!service_handle_.is_valid());
+  service_handle_ = service_handle.Pass();
+
+  DCHECK(app_completed_callback_runner_.is_null());
+  app_completed_callback_runner_ =
+      base::Bind(&base::TaskRunner::PostTask, base::MessageLoopProxy::current(),
+                 FROM_HERE, app_completed_callback);
+
+  DCHECK(!thread_);
+  thread_.reset(new base::DelegateSimpleThread(this, "app_thread"));
+  thread_->Start();
+}
+
+void InProcessDynamicServiceRunner::Run() {
+  DVLOG(2) << "Loading/running Mojo app in process from library: "
+           << app_path_.value()
+           << " thread id=" << base::PlatformThread::CurrentId();
+
+  app_library_ = LoadAndRunService(app_path_, service_handle_.Pass());
+  app_completed_callback_runner_.Run();
+  app_completed_callback_runner_.Reset();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/in_process_dynamic_service_runner.h b/shell/in_process_dynamic_service_runner.h
new file mode 100644
index 0000000..c4310df
--- /dev/null
+++ b/shell/in_process_dynamic_service_runner.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+#define SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/threading/simple_thread.h"
+#include "shell/dynamic_service_runner.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |DynamicServiceRunner| that loads/runs the given app
+// (from the file system) on a separate thread (in the current process).
+class InProcessDynamicServiceRunner
+    : public DynamicServiceRunner,
+      public base::DelegateSimpleThread::Delegate {
+ public:
+  explicit InProcessDynamicServiceRunner(Context* context);
+  ~InProcessDynamicServiceRunner() override;
+
+  // |DynamicServiceRunner| method:
+  void Start(const base::FilePath& app_path,
+             ScopedMessagePipeHandle service_handle,
+             const base::Closure& app_completed_callback) override;
+
+ private:
+  // |base::DelegateSimpleThread::Delegate| method:
+  void Run() override;
+
+  base::FilePath app_path_;
+  ScopedMessagePipeHandle service_handle_;
+  base::Callback<bool(void)> app_completed_callback_runner_;
+
+  base::NativeLibrary app_library_;
+  scoped_ptr<base::DelegateSimpleThread> thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(InProcessDynamicServiceRunner);
+};
+
+typedef DynamicServiceRunnerFactoryImpl<InProcessDynamicServiceRunner>
+    InProcessDynamicServiceRunnerFactory;
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/shell/in_process_dynamic_service_runner_unittest.cc b/shell/in_process_dynamic_service_runner_unittest.cc
new file mode 100644
index 0000000..a2ff900
--- /dev/null
+++ b/shell/in_process_dynamic_service_runner_unittest.cc
@@ -0,0 +1,21 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/context.h"
+#include "shell/in_process_dynamic_service_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+
+TEST(InProcessDynamicServiceRunnerTest, NotStarted) {
+  Context context;
+  base::MessageLoop loop;
+  context.Init();
+  InProcessDynamicServiceRunner runner(&context);
+  // Shouldn't crash or DCHECK on destruction.
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/incoming_connection_listener_posix.cc b/shell/incoming_connection_listener_posix.cc
new file mode 100644
index 0000000..cf9dfb3
--- /dev/null
+++ b/shell/incoming_connection_listener_posix.cc
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/incoming_connection_listener_posix.h"
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/tracked_objects.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "shell/domain_socket/unix_domain_server_socket_posix.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+// TODO(cmasone): Figure out what we should be doing about "authenticating" the
+// process trying to connect.
+bool Yes(const UnixDomainServerSocket::Credentials& ignored) {
+  return true;
+}
+}  // anonymous namespace
+
+IncomingConnectionListenerPosix::IncomingConnectionListenerPosix(
+    const base::FilePath& socket_path,
+    Delegate* delegate)
+    : delegate_(delegate),
+      socket_path_(socket_path),
+      listen_socket_(base::Bind(&Yes), false),
+      incoming_socket_(kInvalidSocket),
+      weak_ptr_factory_(this) {
+  DCHECK(delegate_);
+}
+
+IncomingConnectionListenerPosix::~IncomingConnectionListenerPosix() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  if (!base::DeleteFile(socket_path_, false))
+    PLOG(ERROR) << "Listening Unix domain socket can't be destroyed.";
+}
+
+void IncomingConnectionListenerPosix::StartListening() {
+  DCHECK(listen_thread_checker_.CalledOnValidThread());
+
+  int rv = net::OK;
+  if (!base::DirectoryExists(socket_path_.DirName())) {
+    LOG(ERROR) << "Directory for listening socket does not exist.";
+    rv = net::ERR_FILE_NOT_FOUND;
+  } else if (!base::PathIsWritable(socket_path_.DirName())) {
+    LOG(ERROR) << "Listening socket file path is not writable.";
+    rv = net::ERR_ACCESS_DENIED;
+  } else if (!base::DeleteFile(socket_path_, false)) {
+    PLOG(ERROR) << "Listening socket file exists and can't be deleted";
+    rv = net::ERR_FILE_EXISTS;
+  } else {
+    const std::string& socket_address = socket_path_.value();
+    rv = listen_socket_.ListenWithPath(socket_address, 100);
+  }
+
+  // Call OnListening() before Accept(), so that the delegate is certain to
+  // hear about listening before a connection might be accepted below.
+  delegate_->OnListening(rv);
+  if (rv == net::OK)
+    Accept();
+}
+
+void IncomingConnectionListenerPosix::Accept() {
+  DCHECK(listen_thread_checker_.CalledOnValidThread());
+  int rv = listen_socket_.Accept(
+      &incoming_socket_, base::Bind(&IncomingConnectionListenerPosix::OnAccept,
+                                    weak_ptr_factory_.GetWeakPtr()));
+
+  // If rv == net::ERR_IO_PENDING), listen_socket_ will call
+  // OnAccept() later, when a connection attempt comes in.
+  if (rv != net::ERR_IO_PENDING) {
+    DVLOG_IF(1, rv == net::OK) << "Accept succeeded immediately";
+    OnAccept(rv);
+  }
+}
+
+void IncomingConnectionListenerPosix::OnAccept(int rv) {
+  DCHECK(listen_thread_checker_.CalledOnValidThread());
+
+  if (rv != net::OK || incoming_socket_ == kInvalidSocket) {
+    LOG_IF(ERROR, rv != net::OK) << "Accept failed " << net::ErrorToString(rv);
+    PLOG_IF(ERROR, rv == net::OK) << "Socket invalid";
+  } else {
+    // Passes ownership of incoming_socket_ to delegate_.
+    delegate_->OnConnection(incoming_socket_);
+    incoming_socket_ = kInvalidSocket;
+  }
+
+  // Continue waiting to accept incoming connections...
+  Accept();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/incoming_connection_listener_posix.h b/shell/incoming_connection_listener_posix.h
new file mode 100644
index 0000000..ffd1396
--- /dev/null
+++ b/shell/incoming_connection_listener_posix.h
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_INCOMING_CONNECTION_LISTENER_POSIX_H_
+#define SHELL_INCOMING_CONNECTION_LISTENER_POSIX_H_
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "shell/domain_socket/unix_domain_server_socket_posix.h"
+
+namespace mojo {
+namespace shell {
+
+// Asynchronously listens for incoming connections on a unix domain
+// socket at the provided path. Expects the parent directory in the
+// path to exist.  Must be run on an IO thread.
+class IncomingConnectionListenerPosix {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}  // Abstract base class, so this is safe.
+
+    // Called when listening has started. rv is from
+    // shell/domain_socket/net_error_list.h
+    virtual void OnListening(int rv) = 0;
+
+    // Called every time an incoming connection is accepted. The delegate
+    // takes ownership of incoming.
+    virtual void OnConnection(SocketDescriptor incoming) = 0;
+  };
+
+  IncomingConnectionListenerPosix(const base::FilePath& socket_path,
+                                  Delegate* delegate);
+  virtual ~IncomingConnectionListenerPosix();
+
+  // Attempts to bind a unix domain socket, set up for listening, at
+  // socket_path_.
+  // Regardless of success or failure, calls delegate->OnListening() with a
+  // status code. If the socket was successfully created, begins asynchronously
+  // waiting to accept incoming connections.
+  void StartListening();
+
+ private:
+  // Tells listen_socket_ to perform a non-blocking accept(). It may succeed
+  // or fail immediately, or asynchronously wait for a later connection attempt.
+  // Regardless, when it returns a definitive result (OK or a failing error),
+  // calls OnAccept().
+  void Accept();
+
+  // If rv indicates success, incoming_socket_ should be populated with a
+  // connected FD. Hands this off to delegate->OnConnection() and goes
+  // back to non-blocking accept().
+  // Upon error, logs the error and goes back to non-blocking accept().
+  void OnAccept(int rv);
+
+  Delegate* const delegate_;
+
+  const base::FilePath socket_path_;
+  UnixDomainServerSocket listen_socket_;
+  base::ThreadChecker listen_thread_checker_;
+
+  SocketDescriptor incoming_socket_;
+
+  base::WeakPtrFactory<IncomingConnectionListenerPosix> weak_ptr_factory_;
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_INCOMING_CONNECTION_LISTENER_POSIX_H_
diff --git a/shell/incoming_connection_listener_unittest.cc b/shell/incoming_connection_listener_unittest.cc
new file mode 100644
index 0000000..1c03889
--- /dev/null
+++ b/shell/incoming_connection_listener_unittest.cc
@@ -0,0 +1,150 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/edk/embedder/channel_init.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "shell/domain_socket/net_errors.h"
+#include "shell/domain_socket/socket_descriptor.h"
+#include "shell/domain_socket/test_completion_callback.h"
+#include "shell/domain_socket/unix_domain_client_socket_posix.h"
+#include "shell/external_application_registrar_connection.h"
+#include "shell/incoming_connection_listener_posix.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+// Delegate implementation that expects success.
+class TestDelegate : public IncomingConnectionListenerPosix::Delegate {
+ public:
+  TestDelegate() {}
+  ~TestDelegate() override {}
+
+  void OnListening(int rv) override { EXPECT_EQ(net::OK, rv); }
+  void OnConnection(SocketDescriptor incoming) override {
+    EXPECT_NE(kInvalidSocket, incoming);
+  }
+};
+
+// Delegate implementation that expects a (configurable) failure to listen.
+class ListeningFailsDelegate
+    : public IncomingConnectionListenerPosix::Delegate {
+ public:
+  explicit ListeningFailsDelegate(int expected) : expected_error_(expected) {}
+  ~ListeningFailsDelegate() override {}
+
+  void OnListening(int rv) override { EXPECT_EQ(expected_error_, rv); }
+  void OnConnection(SocketDescriptor incoming) override {
+    FAIL() << "No connection should be attempted.";
+  }
+
+ private:
+  const int expected_error_;
+};
+
+// For ExternalApplicationRegistrarConnection::Connect() callbacks.
+void OnConnect(base::Closure quit_callback, int rv) {
+  EXPECT_EQ(net::OK, rv);
+  base::MessageLoop::current()->PostTask(FROM_HERE, quit_callback);
+}
+
+}  // namespace
+
+class IncomingConnectionListenerTest : public testing::Test {
+ public:
+  IncomingConnectionListenerTest() {}
+  ~IncomingConnectionListenerTest() override {}
+
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    socket_path_ = temp_dir_.path().Append(FILE_PATH_LITERAL("socket"));
+  }
+
+ protected:
+  base::MessageLoopForIO loop_;
+  base::RunLoop run_loop_;
+
+  base::ScopedTempDir temp_dir_;
+  base::FilePath socket_path_;
+};
+
+TEST_F(IncomingConnectionListenerTest, CleanupCheck) {
+  TestDelegate delegate;
+  {
+    IncomingConnectionListenerPosix cleanup_check(socket_path_, &delegate);
+    cleanup_check.StartListening();
+    ASSERT_TRUE(base::PathExists(socket_path_));
+  }
+  ASSERT_FALSE(base::PathExists(socket_path_));
+}
+
+TEST_F(IncomingConnectionListenerTest, ConnectSuccess) {
+  TestDelegate delegate;
+  IncomingConnectionListenerPosix listener(socket_path_, &delegate);
+
+  ASSERT_FALSE(base::PathExists(socket_path_));
+  listener.StartListening();
+  ASSERT_TRUE(base::PathExists(socket_path_));
+
+  ExternalApplicationRegistrarConnection connection(socket_path_);
+  connection.Connect(base::Bind(&OnConnect, run_loop_.QuitClosure()));
+
+  run_loop_.Run();
+}
+
+TEST_F(IncomingConnectionListenerTest, ConnectSuccess_SocketFileExists) {
+  TestDelegate delegate;
+  IncomingConnectionListenerPosix listener(socket_path_, &delegate);
+
+  ASSERT_EQ(1, base::WriteFile(socket_path_, "1", 1));
+  ASSERT_TRUE(base::PathExists(socket_path_));
+  listener.StartListening();
+
+  ExternalApplicationRegistrarConnection connection(socket_path_);
+  connection.Connect(base::Bind(&OnConnect, run_loop_.QuitClosure()));
+
+  run_loop_.Run();
+}
+
+TEST_F(IncomingConnectionListenerTest, ConnectFails_SocketFileUndeletable) {
+  ListeningFailsDelegate fail_delegate(net::ERR_FILE_EXISTS);
+  IncomingConnectionListenerPosix listener(socket_path_, &fail_delegate);
+
+  // Create the socket file.
+  ASSERT_EQ(1, base::WriteFile(socket_path_, "1", 1));
+  ASSERT_TRUE(base::PathExists(socket_path_));
+
+  // Render it undeletable, but in a way that the test harness can recover
+  // later.
+  int temp_dir_perms = 0;
+  ASSERT_TRUE(base::GetPosixFilePermissions(temp_dir_.path(), &temp_dir_perms));
+  ASSERT_TRUE(base::SetPosixFilePermissions(
+      temp_dir_.path(), base::FILE_PERMISSION_READ_BY_USER |
+                            base::FILE_PERMISSION_WRITE_BY_USER));
+  // The listener should fail to start up.
+  listener.StartListening();
+
+  ASSERT_TRUE(base::SetPosixFilePermissions(temp_dir_.path(), temp_dir_perms));
+}
+
+TEST_F(IncomingConnectionListenerTest, ConnectFails_SocketDirNonexistent) {
+  base::FilePath nonexistent_dir(temp_dir_.path()
+                                     .Append(FILE_PATH_LITERAL("dir"))
+                                     .Append(FILE_PATH_LITERAL("file")));
+
+  ListeningFailsDelegate fail_delegate(net::ERR_FILE_NOT_FOUND);
+  IncomingConnectionListenerPosix listener(nonexistent_dir, &fail_delegate);
+
+  // The listener should fail to start up.
+  listener.StartListening();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/init.cc b/shell/init.cc
new file mode 100644
index 0000000..3b93080
--- /dev/null
+++ b/shell/init.cc
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/init.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace shell {
+
+void InitializeLogging() {
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+  // To view log output with IDs and timestamps use "adb logcat -v threadtime".
+  logging::SetLogItems(false,   // Process ID
+                       false,   // Thread ID
+                       false,   // Timestamp
+                       false);  // Tick count
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/init.h b/shell/init.h
new file mode 100644
index 0000000..23a5140
--- /dev/null
+++ b/shell/init.h
@@ -0,0 +1,18 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_INIT_H_
+#define SHELL_INIT_H_
+
+namespace mojo {
+namespace shell {
+
+// Initialization routines shared by desktop and Android main functions.
+
+void InitializeLogging();
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_INIT_H_
diff --git a/shell/launcher_main.cc b/shell/launcher_main.cc
new file mode 100644
index 0000000..a4d0b8a
--- /dev/null
+++ b/shell/launcher_main.cc
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/simple_platform_support.h"
+#include "shell/external_application_registrar_connection.h"
+#include "shell/in_process_dynamic_service_runner.h"
+#include "shell/init.h"
+#include "url/gurl.h"
+
+namespace {
+const char kAppPath[] = "app-path";
+const char kAppURL[] = "app-url";
+const char kShellPath[] = "shell-path";
+}
+
+class Launcher {
+ public:
+  explicit Launcher(base::CommandLine* command_line)
+      : app_path_(command_line->GetSwitchValuePath(kAppPath)),
+        app_url_(command_line->GetSwitchValueASCII(kAppURL)),
+        loop_(base::MessageLoop::TYPE_IO),
+        connection_(
+            base::FilePath(command_line->GetSwitchValuePath(kShellPath))),
+        connect_result_(0) {}
+  ~Launcher() {}
+
+  int Connect() {
+    DCHECK(!run_loop_.get());
+    run_loop_.reset(new base::RunLoop);
+    connection_.Connect(
+        base::Bind(&Launcher::OnConnected, base::Unretained(this)));
+    run_loop_->Run();
+    run_loop_.reset();
+    return connect_result_;
+  }
+
+  bool Register() {
+    DCHECK(!run_loop_.get());
+    DCHECK(connect_result_ == 0);
+    run_loop_.reset(new base::RunLoop);
+    connection_.Register(
+        app_url_, base::Bind(&Launcher::OnRegistered, base::Unretained(this)));
+    run_loop_->Run();
+    run_loop_.reset();
+    return shell_handle_.is_valid();
+  }
+
+  void Run() {
+    DCHECK(!run_loop_.get());
+    DCHECK(shell_handle_.is_valid());
+    mojo::shell::InProcessDynamicServiceRunner service_runner(nullptr);
+    run_loop_.reset(new base::RunLoop);
+    service_runner.Start(
+        app_path_, shell_handle_.Pass(),
+        base::Bind(&Launcher::OnAppCompleted, base::Unretained(this)));
+    run_loop_->Run();
+    run_loop_.reset();
+  }
+
+ private:
+  void OnConnected(int result) {
+    connect_result_ = result;
+    run_loop_->Quit();
+  }
+
+  void OnRegistered(mojo::ShellPtr shell) {
+    shell_handle_ = shell.PassMessagePipe();
+    run_loop_->Quit();
+  }
+
+  void OnAppCompleted() { run_loop_->Quit(); }
+
+  const base::FilePath app_path_;
+  const GURL app_url_;
+  base::MessageLoop loop_;
+  mojo::shell::ExternalApplicationRegistrarConnection connection_;
+  int connect_result_;
+  mojo::ScopedMessagePipeHandle shell_handle_;
+  scoped_ptr<base::RunLoop> run_loop_;
+};
+
+#if defined(OS_WIN)
+int main(int argc, wchar_t** argv) {
+#else
+int main(int argc, char** argv) {
+#endif
+  base::AtExitManager at_exit;
+  mojo::embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
+      new mojo::embedder::SimplePlatformSupport()));
+
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  mojo::shell::InitializeLogging();
+
+  Launcher launcher(command_line);
+  int result = launcher.Connect();
+  if (result < 0) {
+    LOG(ERROR) << "Error(" << result << ") connecting on socket "
+               << command_line->GetSwitchValueASCII(kShellPath);
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  if (!launcher.Register()) {
+    LOG(ERROR) << "Error registering "
+               << command_line->GetSwitchValueASCII(kAppURL);
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  launcher.Run();
+  return MOJO_RESULT_OK;
+}
diff --git a/shell/mojo_url_resolver.cc b/shell/mojo_url_resolver.cc
new file mode 100644
index 0000000..499c6bf
--- /dev/null
+++ b/shell/mojo_url_resolver.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/mojo_url_resolver.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "shell/filename_util.h"
+#include "url/url_util.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+GURL AddTrailingSlashIfNeeded(const GURL& url) {
+  if (!url.has_path() || *url.path().rbegin() == '/')
+    return url;
+
+  std::string path(url.path() + '/');
+  GURL::Replacements replacements;
+  replacements.SetPathStr(path);
+  return url.ReplaceComponents(replacements);
+}
+
+}  // namespace
+
+MojoURLResolver::MojoURLResolver() {
+  // Needed to treat first component of mojo URLs as host, not path.
+  url::AddStandardScheme("mojo");
+
+  // By default, resolve mojo URLs to files living alongside the shell.
+  base::FilePath path;
+  PathService::Get(base::DIR_MODULE, &path);
+  default_base_url_ = AddTrailingSlashIfNeeded(FilePathToFileURL(path));
+}
+
+MojoURLResolver::~MojoURLResolver() {
+}
+
+void MojoURLResolver::SetBaseURL(const GURL& base_url) {
+  DCHECK(base_url.is_valid());
+  // Force a trailing slash on the base_url to simplify resolving
+  // relative files and URLs below.
+  base_url_ = AddTrailingSlashIfNeeded(base_url);
+}
+
+void MojoURLResolver::AddCustomMapping(const GURL& mojo_url,
+                                       const GURL& resolved_url) {
+  url_map_[mojo_url] = resolved_url;
+}
+
+void MojoURLResolver::AddLocalFileMapping(const GURL& mojo_url) {
+  local_file_set_.insert(mojo_url);
+}
+
+GURL MojoURLResolver::Resolve(const GURL& mojo_url) const {
+  const GURL mapped_url(ApplyCustomMappings(mojo_url));
+
+  // Continue resolving if the mapped url is a mojo: url.
+  if (mapped_url.scheme() != "mojo")
+    return mapped_url;
+
+  std::string lib = mapped_url.host() + ".mojo";
+
+  if (!base_url_.is_valid() ||
+      local_file_set_.find(mapped_url) != local_file_set_.end()) {
+    // Resolve to a local file URL.
+    return default_base_url_.Resolve(lib);
+  }
+
+  // Otherwise, resolve to an URL relative to base_url_.
+  return base_url_.Resolve(lib);
+}
+
+GURL MojoURLResolver::ApplyCustomMappings(const GURL& url) const {
+  GURL mapped_url(url);
+  for (;;) {
+    std::map<GURL, GURL>::const_iterator it = url_map_.find(mapped_url);
+    if (it == url_map_.end())
+      break;
+    mapped_url = it->second;
+  }
+  return mapped_url;
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/mojo_url_resolver.h b/shell/mojo_url_resolver.h
new file mode 100644
index 0000000..a18dac4
--- /dev/null
+++ b/shell/mojo_url_resolver.h
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_MOJO_URL_RESOLVER_H_
+#define SHELL_MOJO_URL_RESOLVER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// This class resolves "mojo:" URLs to physical URLs (e.g., "file:" and
+// "https:" URLs). By default, "mojo:" URLs resolve to a file location, but
+// that resolution can be customized via the AddCustomMapping method.
+class MojoURLResolver {
+ public:
+  MojoURLResolver();
+  ~MojoURLResolver();
+
+  // If specified, then unknown "mojo:" URLs will be resolved relative to this
+  // base URL. That is, the portion after the colon will be appeneded to
+  // |base_url| with platform-specific shared library prefix and suffix
+  // inserted.
+  void SetBaseURL(const GURL& base_url);
+
+  // Add a custom mapping for a particular "mojo:" URL. If |resolved_url| is
+  // itself a mojo url normal resolution rules apply.
+  void AddCustomMapping(const GURL& mojo_url, const GURL& resolved_url);
+
+  // Add a local file mapping for a particular "mojo:" URL. This causes the
+  // "mojo:" URL to be resolved to a base::DIR_MODULE-relative shared library.
+  void AddLocalFileMapping(const GURL& mojo_url);
+
+  // Resolve the given "mojo:" URL to the URL that should be used to fetch the
+  // code for the corresponding Mojo App.
+  GURL Resolve(const GURL& mojo_url) const;
+
+ private:
+  // Applies all custom mappings for |url|, returning the last non-mapped url.
+  // For example, if 'a' maps to 'b' and 'b' maps to 'c' calling this with 'a'
+  // returns 'c'.
+  GURL ApplyCustomMappings(const GURL& url) const;
+
+  std::map<GURL, GURL> url_map_;
+  std::set<GURL> local_file_set_;
+  GURL default_base_url_;
+  GURL base_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoURLResolver);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_MOJO_URL_RESOLVER_H_
diff --git a/shell/mojo_url_resolver_unittest.cc b/shell/mojo_url_resolver_unittest.cc
new file mode 100644
index 0000000..77d97db
--- /dev/null
+++ b/shell/mojo_url_resolver_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/mojo_url_resolver.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace test {
+namespace {
+
+typedef testing::Test MojoURLResolverTest;
+
+TEST_F(MojoURLResolverTest, MojoURLsFallThrough) {
+  MojoURLResolver resolver;
+  resolver.AddCustomMapping(GURL("mojo:test"), GURL("mojo:foo"));
+  const GURL base_url("file:/base");
+  resolver.SetBaseURL(base_url);
+  std::string resolved(resolver.Resolve(GURL("mojo:test")).spec());
+  // Resolved must start with |base_url|.
+  EXPECT_EQ(base_url.spec(), resolved.substr(0, base_url.spec().size()));
+  // And must contain foo.
+  EXPECT_NE(std::string::npos, resolved.find("foo"));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/network_application_loader.cc b/shell/network_application_loader.cc
new file mode 100644
index 0000000..320e2bc
--- /dev/null
+++ b/shell/network_application_loader.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/network_application_loader.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/network/network_service_impl.h"
+
+namespace {
+base::FilePath GetBasePath() {
+  base::FilePath path;
+  CHECK(PathService::Get(base::DIR_TEMP, &path));
+  return path.Append(FILE_PATH_LITERAL("network_service"));
+}
+}
+
+namespace mojo {
+namespace shell {
+
+NetworkApplicationLoader::NetworkApplicationLoader() {
+}
+
+NetworkApplicationLoader::~NetworkApplicationLoader() {
+}
+
+void NetworkApplicationLoader::Load(ApplicationManager* manager,
+                                    const GURL& url,
+                                    ScopedMessagePipeHandle shell_handle,
+                                    LoadCallback callback) {
+  DCHECK(shell_handle.is_valid());
+  uintptr_t key = reinterpret_cast<uintptr_t>(manager);
+  if (apps_.find(key) == apps_.end()) {
+    scoped_ptr<ApplicationImpl> app(
+        new ApplicationImpl(this, shell_handle.Pass()));
+    apps_.add(key, app.Pass());
+  }
+}
+
+void NetworkApplicationLoader::OnApplicationError(ApplicationManager* manager,
+                                                  const GURL& url) {
+  apps_.erase(reinterpret_cast<uintptr_t>(manager));
+}
+
+void NetworkApplicationLoader::Initialize(ApplicationImpl* app) {
+  // The context must be created on the same thread as the network service.
+  context_.reset(new NetworkContext(GetBasePath()));
+}
+
+bool NetworkApplicationLoader::ConfigureIncomingConnection(
+    ApplicationConnection* connection) {
+  connection->AddService(this);
+  return true;
+}
+
+void NetworkApplicationLoader::Create(
+    ApplicationConnection* connection,
+    InterfaceRequest<NetworkService> request) {
+  BindToRequest(new NetworkServiceImpl(connection, context_.get()), &request);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/network_application_loader.h b/shell/network_application_loader.h
new file mode 100644
index 0000000..5544e19
--- /dev/null
+++ b/shell/network_application_loader.h
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_NETWORK_APPLICATION_LOADER_H_
+#define SHELL_NETWORK_APPLICATION_LOADER_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/application_manager/application_loader.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/network/network_context.h"
+
+namespace mojo {
+
+class ApplicationImpl;
+class NetworkService;
+
+namespace shell {
+
+// ApplicationLoader responsible for creating connections to the NetworkService.
+class NetworkApplicationLoader : public ApplicationLoader,
+                                 public ApplicationDelegate,
+                                 public InterfaceFactory<NetworkService> {
+ public:
+  NetworkApplicationLoader();
+  virtual ~NetworkApplicationLoader();
+
+ private:
+  // ApplicationLoader overrides:
+  virtual void Load(ApplicationManager* manager,
+                    const GURL& url,
+                    ScopedMessagePipeHandle shell_handle,
+                    LoadCallback callback) override;
+  virtual void OnApplicationError(ApplicationManager* manager,
+                                  const GURL& url) override;
+
+  // ApplicationDelegate overrides.
+  virtual void Initialize(ApplicationImpl* app) override;
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override;
+
+  // InterfaceFactory<NetworkService> overrides.
+  virtual void Create(ApplicationConnection* connection,
+                      InterfaceRequest<NetworkService> request) override;
+
+  base::ScopedPtrHashMap<uintptr_t, ApplicationImpl> apps_;
+  scoped_ptr<NetworkContext> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkApplicationLoader);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_NETWORK_APPLICATION_LOADER_H_
diff --git a/shell/out_of_process_dynamic_service_runner.cc b/shell/out_of_process_dynamic_service_runner.cc
new file mode 100644
index 0000000..f0d32a2
--- /dev/null
+++ b/shell/out_of_process_dynamic_service_runner.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/out_of_process_dynamic_service_runner.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/scoped_native_library.h"
+
+namespace mojo {
+namespace shell {
+
+OutOfProcessDynamicServiceRunner::OutOfProcessDynamicServiceRunner(
+    Context* context)
+    : context_(context) {
+}
+
+OutOfProcessDynamicServiceRunner::~OutOfProcessDynamicServiceRunner() {
+  if (app_child_process_host_) {
+    // TODO(vtl): Race condition: If |AppChildProcessHost::DidStart()| hasn't
+    // been called yet, we shouldn't call |Join()| here. (Until |DidStart()|, we
+    // may not have a child process to wait on.) Probably we should fix
+    // |Join()|.
+    app_child_process_host_->Join();
+  }
+}
+
+void OutOfProcessDynamicServiceRunner::Start(
+    const base::FilePath& app_path,
+    ScopedMessagePipeHandle service_handle,
+    const base::Closure& app_completed_callback) {
+  app_path_ = app_path;
+
+  DCHECK(app_completed_callback_.is_null());
+  app_completed_callback_ = app_completed_callback;
+
+  app_child_process_host_.reset(new AppChildProcessHost(context_, this));
+  app_child_process_host_->Start();
+
+  // TODO(vtl): |app_path.AsUTF8Unsafe()| is unsafe.
+  app_child_process_host_->controller()->StartApp(
+      app_path.AsUTF8Unsafe(), ScopedMessagePipeHandle(MessagePipeHandle(
+                                   service_handle.release().value())));
+}
+
+void OutOfProcessDynamicServiceRunner::AppCompleted(int32_t result) {
+  DVLOG(2) << "OutOfProcessDynamicServiceRunner::AppCompleted(" << result
+           << ")";
+
+  app_completed_callback_.Run();
+  app_completed_callback_.Reset();
+  app_child_process_host_.reset();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/out_of_process_dynamic_service_runner.h b/shell/out_of_process_dynamic_service_runner.h
new file mode 100644
index 0000000..df2c967
--- /dev/null
+++ b/shell/out_of_process_dynamic_service_runner.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+#define SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "shell/app_child_process.mojom.h"
+#include "shell/app_child_process_host.h"
+#include "shell/dynamic_service_runner.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |DynamicServiceRunner| that loads/runs the given app
+// (from the file system) in a separate process (of its own).
+class OutOfProcessDynamicServiceRunner : public DynamicServiceRunner,
+                                         public AppChildControllerClient {
+ public:
+  explicit OutOfProcessDynamicServiceRunner(Context* context);
+  ~OutOfProcessDynamicServiceRunner() override;
+
+  // |DynamicServiceRunner| method:
+  void Start(const base::FilePath& app_path,
+             ScopedMessagePipeHandle service_handle,
+             const base::Closure& app_completed_callback) override;
+
+ private:
+  // |AppChildControllerClient| method:
+  void AppCompleted(int32_t result) override;
+
+  Context* const context_;
+
+  base::FilePath app_path_;
+  base::Closure app_completed_callback_;
+
+  scoped_ptr<AppChildProcessHost> app_child_process_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(OutOfProcessDynamicServiceRunner);
+};
+
+typedef DynamicServiceRunnerFactoryImpl<OutOfProcessDynamicServiceRunner>
+    OutOfProcessDynamicServiceRunnerFactory;
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/shell/shell_test_base.cc b/shell/shell_test_base.cc
new file mode 100644
index 0000000..daeef19
--- /dev/null
+++ b/shell/shell_test_base.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/shell_test_base.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "build/build_config.h"
+#include "shell/filename_util.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+namespace test {
+
+ShellTestBase::ShellTestBase() {
+}
+
+ShellTestBase::~ShellTestBase() {
+}
+
+void ShellTestBase::SetUp() {
+  shell_context_.Init();
+  base::FilePath service_dir;
+  CHECK(PathService::Get(base::DIR_MODULE, &service_dir));
+}
+
+ScopedMessagePipeHandle ShellTestBase::ConnectToService(
+    const GURL& application_url,
+    const std::string& service_name) {
+  // Set the MojoURLResolver origin to be the same as the base file path for
+  // local files. This is primarily for test convenience, so that references
+  // to unknown mojo: urls that do not have specific local file or custom
+  // mappings registered on the URL resolver are treated as shared libraries.
+  base::FilePath service_dir;
+  CHECK(PathService::Get(base::DIR_MODULE, &service_dir));
+  shell_context_.mojo_url_resolver()->SetBaseURL(
+      FilePathToFileURL(service_dir));
+
+  return shell_context_.ConnectToServiceByName(application_url, service_name)
+      .Pass();
+}
+
+ScopedMessagePipeHandle ShellTestBase::ConnectToServiceViaNetwork(
+    const GURL& application_url,
+    const std::string& service_name) {
+  return shell_context_.ConnectToServiceByName(application_url, service_name)
+      .Pass();
+}
+
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/shell_test_base.h b/shell/shell_test_base.h
new file mode 100644
index 0000000..7b0b5bb
--- /dev/null
+++ b/shell/shell_test_base.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_SHELL_TEST_BASE_H_
+#define SHELL_SHELL_TEST_BASE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/system/core.h"
+#include "shell/context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class GURL;
+
+namespace mojo {
+namespace shell {
+namespace test {
+
+class ShellTestBase : public testing::Test {
+ public:
+  ShellTestBase();
+  ~ShellTestBase() override;
+
+  void SetUp() override;
+
+  // |application_url| should typically be a mojo: URL (the origin will be set
+  // to an "appropriate" file: URL).
+  // TODO(tim): Should the test base be a ServiceProvider?
+  ScopedMessagePipeHandle ConnectToService(const GURL& application_url,
+                                           const std::string& service_name);
+
+  ScopedMessagePipeHandle ConnectToServiceViaNetwork(
+      const GURL& application_url,
+      const std::string& service_name);
+
+  template <typename Interface>
+  void ConnectToService(const GURL& application_url,
+                        InterfacePtr<Interface>* ptr) {
+    ptr->Bind(ConnectToService(application_url, Interface::Name_).Pass());
+  }
+
+  template <typename Interface>
+  void ConnectToServiceViaNetwork(const GURL& application_url,
+                                  InterfacePtr<Interface>* ptr) {
+    ptr->Bind(
+        ConnectToServiceViaNetwork(application_url, Interface::Name_).Pass());
+  }
+
+  base::MessageLoop* message_loop() { return &message_loop_; }
+  Context* shell_context() { return &shell_context_; }
+
+ private:
+  Context shell_context_;
+  base::MessageLoop message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShellTestBase);
+};
+
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_SHELL_TEST_BASE_H_
diff --git a/shell/shell_test_base_unittest.cc b/shell/shell_test_base_unittest.cc
new file mode 100644
index 0000000..d6dbb45
--- /dev/null
+++ b/shell/shell_test_base_unittest.cc
@@ -0,0 +1,308 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/shell_test_base.h"
+
+#include "base/bind.h"
+#include "base/i18n/time_formatting.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/system/core.h"
+#include "services/test_service/test_request_tracker.mojom.h"
+#include "services/test_service/test_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using mojo::test::ServiceReport;
+using mojo::test::ServiceReportPtr;
+using mojo::test::TestService;
+using mojo::test::TestTimeService;
+using mojo::test::TestServicePtr;
+using mojo::test::TestTimeServicePtr;
+using mojo::test::TestTrackedRequestService;
+using mojo::test::TestTrackedRequestServicePtr;
+
+namespace mojo {
+namespace shell {
+namespace test {
+namespace {
+
+void GetReportCallback(base::MessageLoop* loop,
+                       std::vector<ServiceReport>* reports_out,
+                       mojo::Array<ServiceReportPtr> report) {
+  for (size_t i = 0; i < report.size(); i++)
+    reports_out->push_back(*report[i]);
+  loop->QuitWhenIdle();
+}
+
+class ShellTestBaseTest : public ShellTestBase {
+ public:
+  // Convenience helpers for use as callbacks in tests.
+  template <typename T>
+  base::Callback<void()> SetAndQuit(T* val, T result) {
+    return base::Bind(&ShellTestBaseTest::SetAndQuitImpl<T>,
+                      base::Unretained(this), val, result);
+  }
+  template <typename T>
+  base::Callback<void(T result)> SetAndQuit(T* val) {
+    return base::Bind(&ShellTestBaseTest::SetAndQuitImpl<T>,
+                      base::Unretained(this), val);
+  }
+  static GURL test_app_url() { return GURL("mojo:test_app"); }
+
+  void GetReport(std::vector<ServiceReport>* report) {
+    ConnectToService(GURL("mojo:test_request_tracker_app"), &request_tracking_);
+    request_tracking_->GetReport(base::Bind(&GetReportCallback,
+                                            base::Unretained(message_loop()),
+                                            base::Unretained(report)));
+    message_loop()->Run();
+  }
+
+ private:
+  template <typename T>
+  void SetAndQuitImpl(T* val, T result) {
+    *val = result;
+    message_loop()->QuitWhenIdle();
+  }
+  TestTrackedRequestServicePtr request_tracking_;
+};
+
+class QuitMessageLoopErrorHandler : public ErrorHandler {
+ public:
+  QuitMessageLoopErrorHandler() {}
+  ~QuitMessageLoopErrorHandler() override {}
+
+  // |ErrorHandler| implementation:
+  void OnConnectionError() override {
+    base::MessageLoop::current()->QuitWhenIdle();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler);
+};
+
+// Tests that we can connect to a single service within a single app.
+TEST_F(ShellTestBaseTest, ConnectBasic) {
+  InterfacePtr<TestService> service;
+  ConnectToService(test_app_url(), &service);
+
+  bool was_run = false;
+  service->Ping(SetAndQuit<bool>(&was_run, true));
+  message_loop()->Run();
+  EXPECT_TRUE(was_run);
+  EXPECT_FALSE(service.encountered_error());
+
+  service.reset();
+
+  // This will run until the test app has actually quit (which it will,
+  // since we killed the only connection to it).
+  message_loop()->Run();
+}
+
+// Tests that trying to connect to a service fails properly if the service
+// doesn't exist. Implicit in this test is verification that the shell
+// terminates if no services are running.
+TEST_F(ShellTestBaseTest, ConnectInvalidService) {
+  InterfacePtr<TestService> test_service;
+  ConnectToService(GURL("mojo:non_existent_service"), &test_service);
+
+  bool was_run = false;
+  test_service->Ping(SetAndQuit<bool>(&was_run, true));
+
+  // This will quit because there's nothing running.
+  message_loop()->Run();
+  EXPECT_FALSE(was_run);
+
+  // It may have quit before an error was processed.
+  if (!test_service.encountered_error()) {
+    QuitMessageLoopErrorHandler quitter;
+    test_service.set_error_handler(&quitter);
+    message_loop()->Run();
+    EXPECT_TRUE(test_service.encountered_error());
+  }
+
+  test_service.reset();
+}
+
+// Tests that we can connect to a single service within a single app using
+// a network based loader instead of local files.
+// TODO(tim): Disabled because network service leaks NSS at exit, meaning
+// subsequent tests can't init properly.
+TEST_F(ShellTestBaseTest, DISABLED_ConnectBasicNetwork) {
+  InterfacePtr<TestService> service;
+  ConnectToService(test_app_url(), &service);
+
+  bool was_run = false;
+  service->Ping(SetAndQuit<bool>(&was_run, true));
+  message_loop()->Run();
+  EXPECT_TRUE(was_run);
+  EXPECT_FALSE(service.encountered_error());
+
+  // Note that use of the network service is implicit in this test.
+  // Since TestService is not the only service in use, the shell won't auto
+  // magically exit when TestService is destroyed (unlike ConnectBasic).
+  // Tearing down the shell context will kill connections. The shell loop will
+  // exit as soon as no more apps are connected.
+  // TODO(tim): crbug.com/392685.  Calling this explicitly shouldn't be
+  // necessary once the shell terminates if the primordial app exits, which
+  // we could enforce here by resetting |service|.
+  shell_context()->application_manager()->TerminateShellConnections();
+  message_loop()->Run();  // Waits for all connections to die.
+}
+
+// Tests that trying to connect to a service over network fails preoprly
+// if the service doesn't exist.
+// TODO(tim): Disabled because network service leaks NSS at exit, meaning
+// subsequent tests can't init properly.
+TEST_F(ShellTestBaseTest, DISABLED_ConnectInvalidServiceNetwork) {
+  InterfacePtr<TestService> test_service;
+  ConnectToServiceViaNetwork(GURL("mojo:non_existent_service"), &test_service);
+  QuitMessageLoopErrorHandler quitter;
+  test_service.set_error_handler(&quitter);
+  bool was_run = false;
+  test_service->Ping(SetAndQuit<bool>(&was_run, true));
+  message_loop()->Run();
+  EXPECT_TRUE(test_service.encountered_error());
+
+  // TODO(tim): crbug.com/392685.  Calling this explicitly shouldn't be
+  // necessary once the shell terminates if the primordial app exits, which
+  // we could enforce here by resetting |service|.
+  shell_context()->application_manager()->TerminateShellConnections();
+  message_loop()->Run();  // Waits for all connections to die.
+}
+
+// Similar to ConnectBasic, but causes the app to instantiate multiple
+// service implementation objects and verifies the shell can reach both.
+TEST_F(ShellTestBaseTest, ConnectMultipleInstancesPerApp) {
+  {
+    TestServicePtr service1, service2;
+    ConnectToService(test_app_url(), &service1);
+    ConnectToService(test_app_url(), &service2);
+
+    bool was_run1 = false;
+    bool was_run2 = false;
+    service1->Ping(SetAndQuit<bool>(&was_run1, true));
+    message_loop()->Run();
+    service2->Ping(SetAndQuit<bool>(&was_run2, true));
+    message_loop()->Run();
+    EXPECT_TRUE(was_run1);
+    EXPECT_TRUE(was_run2);
+    EXPECT_FALSE(service1.encountered_error());
+    EXPECT_FALSE(service2.encountered_error());
+  }
+  message_loop()->Run();
+}
+
+// Tests that service A and service B, both in App 1, can talk to each other
+// and parameters are passed around properly.
+TEST_F(ShellTestBaseTest, ConnectDifferentServicesInSingleApp) {
+  // Have a TestService GetPartyTime on a TestTimeService in the same app.
+  int64 time_message;
+  TestServicePtr service;
+  ConnectToService(test_app_url(), &service);
+  service->ConnectToAppAndGetTime(test_app_url().spec(),
+                                  SetAndQuit<int64>(&time_message));
+  message_loop()->Run();
+
+  // Verify by hitting the TimeService directly.
+  TestTimeServicePtr time_service;
+  ConnectToService(test_app_url(), &time_service);
+  int64 party_time;
+  time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
+  message_loop()->Run();
+
+  EXPECT_EQ(time_message, party_time);
+}
+
+// Tests that a service A in App 1 can talk to service B in App 2 and
+// parameters are passed around properly.
+TEST_F(ShellTestBaseTest, ConnectDifferentServicesInDifferentApps) {
+  int64 time_message;
+  TestServicePtr service;
+  ConnectToService(test_app_url(), &service);
+  service->ConnectToAppAndGetTime("mojo:test_request_tracker_app",
+                                  SetAndQuit<int64>(&time_message));
+  message_loop()->Run();
+
+  // Verify by hitting the TimeService in the request tracker app directly.
+  TestTimeServicePtr time_service;
+  ConnectToService(GURL("mojo:test_request_tracker_app"), &time_service);
+  int64 party_time;
+  time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
+  message_loop()->Run();
+
+  EXPECT_EQ(time_message, party_time);
+}
+
+// Tests that service A in App 1 can be a client of service B in App 2.
+TEST_F(ShellTestBaseTest, ConnectServiceAsClientOfSeparateApp) {
+  TestServicePtr service;
+  ConnectToService(test_app_url(), &service);
+  service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
+  service->Ping(mojo::Callback<void()>());
+  message_loop()->Run();
+
+  for (int i = 0; i < 8; i++)
+    service->Ping(mojo::Callback<void()>());
+  service->Ping(message_loop()->QuitWhenIdleClosure());
+  message_loop()->Run();
+
+  // If everything worked properly, the tracking service should report
+  // 10 pings to TestService.
+  std::vector<ServiceReport> reports;
+  GetReport(&reports);
+  ASSERT_EQ(1U, reports.size());
+  EXPECT_EQ(TestService::Name_, reports[0].service_name);
+  EXPECT_EQ(10U, reports[0].total_requests);
+}
+
+// Connect several services together and use the tracking service to verify
+// communication.
+TEST_F(ShellTestBaseTest, ConnectManyClientsAndServices) {
+  TestServicePtr service;
+  TestTimeServicePtr time_service;
+
+  // Make a request to the TestService and have it contact TimeService in the
+  // tracking app. Do all this with tracking enabled, meaning both services
+  // are connected as clients of the TrackedRequestService.
+  ConnectToService(test_app_url(), &service);
+  service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
+  message_loop()->Run();
+  for (int i = 0; i < 5; i++)
+    service->Ping(mojo::Callback<void()>());
+  int64 time_result;
+  service->ConnectToAppAndGetTime("mojo:test_request_tracker_app",
+                                  SetAndQuit<int64>(&time_result));
+  message_loop()->Run();
+
+  // Also make a few requests to the TimeService in the test_app.
+  ConnectToService(test_app_url(), &time_service);
+  time_service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
+  time_service->GetPartyTime(mojo::Callback<void(uint64_t)>());
+  message_loop()->Run();
+  for (int i = 0; i < 18; i++)
+    time_service->GetPartyTime(mojo::Callback<void(uint64_t)>());
+  // Flush the tasks with one more to quit.
+  int64 party_time = 0;
+  time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
+  message_loop()->Run();
+
+  std::vector<ServiceReport> reports;
+  GetReport(&reports);
+  ASSERT_EQ(3U, reports.size());
+  EXPECT_EQ(TestService::Name_, reports[0].service_name);
+  EXPECT_EQ(6U, reports[0].total_requests);
+  EXPECT_EQ(TestTimeService::Name_, reports[1].service_name);
+  EXPECT_EQ(1U, reports[1].total_requests);
+  EXPECT_EQ(TestTimeService::Name_, reports[2].service_name);
+  EXPECT_EQ(20U, reports[2].total_requests);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/shell_test_helper.cc b/shell/shell_test_helper.cc
new file mode 100644
index 0000000..dc1364d
--- /dev/null
+++ b/shell/shell_test_helper.cc
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/shell_test_helper.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "shell/filename_util.h"
+#include "shell/init.h"
+#include "shell/mojo_url_resolver.h"
+
+namespace mojo {
+namespace shell {
+
+ShellTestHelper::ShellTestHelper() {
+  base::CommandLine::Init(0, NULL);
+  mojo::shell::InitializeLogging();
+}
+
+ShellTestHelper::~ShellTestHelper() {
+}
+
+void ShellTestHelper::Init() {
+  context_.Init();
+  test_api_.reset(
+      new ApplicationManager::TestAPI(context_.application_manager()));
+  base::FilePath service_dir;
+  CHECK(PathService::Get(base::DIR_MODULE, &service_dir));
+  context_.mojo_url_resolver()->SetBaseURL(FilePathToFileURL(service_dir));
+}
+
+void ShellTestHelper::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
+                                      const GURL& url) {
+  context_.application_manager()->SetLoaderForURL(loader.Pass(), url);
+}
+
+void ShellTestHelper::AddCustomMapping(const GURL& mojo_url,
+                                       const GURL& resolved_url) {
+  context_.mojo_url_resolver()->AddCustomMapping(mojo_url, resolved_url);
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/shell_test_helper.h b/shell/shell_test_helper.h
new file mode 100644
index 0000000..2d57bd3
--- /dev/null
+++ b/shell/shell_test_helper.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_SHELL_TEST_HELPER_H_
+#define SHELL_SHELL_TEST_HELPER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "mojo/application_manager/application_loader.h"
+#include "shell/context.h"
+
+class GURL;
+
+namespace mojo {
+
+class ApplicationLoader;
+
+namespace shell {
+
+// ShellTestHelper is useful for tests to establish a connection to the
+// ApplicationManager. Invoke Init() to establish the connection. Once done,
+// application_manager() returns the ApplicationManager.
+class ShellTestHelper {
+ public:
+  ShellTestHelper();
+  ~ShellTestHelper();
+
+  void Init();
+
+  ApplicationManager* application_manager() {
+    return context_.application_manager();
+  }
+
+  // Sets a ApplicationLoader for the specified URL. |loader| is ultimately used
+  // on
+  // the thread this class spawns.
+  void SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, const GURL& url);
+
+  // Adds a mapping that is used when resolving mojo urls. See MojoURLResolver
+  // for details.
+  void AddCustomMapping(const GURL& mojo_url, const GURL& resolved_url);
+
+ private:
+  Context context_;
+  base::MessageLoop shell_loop_;
+  scoped_ptr<ApplicationManager::TestAPI> test_api_;
+  DISALLOW_COPY_AND_ASSIGN(ShellTestHelper);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_SHELL_TEST_HELPER_H_
diff --git a/shell/shell_test_main.cc b/shell/shell_test_main.cc
new file mode 100644
index 0000000..d13238c
--- /dev/null
+++ b/shell/shell_test_main.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "shell/child_process.h"
+#include "shell/switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+  base::CommandLine::Init(argc, argv);
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  if (command_line.HasSwitch(switches::kChildProcessType)) {
+    scoped_ptr<mojo::shell::ChildProcess> child_process =
+        mojo::shell::ChildProcess::Create(command_line);
+    CHECK(child_process);
+    child_process->Main();
+    return 0;
+  }
+
+  base::TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/shell/switches.cc b/shell/switches.cc
new file mode 100644
index 0000000..986cd54
--- /dev/null
+++ b/shell/switches.cc
@@ -0,0 +1,82 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/switches.h"
+
+#include "base/basictypes.h"
+
+namespace switches {
+
+namespace {
+// This controls logging verbosity. It's not strictly a switch for mojo_shell,
+// and isn't included in the public switches, but is included here so that it
+// doesn't trigger an error at startup.
+const char kV[] = "v";
+
+}  // namespace
+
+// Specify configuration arguments for a Mojo application URL. For example:
+// --args-for='mojo:wget http://www.google.com'
+const char kArgsFor[] = "args-for";
+
+// Used to specify the type of child process (switch values from
+// |ChildProcess::Type|).
+const char kChildProcessType[] = "child-process-type";
+
+// Comma separated list like:
+// text/html,mojo:html_viewer,application/bravo,https://abarth.com/bravo
+const char kContentHandlers[] = "content-handlers";
+
+// Force dynamically loaded apps / services to be loaded irrespective of cache
+// instructions.
+const char kDisableCache[] = "disable-cache";
+
+// Allow externally-running applications to discover, connect to, and register
+// themselves with the shell.
+// TODO(cmasone): Work in progress. Once we're sure this works, remove.
+const char kEnableExternalApplications[] = "enable-external-applications";
+
+// Load apps in separate processes.
+// TODO(vtl): Work in progress; doesn't work. Flip this to "disable" (or maybe
+// change it to "single-process") when it works.
+const char kEnableMultiprocess[] = "enable-multiprocess";
+
+// Print the usage message and exit.
+const char kHelp[] = "help";
+
+// Map mojo: URLs to a shared library of similar name at this origin. See
+// mojo_url_resolver.cc for details.
+const char kOrigin[] = "origin";
+
+// Enables the mojo spy, which acts as a man-in-the-middle inspector for
+// message pipes and other activities. This is work in progress.
+const char kSpy[] = "spy";
+
+// Specifies a set of mappings to apply when resolving urls. The value is set of
+// ',' separated mappings, where each mapping consists of a pair of urls giving
+// the to/from url to map. For example, 'a=b,c=d' contains two mappings, the
+// first maps 'a' to 'b' and the second 'c' to 'd'.
+const char kURLMappings[] = "url-mappings";
+
+const char* kSwitchArray[] = {kV,
+                              kArgsFor,
+                              kChildProcessType,
+                              kContentHandlers,
+                              kDisableCache,
+                              kEnableExternalApplications,
+                              kEnableMultiprocess,
+                              kHelp,
+                              kOrigin,
+                              kSpy,
+                              kURLMappings};
+
+const std::set<std::string> GetAllSwitches() {
+  std::set<std::string> switch_set;
+
+  for (size_t i = 0; i < arraysize(kSwitchArray); ++i)
+    switch_set.insert(kSwitchArray[i]);
+  return switch_set;
+}
+
+}  // namespace switches
diff --git a/shell/switches.h b/shell/switches.h
new file mode 100644
index 0000000..c8096fd
--- /dev/null
+++ b/shell/switches.h
@@ -0,0 +1,31 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_SWITCHES_H_
+#define SHELL_SWITCHES_H_
+
+#include <set>
+#include <string>
+
+namespace switches {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file and, as needed,
+// in mojo_main's Usage() function.
+extern const char kArgsFor[];
+extern const char kHelp[];
+extern const char kChildProcessType[];
+extern const char kContentHandlers[];
+extern const char kDisableCache[];
+extern const char kEnableExternalApplications[];
+extern const char kEnableMultiprocess[];
+extern const char kOrigin[];
+extern const char kSpy[];
+extern const char kURLMappings[];
+
+extern const std::set<std::string> GetAllSwitches();
+
+}  // namespace switches
+
+#endif  // SHELL_SWITCHES_H_
diff --git a/shell/task_runners.cc b/shell/task_runners.cc
new file mode 100644
index 0000000..dfadfb8
--- /dev/null
+++ b/shell/task_runners.cc
@@ -0,0 +1,39 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/task_runners.h"
+
+#include "base/threading/sequenced_worker_pool.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+const size_t kMaxBlockingPoolThreads = 3;
+
+scoped_ptr<base::Thread> CreateIOThread(const char* name) {
+  scoped_ptr<base::Thread> thread(new base::Thread(name));
+  base::Thread::Options options;
+  options.message_loop_type = base::MessageLoop::TYPE_IO;
+  thread->StartWithOptions(options);
+  return thread.Pass();
+}
+
+}  // namespace
+
+TaskRunners::TaskRunners(
+    const scoped_refptr<base::SingleThreadTaskRunner>& shell_runner)
+    : shell_runner_(shell_runner),
+      io_thread_(CreateIOThread("io_thread")),
+      blocking_pool_(new base::SequencedWorkerPool(kMaxBlockingPoolThreads,
+                                                   "blocking_pool")) {
+}
+
+TaskRunners::~TaskRunners() {
+  blocking_pool_->Shutdown();
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/task_runners.h b/shell/task_runners.h
new file mode 100644
index 0000000..891d3e1
--- /dev/null
+++ b/shell/task_runners.h
@@ -0,0 +1,53 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_TASK_RUNNERS_H_
+#define SHELL_TASK_RUNNERS_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/thread.h"
+
+namespace base {
+class SequencedWorkerPool;
+}
+
+namespace mojo {
+namespace shell {
+
+// A context object that contains the common task runners for the shell's main
+// process.
+class TaskRunners {
+ public:
+  explicit TaskRunners(
+      const scoped_refptr<base::SingleThreadTaskRunner>& shell_runner);
+  ~TaskRunners();
+
+  base::SingleThreadTaskRunner* shell_runner() const {
+    return shell_runner_.get();
+  }
+
+  base::SingleThreadTaskRunner* io_runner() const {
+    return io_thread_->message_loop_proxy().get();
+  }
+
+  base::SequencedWorkerPool* blocking_pool() const {
+    return blocking_pool_.get();
+  }
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> shell_runner_;
+  scoped_ptr<base::Thread> io_thread_;
+
+  scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskRunners);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_TASK_RUNNERS_H_
diff --git a/shell/test_child_process.cc b/shell/test_child_process.cc
new file mode 100644
index 0000000..85ab796
--- /dev/null
+++ b/shell/test_child_process.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/test_child_process.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+
+namespace mojo {
+namespace shell {
+
+TestChildProcess::TestChildProcess() {
+}
+
+TestChildProcess::~TestChildProcess() {
+}
+
+void TestChildProcess::Main() {
+  VLOG(2) << "TestChildProcess::Main()";
+
+  CHECK(!base::MessageLoop::current());
+}
+
+}  // namespace shell
+}  // namespace mojo
diff --git a/shell/test_child_process.h b/shell/test_child_process.h
new file mode 100644
index 0000000..006774b
--- /dev/null
+++ b/shell/test_child_process.h
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_TEST_CHILD_PROCESS_H_
+#define SHELL_TEST_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "shell/child_process.h"
+
+namespace mojo {
+namespace shell {
+
+class TestChildProcess : public ChildProcess {
+ public:
+  TestChildProcess();
+  ~TestChildProcess() override;
+
+  void Main() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestChildProcess);
+};
+
+}  // namespace shell
+}  // namespace mojo
+
+#endif  // SHELL_TEST_CHILD_PROCESS_H_
diff --git a/shell/ui_application_loader_android.cc b/shell/ui_application_loader_android.cc
new file mode 100644
index 0000000..7c2f2e3
--- /dev/null
+++ b/shell/ui_application_loader_android.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/ui_application_loader_android.h"
+
+#include "base/bind.h"
+#include "mojo/application_manager/application_manager.h"
+#include "shell/context.h"
+
+namespace mojo {
+
+UIApplicationLoader::UIApplicationLoader(
+    scoped_ptr<ApplicationLoader> real_loader,
+    shell::Context* context)
+    : loader_(real_loader.Pass()), context_(context) {
+}
+
+UIApplicationLoader::~UIApplicationLoader() {
+  context_->ui_loop()->PostTask(
+      FROM_HERE, base::Bind(&UIApplicationLoader::ShutdownOnUIThread,
+                            base::Unretained(this)));
+}
+
+void UIApplicationLoader::Load(ApplicationManager* manager,
+                               const GURL& url,
+                               ScopedMessagePipeHandle shell_handle,
+                               LoadCallback callback) {
+  DCHECK(shell_handle.is_valid());
+  context_->ui_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&UIApplicationLoader::LoadOnUIThread, base::Unretained(this),
+                 manager, url, base::Passed(&shell_handle)));
+}
+
+void UIApplicationLoader::OnApplicationError(ApplicationManager* manager,
+                                             const GURL& url) {
+  context_->ui_loop()->PostTask(
+      FROM_HERE, base::Bind(&UIApplicationLoader::OnApplicationErrorOnUIThread,
+                            base::Unretained(this), manager, url));
+}
+
+void UIApplicationLoader::LoadOnUIThread(ApplicationManager* manager,
+                                         const GURL& url,
+                                         ScopedMessagePipeHandle shell_handle) {
+  loader_->Load(manager, url, shell_handle.Pass(), SimpleLoadCallback());
+}
+
+void UIApplicationLoader::OnApplicationErrorOnUIThread(
+    ApplicationManager* manager,
+    const GURL& url) {
+  loader_->OnApplicationError(manager, url);
+}
+
+void UIApplicationLoader::ShutdownOnUIThread() {
+  // Destroy |loader_| on the thread it's actually used on.
+  loader_.reset();
+}
+
+}  // namespace mojo
diff --git a/shell/ui_application_loader_android.h b/shell/ui_application_loader_android.h
new file mode 100644
index 0000000..64b13f0
--- /dev/null
+++ b/shell/ui_application_loader_android.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_UI_APPLICATION_LOADER_ANDROID_H_
+#define SHELL_UI_APPLICATION_LOADER_ANDROID_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/application_manager/application_loader.h"
+
+namespace mojo {
+
+class ApplicationManager;
+
+namespace shell {
+class Context;
+}
+
+// ApplicationLoader implementation that creates a background thread and issues
+// load
+// requests there.
+class UIApplicationLoader : public ApplicationLoader {
+ public:
+  UIApplicationLoader(scoped_ptr<ApplicationLoader> real_loader,
+                      shell::Context* context);
+  ~UIApplicationLoader() override;
+
+  // ApplicationLoader overrides:
+  void Load(ApplicationManager* manager,
+            const GURL& url,
+            ScopedMessagePipeHandle shell_handle,
+            LoadCallback callback) override;
+  void OnApplicationError(ApplicationManager* manager,
+                          const GURL& url) override;
+
+ private:
+  class UILoader;
+
+  // These functions are exected on the background thread. They call through
+  // to |background_loader_| to do the actual loading.
+  // TODO: having this code take a |manager| is fragile (as ApplicationManager
+  // isn't thread safe).
+  void LoadOnUIThread(ApplicationManager* manager,
+                      const GURL& url,
+                      ScopedMessagePipeHandle shell_handle);
+  void OnApplicationErrorOnUIThread(ApplicationManager* manager,
+                                    const GURL& url);
+  void ShutdownOnUIThread();
+
+  scoped_ptr<ApplicationLoader> loader_;
+  shell::Context* context_;
+
+  DISALLOW_COPY_AND_ASSIGN(UIApplicationLoader);
+};
+
+}  // namespace mojo
+
+#endif  // SHELL_UI_APPLICATION_LOADER_ANDROID_H_