/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AndroidBridge.h" #include "base/message_loop.h" #include "mozilla/Monitor.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticPtr.h" #include "nsThread.h" #include "nsThreadManager.h" #include "nsThreadUtils.h" using namespace mozilla; namespace { class AndroidUiThread; StaticRefPtr sThread; static MessageLoop* sMessageLoop; /* * The AndroidUiThread is derived from nsThread so that nsIRunnable objects that get * dispatched may be intercepted. Only nsIRunnable objects that need to be synchronously * executed are passed into the nsThread to be queued. All other nsIRunnable object * are immediately dispatched to the Android UI thread via the AndroidBridge. * AndroidUiThread is derived from nsThread instead of being an nsIEventTarget * wrapper that contains an nsThread object because if nsIRunnable objects with a * delay were dispatch directly to an nsThread object, such as obtained from * nsThreadManager::GetCurrentThread(), the nsIRunnable could get stuck in the * nsThread nsIRunnable queue. This is due to the fact that Android controls the * event loop in the Android UI thread and has no knowledge of when the nsThread * needs to be drained. */ class AndroidUiThread : public nsThread { public: NS_DECL_ISUPPORTS_INHERITED AndroidUiThread() : nsThread(nsThread::NOT_MAIN_THREAD, 0) {} nsresult Dispatch(already_AddRefed aEvent, uint32_t aFlags) override; nsresult DelayedDispatch(already_AddRefed aEvent, uint32_t aDelayMs) override; private: ~AndroidUiThread() {} }; NS_IMPL_ISUPPORTS_INHERITED0(AndroidUiThread, nsThread) NS_IMETHODIMP AndroidUiThread::Dispatch(already_AddRefed aEvent, uint32_t aFlags) { if (aFlags & NS_DISPATCH_SYNC) { return nsThread::Dispatch(Move(aEvent), aFlags); } else { AndroidBridge::Bridge()->PostTaskToUiThread(Move(aEvent), 0); return NS_OK; } } NS_IMETHODIMP AndroidUiThread::DelayedDispatch(already_AddRefed aEvent, uint32_t aDelayMs) { AndroidBridge::Bridge()->PostTaskToUiThread(Move(aEvent), aDelayMs); return NS_OK; } static void PumpEvents() { NS_ProcessPendingEvents(sThread.get()); } class ThreadObserver : public nsIThreadObserver { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSITHREADOBSERVER ThreadObserver() {} private: virtual ~ThreadObserver() {} }; NS_IMPL_ISUPPORTS(ThreadObserver, nsIThreadObserver) NS_IMETHODIMP ThreadObserver::OnDispatchedEvent(nsIThreadInternal *thread) { AndroidBridge::Bridge()->PostTaskToUiThread(NS_NewRunnableFunction(&PumpEvents), 0); return NS_OK; } NS_IMETHODIMP ThreadObserver::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait) { return NS_OK; } NS_IMETHODIMP ThreadObserver::AfterProcessNextEvent(nsIThreadInternal *thread, bool eventWasProcessed) { return NS_OK; } class CreateOnUiThread : public Runnable { public: CreateOnUiThread() : mCreated(false), mThreadCreationMonitor("AndroidUiThreadCreationLock") {} NS_IMETHOD Run() override { MonitorAutoLock lock(mThreadCreationMonitor); sThread = new AndroidUiThread(); sThread->InitCurrentThread(); sThread->SetObserver(new ThreadObserver()); sMessageLoop = new MessageLoop(MessageLoop::TYPE_MOZILLA_ANDROID_UI, sThread.get()); mCreated = true; lock.NotifyAll(); return NS_OK; } void WaitForCreation() { MonitorAutoLock lock(mThreadCreationMonitor); while (!mCreated) { lock.Wait(); } } private: bool mCreated; Monitor mThreadCreationMonitor; }; class DestroyOnUiThread : public Runnable { public: DestroyOnUiThread() : mDestroyed(false), mThreadDestructionMonitor("AndroidUiThreadCreationLock") {} NS_IMETHOD Run() override { MonitorAutoLock lock(mThreadDestructionMonitor); delete sMessageLoop; sMessageLoop = nullptr; MOZ_ASSERT(sThread); nsThreadManager::get().UnregisterCurrentThread(*sThread); sThread = nullptr; mDestroyed = true; lock.NotifyAll(); return NS_OK; } void WaitForDestruction() { MonitorAutoLock lock(mThreadDestructionMonitor); while (!mDestroyed) { lock.Wait(); } } private: bool mDestroyed; Monitor mThreadDestructionMonitor; }; } // namespace namespace mozilla { void CreateAndroidUiThread() { MOZ_ASSERT(!sThread); RefPtr runnable = new CreateOnUiThread; AndroidBridge::Bridge()->PostTaskToUiThread(do_AddRef(runnable), 0); runnable->WaitForCreation(); } void DestroyAndroidUiThread() { MOZ_ASSERT(sThread); RefPtr runnable = new DestroyOnUiThread; // Insure the Android bridge has not already been deconstructed. MOZ_ASSERT(AndroidBridge::Bridge() != nullptr); AndroidBridge::Bridge()->PostTaskToUiThread(do_AddRef(runnable), 0); runnable->WaitForDestruction(); } MessageLoop* GetAndroidUiThreadMessageLoop() { return sMessageLoop; } } // namespace mozilla