| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/public/test/nested_message_pump_android.h" |
| |
| #include "base/android/jni_android.h" |
| #include "base/auto_reset.h" |
| #include "content/public/test/android/test_support_content_jni_headers/NestedSystemMessageHandler_jni.h" |
| |
| namespace content { |
| |
| NestedMessagePumpAndroid::NestedMessagePumpAndroid() = default; |
| NestedMessagePumpAndroid::~NestedMessagePumpAndroid() = default; |
| |
| // We need to override Run() instead of using the implementation from |
| // base::MessagePumpForUI because one of the side-effects of |
| // dispatchOneMessage() is calling Looper::pollOnce(). If that happens while we |
| // are inside Alooper_pollOnce(), we get a crash because Android Looper |
| // isn't re-entrant safe. Instead, we keep the entire run loop in Java (in |
| // MessageQueue.next()). |
| void NestedMessagePumpAndroid::Run(base::MessagePump::Delegate* delegate) { |
| // Preserve delegate and quit state of the current run loop so it can be |
| // restored after the loop below has completed. |
| auto* old_delegate = SetDelegate(delegate); |
| bool old_quit = SetQuit(false); |
| |
| ScheduleWork(); |
| while (!ShouldQuit()) { |
| RunJavaSystemMessageHandler(); |
| |
| // Handle deferred work if necessary. |
| // Note: |deferred_work_type_| and |deferred_do_idle_work_| must be reset |
| // here because another Run() loop can be triggered when we dispatch work. |
| CHECK(!ShouldDeferWork()); |
| auto work_type = std::exchange(deferred_work_type_, kNone); |
| auto do_idle_work = std::exchange(deferred_do_idle_work_, true); |
| switch (work_type) { |
| case kNone: |
| // Do nothing. |
| break; |
| case kDelayed: |
| base::MessagePumpForUI::DoDelayedLooperWork(); |
| break; |
| case kNonDelayed: |
| base::MessagePumpForUI::DoNonDelayedLooperWork(do_idle_work); |
| break; |
| } |
| } |
| |
| // Restore old run loop state. |
| SetDelegate(old_delegate); |
| SetQuit(old_quit); |
| } |
| |
| void NestedMessagePumpAndroid::Quit() { |
| QuitJavaSystemMessageHandler(); |
| SetQuit(true); |
| } |
| |
| // Since this pump allows Run() to be called on the UI thread, there's no need |
| // to also attach the pump on that thread. Making attach a no-op also prevents |
| // the top level run loop from being considered a nested one. |
| void NestedMessagePumpAndroid::Attach(Delegate*) {} |
| |
| void NestedMessagePumpAndroid::DoDelayedLooperWork() { |
| if (ShouldDeferWork()) { |
| switch (deferred_work_type_) { |
| case kNone: |
| deferred_work_type_ = kDelayed; |
| break; |
| case kDelayed: |
| // Do nothing. We are already going to process the next delayed work. |
| break; |
| case kNonDelayed: |
| // Do nothing. The immediate work will be processed and then a |
| // request to process immediate and idle work will be made. This |
| // will unconditionally process the next work item which should be |
| // the delayed item. |
| break; |
| } |
| QuitJavaSystemMessageHandler(); |
| return; |
| } |
| |
| base::MessagePumpForUI::DoDelayedLooperWork(); |
| } |
| |
| void NestedMessagePumpAndroid::DoNonDelayedLooperWork(bool do_idle_work) { |
| if (ShouldDeferWork()) { |
| deferred_work_type_ = kNonDelayed; |
| |
| // Only do idle work if we are consistently asked to do it. If there |
| // is a request to defer the idle work, then we will honor that over |
| // any previous request to do idle work. Once the non-delayed work is |
| // dispatched, another request to do the idle work will be made. |
| deferred_do_idle_work_ &= do_idle_work; |
| |
| QuitJavaSystemMessageHandler(); |
| return; |
| } |
| |
| base::MessagePumpForUI::DoNonDelayedLooperWork(do_idle_work); |
| } |
| |
| void NestedMessagePumpAndroid::RunJavaSystemMessageHandler() { |
| auto* env = base::android::AttachCurrentThread(); |
| CHECK(!quit_message_handler_); |
| CHECK(!inside_run_message_handler_); |
| base::AutoReset<bool> auto_reset(&inside_run_message_handler_, true); |
| // Dispatch the first available Java message and one or more C++ messages as |
| // a side effect. If there are no Java messages available, this call will |
| // block until one becomes available (while continuing to process C++ work). |
| bool ret = Java_NestedSystemMessageHandler_dispatchOneMessage(env); |
| CHECK(ret) << "Error running java message loop, tests will likely fail."; |
| quit_message_handler_ = false; |
| } |
| |
| void NestedMessagePumpAndroid::QuitJavaSystemMessageHandler() { |
| if (!inside_run_message_handler_ || quit_message_handler_) |
| return; |
| |
| quit_message_handler_ = true; |
| |
| // Wake up the Java message dispatcher to exit dispatchOneMessage(). |
| auto* env = base::android::AttachCurrentThread(); |
| Java_NestedSystemMessageHandler_postQuitMessage(env); |
| } |
| |
| } // namespace content |