Bug 1692971 [wpt PR 27638] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / widget / android / nsAppShell.cpp
blobe920444e255c98c80a29a8d9e942de86a168ddda
1 /* -*- Mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsAppShell.h"
8 #include "base/basictypes.h"
9 #include "base/message_loop.h"
10 #include "base/task.h"
11 #include "mozilla/Hal.h"
12 #include "nsExceptionHandler.h"
13 #include "nsIScreen.h"
14 #include "nsWindow.h"
15 #include "nsThreadUtils.h"
16 #include "nsIObserverService.h"
17 #include "nsIAppStartup.h"
18 #include "nsIGeolocationProvider.h"
19 #include "nsCacheService.h"
20 #include "nsIDOMWakeLockListener.h"
21 #include "nsIPowerManagerService.h"
22 #include "nsISpeculativeConnect.h"
23 #include "nsIURIFixup.h"
24 #include "nsCategoryManagerUtils.h"
25 #include "mozilla/dom/GeolocationPosition.h"
27 #include "mozilla/ArrayUtils.h"
28 #include "mozilla/Components.h"
29 #include "mozilla/Telemetry.h"
30 #include "mozilla/Services.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/ProfilerLabels.h"
33 #include "mozilla/Hal.h"
34 #include "mozilla/dom/BrowserChild.h"
35 #include "mozilla/dom/Document.h"
36 #include "mozilla/intl/OSPreferences.h"
37 #include "mozilla/ipc/GeckoChildProcessHost.h"
38 #include "mozilla/java/GeckoAppShellNatives.h"
39 #include "mozilla/java/GeckoThreadNatives.h"
40 #include "mozilla/java/XPCOMEventTargetNatives.h"
41 #include "mozilla/widget/ScreenManager.h"
42 #include "prenv.h"
44 #include "AndroidBridge.h"
45 #include "AndroidBridgeUtilities.h"
46 #include "AndroidSurfaceTexture.h"
47 #include <android/log.h>
48 #include <pthread.h>
49 #include <wchar.h>
51 #ifdef MOZ_ANDROID_HISTORY
52 # include "nsNetUtil.h"
53 # include "nsIURI.h"
54 # include "IHistory.h"
55 #endif
57 #ifdef MOZ_LOGGING
58 # include "mozilla/Logging.h"
59 #endif
61 #include "AndroidAlerts.h"
62 #include "AndroidUiThread.h"
63 #include "GeckoBatteryManager.h"
64 #include "GeckoEditableSupport.h"
65 #include "GeckoNetworkManager.h"
66 #include "GeckoProcessManager.h"
67 #include "GeckoScreenOrientation.h"
68 #include "GeckoSystemStateListener.h"
69 #include "GeckoTelemetryDelegate.h"
70 #include "GeckoVRManager.h"
71 #include "ImageDecoderSupport.h"
72 #include "PrefsHelper.h"
73 #include "ScreenHelperAndroid.h"
74 #include "Telemetry.h"
75 #include "WebExecutorSupport.h"
76 #include "Base64UtilsSupport.h"
77 #include "WebAuthnTokenManager.h"
79 #ifdef DEBUG_ANDROID_EVENTS
80 # define EVLOG(args...) ALOG(args)
81 #else
82 # define EVLOG(args...) \
83 do { \
84 } while (0)
85 #endif
87 using namespace mozilla;
88 using namespace mozilla::widget;
90 nsIGeolocationUpdate* gLocationCallback = nullptr;
92 nsAppShell* nsAppShell::sAppShell;
93 StaticAutoPtr<Mutex> nsAppShell::sAppShellLock;
95 uint32_t nsAppShell::Queue::sLatencyCount[];
96 uint64_t nsAppShell::Queue::sLatencyTime[];
98 NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver)
100 class WakeLockListener final : public nsIDOMMozWakeLockListener {
101 private:
102 ~WakeLockListener() {}
104 public:
105 NS_DECL_ISUPPORTS;
107 nsresult Callback(const nsAString& topic, const nsAString& state) override {
108 java::GeckoAppShell::NotifyWakeLockChanged(topic, state);
109 return NS_OK;
113 NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
114 nsCOMPtr<nsIPowerManagerService> sPowerManagerService = nullptr;
115 StaticRefPtr<WakeLockListener> sWakeLockListener;
117 class GeckoThreadSupport final
118 : public java::GeckoThread::Natives<GeckoThreadSupport> {
119 // When this number goes above 0, the app is paused. When less than or
120 // equal to zero, the app is resumed.
121 static int32_t sPauseCount;
123 public:
124 static void SpeculativeConnect(jni::String::Param aUriStr) {
125 if (!NS_IsMainThread()) {
126 // We will be on the main thread if the call was queued on the Java
127 // side during startup. Otherwise, the call was not queued, which
128 // means Gecko is already sufficiently loaded, and we don't really
129 // care about speculative connections at this point.
130 return;
133 nsCOMPtr<nsIIOService> ioServ = do_GetIOService();
134 nsCOMPtr<nsISpeculativeConnect> specConn = do_QueryInterface(ioServ);
135 if (!specConn) {
136 return;
139 nsCOMPtr<nsIURI> uri = nsAppShell::ResolveURI(aUriStr->ToCString());
140 if (!uri) {
141 return;
144 OriginAttributes attrs;
145 nsCOMPtr<nsIPrincipal> principal =
146 BasePrincipal::CreateContentPrincipal(uri, attrs);
147 specConn->SpeculativeConnect(uri, principal, nullptr);
150 static void OnPause() {
151 MOZ_ASSERT(NS_IsMainThread());
153 sPauseCount++;
154 // If sPauseCount is now 1, we just crossed the threshold from "resumed"
155 // "paused". so we should notify observers and so on.
156 if (sPauseCount != 1) {
157 return;
160 nsCOMPtr<nsIObserverService> obsServ =
161 mozilla::services::GetObserverService();
162 obsServ->NotifyObservers(nullptr, "application-background", nullptr);
164 obsServ->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
166 // If we are OOM killed with the disk cache enabled, the entire
167 // cache will be cleared (bug 105843), so shut down the cache here
168 // and re-init on foregrounding
169 if (nsCacheService::GlobalInstance()) {
170 nsCacheService::GlobalInstance()->Shutdown();
173 // We really want to send a notification like profile-before-change,
174 // but profile-before-change ends up shutting some things down instead
175 // of flushing data
176 Preferences* prefs = static_cast<Preferences*>(Preferences::GetService());
177 if (prefs) {
178 // Force a main thread blocking save
179 prefs->SavePrefFileBlocking();
183 static void OnResume() {
184 MOZ_ASSERT(NS_IsMainThread());
186 sPauseCount--;
187 // If sPauseCount is now 0, we just crossed the threshold from "paused"
188 // to "resumed", so we should notify observers and so on.
189 if (sPauseCount != 0) {
190 return;
193 // If we are OOM killed with the disk cache enabled, the entire
194 // cache will be cleared (bug 105843), so shut down cache on backgrounding
195 // and re-init here
196 if (nsCacheService::GlobalInstance()) {
197 nsCacheService::GlobalInstance()->Init();
200 // We didn't return from one of our own activities, so restore
201 // to foreground status
202 nsCOMPtr<nsIObserverService> obsServ =
203 mozilla::services::GetObserverService();
204 obsServ->NotifyObservers(nullptr, "application-foreground", nullptr);
207 static void CreateServices(jni::String::Param aCategory,
208 jni::String::Param aData) {
209 MOZ_ASSERT(NS_IsMainThread());
211 nsCString category(aCategory->ToCString());
213 NS_CreateServicesFromCategory(category.get(),
214 nullptr, // aOrigin
215 category.get(),
216 aData ? aData->ToString().get() : nullptr);
219 static int64_t RunUiThreadCallback() { return RunAndroidUiTasks(); }
221 static void ForceQuit() {
222 nsCOMPtr<nsIAppStartup> appStartup = components::AppStartup::Service();
224 if (appStartup) {
225 bool userAllowedQuit = true;
226 appStartup->Quit(nsIAppStartup::eForceQuit, 0, &userAllowedQuit);
230 static void Crash() {
231 printf_stderr("Intentionally crashing...\n");
232 MOZ_CRASH("intentional crash");
236 int32_t GeckoThreadSupport::sPauseCount;
238 class GeckoAppShellSupport final
239 : public java::GeckoAppShell::Natives<GeckoAppShellSupport> {
240 public:
241 static void ReportJavaCrash(const jni::Class::LocalRef& aCls,
242 jni::Throwable::Param aException,
243 jni::String::Param aStack) {
244 if (!jni::ReportException(aCls.Env(), aException.Get(), aStack.Get())) {
245 // Only crash below if crash reporter is initialized and annotation
246 // succeeded. Otherwise try other means of reporting the crash in
247 // Java.
248 return;
251 MOZ_CRASH("Uncaught Java exception");
254 static void NotifyObservers(jni::String::Param aTopic,
255 jni::String::Param aData) {
256 MOZ_ASSERT(NS_IsMainThread());
257 MOZ_ASSERT(aTopic);
259 nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
260 if (!obsServ) {
261 return;
264 obsServ->NotifyObservers(nullptr, aTopic->ToCString().get(),
265 aData ? aData->ToString().get() : nullptr);
268 static void AppendAppNotesToCrashReport(jni::String::Param aNotes) {
269 MOZ_ASSERT(NS_IsMainThread());
270 MOZ_ASSERT(aNotes);
271 CrashReporter::AppendAppNotesToCrashReport(aNotes->ToCString());
274 static void OnSensorChanged(int32_t aType, float aX, float aY, float aZ,
275 float aW, int64_t aTime) {
276 AutoTArray<float, 4> values;
278 switch (aType) {
279 // Bug 938035, transfer HAL data for orientation sensor to meet w3c
280 // spec, ex: HAL report alpha=90 means East but alpha=90 means West
281 // in w3c spec
282 case hal::SENSOR_ORIENTATION:
283 values.AppendElement(360.0f - aX);
284 values.AppendElement(-aY);
285 values.AppendElement(-aZ);
286 break;
288 case hal::SENSOR_LINEAR_ACCELERATION:
289 case hal::SENSOR_ACCELERATION:
290 case hal::SENSOR_GYROSCOPE:
291 case hal::SENSOR_PROXIMITY:
292 values.AppendElement(aX);
293 values.AppendElement(aY);
294 values.AppendElement(aZ);
295 break;
297 case hal::SENSOR_LIGHT:
298 values.AppendElement(aX);
299 break;
301 case hal::SENSOR_ROTATION_VECTOR:
302 case hal::SENSOR_GAME_ROTATION_VECTOR:
303 values.AppendElement(aX);
304 values.AppendElement(aY);
305 values.AppendElement(aZ);
306 values.AppendElement(aW);
307 break;
309 default:
310 __android_log_print(ANDROID_LOG_ERROR, "Gecko",
311 "Unknown sensor type %d", aType);
314 hal::SensorData sdata(hal::SensorType(aType), aTime, values);
315 hal::NotifySensorChange(sdata);
318 static void OnLocationChanged(double aLatitude, double aLongitude,
319 double aAltitude, float aAccuracy,
320 float aAltitudeAccuracy, float aHeading,
321 float aSpeed, int64_t aTime) {
322 if (!gLocationCallback) {
323 return;
326 RefPtr<nsIDOMGeoPosition> geoPosition(
327 new nsGeoPosition(aLatitude, aLongitude, aAltitude, aAccuracy,
328 aAltitudeAccuracy, aHeading, aSpeed, aTime));
329 gLocationCallback->Update(geoPosition);
332 static void NotifyAlertListener(jni::String::Param aName,
333 jni::String::Param aTopic,
334 jni::String::Param aCookie) {
335 if (!aName || !aTopic || !aCookie) {
336 return;
339 widget::AndroidAlerts::NotifyListener(aName->ToString(),
340 aTopic->ToCString().get(),
341 aCookie->ToString().get());
345 class XPCOMEventTargetWrapper final
346 : public java::XPCOMEventTarget::Natives<XPCOMEventTargetWrapper> {
347 public:
348 // Wraps a java runnable into an XPCOM runnable and dispatches it to mTarget.
349 void DispatchNative(mozilla::jni::Object::Param aJavaRunnable) {
350 java::XPCOMEventTarget::JNIRunnable::GlobalRef r =
351 java::XPCOMEventTarget::JNIRunnable::Ref::From(aJavaRunnable);
352 mTarget->Dispatch(NS_NewRunnableFunction(
353 "XPCOMEventTargetWrapper::DispatchNative",
354 [runnable = std::move(r)]() { runnable->Run(); }));
357 bool IsOnCurrentThread() { return mTarget->IsOnCurrentThread(); }
359 static void Init() {
360 java::XPCOMEventTarget::Natives<XPCOMEventTargetWrapper>::Init();
361 CreateWrapper(u"main"_ns, do_GetMainThread());
362 if (XRE_IsParentProcess()) {
363 CreateWrapper(u"launcher"_ns, ipc::GetIPCLauncher());
367 static void CreateWrapper(mozilla::jni::String::Param aName,
368 nsCOMPtr<nsIEventTarget> aTarget) {
369 auto java = java::XPCOMEventTarget::New();
370 auto native = MakeUnique<XPCOMEventTargetWrapper>(aTarget.forget());
371 AttachNative(java, std::move(native));
373 java::XPCOMEventTarget::SetTarget(aName, java);
376 static void ResolveAndDispatchNative(mozilla::jni::String::Param aName,
377 mozilla::jni::Object::Param aRunnable) {
378 java::XPCOMEventTarget::ResolveAndDispatch(aName, aRunnable);
381 explicit XPCOMEventTargetWrapper(already_AddRefed<nsIEventTarget> aTarget)
382 : mTarget(aTarget) {}
384 private:
385 nsCOMPtr<nsIEventTarget> mTarget;
388 nsAppShell::nsAppShell()
389 : mSyncRunFinished(*(sAppShellLock = new Mutex("nsAppShell")),
390 "nsAppShell.SyncRun"),
391 mSyncRunQuit(false) {
393 MutexAutoLock lock(*sAppShellLock);
394 sAppShell = this;
397 hal::Init();
399 if (!XRE_IsParentProcess()) {
400 if (jni::IsAvailable()) {
401 GeckoThreadSupport::Init();
402 GeckoAppShellSupport::Init();
403 XPCOMEventTargetWrapper::Init();
404 mozilla::widget::Telemetry::Init();
405 mozilla::widget::GeckoTelemetryDelegate::Init();
407 // Set the corresponding state in GeckoThread.
408 java::GeckoThread::SetState(java::GeckoThread::State::RUNNING());
410 return;
413 if (jni::IsAvailable()) {
414 ScreenManager& screenManager = ScreenManager::GetSingleton();
415 screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperAndroid>());
417 // Initialize JNI and Set the corresponding state in GeckoThread.
418 AndroidBridge::ConstructBridge();
419 GeckoAppShellSupport::Init();
420 GeckoThreadSupport::Init();
421 XPCOMEventTargetWrapper::Init();
422 mozilla::GeckoBatteryManager::Init();
423 mozilla::GeckoNetworkManager::Init();
424 mozilla::GeckoProcessManager::Init();
425 mozilla::GeckoScreenOrientation::Init();
426 mozilla::GeckoSystemStateListener::Init();
427 mozilla::PrefsHelper::Init();
428 mozilla::widget::Telemetry::Init();
429 mozilla::widget::ImageDecoderSupport::Init();
430 mozilla::widget::WebExecutorSupport::Init();
431 mozilla::widget::Base64UtilsSupport::Init();
432 nsWindow::InitNatives();
433 mozilla::gl::AndroidSurfaceTexture::Init();
434 mozilla::WebAuthnTokenManager::Init();
435 mozilla::widget::GeckoTelemetryDelegate::Init();
437 java::GeckoThread::SetState(java::GeckoThread::State::JNI_READY());
439 CreateAndroidUiThread();
442 sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
444 if (sPowerManagerService) {
445 sWakeLockListener = new WakeLockListener();
446 } else {
447 NS_WARNING(
448 "Failed to retrieve PowerManagerService, wakelocks will be broken!");
452 nsAppShell::~nsAppShell() {
454 // Release any thread waiting for a sync call to finish.
455 MutexAutoLock lock(*sAppShellLock);
456 sAppShell = nullptr;
457 mSyncRunFinished.NotifyAll();
460 while (mEventQueue.Pop(/* mayWait */ false)) {
461 NS_WARNING("Discarded event on shutdown");
464 if (sPowerManagerService) {
465 sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
467 sPowerManagerService = nullptr;
468 sWakeLockListener = nullptr;
471 hal::Shutdown();
473 if (jni::IsAvailable() && XRE_IsParentProcess()) {
474 DestroyAndroidUiThread();
475 AndroidBridge::DeconstructBridge();
479 void nsAppShell::NotifyNativeEvent() { mEventQueue.Signal(); }
481 void nsAppShell::RecordLatencies() {
482 if (!mozilla::Telemetry::CanRecordExtended()) {
483 return;
486 const mozilla::Telemetry::HistogramID timeIDs[] = {
487 mozilla::Telemetry::HistogramID::FENNEC_LOOP_UI_LATENCY,
488 mozilla::Telemetry::HistogramID::FENNEC_LOOP_OTHER_LATENCY};
490 static_assert(ArrayLength(Queue::sLatencyCount) == Queue::LATENCY_COUNT,
491 "Count array length mismatch");
492 static_assert(ArrayLength(Queue::sLatencyTime) == Queue::LATENCY_COUNT,
493 "Time array length mismatch");
494 static_assert(ArrayLength(timeIDs) == Queue::LATENCY_COUNT,
495 "Time ID array length mismatch");
497 for (size_t i = 0; i < Queue::LATENCY_COUNT; i++) {
498 if (!Queue::sLatencyCount[i]) {
499 continue;
502 const uint64_t time =
503 Queue::sLatencyTime[i] / 1000ull / Queue::sLatencyCount[i];
504 if (time) {
505 mozilla::Telemetry::Accumulate(
506 timeIDs[i], uint32_t(std::min<uint64_t>(UINT32_MAX, time)));
509 // Reset latency counts.
510 Queue::sLatencyCount[i] = 0;
511 Queue::sLatencyTime[i] = 0;
515 nsresult nsAppShell::Init() {
516 nsresult rv = nsBaseAppShell::Init();
517 nsCOMPtr<nsIObserverService> obsServ =
518 mozilla::services::GetObserverService();
519 if (obsServ) {
520 obsServ->AddObserver(this, "browser-delayed-startup-finished", false);
521 obsServ->AddObserver(this, "geckoview-startup-complete", false);
522 obsServ->AddObserver(this, "profile-after-change", false);
523 obsServ->AddObserver(this, "quit-application", false);
524 obsServ->AddObserver(this, "quit-application-granted", false);
525 obsServ->AddObserver(this, "xpcom-shutdown", false);
527 if (XRE_IsParentProcess()) {
528 obsServ->AddObserver(this, "chrome-document-loaded", false);
529 } else {
530 obsServ->AddObserver(this, "content-document-global-created", false);
531 obsServ->AddObserver(this, "geckoview-content-global-transferred", false);
535 if (sPowerManagerService)
536 sPowerManagerService->AddWakeLockListener(sWakeLockListener);
538 return rv;
541 NS_IMETHODIMP
542 nsAppShell::Observe(nsISupports* aSubject, const char* aTopic,
543 const char16_t* aData) {
544 bool removeObserver = false;
546 if (!strcmp(aTopic, "xpcom-shutdown")) {
548 // Release any thread waiting for a sync call to finish.
549 mozilla::MutexAutoLock shellLock(*sAppShellLock);
550 mSyncRunQuit = true;
551 mSyncRunFinished.NotifyAll();
553 // We need to ensure no observers stick around after XPCOM shuts down
554 // or we'll see crashes, as the app shell outlives XPConnect.
555 mObserversHash.Clear();
556 return nsBaseAppShell::Observe(aSubject, aTopic, aData);
558 } else if (!strcmp(aTopic, "browser-delayed-startup-finished")) {
559 NS_CreateServicesFromCategory("browser-delayed-startup-finished", nullptr,
560 "browser-delayed-startup-finished");
561 } else if (!strcmp(aTopic, "geckoview-startup-complete")) {
562 if (jni::IsAvailable()) {
563 java::GeckoThread::CheckAndSetState(
564 java::GeckoThread::State::PROFILE_READY(),
565 java::GeckoThread::State::RUNNING());
567 } else if (!strcmp(aTopic, "profile-after-change")) {
568 if (jni::IsAvailable()) {
569 java::GeckoThread::SetState(java::GeckoThread::State::PROFILE_READY());
571 // Gecko on Android follows the Android app model where it never
572 // stops until it is killed by the system or told explicitly to
573 // quit. Therefore, we should *not* exit Gecko when there is no
574 // window or the last window is closed. nsIAppStartup::Quit will
575 // still force Gecko to exit.
576 nsCOMPtr<nsIAppStartup> appStartup = components::AppStartup::Service();
577 if (appStartup) {
578 appStartup->EnterLastWindowClosingSurvivalArea();
581 removeObserver = true;
583 } else if (!strcmp(aTopic, "chrome-document-loaded")) {
584 // Set the global ready state and enable the window event dispatcher
585 // for this particular GeckoView.
586 nsCOMPtr<dom::Document> doc = do_QueryInterface(aSubject);
587 MOZ_ASSERT(doc);
588 if (const RefPtr<nsWindow> window = nsWindow::From(doc->GetWindow())) {
589 window->OnGeckoViewReady();
591 } else if (!strcmp(aTopic, "quit-application")) {
592 if (jni::IsAvailable()) {
593 const bool restarting = aData && u"restart"_ns.Equals(aData);
594 java::GeckoThread::SetState(restarting
595 ? java::GeckoThread::State::RESTARTING()
596 : java::GeckoThread::State::EXITING());
598 removeObserver = true;
600 } else if (!strcmp(aTopic, "quit-application-granted")) {
601 if (jni::IsAvailable()) {
602 // We are told explicitly to quit, perhaps due to
603 // nsIAppStartup::Quit being called. We should release our hold on
604 // nsIAppStartup and let it continue to quit.
605 nsCOMPtr<nsIAppStartup> appStartup = components::AppStartup::Service();
606 if (appStartup) {
607 appStartup->ExitLastWindowClosingSurvivalArea();
610 removeObserver = true;
612 } else if (!strcmp(aTopic, "nsPref:changed")) {
613 if (jni::IsAvailable()) {
614 mozilla::PrefsHelper::OnPrefChange(aData);
617 } else if (!strcmp(aTopic, "content-document-global-created")) {
618 // Associate the PuppetWidget of the newly-created BrowserChild with a
619 // GeckoEditableChild instance.
620 MOZ_ASSERT(!XRE_IsParentProcess());
622 nsCOMPtr<mozIDOMWindowProxy> domWindow = do_QueryInterface(aSubject);
623 MOZ_ASSERT(domWindow);
624 nsCOMPtr<nsIWidget> domWidget = widget::WidgetUtils::DOMWindowToWidget(
625 nsPIDOMWindowOuter::From(domWindow));
626 NS_ENSURE_TRUE(domWidget, NS_OK);
628 widget::GeckoEditableSupport::SetOnBrowserChild(
629 domWidget->GetOwningBrowserChild());
631 } else if (!strcmp(aTopic, "geckoview-content-global-transferred")) {
632 // We're transferring to a new GeckoEditableParent, so notify the
633 // existing GeckoEditableChild instance associated with the docshell.
634 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aSubject);
635 widget::GeckoEditableSupport::SetOnBrowserChild(
636 dom::BrowserChild::GetFrom(docShell));
639 if (removeObserver) {
640 nsCOMPtr<nsIObserverService> obsServ =
641 mozilla::services::GetObserverService();
642 if (obsServ) {
643 obsServ->RemoveObserver(this, aTopic);
646 return NS_OK;
649 bool nsAppShell::ProcessNextNativeEvent(bool mayWait) {
650 EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait);
652 AUTO_PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent", OTHER);
654 mozilla::UniquePtr<Event> curEvent;
657 curEvent = mEventQueue.Pop(/* mayWait */ false);
659 if (!curEvent && mayWait) {
660 // This processes messages in the Android Looper. Note that we only
661 // get here if the normal Gecko event loop has been awoken
662 // (bug 750713). Looper messages effectively have the lowest
663 // priority because we only process them before we're about to
664 // wait for new events.
665 if (jni::IsAvailable() && XRE_IsParentProcess() &&
666 AndroidBridge::Bridge()->PumpMessageLoop()) {
667 return true;
670 AUTO_PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent:Wait", IDLE);
671 mozilla::BackgroundHangMonitor().NotifyWait();
673 curEvent = mEventQueue.Pop(/* mayWait */ true);
677 if (!curEvent) return false;
679 mozilla::BackgroundHangMonitor().NotifyActivity();
681 curEvent->Run();
682 return true;
685 bool nsAppShell::SyncRunEvent(
686 Event&& event, UniquePtr<Event> (*eventFactory)(UniquePtr<Event>&&),
687 const TimeDuration timeout) {
688 // Perform the call on the Gecko thread in a separate lambda, and wait
689 // on the monitor on the current thread.
690 MOZ_ASSERT(!NS_IsMainThread());
692 // This is the lock to check that app shell is still alive,
693 // and to wait on for the sync call to complete.
694 mozilla::MutexAutoLock shellLock(*sAppShellLock);
695 nsAppShell* const appShell = sAppShell;
697 if (MOZ_UNLIKELY(!appShell)) {
698 // Post-shutdown.
699 return false;
702 bool finished = false;
703 auto runAndNotify = [&event, &finished] {
704 nsAppShell* const appShell = nsAppShell::Get();
705 if (MOZ_UNLIKELY(!appShell || appShell->mSyncRunQuit)) {
706 return false;
708 event.Run();
709 finished = true;
710 mozilla::MutexAutoLock shellLock(*sAppShellLock);
711 appShell->mSyncRunFinished.NotifyAll();
712 return finished;
715 UniquePtr<Event> runAndNotifyEvent =
716 mozilla::MakeUnique<LambdaEvent<decltype(runAndNotify)>>(
717 std::move(runAndNotify));
719 if (eventFactory) {
720 runAndNotifyEvent = (*eventFactory)(std::move(runAndNotifyEvent));
723 appShell->mEventQueue.Post(std::move(runAndNotifyEvent));
725 while (!finished && MOZ_LIKELY(sAppShell && !sAppShell->mSyncRunQuit)) {
726 appShell->mSyncRunFinished.Wait(timeout);
729 return finished;
732 already_AddRefed<nsIURI> nsAppShell::ResolveURI(const nsCString& aUriStr) {
733 nsCOMPtr<nsIIOService> ioServ = do_GetIOService();
734 nsCOMPtr<nsIURI> uri;
736 if (NS_SUCCEEDED(
737 ioServ->NewURI(aUriStr, nullptr, nullptr, getter_AddRefs(uri)))) {
738 return uri.forget();
741 nsCOMPtr<nsIURIFixup> fixup = components::URIFixup::Service();
742 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
743 if (fixup &&
744 NS_SUCCEEDED(fixup->GetFixupURIInfo(aUriStr, nsIURIFixup::FIXUP_FLAG_NONE,
745 getter_AddRefs(fixupInfo))) &&
746 NS_SUCCEEDED(fixupInfo->GetPreferredURI(getter_AddRefs(uri)))) {
747 return uri.forget();
749 return nullptr;
752 nsresult nsAppShell::AddObserver(const nsAString& aObserverKey,
753 nsIObserver* aObserver) {
754 NS_ASSERTION(aObserver != nullptr,
755 "nsAppShell::AddObserver: aObserver is null!");
756 mObserversHash.Put(aObserverKey, aObserver);
757 return NS_OK;
760 // Used by IPC code
761 namespace mozilla {
763 bool ProcessNextEvent() {
764 nsAppShell* const appShell = nsAppShell::Get();
765 if (!appShell) {
766 return false;
769 return appShell->ProcessNextNativeEvent(true) ? true : false;
772 void NotifyEvent() {
773 nsAppShell* const appShell = nsAppShell::Get();
774 if (!appShell) {
775 return;
777 appShell->NotifyNativeEvent();
780 } // namespace mozilla