| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "tonic/dart_message_handler.h" |
| |
| #include "base/bind.h" |
| #include "dart/runtime/include/dart_api.h" |
| #include "dart/runtime/include/dart_native_api.h" |
| #include "dart/runtime/include/dart_tools_api.h" |
| #include "tonic/dart_error.h" |
| #include "tonic/dart_state.h" |
| |
| namespace tonic { |
| |
| DartMessageHandler::DartMessageHandler() |
| : handled_first_message_(false), |
| quit_message_loop_when_isolate_exits_(true), |
| isolate_exited_(false), |
| isolate_had_uncaught_exception_error_(false), |
| task_runner_(nullptr) { |
| } |
| |
| DartMessageHandler::~DartMessageHandler() { |
| task_runner_ = nullptr; |
| } |
| |
| void DartMessageHandler::Initialize( |
| const scoped_refptr<base::SingleThreadTaskRunner>& runner) { |
| // Only can be called once. |
| CHECK(!task_runner_); |
| task_runner_ = runner; |
| CHECK(task_runner_); |
| Dart_SetMessageNotifyCallback(MessageNotifyCallback); |
| } |
| |
| void DartMessageHandler::OnMessage(DartState* dart_state) { |
| auto task_runner = dart_state->message_handler().task_runner(); |
| |
| // Schedule a task to run on the message loop thread. |
| task_runner->PostTask(FROM_HERE, |
| base::Bind(&HandleMessage, dart_state->GetWeakPtr())); |
| } |
| |
| void DartMessageHandler::OnHandleMessage(DartState* dart_state) { |
| DartIsolateScope scope(dart_state->isolate()); |
| DartApiScope dart_api_scope; |
| |
| bool error = false; |
| |
| // On the first message, check if we should pause on isolate start. |
| if (!handled_first_message()) { |
| set_handled_first_message(true); |
| if (Dart_ShouldPauseOnStart()) { |
| // Mark that we are paused on isolate start. |
| Dart_SetPausedOnStart(true); |
| } |
| } |
| |
| if (Dart_IsPausedOnStart()) { |
| // We are paused on isolate start. Only handle service messages until we are |
| // requested to resume. |
| if (Dart_HasServiceMessages()) { |
| bool resume = Dart_HandleServiceMessages(); |
| if (!resume) { |
| return; |
| } |
| Dart_SetPausedOnStart(false); |
| // We've resumed, handle *all* normal messages that are in the queue. |
| error = LogIfError(Dart_HandleMessages()); |
| } |
| } else if (Dart_IsPausedOnExit()) { |
| // We are paused on isolate exit. Only handle service messages until we are |
| // requested to resume. |
| if (Dart_HasServiceMessages()) { |
| bool resume = Dart_HandleServiceMessages(); |
| if (!resume) { |
| return; |
| } |
| Dart_SetPausedOnExit(false); |
| } |
| } else { |
| // We are processing messages normally. |
| error = LogIfError(Dart_HandleMessage()); |
| } |
| |
| if (error) { |
| // Remember that we had an uncaught exception error. |
| isolate_had_uncaught_exception_error_ = true; |
| } |
| |
| if (error || !Dart_HasLivePorts()) { |
| // The isolate has no live ports and would like to exit. |
| if (Dart_ShouldPauseOnExit()) { |
| // Mark that we are paused on exit. |
| Dart_SetPausedOnExit(true); |
| } else { |
| isolate_exited_ = true; |
| if (quit_message_loop_when_isolate_exits()) { |
| // Quit. |
| base::MessageLoop::current()->QuitWhenIdle(); |
| } |
| } |
| } |
| } |
| |
| void DartMessageHandler::MessageNotifyCallback(Dart_Isolate dest_isolate) { |
| auto dart_state = DartState::From(dest_isolate); |
| CHECK(dart_state); |
| dart_state->message_handler().OnMessage(dart_state); |
| } |
| |
| void DartMessageHandler::HandleMessage( |
| base::WeakPtr<DartState> dart_state) { |
| if (!dart_state) |
| return; |
| dart_state->message_handler().OnHandleMessage(dart_state.get()); |
| } |
| |
| } // namespace tonic |