mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 21:55:31 +00:00
bfe19431c6
Bug 1305498 - 1. Remove NotificationClient task queue; r=sebastian Not sure why we needed a task queue for NotificationClient actions. The actions all go through IPC and are non-blocking, so it's perfectly fine to perform them off of whatever thread we're on. Bug 1305498 - 2. Integrate NotificationHandler et al into NotificationCllient; r=sebastian There's no reason to have NotificationHandler, AppNotificationClient, and ServiceNotificationClient all separate from the base NotificationClient class. This patch adds the functionality of those three classes to NotificationClient. The notifications hash map is changed from a ConcurrentHashMap to a regular HashMap with synchronization because I think the use case here doesn't warrant the added performance and overhead of ConcurrentHashMap. NotificationService is changed to match the new NotificationClient. Now the only job for NotificationService is to set a notification as foreground, rather than to manage all notifications like before. NotificationHandler, AppNotificationClient, and ServiceNotificationClient will be removed in a later patch. Bug 1305498 - 3. Set NotificationListener in GeckoApplication; r=sebastian Set NotificationListener once in GeckoApplication.onCreate, instead of spreading it out in GeckoApp, BrowserApp, and GeckoService. This is possible because there's no longer a distinction between AppNotificationClient and ServiceNotificationClient in the new, consolidated NotificationClient. Bug 1305498 - 4. Remove obsolete notification classes; r=sebastian Remove AppNotificationClient, ServiceNotificationClient, and NotificationHandler, now that they've all been replaced by the new, consolidated NotificationClient. Bug 1305498 - 5. Use NotificationReceiver for web notification callbacks; r=sebastian Previously, web notification callbacks went to GeckoApp directly, but that presented some problems such as not being able to implement the on-close callback, because we don't want to launch GeckoApp when the notification is closed by swiping. This patch makes us use NotificationReceiver for callbacks, and let NotificationReceiver launch GeckoApp if necessary. Bug 1305498 - 6. Don't keep notification cookie in native code; r=sebastian Keep the notification cookie a single location (in the notification intent itself), and simplify the native notification handling code. Bug 1305498 - 7. Use NotificationReceiver for persistent notifications; r=sebastian Currently, persistent notification callbacks go through a different code path, but it'd be more consistent and correct to let persistent notification callbacks go through NotificationReceiver as well. This takes care of some housekeeping work that was missing for persistent notifications, such as deleting the mNotifications entry when the notification is closed.
689 lines
21 KiB
C++
689 lines
21 KiB
C++
/* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
|
|
/* 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 "nsAppShell.h"
|
|
|
|
#include "base/basictypes.h"
|
|
#include "base/message_loop.h"
|
|
#include "base/task.h"
|
|
#include "mozilla/Hal.h"
|
|
#include "nsIScreen.h"
|
|
#include "nsIScreenManager.h"
|
|
#include "nsWindow.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsICommandLineRunner.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIAppStartup.h"
|
|
#include "nsIGeolocationProvider.h"
|
|
#include "nsCacheService.h"
|
|
#include "nsIDOMEventListener.h"
|
|
#include "nsIDOMClientRectList.h"
|
|
#include "nsIDOMClientRect.h"
|
|
#include "nsIDOMWakeLockListener.h"
|
|
#include "nsIPowerManagerService.h"
|
|
#include "nsISpeculativeConnect.h"
|
|
#include "nsIURIFixup.h"
|
|
#include "nsCategoryManagerUtils.h"
|
|
#include "nsCDefaultURIFixup.h"
|
|
#include "nsToolkitCompsCID.h"
|
|
#include "nsGeoPosition.h"
|
|
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/Hal.h"
|
|
#include "prenv.h"
|
|
|
|
#include "AndroidBridge.h"
|
|
#include "AndroidBridgeUtilities.h"
|
|
#include "GeneratedJNINatives.h"
|
|
#include <android/log.h>
|
|
#include <pthread.h>
|
|
#include <wchar.h>
|
|
|
|
#include "GeckoProfiler.h"
|
|
#ifdef MOZ_ANDROID_HISTORY
|
|
#include "nsNetUtil.h"
|
|
#include "nsIURI.h"
|
|
#include "IHistory.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_LOGGING
|
|
#include "mozilla/Logging.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
#include "nsICrashReporter.h"
|
|
#include "nsExceptionHandler.h"
|
|
#endif
|
|
|
|
#include "AndroidAlerts.h"
|
|
#include "ANRReporter.h"
|
|
#include "GeckoBatteryManager.h"
|
|
#include "GeckoNetworkManager.h"
|
|
#include "GeckoScreenOrientation.h"
|
|
#include "PrefsHelper.h"
|
|
#include "fennec/MemoryMonitor.h"
|
|
#include "fennec/Telemetry.h"
|
|
#include "fennec/ThumbnailHelper.h"
|
|
|
|
#ifdef DEBUG_ANDROID_EVENTS
|
|
#define EVLOG(args...) ALOG(args)
|
|
#else
|
|
#define EVLOG(args...) do { } while (0)
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
|
|
nsIGeolocationUpdate *gLocationCallback = nullptr;
|
|
|
|
nsAppShell* nsAppShell::sAppShell;
|
|
StaticAutoPtr<Mutex> nsAppShell::sAppShellLock;
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver)
|
|
|
|
class WakeLockListener final : public nsIDOMMozWakeLockListener {
|
|
private:
|
|
~WakeLockListener() {}
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS;
|
|
|
|
nsresult Callback(const nsAString& topic, const nsAString& state) override {
|
|
java::GeckoAppShell::NotifyWakeLockChanged(topic, state);
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
|
|
nsCOMPtr<nsIPowerManagerService> sPowerManagerService = nullptr;
|
|
StaticRefPtr<WakeLockListener> sWakeLockListener;
|
|
|
|
|
|
class GeckoThreadSupport final
|
|
: public java::GeckoThread::Natives<GeckoThreadSupport>
|
|
{
|
|
// When this number goes above 0, the app is paused. When less than or
|
|
// equal to zero, the app is resumed.
|
|
static int32_t sPauseCount;
|
|
|
|
public:
|
|
static void SpeculativeConnect(jni::String::Param aUriStr)
|
|
{
|
|
if (!NS_IsMainThread()) {
|
|
// We will be on the main thread if the call was queued on the Java
|
|
// side during startup. Otherwise, the call was not queued, which
|
|
// means Gecko is already sufficiently loaded, and we don't really
|
|
// care about speculative connections at this point.
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIIOService> ioServ = do_GetIOService();
|
|
nsCOMPtr<nsISpeculativeConnect> specConn = do_QueryInterface(ioServ);
|
|
if (!specConn) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri = nsAppShell::ResolveURI(aUriStr->ToCString());
|
|
if (!uri) {
|
|
return;
|
|
}
|
|
specConn->SpeculativeConnect(uri, nullptr);
|
|
}
|
|
|
|
static void WaitOnGecko()
|
|
{
|
|
struct NoOpEvent : nsAppShell::Event {
|
|
void Run() override {}
|
|
};
|
|
nsAppShell::SyncRunEvent(NoOpEvent());
|
|
}
|
|
|
|
static void OnPause()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
sPauseCount++;
|
|
// If sPauseCount is now 1, we just crossed the threshold from "resumed"
|
|
// "paused". so we should notify observers and so on.
|
|
if (sPauseCount != 1) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> obsServ =
|
|
mozilla::services::GetObserverService();
|
|
obsServ->NotifyObservers(nullptr, "application-background", nullptr);
|
|
|
|
NS_NAMED_LITERAL_STRING(minimize, "heap-minimize");
|
|
obsServ->NotifyObservers(nullptr, "memory-pressure", minimize.get());
|
|
|
|
// If we are OOM killed with the disk cache enabled, the entire
|
|
// cache will be cleared (bug 105843), so shut down the cache here
|
|
// and re-init on foregrounding
|
|
if (nsCacheService::GlobalInstance()) {
|
|
nsCacheService::GlobalInstance()->Shutdown();
|
|
}
|
|
|
|
// We really want to send a notification like profile-before-change,
|
|
// but profile-before-change ends up shutting some things down instead
|
|
// of flushing data
|
|
nsIPrefService* prefs = Preferences::GetService();
|
|
if (prefs) {
|
|
prefs->SavePrefFile(nullptr);
|
|
}
|
|
}
|
|
|
|
static void OnResume()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
sPauseCount--;
|
|
// If sPauseCount is now 0, we just crossed the threshold from "paused"
|
|
// to "resumed", so we should notify observers and so on.
|
|
if (sPauseCount != 0) {
|
|
return;
|
|
}
|
|
|
|
// If we are OOM killed with the disk cache enabled, the entire
|
|
// cache will be cleared (bug 105843), so shut down cache on backgrounding
|
|
// and re-init here
|
|
if (nsCacheService::GlobalInstance()) {
|
|
nsCacheService::GlobalInstance()->Init();
|
|
}
|
|
|
|
// We didn't return from one of our own activities, so restore
|
|
// to foreground status
|
|
nsCOMPtr<nsIObserverService> obsServ =
|
|
mozilla::services::GetObserverService();
|
|
obsServ->NotifyObservers(nullptr, "application-foreground", nullptr);
|
|
}
|
|
|
|
static void CreateServices(jni::String::Param aCategory, jni::String::Param aData)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsCString category(aCategory->ToCString());
|
|
|
|
NS_CreateServicesFromCategory(
|
|
category.get(),
|
|
nullptr, // aOrigin
|
|
category.get(),
|
|
aData ? aData->ToString().get() : nullptr);
|
|
}
|
|
|
|
static int64_t RunUiThreadCallback()
|
|
{
|
|
if (!AndroidBridge::Bridge()) {
|
|
return -1;
|
|
}
|
|
|
|
return AndroidBridge::Bridge()->RunDelayedUiThreadTasks();
|
|
}
|
|
};
|
|
|
|
int32_t GeckoThreadSupport::sPauseCount;
|
|
|
|
|
|
class GeckoAppShellSupport final
|
|
: public java::GeckoAppShell::Natives<GeckoAppShellSupport>
|
|
{
|
|
public:
|
|
static void ReportJavaCrash(const jni::Class::LocalRef& aCls,
|
|
jni::Throwable::Param aException,
|
|
jni::String::Param aStack)
|
|
{
|
|
if (!jni::ReportException(aCls.Env(), aException.Get(), aStack.Get())) {
|
|
// Only crash below if crash reporter is initialized and annotation
|
|
// succeeded. Otherwise try other means of reporting the crash in
|
|
// Java.
|
|
return;
|
|
}
|
|
|
|
MOZ_CRASH("Uncaught Java exception");
|
|
}
|
|
|
|
static void SyncNotifyObservers(jni::String::Param aTopic,
|
|
jni::String::Param aData)
|
|
{
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
NotifyObservers(aTopic, aData);
|
|
}
|
|
|
|
static void NotifyObservers(jni::String::Param aTopic,
|
|
jni::String::Param aData)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aTopic);
|
|
|
|
nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
|
|
if (!obsServ) {
|
|
return;
|
|
}
|
|
|
|
obsServ->NotifyObservers(nullptr, aTopic->ToCString().get(),
|
|
aData ? aData->ToString().get() : nullptr);
|
|
}
|
|
|
|
static void OnSensorChanged(int32_t aType, float aX, float aY, float aZ,
|
|
float aW, int32_t aAccuracy, int64_t aTime)
|
|
{
|
|
AutoTArray<float, 4> values;
|
|
|
|
switch (aType) {
|
|
// Bug 938035, transfer HAL data for orientation sensor to meet w3c
|
|
// spec, ex: HAL report alpha=90 means East but alpha=90 means West
|
|
// in w3c spec
|
|
case hal::SENSOR_ORIENTATION:
|
|
values.AppendElement(360.0f - aX);
|
|
values.AppendElement(-aY);
|
|
values.AppendElement(-aZ);
|
|
break;
|
|
|
|
case hal::SENSOR_LINEAR_ACCELERATION:
|
|
case hal::SENSOR_ACCELERATION:
|
|
case hal::SENSOR_GYROSCOPE:
|
|
case hal::SENSOR_PROXIMITY:
|
|
values.AppendElement(aX);
|
|
values.AppendElement(aY);
|
|
values.AppendElement(aZ);
|
|
break;
|
|
|
|
case hal::SENSOR_LIGHT:
|
|
values.AppendElement(aX);
|
|
break;
|
|
|
|
case hal::SENSOR_ROTATION_VECTOR:
|
|
case hal::SENSOR_GAME_ROTATION_VECTOR:
|
|
values.AppendElement(aX);
|
|
values.AppendElement(aY);
|
|
values.AppendElement(aZ);
|
|
values.AppendElement(aW);
|
|
break;
|
|
|
|
default:
|
|
__android_log_print(ANDROID_LOG_ERROR, "Gecko",
|
|
"Unknown sensor type %d", aType);
|
|
}
|
|
|
|
hal::SensorData sdata(hal::SensorType(aType), aTime, values,
|
|
hal::SensorAccuracyType(aAccuracy));
|
|
hal::NotifySensorChange(sdata);
|
|
}
|
|
|
|
static void OnLocationChanged(double aLatitude, double aLongitude,
|
|
double aAltitude, float aAccuracy,
|
|
float aBearing, float aSpeed, int64_t aTime)
|
|
{
|
|
if (!gLocationCallback) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<nsIDOMGeoPosition> geoPosition(
|
|
new nsGeoPosition(aLatitude, aLongitude, aAltitude, aAccuracy,
|
|
aAccuracy, aBearing, aSpeed, aTime));
|
|
gLocationCallback->Update(geoPosition);
|
|
}
|
|
|
|
static void NotifyUriVisited(jni::String::Param aUri)
|
|
{
|
|
#ifdef MOZ_ANDROID_HISTORY
|
|
nsCOMPtr<IHistory> history = services::GetHistoryService();
|
|
nsCOMPtr<nsIURI> visitedURI;
|
|
if (history &&
|
|
NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI),
|
|
aUri->ToString()))) {
|
|
history->NotifyVisited(visitedURI);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void NotifyAlertListener(jni::String::Param aName,
|
|
jni::String::Param aTopic,
|
|
jni::String::Param aCookie)
|
|
{
|
|
if (!aName || !aTopic || !aCookie) {
|
|
return;
|
|
}
|
|
|
|
AndroidAlerts::NotifyListener(
|
|
aName->ToString(), aTopic->ToCString().get(),
|
|
aCookie->ToString().get());
|
|
}
|
|
|
|
static void OnFullScreenPluginHidden(jni::Object::Param aView)
|
|
{
|
|
nsPluginInstanceOwner::ExitFullScreen(aView.Get());
|
|
}
|
|
};
|
|
|
|
nsAppShell::nsAppShell()
|
|
: mSyncRunFinished(*(sAppShellLock = new Mutex("nsAppShell")),
|
|
"nsAppShell.SyncRun")
|
|
, mSyncRunQuit(false)
|
|
{
|
|
{
|
|
MutexAutoLock lock(*sAppShellLock);
|
|
sAppShell = this;
|
|
}
|
|
|
|
if (!XRE_IsParentProcess()) {
|
|
return;
|
|
}
|
|
|
|
if (jni::IsAvailable()) {
|
|
// Initialize JNI and Set the corresponding state in GeckoThread.
|
|
AndroidBridge::ConstructBridge();
|
|
GeckoAppShellSupport::Init();
|
|
GeckoThreadSupport::Init();
|
|
mozilla::GeckoBatteryManager::Init();
|
|
mozilla::GeckoNetworkManager::Init();
|
|
mozilla::GeckoScreenOrientation::Init();
|
|
mozilla::PrefsHelper::Init();
|
|
nsWindow::InitNatives();
|
|
|
|
if (jni::IsFennec()) {
|
|
mozilla::ANRReporter::Init();
|
|
mozilla::MemoryMonitor::Init();
|
|
mozilla::widget::Telemetry::Init();
|
|
mozilla::ThumbnailHelper::Init();
|
|
}
|
|
|
|
java::GeckoThread::SetState(java::GeckoThread::State::JNI_READY());
|
|
}
|
|
|
|
sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
|
|
|
|
if (sPowerManagerService) {
|
|
sWakeLockListener = new WakeLockListener();
|
|
} else {
|
|
NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
|
|
}
|
|
}
|
|
|
|
nsAppShell::~nsAppShell()
|
|
{
|
|
{
|
|
MutexAutoLock lock(*sAppShellLock);
|
|
sAppShell = nullptr;
|
|
}
|
|
|
|
while (mEventQueue.Pop(/* mayWait */ false)) {
|
|
NS_WARNING("Discarded event on shutdown");
|
|
}
|
|
|
|
if (sPowerManagerService) {
|
|
sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
|
|
|
|
sPowerManagerService = nullptr;
|
|
sWakeLockListener = nullptr;
|
|
}
|
|
|
|
if (jni::IsAvailable()) {
|
|
AndroidBridge::DeconstructBridge();
|
|
}
|
|
}
|
|
|
|
void
|
|
nsAppShell::NotifyNativeEvent()
|
|
{
|
|
mEventQueue.Signal();
|
|
}
|
|
|
|
#define PREFNAME_COALESCE_TOUCHES "dom.event.touch.coalescing.enabled"
|
|
static const char* kObservedPrefs[] = {
|
|
PREFNAME_COALESCE_TOUCHES,
|
|
nullptr
|
|
};
|
|
|
|
nsresult
|
|
nsAppShell::Init()
|
|
{
|
|
nsresult rv = nsBaseAppShell::Init();
|
|
nsCOMPtr<nsIObserverService> obsServ =
|
|
mozilla::services::GetObserverService();
|
|
if (obsServ) {
|
|
obsServ->AddObserver(this, "browser-delayed-startup-finished", false);
|
|
obsServ->AddObserver(this, "profile-after-change", false);
|
|
obsServ->AddObserver(this, "chrome-document-loaded", false);
|
|
obsServ->AddObserver(this, "quit-application-granted", false);
|
|
obsServ->AddObserver(this, "xpcom-shutdown", false);
|
|
}
|
|
|
|
if (sPowerManagerService)
|
|
sPowerManagerService->AddWakeLockListener(sWakeLockListener);
|
|
|
|
Preferences::AddStrongObservers(this, kObservedPrefs);
|
|
mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsAppShell::Observe(nsISupports* aSubject,
|
|
const char* aTopic,
|
|
const char16_t* aData)
|
|
{
|
|
bool removeObserver = false;
|
|
|
|
if (!strcmp(aTopic, "xpcom-shutdown")) {
|
|
{
|
|
// Release any thread waiting for a sync call to finish.
|
|
mozilla::MutexAutoLock shellLock(*sAppShellLock);
|
|
mSyncRunQuit = true;
|
|
mSyncRunFinished.NotifyAll();
|
|
}
|
|
// We need to ensure no observers stick around after XPCOM shuts down
|
|
// or we'll see crashes, as the app shell outlives XPConnect.
|
|
mObserversHash.Clear();
|
|
return nsBaseAppShell::Observe(aSubject, aTopic, aData);
|
|
|
|
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
|
|
aData &&
|
|
nsDependentString(aData).Equals(NS_LITERAL_STRING(PREFNAME_COALESCE_TOUCHES))) {
|
|
mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
|
|
return NS_OK;
|
|
|
|
} else if (!strcmp(aTopic, "browser-delayed-startup-finished")) {
|
|
NS_CreateServicesFromCategory("browser-delayed-startup-finished", nullptr,
|
|
"browser-delayed-startup-finished");
|
|
|
|
} else if (!strcmp(aTopic, "profile-after-change")) {
|
|
if (jni::IsAvailable()) {
|
|
// See if we want to force 16-bit color before doing anything
|
|
if (Preferences::GetBool("gfx.android.rgb16.force", false)) {
|
|
java::GeckoAppShell::SetScreenDepthOverride(16);
|
|
}
|
|
|
|
java::GeckoThread::SetState(
|
|
java::GeckoThread::State::PROFILE_READY());
|
|
|
|
// Gecko on Android follows the Android app model where it never
|
|
// stops until it is killed by the system or told explicitly to
|
|
// quit. Therefore, we should *not* exit Gecko when there is no
|
|
// window or the last window is closed. nsIAppStartup::Quit will
|
|
// still force Gecko to exit.
|
|
nsCOMPtr<nsIAppStartup> appStartup =
|
|
do_GetService(NS_APPSTARTUP_CONTRACTID);
|
|
if (appStartup) {
|
|
appStartup->EnterLastWindowClosingSurvivalArea();
|
|
}
|
|
}
|
|
removeObserver = true;
|
|
|
|
} else if (!strcmp(aTopic, "chrome-document-loaded")) {
|
|
if (jni::IsAvailable()) {
|
|
// Our first window has loaded, assume any JS initialization has run.
|
|
java::GeckoThread::CheckAndSetState(
|
|
java::GeckoThread::State::PROFILE_READY(),
|
|
java::GeckoThread::State::RUNNING());
|
|
}
|
|
removeObserver = true;
|
|
|
|
} else if (!strcmp(aTopic, "quit-application-granted")) {
|
|
if (jni::IsAvailable()) {
|
|
java::GeckoThread::SetState(
|
|
java::GeckoThread::State::EXITING());
|
|
|
|
// We are told explicitly to quit, perhaps due to
|
|
// nsIAppStartup::Quit being called. We should release our hold on
|
|
// nsIAppStartup and let it continue to quit.
|
|
nsCOMPtr<nsIAppStartup> appStartup =
|
|
do_GetService(NS_APPSTARTUP_CONTRACTID);
|
|
if (appStartup) {
|
|
appStartup->ExitLastWindowClosingSurvivalArea();
|
|
}
|
|
}
|
|
removeObserver = true;
|
|
|
|
} else if (!strcmp(aTopic, "nsPref:changed")) {
|
|
if (jni::IsAvailable()) {
|
|
mozilla::PrefsHelper::OnPrefChange(aData);
|
|
}
|
|
}
|
|
|
|
if (removeObserver) {
|
|
nsCOMPtr<nsIObserverService> obsServ =
|
|
mozilla::services::GetObserverService();
|
|
if (obsServ) {
|
|
obsServ->RemoveObserver(this, aTopic);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
bool
|
|
nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
|
{
|
|
EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait);
|
|
|
|
PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent",
|
|
js::ProfileEntry::Category::EVENTS);
|
|
|
|
mozilla::UniquePtr<Event> curEvent;
|
|
|
|
{
|
|
curEvent = mEventQueue.Pop(/* mayWait */ false);
|
|
|
|
if (!curEvent && mayWait) {
|
|
// This processes messages in the Android Looper. Note that we only
|
|
// get here if the normal Gecko event loop has been awoken
|
|
// (bug 750713). Looper messages effectively have the lowest
|
|
// priority because we only process them before we're about to
|
|
// wait for new events.
|
|
if (jni::IsAvailable() &&
|
|
AndroidBridge::Bridge()->PumpMessageLoop()) {
|
|
return true;
|
|
}
|
|
|
|
PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent::Wait",
|
|
js::ProfileEntry::Category::EVENTS);
|
|
mozilla::HangMonitor::Suspend();
|
|
|
|
curEvent = mEventQueue.Pop(/* mayWait */ true);
|
|
}
|
|
}
|
|
|
|
if (!curEvent)
|
|
return false;
|
|
|
|
mozilla::HangMonitor::NotifyActivity(curEvent->ActivityType());
|
|
|
|
curEvent->Run();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsAppShell::SyncRunEvent(Event&& event,
|
|
UniquePtr<Event>(*eventFactory)(UniquePtr<Event>&&))
|
|
{
|
|
// Perform the call on the Gecko thread in a separate lambda, and wait
|
|
// on the monitor on the current thread.
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
// This is the lock to check that app shell is still alive,
|
|
// and to wait on for the sync call to complete.
|
|
mozilla::MutexAutoLock shellLock(*sAppShellLock);
|
|
nsAppShell* const appShell = sAppShell;
|
|
|
|
if (MOZ_UNLIKELY(!appShell)) {
|
|
// Post-shutdown.
|
|
return;
|
|
}
|
|
|
|
bool finished = false;
|
|
auto runAndNotify = [&event, &finished] {
|
|
mozilla::MutexAutoLock shellLock(*sAppShellLock);
|
|
nsAppShell* const appShell = sAppShell;
|
|
if (MOZ_UNLIKELY(!appShell || appShell->mSyncRunQuit)) {
|
|
return;
|
|
}
|
|
event.Run();
|
|
finished = true;
|
|
appShell->mSyncRunFinished.NotifyAll();
|
|
};
|
|
|
|
UniquePtr<Event> runAndNotifyEvent = mozilla::MakeUnique<
|
|
LambdaEvent<decltype(runAndNotify)>>(mozilla::Move(runAndNotify));
|
|
|
|
if (eventFactory) {
|
|
runAndNotifyEvent = (*eventFactory)(mozilla::Move(runAndNotifyEvent));
|
|
}
|
|
|
|
appShell->mEventQueue.Post(mozilla::Move(runAndNotifyEvent));
|
|
|
|
while (!finished && MOZ_LIKELY(sAppShell && !sAppShell->mSyncRunQuit)) {
|
|
appShell->mSyncRunFinished.Wait();
|
|
}
|
|
}
|
|
|
|
already_AddRefed<nsIURI>
|
|
nsAppShell::ResolveURI(const nsCString& aUriStr)
|
|
{
|
|
nsCOMPtr<nsIIOService> ioServ = do_GetIOService();
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
if (NS_SUCCEEDED(ioServ->NewURI(aUriStr, nullptr,
|
|
nullptr, getter_AddRefs(uri)))) {
|
|
return uri.forget();
|
|
}
|
|
|
|
nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
|
|
if (fixup && NS_SUCCEEDED(
|
|
fixup->CreateFixupURI(aUriStr, 0, nullptr, getter_AddRefs(uri)))) {
|
|
return uri.forget();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
nsresult
|
|
nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver)
|
|
{
|
|
NS_ASSERTION(aObserver != nullptr, "nsAppShell::AddObserver: aObserver is null!");
|
|
mObserversHash.Put(aObserverKey, aObserver);
|
|
return NS_OK;
|
|
}
|
|
|
|
// Used by IPC code
|
|
namespace mozilla {
|
|
|
|
bool ProcessNextEvent()
|
|
{
|
|
nsAppShell* const appShell = nsAppShell::Get();
|
|
if (!appShell) {
|
|
return false;
|
|
}
|
|
|
|
return appShell->ProcessNextNativeEvent(true) ? true : false;
|
|
}
|
|
|
|
void NotifyEvent()
|
|
{
|
|
nsAppShell* const appShell = nsAppShell::Get();
|
|
if (!appShell) {
|
|
return;
|
|
}
|
|
appShell->NotifyNativeEvent();
|
|
}
|
|
|
|
}
|