diff --git a/engine/bindings/BUILD.gn b/engine/bindings/BUILD.gn index b7dbafe6c15..fd6a30c645f 100644 --- a/engine/bindings/BUILD.gn +++ b/engine/bindings/BUILD.gn @@ -30,6 +30,9 @@ source_set("bindings") { "scheduled_action.cc", "scheduled_action.h", ] + + defines = [ "DART_IO_SECURE_SOCKET_DISABLED" ] + deps = [ "//base", "//dart/runtime/bin:libdart_withcore", @@ -38,12 +41,14 @@ source_set("bindings") { "//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/engine/bindings/builtin.cc b/engine/bindings/builtin.cc index 81fd9d3bce5..3fa5c39ee07 100644 --- a/engine/bindings/builtin.cc +++ b/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 @@ const LibraryDescriptor kBuiltinLibraries[] = { 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/engine/bindings/builtin.h b/engine/bindings/builtin.h index 8c3bb05ad9c..93f67a258e2 100644 --- a/engine/bindings/builtin.h +++ b/engine/bindings/builtin.h @@ -18,6 +18,7 @@ class Builtin { kBuiltinLibrary, kSkyLibrary, kMojoInternalLibrary, + kIOLibrary, kInvalidLibrary, }; diff --git a/engine/bindings/builtin_natives.cc b/engine/bindings/builtin_natives.cc index 55584a3d017..90bec2e7456 100644 --- a/engine/bindings/builtin_natives.cc +++ b/engine/bindings/builtin_natives.cc @@ -87,7 +87,8 @@ static Dart_Handle GetClosure(Dart_Handle builtin_library, const char* name) { 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 @@ static void InitDartInternal(Dart_Handle builtin_library) { 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 @@ void ScheduleMicrotask(Dart_NativeArguments args) { 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 @@ void Timer_create(Dart_NativeArguments args) { 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 @@ void Timer_cancel(Dart_NativeArguments args) { DART_CHECK_VALID(Dart_GetNativeIntegerArgument(args, 0, &timer_id)); DOMDartState* state = DOMDartState::Current(); + CHECK(state); DOMTimer::removeByID(state->document(), timer_id); } diff --git a/engine/bindings/builtin_natives.h b/engine/bindings/builtin_natives.h index a08fc193214..3f525bc8e64 100644 --- a/engine/bindings/builtin_natives.h +++ b/engine/bindings/builtin_natives.h @@ -12,12 +12,17 @@ namespace blink { 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/engine/bindings/snapshot.dart b/engine/bindings/snapshot.dart index 51fd6f4d20a..d620521772f 100644 --- a/engine/bindings/snapshot.dart +++ b/engine/bindings/snapshot.dart @@ -6,6 +6,7 @@ import 'dart:async'; 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/engine/core/BUILD.gn b/engine/core/BUILD.gn index d01e064df53..661835c3bd4 100644 --- a/engine/core/BUILD.gn +++ b/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 @@ source_set("prerequisites") { ] } +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/engine/core/core.gni b/engine/core/core.gni index 5ecbe0a7e25..2a78c74f4d9 100644 --- a/engine/core/core.gni +++ b/engine/core/core.gni @@ -1009,12 +1009,17 @@ sky_core_files = [ "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/engine/core/script/dart_controller.cc b/engine/core/script/dart_controller.cc index dc7ce4c78a6..e03cb3435de 100644 --- a/engine/core/script/dart_controller.cc +++ b/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 @@ static bool IsServiceIsolateURL(const char* url_name) { 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 @@ static Dart_Isolate IsolateCreateCallback(const char* script_uri, 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 @@ static Dart_Isolate IsolateCreateCallback(const char* script_uri, 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 @@ static void MessageNotifyCallback(Dart_Isolate dest_isolate) { 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 @@ void DartController::CreateIsolateFor(Document* document) { 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 @@ void DartController::InitVM() { 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/engine/core/script/dart_debugger.cc b/engine/core/script/dart_debugger.cc new file mode 100644 index 00000000000..f7c1c492a5c --- /dev/null +++ b/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>(); +} + +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 debugger_isolate = + std::unique_ptr(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>* DartDebugger::isolates_ = + nullptr; + +} // namespace blink diff --git a/engine/core/script/dart_debugger.h b/engine/core/script/dart_debugger.h new file mode 100644 index 00000000000..8c93b6cecb8 --- /dev/null +++ b/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 +#include + +#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>* isolates_; + + friend class DartDebuggerIsolate; +}; + +} // namespace blink + +#endif // SKY_ENGINE_CORE_SCRIPT_DART_DEBUGGER_H_ \ No newline at end of file diff --git a/engine/core/script/dart_service_isolate.cc b/engine/core/script/dart_service_isolate.cc new file mode 100644 index 00000000000..d1bf545b572 --- /dev/null +++ b/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/engine/core/script/dart_service_isolate.h b/engine/core/script/dart_service_isolate.h new file mode 100644 index 00000000000..450ad16f4dd --- /dev/null +++ b/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 + +#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/engine/core/script/dart_service_isolate/loader.dart b/engine/core/script/dart_service_isolate/loader.dart new file mode 100644 index 00000000000..a8d0d94d532 --- /dev/null +++ b/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/engine/core/script/dart_service_isolate/main.dart b/engine/core/script/dart_service_isolate/main.dart new file mode 100644 index 00000000000..1eb027d3de7 --- /dev/null +++ b/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/engine/core/script/dart_service_isolate/resources.dart b/engine/core/script/dart_service_isolate/resources.dart new file mode 100644 index 00000000000..11e347e0be0 --- /dev/null +++ b/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 data; + Resource(this.name, this.mimeType, this.data); + static final Map resources = new Map(); +} + + +_addResource(String name, List data) { + var mimeType = detectMimeType(name); + Resource resource = new Resource(name, mimeType, data); + Resource.resources[name] = resource; +} + +_triggerResourceLoad() native "ServiceIsolate_TriggerResourceLoad"; diff --git a/engine/core/script/dart_service_isolate/server.dart b/engine/core/script/dart_service_isolate/server.dart new file mode 100644 index 00000000000..4e069f97edc --- /dev/null +++ b/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/engine/core/script/monitor.h b/engine/core/script/monitor.h new file mode 100644 index 00000000000..af784ecce6f --- /dev/null +++ b/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/tools/webkitpy/layout_tests/controllers/single_test_runner.py b/tools/webkitpy/layout_tests/controllers/single_test_runner.py index 31d4d8cda39..56af871b1fd 100644 --- a/tools/webkitpy/layout_tests/controllers/single_test_runner.py +++ b/tools/webkitpy/layout_tests/controllers/single_test_runner.py @@ -307,6 +307,10 @@ class SingleTestRunner(object): 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 @@ class SingleTestRunner(object): # 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?