Add Observatory to sky dart_controller

- Bump Dart and Observatory DEPS to 45576 and 45565 respectively.
- Include 'dart:io' in snapshot
- Add 'dart:io' native bindings to sky bindings.
- Initialize 'dart:io' in sky dart_controller.
- Include Observatory and service isolate resources in build.
- Bring up service isolate.
- Start handle watcher isolate from service isolate (hides handle watcher isolate from debugger and Observatory).
- Hook up debugger.

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/1107803002
diff --git a/DEPS b/DEPS
index b16c545..a11b056 100644
--- a/DEPS
+++ b/DEPS
@@ -25,8 +25,8 @@
   'v8_revision': '230d131d173ab2d60291d303177bc04ec3f6e519',
   'angle_revision': 'bdd419f9f5b006e913606e7363125942c8ae06bc',
   'buildtools_revision': '15308f469a704c45d15567fa69cd94ce07ad0e1b',
-  'dart_revision': '45304',
-  'dart_observatory_packages_revision': '43830',
+  'dart_revision': '45576',
+  'dart_observatory_packages_revision': '45565',
   'pdfium_revision': 'b0115665b0f33971f1b7077740d51e155583cec0',
   'boringssl_revision': '642f1498d056dbba3e50ed5a232ab2f482626dec',
   'lss_revision': 'e079768b7e3a94dcbe7d338496c0c3bde7151b6e',
diff --git a/mojo/dart/embedder/BUILD.gn b/mojo/dart/embedder/BUILD.gn
index 95fcc46..26a8bdd 100644
--- a/mojo/dart/embedder/BUILD.gn
+++ b/mojo/dart/embedder/BUILD.gn
@@ -69,6 +69,7 @@
     "//mojo/dart/embedder/io/socket_patch.dart",
     "//mojo/dart/embedder/io/server_socket_patch.dart",
   ]
+  root_prefix = "//mojo/dart/embedder/"
   output = "$target_gen_dir/dart_embedder_patch_resources.cc"
   table_name = "dart_embedder_patch"
 }
@@ -143,6 +144,7 @@
     "//mojo/dart/embedder/vmservice/resources.dart",
     "//mojo/dart/embedder/vmservice/server.dart",
   ]
+  root_prefix = "//mojo/dart/embedder/"
   input_directory = "$root_out_dir/observatory/deployed/web/"
   output = "$target_gen_dir/dart_embedder_service_isolate_resources.cc"
   table_name = "dart_embedder_service_isolate"
@@ -252,7 +254,7 @@
 
   outputs = [
     "$root_out_dir/observatory/build/web/index.html",
-    "$root_out_dir/observatory/build/web/index.html_bootstrap.dart.js",
+    "$root_out_dir/observatory/build/web/index.html.polymer.bootstrap.dart.js",
   ]
 }
 
@@ -266,7 +268,7 @@
   inputs = [
     script,
     "$root_out_dir/observatory/build/web/index.html",
-    "$root_out_dir/observatory/build/web/index.html_bootstrap.dart.js",
+    "$root_out_dir/observatory/build/web/index.html.polymer.bootstrap.dart.js",
   ]
 
   args = [
@@ -281,7 +283,7 @@
 
   outputs = [
     "$root_out_dir/observatory/deployed/web/index.html",
-    "$root_out_dir/observatory/deployed/web/index.html_bootstrap.dart.js",
+    "$root_out_dir/observatory/deployed/web/index.html.polymer.bootstrap.dart.js",
   ]
 }
 
diff --git a/mojo/dart/embedder/dart_debugger.cc b/mojo/dart/embedder/dart_debugger.cc
index 31e9ee4..684acc5 100644
--- a/mojo/dart/embedder/dart_debugger.cc
+++ b/mojo/dart/embedder/dart_debugger.cc
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <vector>
-
+#include "mojo/dart/embedder/dart_debugger.h"
 #include "dart/runtime/include/dart_api.h"
 #include "dart/runtime/include/dart_debugger_api.h"
 #include "dart/runtime/include/dart_native_api.h"
-#include "mojo/dart/embedder/dart_debugger.h"
+
 
 namespace mojo {
 namespace dart {
@@ -45,10 +44,9 @@
                                       intptr_t bp_id,
                                       const Dart_CodeLocation& loc) {
   Dart_EnterScope();
-  DartDebuggerIsolate* debugger_isolate =
-      FindIsolateById(isolate_id);
-  CHECK(debugger_isolate != nullptr);
-  debugger_isolate->MessageLoop();
+  intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+  CHECK(isolate_index != -1);
+  (*isolates_)[isolate_index]->MessageLoop();
   Dart_ExitScope();
 }
 
@@ -56,10 +54,9 @@
                                           Dart_Handle exception,
                                           Dart_StackTrace stack_trace) {
   Dart_EnterScope();
-  DartDebuggerIsolate* debugger_isolate =
-      FindIsolateById(isolate_id);
-  CHECK(debugger_isolate != nullptr);
-  debugger_isolate->MessageLoop();
+  intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+  CHECK(isolate_index != -1);
+  (*isolates_)[isolate_index]->MessageLoop();
   Dart_ExitScope();
 }
 
@@ -69,11 +66,10 @@
   if (kind == Dart_IsolateEvent::kCreated) {
     AddIsolate(isolate_id);
   } else {
-    DartDebuggerIsolate* debugger_isolate =
-        FindIsolateById(isolate_id);
-    CHECK(debugger_isolate != nullptr);
+    intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+    CHECK(isolate_index != -1);
     if (kind == Dart_IsolateEvent::kInterrupted) {
-      debugger_isolate->MessageLoop();
+      (*isolates_)[isolate_index]->MessageLoop();
     } else {
       CHECK(kind == Dart_IsolateEvent::kShutdown);
       RemoveIsolate(isolate_id);
@@ -85,10 +81,9 @@
 void DartDebugger::NotifyIsolate(Dart_Isolate isolate) {
   base::AutoLock al(*lock_);
   Dart_IsolateId isolate_id = Dart_GetIsolateId(isolate);
-  DartDebuggerIsolate* debugger_isolate =
-      FindIsolateByIdLocked(isolate_id);
-  if (debugger_isolate != nullptr) {
-    debugger_isolate->Notify();
+  intptr_t isolate_index = FindIsolateIndexByIdLocked(isolate_id);
+  if (isolate_index >= 0) {
+    (*isolates_)[isolate_index]->Notify();
   }
 }
 
@@ -98,40 +93,38 @@
   Dart_SetBreakpointResolvedHandler(BptResolvedHandler);
   Dart_SetExceptionThrownHandler(ExceptionThrownHandler);
   lock_ = new base::Lock();
+  isolates_ = new std::vector<std::unique_ptr<DartDebuggerIsolate>>();
 }
 
-DartDebuggerIsolate* DartDebugger::FindIsolateById(Dart_IsolateId id) {
+intptr_t DartDebugger::FindIsolateIndexById(Dart_IsolateId id) {
   base::AutoLock al(*lock_);
-  return FindIsolateByIdLocked(id);
+  return FindIsolateIndexByIdLocked(id);
 }
 
-DartDebuggerIsolate* DartDebugger::FindIsolateByIdLocked(
+intptr_t DartDebugger::FindIsolateIndexByIdLocked(
       Dart_IsolateId id) {
   lock_->AssertAcquired();
-  for (size_t i = 0; i < isolates_.size(); i++) {
-    DartDebuggerIsolate* isolate = isolates_[i];
-    if (id == isolate->id()) {
-      return isolate;
+  for (size_t i = 0; i < isolates_->size(); i++) {
+    if ((*isolates_)[i]->id() == id) {
+      return i;
     }
   }
-  return nullptr;
+  return -1;
 }
 
-DartDebuggerIsolate* DartDebugger::AddIsolate(Dart_IsolateId id) {
+void DartDebugger::AddIsolate(Dart_IsolateId id) {
   base::AutoLock al(*lock_);
-  CHECK(FindIsolateByIdLocked(id) == nullptr);
-  DartDebuggerIsolate* debugger_isolate =
-      new DartDebuggerIsolate(id);
-  isolates_.push_back(debugger_isolate);
-  return debugger_isolate;
+  CHECK(FindIsolateIndexByIdLocked(id) == -1);
+  std::unique_ptr<DartDebuggerIsolate> debugger_isolate =
+      std::unique_ptr<DartDebuggerIsolate>(new DartDebuggerIsolate(id));
+  isolates_->push_back(std::move(debugger_isolate));
 }
 
 void DartDebugger::RemoveIsolate(Dart_IsolateId id) {
   base::AutoLock al(*lock_);
-  for (size_t i = 0; i < isolates_.size(); i++) {
-    DartDebuggerIsolate* isolate = isolates_[i];
-    if (id == isolate->id()) {
-      isolates_.erase(isolates_.begin() + i);
+  for (size_t i = 0; i < isolates_->size(); i++) {
+    if (id == (*isolates_)[i]->id()) {
+      isolates_->erase(isolates_->begin() + i);
       return;
     }
   }
@@ -139,7 +132,8 @@
 }
 
 base::Lock* DartDebugger::lock_ = nullptr;
-std::vector<DartDebuggerIsolate*> DartDebugger::isolates_;
+std::vector<std::unique_ptr<DartDebuggerIsolate>>* DartDebugger::isolates_ =
+    nullptr;
 
-}  // namespace apps
+}  // namespace dart
 }  // namespace mojo
diff --git a/mojo/dart/embedder/dart_debugger.h b/mojo/dart/embedder/dart_debugger.h
index fe7ef2d..7db7986 100644
--- a/mojo/dart/embedder/dart_debugger.h
+++ b/mojo/dart/embedder/dart_debugger.h
@@ -5,6 +5,9 @@
 #ifndef MOJO_DART_EMBEDDER_DART_DEBUGGER_H_
 #define MOJO_DART_EMBEDDER_DART_DEBUGGER_H_
 
+#include <memory>
+#include <vector>
+
 #include "dart/runtime/include/dart_api.h"
 #include "dart/runtime/include/dart_debugger_api.h"
 #include "dart/runtime/include/dart_native_api.h"
@@ -60,16 +63,16 @@
 
   static void NotifyIsolate(Dart_Isolate isolate);
 
-  static DartDebuggerIsolate* FindIsolateById(Dart_IsolateId id);
+  static intptr_t FindIsolateIndexById(Dart_IsolateId id);
 
-  static DartDebuggerIsolate* FindIsolateByIdLocked(Dart_IsolateId id);
+  static intptr_t FindIsolateIndexByIdLocked(Dart_IsolateId id);
 
-  static DartDebuggerIsolate* AddIsolate(Dart_IsolateId id);
+  static void AddIsolate(Dart_IsolateId id);
 
   static void RemoveIsolate(Dart_IsolateId id);
 
   static base::Lock* lock_;
-  static std::vector<DartDebuggerIsolate*> isolates_;
+  static std::vector<std::unique_ptr<DartDebuggerIsolate>>* isolates_;
 
   friend class DartDebuggerIsolate;
 };
diff --git a/mojo/dart/embedder/embedder.gni b/mojo/dart/embedder/embedder.gni
index b5d3de3..dd9dcdf 100644
--- a/mojo/dart/embedder/embedder.gni
+++ b/mojo/dart/embedder/embedder.gni
@@ -48,6 +48,7 @@
 #   String output (name of output file)
 #   List inputs (list of input files to be included)
 #   String table_name (name of symbol for resource table)
+#   String root_prefix (base directory of resources)
 # Optional invoker inputs:
 #   String input_directory (directory of resources that are recursively added)
 #   List deps
@@ -71,6 +72,8 @@
 
     inputs = [ script ] + invoker.inputs
 
+    root_prefix = rebase_path(invoker.root_prefix)
+
     args = [
       "--output",
       rebase_path(output),
@@ -81,7 +84,7 @@
       "--table_name",
       invoker.table_name,
       "--root_prefix",
-      rebase_path("//mojo/dart/embedder/"),
+      root_prefix,
     ]
     if (defined(invoker.input_directory)) {
       args += [
diff --git a/mojo/dart/embedder/vmservice/server.dart b/mojo/dart/embedder/vmservice/server.dart
index 188b532..4e069f9 100644
--- a/mojo/dart/embedder/vmservice/server.dart
+++ b/mojo/dart/embedder/vmservice/server.dart
@@ -35,17 +35,9 @@
     }
   }
 
-  post(var serial, dynamic result) {
+  post(dynamic result) {
     try {
-      if (serial == null && result is! String) {
-        socket.add(result);
-      } else {
-        Map map = {
-          'id': serial,
-          'result': result
-        };
-        socket.add(JSON.encode(map));
-      }
+      socket.add(result);
     } catch (_) {
       print("Ignoring error posting over WebSocket.");
     }
@@ -67,7 +59,7 @@
 
   HttpRequestClient(this.request, VMService service) : super(service);
 
-  post(var serial, String result) {
+  post(String result) {
     request.response..headers.contentType = jsonContentType
                     ..write(result)
                     ..close();
diff --git a/sky/engine/bindings/BUILD.gn b/sky/engine/bindings/BUILD.gn
index b7dbafe..fd6a30c 100644
--- a/sky/engine/bindings/BUILD.gn
+++ b/sky/engine/bindings/BUILD.gn
@@ -30,6 +30,9 @@
     "scheduled_action.cc",
     "scheduled_action.h",
   ]
+
+  defines = [ "DART_IO_SECURE_SOCKET_DISABLED" ]
+
   deps = [
     "//base",
     "//dart/runtime/bin:libdart_withcore",
@@ -38,12 +41,14 @@
     "//sky/engine/platform:platform",
     "//sky/engine/tonic",
     "//sky/engine/wtf",
+    "//dart/runtime/bin:embedded_dart_io",
     ":generated_bindings",
     ":snapshot_cc",
   ]
   include_dirs = [
     "..",
     "$root_build_dir",
+    rebase_path("//dart/runtime"),
   ]
 }
 
diff --git a/sky/engine/bindings/builtin.cc b/sky/engine/bindings/builtin.cc
index 81fd9d3..3fa5c39 100644
--- a/sky/engine/bindings/builtin.cc
+++ b/sky/engine/bindings/builtin.cc
@@ -6,6 +6,7 @@
 #include "sky/engine/bindings/builtin.h"
 
 #include "base/logging.h"
+#include "bin/io_natives.h"
 #include "dart/runtime/include/dart_api.h"
 #include "gen/sky/bindings/DartGlobal.h"
 #include "sky/engine/bindings/builtin_natives.h"
@@ -31,6 +32,7 @@
      BuiltinNatives::NativeLookup},
     {"dart:sky", true, skySnapshotSymbolizer, skySnapshotResolver},
     {"dart:mojo.internal", true, MojoNativeSymbol, MojoNativeLookup},
+    {"dart:io", true, dart::bin::IONativeSymbol, dart::bin::IONativeLookup },
 };
 
 }  // namespace
diff --git a/sky/engine/bindings/builtin.h b/sky/engine/bindings/builtin.h
index 8c3bb05..93f67a2 100644
--- a/sky/engine/bindings/builtin.h
+++ b/sky/engine/bindings/builtin.h
@@ -18,6 +18,7 @@
     kBuiltinLibrary,
     kSkyLibrary,
     kMojoInternalLibrary,
+    kIOLibrary,
     kInvalidLibrary,
   };
 
diff --git a/sky/engine/bindings/builtin_natives.cc b/sky/engine/bindings/builtin_natives.cc
index 55584a3..90bec2e 100644
--- a/sky/engine/bindings/builtin_natives.cc
+++ b/sky/engine/bindings/builtin_natives.cc
@@ -87,7 +87,8 @@
   return closure;
 }
 
-static void InitDartInternal(Dart_Handle builtin_library) {
+static void InitDartInternal(Dart_Handle builtin_library,
+                             BuiltinNatives::IsolateType isolate_type) {
   Dart_Handle print = GetClosure(builtin_library, "_getPrintClosure");
   Dart_Handle timer = GetClosure(builtin_library, "_getCreateTimerClosure");
 
@@ -96,35 +97,55 @@
   DART_CHECK_VALID(Dart_SetField(
       internal_library, ToDart("_printClosure"), print));
 
-  Dart_Handle vm_hooks_name = ToDart("VMLibraryHooks");
-  Dart_Handle vm_hooks = Dart_GetClass(internal_library, vm_hooks_name);
-  DART_CHECK_VALID(vm_hooks);
-  Dart_Handle timer_name = ToDart("timerFactory");
-  DART_CHECK_VALID(Dart_SetField(vm_hooks, timer_name, timer));
+  if (isolate_type == BuiltinNatives::MainIsolate) {
+    Dart_Handle vm_hooks_name = ToDart("VMLibraryHooks");
+    Dart_Handle vm_hooks = Dart_GetClass(internal_library, vm_hooks_name);
+    DART_CHECK_VALID(vm_hooks);
+    Dart_Handle timer_name = ToDart("timerFactory");
+    DART_CHECK_VALID(Dart_SetField(vm_hooks, timer_name, timer));
+  } else {
+    CHECK(isolate_type == BuiltinNatives::DartIOIsolate);
+    Dart_Handle io_lib = DartBuiltin::LookupLibrary("dart:io");
+    Dart_Handle setup_hooks = Dart_NewStringFromCString("_setupHooks");
+    DART_CHECK_VALID(Dart_Invoke(io_lib, setup_hooks, 0, NULL));
+    Dart_Handle isolate_lib = DartBuiltin::LookupLibrary("dart:isolate");
+    DART_CHECK_VALID(Dart_Invoke(isolate_lib, setup_hooks, 0, NULL));
+  }
 }
 
-static void InitDartCore(Dart_Handle builtin) {
+static void InitDartCore(Dart_Handle builtin,
+                         BuiltinNatives::IsolateType isolate_type) {
   Dart_Handle get_base_url = GetClosure(builtin, "_getGetBaseURLClosure");
   Dart_Handle core_library = DartBuiltin::LookupLibrary("dart:core");
   DART_CHECK_VALID(Dart_SetField(core_library,
       ToDart("_uriBaseClosure"), get_base_url));
 }
 
-static void InitDartAsync(Dart_Handle builtin_library) {
-  Dart_Handle schedule_microtask =
-      GetClosure(builtin_library, "_getScheduleMicrotaskClosure");
+static void InitDartAsync(Dart_Handle builtin_library,
+                          BuiltinNatives::IsolateType isolate_type) {
+  Dart_Handle schedule_microtask;
+  if (isolate_type == BuiltinNatives::MainIsolate) {
+    schedule_microtask =
+        GetClosure(builtin_library, "_getScheduleMicrotaskClosure");
+  } else {
+    CHECK(isolate_type == BuiltinNatives::DartIOIsolate);
+    Dart_Handle isolate_lib = DartBuiltin::LookupLibrary("dart:isolate");
+    Dart_Handle method_name =
+        Dart_NewStringFromCString("_getIsolateScheduleImmediateClosure");
+    schedule_microtask = Dart_Invoke(isolate_lib, method_name, 0, NULL);
+  }
   Dart_Handle async_library = DartBuiltin::LookupLibrary("dart:async");
   Dart_Handle set_schedule_microtask = ToDart("_setScheduleImmediateClosure");
   DART_CHECK_VALID(Dart_Invoke(async_library, set_schedule_microtask, 1,
                                &schedule_microtask));
 }
 
-void BuiltinNatives::Init() {
+void BuiltinNatives::Init(IsolateType isolate_type) {
   Dart_Handle builtin = Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
   DART_CHECK_VALID(builtin);
-  InitDartInternal(builtin);
-  InitDartCore(builtin);
-  InitDartAsync(builtin);
+  InitDartInternal(builtin, isolate_type);
+  InitDartCore(builtin, isolate_type);
+  InitDartAsync(builtin, isolate_type);
 }
 
 // Implementation of native functions which are used for some
@@ -163,6 +184,7 @@
   if (LogIfError(closure) || !Dart_IsClosure(closure))
     return;
   DartState* dart_state = DartState::Current();
+  CHECK(dart_state);
   Microtask::enqueueMicrotask(base::Bind(&ExecuteMicrotask,
     dart_state->GetWeakPtr(), DartValue::Create(dart_state, closure)));
 }
@@ -182,6 +204,7 @@
   DART_CHECK_VALID(Dart_GetNativeBooleanArgument(args, 2, &repeating));
 
   DOMDartState* state = DOMDartState::Current();
+  CHECK(state);
   int timer_id = DOMTimer::install(state->document(),
                                    ScheduledAction::Create(state, closure),
                                    milliseconds,
@@ -194,6 +217,7 @@
   DART_CHECK_VALID(Dart_GetNativeIntegerArgument(args, 0, &timer_id));
 
   DOMDartState* state = DOMDartState::Current();
+  CHECK(state);
   DOMTimer::removeByID(state->document(), timer_id);
 }
 
diff --git a/sky/engine/bindings/builtin_natives.h b/sky/engine/bindings/builtin_natives.h
index a08fc19..3f525bc 100644
--- a/sky/engine/bindings/builtin_natives.h
+++ b/sky/engine/bindings/builtin_natives.h
@@ -12,12 +12,17 @@
 
 class BuiltinNatives {
  public:
+  enum IsolateType {
+    MainIsolate,
+    DartIOIsolate,
+  };
+
   static Dart_NativeFunction NativeLookup(Dart_Handle name,
                                           int argument_count,
                                           bool* auto_setup_scope);
   static const uint8_t* NativeSymbol(Dart_NativeFunction native_function);
 
-  static void Init();
+  static void Init(IsolateType isolate_type);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(BuiltinNatives);
diff --git a/sky/engine/bindings/snapshot.dart b/sky/engine/bindings/snapshot.dart
index 51fd6f4..d620521 100644
--- a/sky/engine/bindings/snapshot.dart
+++ b/sky/engine/bindings/snapshot.dart
@@ -6,6 +6,7 @@
 import 'dart:collection';
 import 'dart:convert';
 import 'dart:core';
+import 'dart:io';
 import 'dart:isolate';
 import 'dart:math';
 import 'dart:mojo.internal';
diff --git a/sky/engine/core/BUILD.gn b/sky/engine/core/BUILD.gn
index d01e064..661835c 100644
--- a/sky/engine/core/BUILD.gn
+++ b/sky/engine/core/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//sky/engine/build/scripts/scripts.gni")
 import("//sky/engine/core/core.gni")
+import("//mojo/dart/embedder/embedder.gni")
 
 visibility = [ "//sky/engine/*" ]
 
@@ -52,20 +53,40 @@
   ]
 }
 
+dart_embedder_resources("generate_sky_embedder_service_isolate_resources_cc") {
+  deps = [
+    "//mojo/dart/embedder:deploy_observatory",
+  ]
+  inputs = [
+    "//sky/engine/core/script/dart_service_isolate/loader.dart",
+    "//sky/engine/core/script/dart_service_isolate/main.dart",
+    "//sky/engine/core/script/dart_service_isolate/resources.dart",
+    "//sky/engine/core/script/dart_service_isolate/server.dart",
+  ]
+  root_prefix = "//sky/engine/core/script/"
+  input_directory = "$root_out_dir/observatory/deployed/web/"
+  output = "$target_gen_dir/sky_embedder_service_isolate_resources.cc"
+  table_name = "sky_embedder_service_isolate"
+}
+
 static_library("core") {
   output_name = "sky_core"
 
   deps = [
     ":core_generated",
+    ":generate_sky_embedder_service_isolate_resources_cc",
     ":libraries",
     ":prerequisites",
     "//sky/engine/platform",
     "//sky/engine/bindings",
+    "//dart/runtime/bin:embedded_dart_io",
     "//dart/runtime/bin:libdart_withcore",
   ]
 
   sources = sky_core_files
 
+  sources += [ "$target_gen_dir/sky_embedder_service_isolate_resources.cc" ]
+
   include_dirs = [
     # Needed for dart_mirrors_api.h in dart_controller.cc
     rebase_path("//dart/runtime"),
diff --git a/sky/engine/core/core.gni b/sky/engine/core/core.gni
index 5ecbe0a..2a78c74 100644
--- a/sky/engine/core/core.gni
+++ b/sky/engine/core/core.gni
@@ -1009,12 +1009,17 @@
   "rendering/VerticalPositionCache.h",
   "script/dart_controller.cc",
   "script/dart_controller.h",
+  "script/dart_debugger.cc",
+  "script/dart_debugger.h",
   "script/dart_dependency_catcher.cc",
   "script/dart_dependency_catcher.h",
   "script/dart_loader.cc",
   "script/dart_loader.h",
+  "script/dart_service_isolate.cc",
+  "script/dart_service_isolate.h",
   "script/dom_dart_state.cc",
   "script/dom_dart_state.h",
+  "script/monitor.h",
 ]
 
 core_idl_files = get_path_info([
diff --git a/sky/engine/core/script/dart_controller.cc b/sky/engine/core/script/dart_controller.cc
index dc7ce4c..e03cb34 100644
--- a/sky/engine/core/script/dart_controller.cc
+++ b/sky/engine/core/script/dart_controller.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
+#include "dart/runtime/bin/embedded_dart_io.h"
 #include "dart/runtime/include/dart_mirrors_api.h"
 #include "sky/engine/bindings/builtin.h"
 #include "sky/engine/bindings/builtin_natives.h"
@@ -21,8 +22,10 @@
 #include "sky/engine/core/html/imports/HTMLImport.h"
 #include "sky/engine/core/html/imports/HTMLImportChild.h"
 #include "sky/engine/core/loader/FrameLoaderClient.h"
+#include "sky/engine/core/script/dart_debugger.h"
 #include "sky/engine/core/script/dart_dependency_catcher.h"
 #include "sky/engine/core/script/dart_loader.h"
+#include "sky/engine/core/script/dart_service_isolate.h"
 #include "sky/engine/core/script/dom_dart_state.h"
 #include "sky/engine/public/platform/Platform.h"
 #include "sky/engine/tonic/dart_api_scope.h"
@@ -176,9 +179,36 @@
       String(url_name) == DART_VM_SERVICE_ISOLATE_NAME;
 }
 
+static void EnsureHandleWatcherStarted() {
+  static bool handle_watcher_started = false;
+  if (handle_watcher_started)
+    return;
+
+  // TODO(dart): Call Dart_Cleanup (ensure the handle watcher isolate is closed)
+  // during shutdown.
+  Dart_Handle mojo_core_lib =
+      Builtin::LoadAndCheckLibrary(Builtin::kMojoInternalLibrary);
+  CHECK(!LogIfError((mojo_core_lib)));
+  Dart_Handle handle_watcher_type = Dart_GetType(
+      mojo_core_lib,
+      Dart_NewStringFromCString("MojoHandleWatcher"),
+      0,
+      nullptr);
+  CHECK(!LogIfError(handle_watcher_type));
+  CHECK(!LogIfError(Dart_Invoke(
+      handle_watcher_type,
+      Dart_NewStringFromCString("_start"),
+      0,
+      nullptr)));
+
+  // RunLoop until the handle watcher isolate is spun-up.
+  CHECK(!LogIfError(Dart_RunLoop()));
+  handle_watcher_started = true;
+}
+
 // TODO(rafaelw): Right now this only supports the creation of the handle
-// watcher isolate. Presumably, we'll want application isolates to spawn their
-// own isolates.
+// watcher isolate and the service isolate. Presumably, we'll want application
+// isolates to spawn their own isolates.
 static Dart_Isolate IsolateCreateCallback(const char* script_uri,
                                           const char* main,
                                           const char* package_root,
@@ -186,8 +216,34 @@
                                           char** error) {
 
   if (IsServiceIsolateURL(script_uri)) {
-    return Dart_CreateIsolate(script_uri, "main", kDartIsolateSnapshotBuffer,
-          nullptr, error);
+    CHECK(kDartIsolateSnapshotBuffer);
+    DartState* dart_state = new DartState();
+    Dart_Isolate isolate = Dart_CreateIsolate(script_uri,
+                                              "main",
+                                              kDartIsolateSnapshotBuffer,
+                                              nullptr,
+                                              error);
+    CHECK(isolate) << error;
+    dart_state->set_isolate(isolate);
+    CHECK(Dart_IsServiceIsolate(isolate));
+    CHECK(!LogIfError(Dart_SetLibraryTagHandler(LibraryTagHandler)));
+    {
+      DartApiScope apiScope;
+      Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
+      Builtin::SetNativeResolver(Builtin::kMojoInternalLibrary);
+      Builtin::SetNativeResolver(Builtin::kIOLibrary);
+      BuiltinNatives::Init(BuiltinNatives::DartIOIsolate);
+      // Start the handle watcher from the service isolate so it isn't available
+      // for debugging or general Observatory interaction.
+      EnsureHandleWatcherStarted();
+      std::string ip = "127.0.0.1";
+      const intptr_t port = 0;  // Automatic port assignment.
+      const bool service_isolate_booted =
+          DartServiceIsolate::Startup(ip, port, LibraryTagHandler, error);
+      CHECK(service_isolate_booted) << error;
+    }
+    Dart_ExitIsolate();
+    return isolate;
   }
 
   // Create & start the handle watcher isolate
@@ -204,6 +260,7 @@
     DartApiScope apiScope;
     Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
     Builtin::SetNativeResolver(Builtin::kMojoInternalLibrary);
+    Builtin::SetNativeResolver(Builtin::kIOLibrary);
 
     // Ensure the isolate has a root library.
     Dart_LoadScript(Dart_NewStringFromCString("dart:empty"),
@@ -233,33 +290,6 @@
       base::Bind(&CallHandleMessage, DartState::From(dest_isolate)->GetWeakPtr()));
 }
 
-static void EnsureHandleWatcherStarted() {
-  static bool handle_watcher_started = false;
-  if (handle_watcher_started)
-    return;
-
-  // TODO(dart): Call Dart_Cleanup (ensure the handle watcher isolate is closed)
-  // during shutdown.
-  Dart_Handle mojo_core_lib =
-      Builtin::LoadAndCheckLibrary(Builtin::kMojoInternalLibrary);
-  CHECK(!LogIfError((mojo_core_lib)));
-  Dart_Handle handle_watcher_type = Dart_GetType(
-      mojo_core_lib,
-      Dart_NewStringFromCString("MojoHandleWatcher"),
-      0,
-      nullptr);
-  CHECK(!LogIfError(handle_watcher_type));
-  CHECK(!LogIfError(Dart_Invoke(
-      handle_watcher_type,
-      Dart_NewStringFromCString("_start"),
-      0,
-      nullptr)));
-
-  // RunLoop until the handle watcher isolate is spun-up.
-  CHECK(!LogIfError(Dart_RunLoop()));
-  handle_watcher_started = true;
-}
-
 void DartController::CreateIsolateFor(Document* document) {
   DCHECK(document);
   CHECK(kDartIsolateSnapshotBuffer);
@@ -283,7 +313,8 @@
 
     Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
     Builtin::SetNativeResolver(Builtin::kMojoInternalLibrary);
-    BuiltinNatives::Init();
+    Builtin::SetNativeResolver(Builtin::kIOLibrary);
+    BuiltinNatives::Init(BuiltinNatives::MainIsolate);
 
     builtin_sky_ = adoptPtr(new BuiltinSky(dart_state()));
     dart_state()->class_library().set_provider(builtin_sky_.get());
@@ -312,13 +343,20 @@
   argv = kCheckedModeArgs;
 #endif
 
+  dart::bin::BootstrapDartIo();
+
   CHECK(Dart_SetVMFlags(argc, argv));
+  // This should be called before calling Dart_Initialize.
+  DartDebugger::InitDebugger();
   CHECK(Dart_Initialize(kDartVmIsolateSnapshotBuffer,
                         IsolateCreateCallback,
                         nullptr,  // Isolate interrupt callback.
                         UnhandledExceptionCallback, IsolateShutdownCallback,
                         // File IO callbacks.
                         nullptr, nullptr, nullptr, nullptr, nullptr));
+  // Wait for load port- ensures handle watcher and service isolates are
+  // running.
+  Dart_ServiceWaitForLoadPort();
 }
 
 } // namespace blink
diff --git a/sky/engine/core/script/dart_debugger.cc b/sky/engine/core/script/dart_debugger.cc
new file mode 100644
index 0000000..f7c1c49
--- /dev/null
+++ b/sky/engine/core/script/dart_debugger.cc
@@ -0,0 +1,138 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sky/engine/core/script/dart_debugger.h"
+
+#include "dart/runtime/include/dart_api.h"
+#include "dart/runtime/include/dart_debugger_api.h"
+#include "dart/runtime/include/dart_native_api.h"
+
+
+namespace blink {
+
+void DartDebuggerIsolate::MessageLoop() {
+  MonitorLocker ml(&monitor_);
+  // Request notification on isolate messages.  This allows us to
+  // respond to vm service messages while at breakpoint.
+  Dart_SetMessageNotifyCallback(DartDebugger::NotifyIsolate);
+  while (true) {
+    // Handle all available vm service messages, up to a resume
+    // request.
+    bool resume = false;
+    while (!resume && Dart_HasServiceMessages()) {
+      monitor_.Exit();
+      resume = Dart_HandleServiceMessages();
+      monitor_.Enter();
+    }
+    if (resume) {
+      break;
+    }
+    ml.Wait();
+  }
+  Dart_SetMessageNotifyCallback(nullptr);
+}
+
+void DartDebugger::BptResolvedHandler(Dart_IsolateId isolate_id,
+                                      intptr_t bp_id,
+                                      const Dart_CodeLocation& location) {
+  // Nothing to do here. Service event is dispatched to let Observatory know
+  // that a breakpoint was resolved.
+}
+
+void DartDebugger::PausedEventHandler(Dart_IsolateId isolate_id,
+                                      intptr_t bp_id,
+                                      const Dart_CodeLocation& loc) {
+  Dart_EnterScope();
+  intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+  CHECK(isolate_index != -1);
+  (*isolates_)[isolate_index]->MessageLoop();
+  Dart_ExitScope();
+}
+
+void DartDebugger::ExceptionThrownHandler(Dart_IsolateId isolate_id,
+                                          Dart_Handle exception,
+                                          Dart_StackTrace stack_trace) {
+  Dart_EnterScope();
+  intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+  CHECK(isolate_index != -1);
+  (*isolates_)[isolate_index]->MessageLoop();
+  Dart_ExitScope();
+}
+
+void DartDebugger::IsolateEventHandler(Dart_IsolateId isolate_id,
+                                       Dart_IsolateEvent kind) {
+  Dart_EnterScope();
+  if (kind == Dart_IsolateEvent::kCreated) {
+    AddIsolate(isolate_id);
+  } else {
+    intptr_t isolate_index = FindIsolateIndexById(isolate_id);
+    CHECK(isolate_index != -1);
+    if (kind == Dart_IsolateEvent::kInterrupted) {
+      (*isolates_)[isolate_index]->MessageLoop();
+    } else {
+      CHECK(kind == Dart_IsolateEvent::kShutdown);
+      RemoveIsolate(isolate_id);
+    }
+  }
+  Dart_ExitScope();
+}
+
+void DartDebugger::NotifyIsolate(Dart_Isolate isolate) {
+  base::AutoLock al(*lock_);
+  Dart_IsolateId isolate_id = Dart_GetIsolateId(isolate);
+  intptr_t isolate_index = FindIsolateIndexByIdLocked(isolate_id);
+  if (isolate_index >= 0) {
+    (*isolates_)[isolate_index]->Notify();
+  }
+}
+
+void DartDebugger::InitDebugger() {
+  Dart_SetIsolateEventHandler(IsolateEventHandler);
+  Dart_SetPausedEventHandler(PausedEventHandler);
+  Dart_SetBreakpointResolvedHandler(BptResolvedHandler);
+  Dart_SetExceptionThrownHandler(ExceptionThrownHandler);
+  lock_ = new base::Lock();
+  isolates_ = new std::vector<std::unique_ptr<DartDebuggerIsolate>>();
+}
+
+intptr_t DartDebugger::FindIsolateIndexById(Dart_IsolateId id) {
+  base::AutoLock al(*lock_);
+  return FindIsolateIndexByIdLocked(id);
+}
+
+intptr_t DartDebugger::FindIsolateIndexByIdLocked(
+      Dart_IsolateId id) {
+  lock_->AssertAcquired();
+  for (size_t i = 0; i < isolates_->size(); i++) {
+    if ((*isolates_)[i]->id() == id) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+void DartDebugger::AddIsolate(Dart_IsolateId id) {
+  base::AutoLock al(*lock_);
+  CHECK(FindIsolateIndexByIdLocked(id) == -1);
+  std::unique_ptr<DartDebuggerIsolate> debugger_isolate =
+      std::unique_ptr<DartDebuggerIsolate>(new DartDebuggerIsolate(id));
+  isolates_->push_back(std::move(debugger_isolate));
+}
+
+void DartDebugger::RemoveIsolate(Dart_IsolateId id) {
+  base::AutoLock al(*lock_);
+  for (size_t i = 0; i < isolates_->size(); i++) {
+    if (id == (*isolates_)[i]->id()) {
+      isolates_->erase(isolates_->begin() + i);
+      return;
+    }
+  }
+  NOTREACHED();
+}
+
+base::Lock* DartDebugger::lock_ = nullptr;
+std::vector<std::unique_ptr<DartDebuggerIsolate>>* DartDebugger::isolates_ =
+    nullptr;
+
+}  // namespace blink
diff --git a/sky/engine/core/script/dart_debugger.h b/sky/engine/core/script/dart_debugger.h
new file mode 100644
index 0000000..8c93b6c
--- /dev/null
+++ b/sky/engine/core/script/dart_debugger.h
@@ -0,0 +1,81 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKY_ENGINE_CORE_SCRIPT_DART_DEBUGGER_H_
+#define SKY_ENGINE_CORE_SCRIPT_DART_DEBUGGER_H_
+
+#include <memory>
+#include <vector>
+
+#include "dart/runtime/include/dart_api.h"
+#include "dart/runtime/include/dart_debugger_api.h"
+#include "dart/runtime/include/dart_native_api.h"
+#include "sky/engine/core/script/monitor.h"
+
+namespace base {
+  class Lock;
+}
+
+namespace blink {
+
+class DartDebuggerIsolate {
+ public:
+  DartDebuggerIsolate(Dart_IsolateId id)
+      : id_(id) {
+  }
+
+  Dart_IsolateId id() const {
+    return id_;
+  }
+
+  void Notify() {
+    monitor_.Notify();
+  }
+
+  void MessageLoop();
+
+ private:
+  const Dart_IsolateId id_;
+  Monitor monitor_;
+};
+
+class DartDebugger {
+ public:
+  static void InitDebugger();
+
+ private:
+  static void BptResolvedHandler(Dart_IsolateId isolate_id,
+                                 intptr_t bp_id,
+                                 const Dart_CodeLocation& location);
+
+  static void PausedEventHandler(Dart_IsolateId isolate_id,
+                                 intptr_t bp_id,
+                                 const Dart_CodeLocation& loc);
+
+  static void ExceptionThrownHandler(Dart_IsolateId isolate_id,
+                                     Dart_Handle exception,
+                                     Dart_StackTrace stack_trace);
+
+  static void IsolateEventHandler(Dart_IsolateId isolate_id,
+                                  Dart_IsolateEvent kind);
+
+  static void NotifyIsolate(Dart_Isolate isolate);
+
+  static intptr_t FindIsolateIndexById(Dart_IsolateId id);
+
+  static intptr_t FindIsolateIndexByIdLocked(Dart_IsolateId id);
+
+  static void AddIsolate(Dart_IsolateId id);
+
+  static void RemoveIsolate(Dart_IsolateId id);
+
+  static base::Lock* lock_;
+  static std::vector<std::unique_ptr<DartDebuggerIsolate>>* isolates_;
+
+  friend class DartDebuggerIsolate;
+};
+
+}  // namespace blink
+
+#endif  // SKY_ENGINE_CORE_SCRIPT_DART_DEBUGGER_H_
\ No newline at end of file
diff --git a/sky/engine/core/script/dart_service_isolate.cc b/sky/engine/core/script/dart_service_isolate.cc
new file mode 100644
index 0000000..d1bf545
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate.cc
@@ -0,0 +1,304 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dart_service_isolate.h"
+
+#include "base/logging.h"
+#include "dart/runtime/include/dart_api.h"
+#include "sky/engine/tonic/dart_error.h"
+#include "sky/engine/tonic/dart_string.h"
+
+#define RETURN_ERROR_HANDLE(handle)                             \
+  if (Dart_IsError(handle)) {                                   \
+    return handle;                                              \
+  }
+
+#define SHUTDOWN_ON_ERROR(handle)                               \
+  if (Dart_IsError(handle)) {                                   \
+    *error = strdup(Dart_GetError(handle));                     \
+    Dart_ExitScope();                                           \
+    Dart_ShutdownIsolate();                                     \
+    return false;                                               \
+  }
+
+#define kLibrarySourceNamePrefix "/dart_service_isolate"
+static const char* kServiceIsolateScript = "main.dart";
+
+struct ResourcesEntry {
+  const char* path_;
+  const char* resource_;
+  int length_;
+};
+
+namespace mojo {
+  namespace dart {
+    extern ResourcesEntry __sky_embedder_service_isolate_resources_[];
+  }
+}
+
+namespace blink {
+
+class Resources {
+ public:
+  static const int kNoSuchInstance = -1;
+  static int ResourceLookup(const char* path, const char** resource) {
+    ResourcesEntry* table = ResourcesTable();
+    for (int i = 0; table[i].path_ != NULL; i++) {
+      const ResourcesEntry& entry = table[i];
+      if (strcmp(path, entry.path_) == 0) {
+        *resource = entry.resource_;
+        DCHECK(entry.length_ > 0);
+        return entry.length_;
+      }
+    }
+    return kNoSuchInstance;
+  }
+
+  static const char* Path(int idx) {
+    DCHECK(idx >= 0);
+    ResourcesEntry* entry = At(idx);
+    if (entry == NULL) {
+      return NULL;
+    }
+    DCHECK(entry->path_ != NULL);
+    return entry->path_;
+  }
+
+ private:
+  static ResourcesEntry* At(int idx) {
+    DCHECK(idx >= 0);
+    ResourcesEntry* table = ResourcesTable();
+    for (int i = 0; table[i].path_ != NULL; i++) {
+      if (idx == i) {
+        return &table[i];
+      }
+    }
+    return NULL;
+  }
+  static ResourcesEntry* ResourcesTable() {
+    return &mojo::dart::__sky_embedder_service_isolate_resources_[0];
+  }
+};
+
+void DartServiceIsolate::TriggerResourceLoad(Dart_NativeArguments args) {
+  Dart_Handle library = Dart_RootLibrary();
+  DCHECK(!Dart_IsError(library));
+  Dart_Handle result = LoadResources(library);
+  DCHECK(!Dart_IsError(result));
+}
+
+void DartServiceIsolate::NotifyServerState(Dart_NativeArguments args) {
+  // NO-OP.
+}
+
+void DartServiceIsolate::Shutdown(Dart_NativeArguments args) {
+  // NO-OP.
+}
+
+DartBuiltin::Natives DartServiceIsolate::native_entries_[] = {
+  {"ServiceIsolate_TriggerResourceLoad", TriggerResourceLoad, 0 },
+  {"ServiceIsolate_NotifyServerState", NotifyServerState, 2 },
+  {"ServiceIsolate_Shutdown", Shutdown, 0 },
+};
+
+Dart_NativeFunction DartServiceIsolate::NativeResolver(Dart_Handle name,
+                                                       int argument_count,
+                                                       bool* auto_setup_scope) {
+  CHECK(builtins_);
+  return builtins_->Resolver(name, argument_count, auto_setup_scope);
+}
+
+const uint8_t* DartServiceIsolate::NativeSymbolizer(
+    Dart_NativeFunction native_function) {
+  CHECK(builtins_);
+  return builtins_->Symbolizer(native_function);
+}
+
+Dart_LibraryTagHandler DartServiceIsolate::embedder_tag_handler_ = nullptr;
+DartBuiltin* DartServiceIsolate::builtins_ = nullptr;
+
+bool DartServiceIsolate::Startup(std::string server_ip,
+                                 intptr_t server_port,
+                                 Dart_LibraryTagHandler embedder_tag_handler,
+                                 char** error) {
+  Dart_Isolate isolate = Dart_CurrentIsolate();
+  CHECK(isolate);
+
+  // Remember the embedder's library tag handler.
+  embedder_tag_handler_ = embedder_tag_handler;
+  CHECK(embedder_tag_handler_);
+
+  // Setup native entries.
+  builtins_ =
+      new DartBuiltin(&DartServiceIsolate::native_entries_[0],
+                      arraysize(native_entries_));
+
+  Dart_Handle result;
+
+  // Use our own library tag handler when loading service isolate sources.
+  Dart_SetLibraryTagHandler(DartServiceIsolate::LibraryTagHandler);
+  // Load main script.
+  Dart_Handle library = LoadScript(kServiceIsolateScript);
+  DCHECK(library != Dart_Null());
+  SHUTDOWN_ON_ERROR(library);
+  // Setup native entry resolution.
+  result = Dart_SetNativeResolver(library, NativeResolver, NativeSymbolizer);
+
+  SHUTDOWN_ON_ERROR(result);
+  // Finalize loading.
+  result = Dart_FinalizeLoading(false);
+  SHUTDOWN_ON_ERROR(result);
+
+  // Make runnable.
+  Dart_ExitScope();
+  Dart_ExitIsolate();
+  bool retval = Dart_IsolateMakeRunnable(isolate);
+  if (!retval) {
+    Dart_EnterIsolate(isolate);
+    Dart_ShutdownIsolate();
+    *error = strdup("Invalid isolate state - Unable to make it runnable.");
+    return false;
+  }
+  Dart_EnterIsolate(isolate);
+  Dart_EnterScope();
+
+  library = Dart_RootLibrary();
+  SHUTDOWN_ON_ERROR(library);
+
+  // Set the HTTP server's ip.
+  result = Dart_SetField(library,
+                         Dart_NewStringFromCString("_ip"),
+                         Dart_NewStringFromCString(server_ip.c_str()));
+  SHUTDOWN_ON_ERROR(result);
+  // If we have a port specified, start the server immediately.
+  bool auto_start = server_port >= 0;
+  if (server_port < 0) {
+    // Adjust server_port to port 0 which will result in the first available
+    // port when the HTTP server is started.
+    server_port = 0;
+  }
+  // Set the HTTP's servers port.
+  result = Dart_SetField(library,
+                         Dart_NewStringFromCString("_port"),
+                         Dart_NewInteger(server_port));
+  SHUTDOWN_ON_ERROR(result);
+  result = Dart_SetField(library,
+                         Dart_NewStringFromCString("_autoStart"),
+                         Dart_NewBoolean(auto_start));
+  SHUTDOWN_ON_ERROR(result);
+  return true;
+}
+
+Dart_Handle DartServiceIsolate::GetSource(const char* name) {
+  const intptr_t kBufferSize = 512;
+  char buffer[kBufferSize];
+  snprintf(&buffer[0], kBufferSize-1, "%s/%s", kLibrarySourceNamePrefix, name);
+  const char* vmservice_source = NULL;
+  int r = Resources::ResourceLookup(buffer, &vmservice_source);
+  DCHECK(r != Resources::kNoSuchInstance);
+  return Dart_NewStringFromCString(vmservice_source);
+}
+
+Dart_Handle DartServiceIsolate::LoadScript(const char* name) {
+  Dart_Handle url = Dart_NewStringFromCString("dart:vmservice_sky");
+  Dart_Handle source = GetSource(name);
+  return Dart_LoadScript(url, source, 0, 0);
+}
+
+Dart_Handle DartServiceIsolate::LoadSource(Dart_Handle library, const char* name) {
+  Dart_Handle url = Dart_NewStringFromCString(name);
+  Dart_Handle source = GetSource(name);
+  return Dart_LoadSource(library, url, source, 0, 0);
+}
+
+Dart_Handle DartServiceIsolate::LoadResource(Dart_Handle library,
+                                    const char* resource_name) {
+  // Prepare for invoke call.
+  Dart_Handle name = Dart_NewStringFromCString(resource_name);
+  RETURN_ERROR_HANDLE(name);
+  const char* data_buffer = NULL;
+  int data_buffer_length = Resources::ResourceLookup(resource_name,
+                                                     &data_buffer);
+  DCHECK(data_buffer_length != Resources::kNoSuchInstance);
+  Dart_Handle data_list = Dart_NewTypedData(Dart_TypedData_kUint8,
+                                            data_buffer_length);
+  RETURN_ERROR_HANDLE(data_list);
+  Dart_TypedData_Type type = Dart_TypedData_kInvalid;
+  void* data_list_buffer = NULL;
+  intptr_t data_list_buffer_length = 0;
+  Dart_Handle result = Dart_TypedDataAcquireData(data_list, &type,
+                                                 &data_list_buffer,
+                                                 &data_list_buffer_length);
+  RETURN_ERROR_HANDLE(result);
+  DCHECK(data_buffer_length == data_list_buffer_length);
+  DCHECK(data_list_buffer != NULL);
+  DCHECK(type = Dart_TypedData_kUint8);
+  memmove(data_list_buffer, &data_buffer[0], data_buffer_length);
+  result = Dart_TypedDataReleaseData(data_list);
+  RETURN_ERROR_HANDLE(result);
+
+  // Make invoke call.
+  const intptr_t kNumArgs = 2;
+  Dart_Handle args[kNumArgs] = { name, data_list };
+  result = Dart_Invoke(library, Dart_NewStringFromCString("_addResource"),
+                       kNumArgs, args);
+  return result;
+}
+
+Dart_Handle DartServiceIsolate::LoadResources(Dart_Handle library) {
+  Dart_Handle result = Dart_Null();
+  intptr_t prefixLen = strlen(kLibrarySourceNamePrefix);
+  for (intptr_t i = 0; Resources::Path(i) != NULL; i++) {
+    const char* path = Resources::Path(i);
+    // If it doesn't begin with kLibrarySourceNamePrefix it is a frontend
+    // resource.
+    if (strncmp(path, kLibrarySourceNamePrefix, prefixLen) != 0) {
+      result = LoadResource(library, path);
+      if (Dart_IsError(result)) {
+        break;
+      }
+    }
+  }
+  return result;
+}
+
+Dart_Handle DartServiceIsolate::LibraryTagHandler(Dart_LibraryTag tag,
+                                         Dart_Handle library,
+                                         Dart_Handle url) {
+  if (!Dart_IsLibrary(library)) {
+    return Dart_NewApiError("not a library");
+  }
+  if (!Dart_IsString(url)) {
+    return Dart_NewApiError("url is not a string");
+  }
+  const char* url_string = NULL;
+  Dart_Handle result = Dart_StringToCString(url, &url_string);
+  if (Dart_IsError(result)) {
+    return result;
+  }
+  Dart_Handle library_url = Dart_LibraryUrl(library);
+  const char* library_url_string = NULL;
+  result = Dart_StringToCString(library_url, &library_url_string);
+  if (Dart_IsError(result)) {
+    return result;
+  }
+  if (tag == Dart_kImportTag) {
+    // Embedder handles all requests for external libraries.
+    return embedder_tag_handler_(tag, library, url);
+  }
+  DCHECK((tag == Dart_kSourceTag) || (tag == Dart_kCanonicalizeUrl));
+  if (tag == Dart_kCanonicalizeUrl) {
+    // url is already canonicalized.
+    return url;
+  }
+  // Get source from builtin resources.
+  Dart_Handle source = GetSource(url_string);
+  if (Dart_IsError(source)) {
+    return source;
+  }
+  return Dart_LoadSource(library, url, source, 0, 0);
+}
+
+
+}  // namespace blink
diff --git a/sky/engine/core/script/dart_service_isolate.h b/sky/engine/core/script/dart_service_isolate.h
new file mode 100644
index 0000000..450ad16
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate.h
@@ -0,0 +1,55 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKY_ENGINE_CORE_SCRIPT_DART_SERVICE_ISOLATE_H_
+#define SKY_ENGINE_CORE_SCRIPT_DART_SERVICE_ISOLATE_H_
+
+#include <string>
+
+#include "include/dart_api.h"
+#include "sky/engine/config.h"
+#include "sky/engine/tonic/dart_builtin.h"
+
+namespace blink {
+
+class DartServiceIsolate {
+ public:
+  static bool Bootstrap();
+
+  static bool Startup(std::string server_ip,
+                      intptr_t server_port,
+                      Dart_LibraryTagHandler embedder_tag_handler,
+                      char** error);
+
+ private:
+  // Native entries.
+  static void TriggerResourceLoad(Dart_NativeArguments args);
+  static void NotifyServerState(Dart_NativeArguments args);
+  static void Shutdown(Dart_NativeArguments args);
+  // Native entry resolution.
+  static Dart_NativeFunction NativeResolver(Dart_Handle name,
+                                            int argument_count,
+                                            bool* auto_setup_scope);
+  static const uint8_t* NativeSymbolizer(Dart_NativeFunction native_function);
+  static DartBuiltin::Natives native_entries_[];
+  static DartBuiltin* builtins_;
+
+  // Script loading.
+  static Dart_Handle GetSource(const char* name);
+  static Dart_Handle LoadScript(const char* name);
+  static Dart_Handle LoadSource(Dart_Handle library, const char* name);
+  static Dart_Handle LibraryTagHandler(Dart_LibraryTag tag, Dart_Handle library,
+                                       Dart_Handle url);
+
+  // Observatory resource loading.
+  static Dart_Handle LoadResources(Dart_Handle library);
+  static Dart_Handle LoadResource(Dart_Handle library, const char* name);
+
+  static Dart_LibraryTagHandler embedder_tag_handler_;
+};
+
+
+}  // namespace blink
+
+#endif  // SKY_ENGINE_CORE_SCRIPT_DART_SERVICE_ISOLATE_H_
diff --git a/sky/engine/core/script/dart_service_isolate/loader.dart b/sky/engine/core/script/dart_service_isolate/loader.dart
new file mode 100644
index 0000000..a8d0d94
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate/loader.dart
@@ -0,0 +1,11 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of dart_controller_service_isolate;
+
+_processLoadRequest(request) {
+  var sp = request[0];
+  var uri = Uri.parse(request[1]);
+  sp.send('Service isolate loading not supported by embedder (uri = $uri).');
+}
\ No newline at end of file
diff --git a/sky/engine/core/script/dart_service_isolate/main.dart b/sky/engine/core/script/dart_service_isolate/main.dart
new file mode 100644
index 0000000..1eb027d
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate/main.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library sky_shell_dart_controller_service_isolate;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'dart:isolate';
+import 'dart:vmservice';
+
+part 'loader.dart';
+part 'resources.dart';
+part 'server.dart';
+
+// The TCP ip/port that the HTTP server listens on.
+int _port;
+String _ip;
+// Should the HTTP server auto start?
+bool _autoStart;
+
+// HTTP server.
+Server server;
+
+_onShutdown() {
+  if (server != null) {
+    server.close(true).catchError((e, st) {
+      print(e);
+    }).whenComplete(_shutdown);
+  } else {
+    _shutdown();
+  }
+}
+
+void _bootServer() {
+  // Load resources.
+  _triggerResourceLoad();
+  // Lazily create service.
+  var service = new VMService();
+  service.onShutdown = _onShutdown;
+  // Lazily create server.
+  server = new Server(service, _ip, _port);
+}
+
+main() {
+  if (_autoStart) {
+    _bootServer();
+    if (server != null) {
+      server.startup();
+    }
+  }
+  scriptLoadPort.handler = _processLoadRequest;
+  // It's just here to push an event on the event loop so that we invoke the
+  // scheduled microtasks.
+  Timer.run(() {});
+  return scriptLoadPort;
+}
+
+_shutdown() native "ServiceIsolate_Shutdown";
diff --git a/sky/engine/core/script/dart_service_isolate/resources.dart b/sky/engine/core/script/dart_service_isolate/resources.dart
new file mode 100644
index 0000000..11e347e
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate/resources.dart
@@ -0,0 +1,50 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of dart_controller_service_isolate;
+
+String detectMimeType(String name) {
+  var extensionStart = name.lastIndexOf('.');
+  var extension = name.substring(extensionStart+1);
+  switch (extension) {
+    case 'html':
+      return 'text/html; charset=UTF-8';
+    case 'dart':
+      return 'application/dart; charset=UTF-8';
+    case 'js':
+      return 'application/javascript; charset=UTF-8';
+    case 'css':
+      return 'text/css; charset=UTF-8';
+    case 'gif':
+      return 'image/gif';
+    case 'png':
+      return 'image/png';
+    case 'jpg':
+      return 'image/jpeg';
+    case 'jpeg':
+      return 'image/jpeg';
+    case 'svg':
+      return 'image/svg+xml';
+    default:
+      return 'text/plain';
+  }
+}
+
+
+class Resource {
+  final String name;
+  final String mimeType;
+  final List<int> data;
+  Resource(this.name, this.mimeType, this.data);
+  static final Map<String, Resource> resources = new Map<String, Resource>();
+}
+
+
+_addResource(String name, List<int> data) {
+  var mimeType = detectMimeType(name);
+  Resource resource = new Resource(name, mimeType, data);
+  Resource.resources[name] = resource;
+}
+
+_triggerResourceLoad() native "ServiceIsolate_TriggerResourceLoad";
diff --git a/sky/engine/core/script/dart_service_isolate/server.dart b/sky/engine/core/script/dart_service_isolate/server.dart
new file mode 100644
index 0000000..4e069f9
--- /dev/null
+++ b/sky/engine/core/script/dart_service_isolate/server.dart
@@ -0,0 +1,225 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+part of dart_controller_service_isolate;
+
+class WebSocketClient extends Client {
+  static const int PARSE_ERROR_CODE = 4000;
+  static const int BINARY_MESSAGE_ERROR_CODE = 4001;
+  static const int NOT_MAP_ERROR_CODE = 4002;
+  final WebSocket socket;
+
+  WebSocketClient(this.socket, VMService service) : super(service) {
+    socket.listen((message) => onWebSocketMessage(message));
+    socket.done.then((_) => close());
+  }
+
+  onWebSocketMessage(message) {
+    if (message is String) {
+      var map;
+      try {
+        map = JSON.decode(message);
+      } catch (e) {
+        socket.close(PARSE_ERROR_CODE, 'Message parse error: $e');
+        return;
+      }
+      if (map is! Map) {
+        socket.close(NOT_MAP_ERROR_CODE, 'Message must be a JSON map.');
+        return;
+      }
+      var serial = map['id'];
+      onMessage(serial, new Message.fromJsonRpc(map));
+    } else {
+      socket.close(BINARY_MESSAGE_ERROR_CODE, 'Message must be a string.');
+    }
+  }
+
+  post(dynamic result) {
+    try {
+      socket.add(result);
+    } catch (_) {
+      print("Ignoring error posting over WebSocket.");
+    }
+  }
+
+  dynamic toJson() {
+    Map map = super.toJson();
+    map['type'] = 'WebSocketClient';
+    map['socket'] = '$socket';
+    return map;
+  }
+}
+
+
+class HttpRequestClient extends Client {
+  static ContentType jsonContentType =
+      new ContentType("application", "json", charset: "utf-8");
+  final HttpRequest request;
+
+  HttpRequestClient(this.request, VMService service) : super(service);
+
+  post(String result) {
+    request.response..headers.contentType = jsonContentType
+                    ..write(result)
+                    ..close();
+    close();
+  }
+
+  dynamic toJson() {
+    Map map = super.toJson();
+    map['type'] = 'HttpRequestClient';
+    map['request'] = '$request';
+    return map;
+  }
+}
+
+class Server {
+  static const WEBSOCKET_PATH = '/ws';
+  static const ROOT_REDIRECT_PATH = '/index.html';
+
+  final VMService _service;
+  final String _ip;
+  final int _port;
+
+  HttpServer _server;
+  bool get running => _server != null;
+  bool _displayMessages = false;
+
+  Server(this._service, this._ip, this._port) {
+    _displayMessages = (_ip != '127.0.0.1' || _port != 8181);
+  }
+
+  bool _shouldServeObservatory(HttpRequest request) {
+    if (request.headers['Observatory-Version'] != null) {
+      // Request is already coming from Observatory.
+      return false;
+    }
+    // TODO(johnmccutchan): Test with obscure browsers.
+    if (request.headers.value(HttpHeaders.USER_AGENT).contains('Mozilla')) {
+      // Request is coming from a browser but not Observatory application.
+      // Serve Observatory and let the Observatory make the real request.
+      return true;
+    }
+    // All other user agents are assumed to be textual.
+    return false;
+  }
+
+  _onServerShutdown() {
+  }
+
+  _serverError(error, stackTrace) {
+    _onServerShutdown();
+  }
+
+  _serverDone() {
+    _onServerShutdown();
+  }
+
+  _requestHandler(HttpRequest request) {
+    // Allow cross origin requests with 'observatory' header.
+    request.response.headers.add('Access-Control-Allow-Origin', '*');
+    request.response.headers.add('Access-Control-Allow-Headers',
+                                 'Observatory-Version');
+
+    if (request.method != 'GET') {
+      // Not a GET request. Do nothing.
+      request.response.close();
+      return;
+    }
+
+    final String path =
+          request.uri.path == '/' ? ROOT_REDIRECT_PATH : request.uri.path;
+
+    if (path == WEBSOCKET_PATH) {
+      WebSocketTransformer.upgrade(request).then((WebSocket webSocket) {
+        new WebSocketClient(webSocket, _service);
+      });
+      return;
+    }
+
+    var resource = Resource.resources[path];
+    if (resource == null && _shouldServeObservatory(request)) {
+      resource = Resource.resources[ROOT_REDIRECT_PATH];
+      assert(resource != null);
+    }
+    if (resource != null) {
+      // Serving up a static resource (e.g. .css, .html, .png).
+      request.response.headers.contentType =
+          ContentType.parse(resource.mimeType);
+      request.response.add(resource.data);
+      request.response.close();
+      return;
+    }
+    var message = new Message.fromUri(request.uri);
+    var client = new HttpRequestClient(request, _service);
+    client.onMessage(null, message);
+  }
+
+  Future startup() {
+    if (_server != null) {
+      // Already running.
+      return new Future.value(this);
+    }
+
+    // Startup HTTP server.
+    var address = new InternetAddress('127.0.0.1');
+    return HttpServer.bind(address, _port).then((s) {
+      _server = s;
+      _server.listen(_requestHandler,
+                     onError: _serverError,
+                     onDone: _serverDone,
+                     cancelOnError: true);
+      var ip = _server.address.address.toString();
+      if (_displayMessages) {
+        var port = _server.port.toString();
+        print('Observatory listening on http://$ip:$port');
+      }
+      // Server is up and running.
+      _notifyServerState(ip, _server.port);
+      return this;
+    }).catchError((e, st) {
+      print('Could not start Observatory HTTP server:\n$e\n$st\n');
+      _notifyServerState("", 0);
+      return this;
+    });
+  }
+
+  close(bool force) {
+    if (_server == null) {
+      return new Future.value(null);
+    }
+    return _server.close(force: force);
+  }
+
+  Future shutdown(bool forced) {
+    if (_server == null) {
+      // Not started.
+      return new Future.value(this);
+    }
+
+    // Force displaying of status messages if we are forcibly shutdown.
+    _displayMessages = _displayMessages || forced;
+
+    // Shutdown HTTP server and subscription.
+    var ip = _server.address.address.toString();
+    var port = _server.port.toString();
+    return close(forced).then((_) {
+      if (_displayMessages) {
+        print('Observatory no longer listening on http://$ip:$port');
+      }
+      _server = null;
+      _notifyServerState("", 0);
+      return this;
+    }).catchError((e, st) {
+      _server = null;
+      print('Could not shutdown Observatory HTTP server:\n$e\n$st\n');
+      _notifyServerState("", 0);
+      return this;
+    });
+  }
+
+}
+
+_notifyServerState(String ip, int port)
+    native "ServiceIsolate_NotifyServerState";
diff --git a/sky/engine/core/script/monitor.h b/sky/engine/core/script/monitor.h
new file mode 100644
index 0000000..af784ec
--- /dev/null
+++ b/sky/engine/core/script/monitor.h
@@ -0,0 +1,75 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKY_ENGINE_CORE_SCRIPT_MONITOR_H_
+#define SKY_ENGINE_CORE_SCRIPT_MONITOR_H_
+
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+
+namespace blink {
+
+class Monitor {
+ public:
+  Monitor() {
+    lock_ = new base::Lock();
+    condition_variable_ = new base::ConditionVariable(lock_);
+  }
+
+  ~Monitor() {
+    delete condition_variable_;
+    delete lock_;
+  }
+
+  void Enter() {
+    lock_->Acquire();
+  }
+
+  void Exit() {
+    lock_->Release();
+  }
+
+  void Notify() {
+    condition_variable_->Signal();
+  }
+
+  void Wait() {
+    condition_variable_->Wait();
+  }
+
+ private:
+  base::Lock* lock_;
+  base::ConditionVariable* condition_variable_;
+  DISALLOW_COPY_AND_ASSIGN(Monitor);
+};
+
+class MonitorLocker {
+ public:
+  explicit MonitorLocker(Monitor* monitor) : monitor_(monitor) {
+    CHECK(monitor_);
+    monitor_->Enter();
+  }
+
+  virtual ~MonitorLocker() {
+    monitor_->Exit();
+  }
+
+  void Wait() {
+    return monitor_->Wait();
+  }
+
+  void Notify() {
+    monitor_->Notify();
+  }
+
+ private:
+  Monitor* const monitor_;
+
+  DISALLOW_COPY_AND_ASSIGN(MonitorLocker);
+};
+
+}  // namespace blink
+
+
+#endif  // SKY_ENGINE_CORE_SCRIPT_MONITOR_H_
\ No newline at end of file
diff --git a/sky/tools/webkitpy/layout_tests/controllers/single_test_runner.py b/sky/tools/webkitpy/layout_tests/controllers/single_test_runner.py
index 31d4d8c..56af871 100644
--- a/sky/tools/webkitpy/layout_tests/controllers/single_test_runner.py
+++ b/sky/tools/webkitpy/layout_tests/controllers/single_test_runner.py
@@ -307,6 +307,10 @@
             failures.append(test_failures.FailureMissingAudio())
         return failures
 
+    # FIXME: This won't be needed once we have flags for Sky that suppress the
+    # Observatory messages.
+    _filter_observatory_messages = re.compile(r"^CONSOLE: Observatory listening on.*\n", re.MULTILINE)
+
     def _get_normalized_output_text(self, output):
         """Returns the normalized text output, i.e. the output in which
         the end-of-line characters are normalized to "\n"."""
@@ -314,7 +318,9 @@
         # changed to "\r\n" by our system (Python/Cygwin), resulting in
         # "\r\r\n", when, in fact, we wanted to compare the text output with
         # the normalized text expectation files.
-        return output.replace("\r\r\n", "\r\n").replace("\r\n", "\n")
+        normalized_lines = output.replace("\r\r\n", "\r\n").replace("\r\n", "\n")
+        normalized_lines = re.sub(self._filter_observatory_messages, r"", normalized_lines)
+        return normalized_lines
 
     # FIXME: This function also creates the image diff. Maybe that work should
     # be handled elsewhere?