michael@0: /* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsAppShell.h" michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "base/message_loop.h" michael@0: #include "base/task.h" michael@0: #include "mozilla/Hal.h" michael@0: #include "nsIScreen.h" michael@0: #include "nsIScreenManager.h" michael@0: #include "nsWindow.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsICommandLineRunner.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIAppStartup.h" michael@0: #include "nsIGeolocationProvider.h" michael@0: #include "nsCacheService.h" michael@0: #include "nsIDOMEventListener.h" michael@0: #include "nsIDOMClientRectList.h" michael@0: #include "nsIDOMClientRect.h" michael@0: #include "nsIDOMWakeLockListener.h" michael@0: #include "nsIPowerManagerService.h" michael@0: #include "nsFrameManager.h" michael@0: #include "nsINetworkLinkService.h" michael@0: michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Hal.h" michael@0: #include "prenv.h" michael@0: michael@0: #include "AndroidBridge.h" michael@0: #include "AndroidBridgeUtilities.h" michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "mozilla/dom/ScreenOrientation.h" michael@0: michael@0: #include "GeckoProfiler.h" michael@0: #ifdef MOZ_ANDROID_HISTORY michael@0: #include "nsNetUtil.h" michael@0: #include "IHistory.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG michael@0: #include "prlog.h" michael@0: #endif michael@0: michael@0: #ifdef DEBUG_ANDROID_EVENTS michael@0: #define EVLOG(args...) ALOG(args) michael@0: #else michael@0: #define EVLOG(args...) do { } while (0) michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #ifdef PR_LOGGING michael@0: PRLogModuleInfo *gWidgetLog = nullptr; michael@0: #endif michael@0: michael@0: nsIGeolocationUpdate *gLocationCallback = nullptr; michael@0: nsAutoPtr gLastSizeChange; michael@0: michael@0: nsAppShell *nsAppShell::gAppShell = nullptr; michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver) michael@0: michael@0: class ThumbnailRunnable : public nsRunnable { michael@0: public: michael@0: ThumbnailRunnable(nsIAndroidBrowserApp* aBrowserApp, int aTabId, michael@0: const nsTArray& aPoints, RefCountedJavaObject* aBuffer): michael@0: mBrowserApp(aBrowserApp), mPoints(aPoints), mTabId(aTabId), mBuffer(aBuffer) {} michael@0: michael@0: virtual nsresult Run() { michael@0: jobject buffer = mBuffer->GetObject(); michael@0: nsCOMPtr domWindow; michael@0: nsCOMPtr tab; michael@0: mBrowserApp->GetBrowserTab(mTabId, getter_AddRefs(tab)); michael@0: if (!tab) { michael@0: mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, false); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: tab->GetWindow(getter_AddRefs(domWindow)); michael@0: if (!domWindow) { michael@0: mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, false); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_ASSERTION(mPoints.Length() == 1, "Thumbnail event does not have enough coordinates"); michael@0: michael@0: nsresult rv = AndroidBridge::Bridge()->CaptureThumbnail(domWindow, mPoints[0].x, mPoints[0].y, mTabId, buffer); michael@0: mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, NS_SUCCEEDED(rv)); michael@0: return rv; michael@0: } michael@0: private: michael@0: nsCOMPtr mBrowserApp; michael@0: nsTArray mPoints; michael@0: int mTabId; michael@0: nsRefPtr mBuffer; michael@0: }; michael@0: michael@0: class WakeLockListener MOZ_FINAL : public nsIDOMMozWakeLockListener { michael@0: public: michael@0: NS_DECL_ISUPPORTS; michael@0: michael@0: nsresult Callback(const nsAString& topic, const nsAString& state) { michael@0: mozilla::widget::android::GeckoAppShell::NotifyWakeLockChanged(topic, state); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener) michael@0: nsCOMPtr sPowerManagerService = nullptr; michael@0: StaticRefPtr sWakeLockListener; michael@0: michael@0: nsAppShell::nsAppShell() michael@0: : mQueueLock("nsAppShell.mQueueLock"), michael@0: mCondLock("nsAppShell.mCondLock"), michael@0: mQueueCond(mCondLock, "nsAppShell.mQueueCond"), michael@0: mQueuedDrawEvent(nullptr), michael@0: mQueuedViewportEvent(nullptr), michael@0: mAllowCoalescingNextDraw(false) michael@0: { michael@0: gAppShell = this; michael@0: michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: return; michael@0: } michael@0: michael@0: sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID); michael@0: michael@0: if (sPowerManagerService) { michael@0: sWakeLockListener = new WakeLockListener(); michael@0: } else { michael@0: NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!"); michael@0: } michael@0: michael@0: } michael@0: michael@0: nsAppShell::~nsAppShell() michael@0: { michael@0: gAppShell = nullptr; michael@0: michael@0: if (sPowerManagerService) { michael@0: sPowerManagerService->RemoveWakeLockListener(sWakeLockListener); michael@0: michael@0: sPowerManagerService = nullptr; michael@0: sWakeLockListener = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsAppShell::NotifyNativeEvent() michael@0: { michael@0: MutexAutoLock lock(mCondLock); michael@0: mQueueCond.Notify(); michael@0: } michael@0: michael@0: #define PREFNAME_COALESCE_TOUCHES "dom.event.touch.coalescing.enabled" michael@0: static const char* kObservedPrefs[] = { michael@0: PREFNAME_COALESCE_TOUCHES, michael@0: nullptr michael@0: }; michael@0: michael@0: nsresult michael@0: nsAppShell::Init() michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (!gWidgetLog) michael@0: gWidgetLog = PR_NewLogModule("Widget"); michael@0: #endif michael@0: michael@0: nsresult rv = nsBaseAppShell::Init(); michael@0: nsCOMPtr obsServ = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsServ) { michael@0: obsServ->AddObserver(this, "xpcom-shutdown", false); michael@0: } michael@0: michael@0: if (sPowerManagerService) michael@0: sPowerManagerService->AddWakeLockListener(sWakeLockListener); michael@0: michael@0: Preferences::AddStrongObservers(this, kObservedPrefs); michael@0: mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAppShell::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (!strcmp(aTopic, "xpcom-shutdown")) { michael@0: // We need to ensure no observers stick around after XPCOM shuts down michael@0: // or we'll see crashes, as the app shell outlives XPConnect. michael@0: mObserversHash.Clear(); michael@0: return nsBaseAppShell::Observe(aSubject, aTopic, aData); michael@0: } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) && michael@0: aData && michael@0: nsDependentString(aData).Equals(NS_LITERAL_STRING(PREFNAME_COALESCE_TOUCHES))) { michael@0: mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true); michael@0: return NS_OK; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsAppShell::ScheduleNativeEventCallback() michael@0: { michael@0: EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread()); michael@0: michael@0: // this is valid to be called from any thread, so do so. michael@0: PostEvent(AndroidGeckoEvent::MakeNativePoke()); michael@0: } michael@0: michael@0: bool michael@0: nsAppShell::ProcessNextNativeEvent(bool mayWait) michael@0: { michael@0: EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait); michael@0: michael@0: PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent"); michael@0: nsAutoPtr curEvent; michael@0: { michael@0: MutexAutoLock lock(mCondLock); michael@0: michael@0: curEvent = PopNextEvent(); michael@0: if (!curEvent && mayWait) { michael@0: PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent", "Wait"); michael@0: // hmm, should we really hardcode this 10s? michael@0: #if defined(DEBUG_ANDROID_EVENTS) michael@0: PRTime t0, t1; michael@0: EVLOG("nsAppShell: waiting on mQueueCond"); michael@0: t0 = PR_Now(); michael@0: mQueueCond.Wait(PR_MillisecondsToInterval(10000)); michael@0: t1 = PR_Now(); michael@0: EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000); michael@0: #else michael@0: mQueueCond.Wait(); michael@0: #endif michael@0: michael@0: curEvent = PopNextEvent(); michael@0: } michael@0: } michael@0: michael@0: if (!curEvent) michael@0: return false; michael@0: michael@0: EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type()); michael@0: michael@0: switch (curEvent->Type()) { michael@0: case AndroidGeckoEvent::NATIVE_POKE: michael@0: NativeEventCallback(); michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::SENSOR_EVENT: { michael@0: InfallibleTArray values; michael@0: mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); michael@0: michael@0: switch (type) { michael@0: // Bug 938035, transfer HAL data for orientation sensor to meet w3c michael@0: // spec, ex: HAL report alpha=90 means East but alpha=90 means West michael@0: // in w3c spec michael@0: case hal::SENSOR_ORIENTATION: michael@0: values.AppendElement(360 -curEvent->X()); michael@0: values.AppendElement(-curEvent->Y()); michael@0: values.AppendElement(-curEvent->Z()); michael@0: break; michael@0: case hal::SENSOR_LINEAR_ACCELERATION: michael@0: case hal::SENSOR_ACCELERATION: michael@0: case hal::SENSOR_GYROSCOPE: michael@0: case hal::SENSOR_PROXIMITY: michael@0: values.AppendElement(curEvent->X()); michael@0: values.AppendElement(curEvent->Y()); michael@0: values.AppendElement(curEvent->Z()); michael@0: break; michael@0: michael@0: case hal::SENSOR_LIGHT: michael@0: values.AppendElement(curEvent->X()); michael@0: break; michael@0: michael@0: default: michael@0: __android_log_print(ANDROID_LOG_ERROR, michael@0: "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d", michael@0: type); michael@0: } michael@0: michael@0: const hal::SensorAccuracyType &accuracy = (hal::SensorAccuracyType) curEvent->MetaState(); michael@0: hal::SensorData sdata(type, PR_Now(), values, accuracy); michael@0: hal::NotifySensorChange(sdata); michael@0: } michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::LOCATION_EVENT: { michael@0: if (!gLocationCallback) michael@0: break; michael@0: michael@0: nsGeoPosition* p = curEvent->GeoPosition(); michael@0: if (p) michael@0: gLocationCallback->Update(curEvent->GeoPosition()); michael@0: else michael@0: NS_WARNING("Received location event without geoposition!"); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::APP_BACKGROUNDING: { michael@0: nsCOMPtr obsServ = michael@0: mozilla::services::GetObserverService(); michael@0: obsServ->NotifyObservers(nullptr, "application-background", nullptr); michael@0: michael@0: NS_NAMED_LITERAL_STRING(minimize, "heap-minimize"); michael@0: obsServ->NotifyObservers(nullptr, "memory-pressure", minimize.get()); michael@0: michael@0: // If we are OOM killed with the disk cache enabled, the entire michael@0: // cache will be cleared (bug 105843), so shut down the cache here michael@0: // and re-init on foregrounding michael@0: if (nsCacheService::GlobalInstance()) { michael@0: nsCacheService::GlobalInstance()->Shutdown(); michael@0: } michael@0: michael@0: // We really want to send a notification like profile-before-change, michael@0: // but profile-before-change ends up shutting some things down instead michael@0: // of flushing data michael@0: nsIPrefService* prefs = Preferences::GetService(); michael@0: if (prefs) { michael@0: // reset the crash loop state michael@0: nsCOMPtr prefBranch; michael@0: prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch)); michael@0: if (prefBranch) michael@0: prefBranch->SetIntPref("recent_crashes", 0); michael@0: michael@0: prefs->SavePrefFile(nullptr); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::APP_FOREGROUNDING: { michael@0: // If we are OOM killed with the disk cache enabled, the entire michael@0: // cache will be cleared (bug 105843), so shut down cache on backgrounding michael@0: // and re-init here michael@0: if (nsCacheService::GlobalInstance()) { michael@0: nsCacheService::GlobalInstance()->Init(); michael@0: } michael@0: michael@0: // We didn't return from one of our own activities, so restore michael@0: // to foreground status michael@0: nsCOMPtr obsServ = michael@0: mozilla::services::GetObserverService(); michael@0: obsServ->NotifyObservers(nullptr, "application-foreground", nullptr); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::THUMBNAIL: { michael@0: if (!mBrowserApp) michael@0: break; michael@0: michael@0: int32_t tabId = curEvent->MetaState(); michael@0: const nsTArray& points = curEvent->Points(); michael@0: RefCountedJavaObject* buffer = curEvent->ByteBuffer(); michael@0: nsRefPtr sr = new ThumbnailRunnable(mBrowserApp, tabId, points, buffer); michael@0: MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableMethod(sr.get(), &ThumbnailRunnable::Run)); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::VIEWPORT: michael@0: case AndroidGeckoEvent::BROADCAST: { michael@0: if (curEvent->Characters().Length() == 0) michael@0: break; michael@0: michael@0: nsCOMPtr obsServ = michael@0: mozilla::services::GetObserverService(); michael@0: michael@0: const NS_ConvertUTF16toUTF8 topic(curEvent->Characters()); michael@0: const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra()); michael@0: michael@0: obsServ->NotifyObservers(nullptr, topic.get(), data.get()); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::TELEMETRY_UI_SESSION_STOP: { michael@0: if (curEvent->Characters().Length() == 0) michael@0: break; michael@0: michael@0: nsCOMPtr obs; michael@0: mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); michael@0: if (!obs) michael@0: break; michael@0: michael@0: obs->StopSession( michael@0: nsString(curEvent->Characters()).get(), michael@0: nsString(curEvent->CharactersExtra()).get(), michael@0: curEvent->Time() michael@0: ); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::TELEMETRY_UI_SESSION_START: { michael@0: if (curEvent->Characters().Length() == 0) michael@0: break; michael@0: michael@0: nsCOMPtr obs; michael@0: mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); michael@0: if (!obs) michael@0: break; michael@0: michael@0: obs->StartSession( michael@0: nsString(curEvent->Characters()).get(), michael@0: curEvent->Time() michael@0: ); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::TELEMETRY_UI_EVENT: { michael@0: if (curEvent->Data().Length() == 0) michael@0: break; michael@0: michael@0: nsCOMPtr obs; michael@0: mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); michael@0: if (!obs) michael@0: break; michael@0: michael@0: obs->AddEvent( michael@0: nsString(curEvent->Data()).get(), michael@0: nsString(curEvent->Characters()).get(), michael@0: curEvent->Time(), michael@0: nsString(curEvent->CharactersExtra()).get() michael@0: ); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::LOAD_URI: { michael@0: nsCOMPtr cmdline michael@0: (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); michael@0: if (!cmdline) michael@0: break; michael@0: michael@0: if (curEvent->Characters().Length() == 0) michael@0: break; michael@0: michael@0: char *uri = ToNewUTF8String(curEvent->Characters()); michael@0: if (!uri) michael@0: break; michael@0: michael@0: char *flag = ToNewUTF8String(curEvent->CharactersExtra()); michael@0: michael@0: const char *argv[4] = { michael@0: "dummyappname", michael@0: "-url", michael@0: uri, michael@0: flag ? flag : "" michael@0: }; michael@0: nsresult rv = cmdline->Init(4, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO); michael@0: if (NS_SUCCEEDED(rv)) michael@0: cmdline->Run(); michael@0: nsMemory::Free(uri); michael@0: if (flag) michael@0: nsMemory::Free(flag); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::SIZE_CHANGED: { michael@0: // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event michael@0: if (curEvent != gLastSizeChange) { michael@0: gLastSizeChange = AndroidGeckoEvent::CopyResizeEvent(curEvent); michael@0: } michael@0: nsWindow::OnGlobalAndroidEvent(curEvent); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::VISITED: { michael@0: #ifdef MOZ_ANDROID_HISTORY michael@0: nsCOMPtr history = services::GetHistoryService(); michael@0: nsCOMPtr visitedURI; michael@0: if (history && michael@0: NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI), michael@0: nsString(curEvent->Characters())))) { michael@0: history->NotifyVisited(visitedURI); michael@0: } michael@0: #endif michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::NETWORK_CHANGED: { michael@0: hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->ConnectionType(), michael@0: curEvent->IsWifi(), michael@0: curEvent->DHCPGateway())); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::SCREENORIENTATION_CHANGED: { michael@0: nsresult rv; michael@0: nsCOMPtr screenMgr = michael@0: do_GetService("@mozilla.org/gfx/screenmanager;1", &rv); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("Can't find nsIScreenManager!"); michael@0: break; michael@0: } michael@0: michael@0: nsIntRect rect; michael@0: int32_t colorDepth, pixelDepth; michael@0: dom::ScreenOrientation orientation; michael@0: nsCOMPtr screen; michael@0: michael@0: screenMgr->GetPrimaryScreen(getter_AddRefs(screen)); michael@0: screen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height); michael@0: screen->GetColorDepth(&colorDepth); michael@0: screen->GetPixelDepth(&pixelDepth); michael@0: orientation = michael@0: static_cast(curEvent->ScreenOrientation()); michael@0: michael@0: hal::NotifyScreenConfigurationChange( michael@0: hal::ScreenConfiguration(rect, orientation, colorDepth, pixelDepth)); michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::CALL_OBSERVER: michael@0: { michael@0: nsCOMPtr observer; michael@0: mObserversHash.Get(curEvent->Characters(), getter_AddRefs(observer)); michael@0: michael@0: if (observer) { michael@0: observer->Observe(nullptr, NS_ConvertUTF16toUTF8(curEvent->CharactersExtra()).get(), michael@0: nsString(curEvent->Data()).get()); michael@0: } else { michael@0: ALOG("Call_Observer event: Observer was not found!"); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::REMOVE_OBSERVER: michael@0: mObserversHash.Remove(curEvent->Characters()); michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::ADD_OBSERVER: michael@0: AddObserver(curEvent->Characters(), curEvent->Observer()); michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::PREFERENCES_GET: michael@0: case AndroidGeckoEvent::PREFERENCES_OBSERVE: { michael@0: const nsTArray &prefNames = curEvent->PrefNames(); michael@0: size_t count = prefNames.Length(); michael@0: nsAutoArrayPtr prefNamePtrs(new const char16_t*[count]); michael@0: for (size_t i = 0; i < count; ++i) { michael@0: prefNamePtrs[i] = prefNames[i].get(); michael@0: } michael@0: michael@0: if (curEvent->Type() == AndroidGeckoEvent::PREFERENCES_GET) { michael@0: mBrowserApp->GetPreferences(curEvent->RequestId(), prefNamePtrs, count); michael@0: } else { michael@0: mBrowserApp->ObservePreferences(curEvent->RequestId(), prefNamePtrs, count); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::PREFERENCES_REMOVE_OBSERVERS: michael@0: mBrowserApp->RemovePreferenceObservers(curEvent->RequestId()); michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::LOW_MEMORY: michael@0: // TODO hook in memory-reduction stuff for different levels here michael@0: if (curEvent->MetaState() >= AndroidGeckoEvent::MEMORY_PRESSURE_MEDIUM) { michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: os->NotifyObservers(nullptr, michael@0: "memory-pressure", michael@0: MOZ_UTF16("low-memory")); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::NETWORK_LINK_CHANGE: michael@0: { michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: os->NotifyObservers(nullptr, michael@0: NS_NETWORK_LINK_TOPIC, michael@0: nsString(curEvent->Characters()).get()); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case AndroidGeckoEvent::TELEMETRY_HISTOGRAM_ADD: michael@0: Telemetry::Accumulate(NS_ConvertUTF16toUTF8(curEvent->Characters()).get(), michael@0: curEvent->Count()); michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::NOOP: michael@0: break; michael@0: michael@0: default: michael@0: nsWindow::OnGlobalAndroidEvent(curEvent); michael@0: break; michael@0: } michael@0: michael@0: if (curEvent->AckNeeded()) { michael@0: mozilla::widget::android::GeckoAppShell::AcknowledgeEvent(); michael@0: } michael@0: michael@0: EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsAppShell::ResendLastResizeEvent(nsWindow* aDest) { michael@0: if (gLastSizeChange) { michael@0: nsWindow::OnGlobalAndroidEvent(gLastSizeChange); michael@0: } michael@0: } michael@0: michael@0: AndroidGeckoEvent* michael@0: nsAppShell::PopNextEvent() michael@0: { michael@0: AndroidGeckoEvent *ae = nullptr; michael@0: MutexAutoLock lock(mQueueLock); michael@0: if (mEventQueue.Length()) { michael@0: ae = mEventQueue[0]; michael@0: mEventQueue.RemoveElementAt(0); michael@0: if (mQueuedDrawEvent == ae) { michael@0: mQueuedDrawEvent = nullptr; michael@0: } else if (mQueuedViewportEvent == ae) { michael@0: mQueuedViewportEvent = nullptr; michael@0: } michael@0: } michael@0: michael@0: return ae; michael@0: } michael@0: michael@0: AndroidGeckoEvent* michael@0: nsAppShell::PeekNextEvent() michael@0: { michael@0: AndroidGeckoEvent *ae = nullptr; michael@0: MutexAutoLock lock(mQueueLock); michael@0: if (mEventQueue.Length()) { michael@0: ae = mEventQueue[0]; michael@0: } michael@0: michael@0: return ae; michael@0: } michael@0: michael@0: void michael@0: nsAppShell::PostEvent(AndroidGeckoEvent *ae) michael@0: { michael@0: { michael@0: // set this to true when inserting events that we can coalesce michael@0: // viewport events across. this is effectively maintaining a whitelist michael@0: // of events that are unaffected by viewport changes. michael@0: bool allowCoalescingNextViewport = false; michael@0: michael@0: MutexAutoLock lock(mQueueLock); michael@0: EVLOG("nsAppShell::PostEvent %p %d", ae, ae->Type()); michael@0: switch (ae->Type()) { michael@0: case AndroidGeckoEvent::COMPOSITOR_CREATE: michael@0: case AndroidGeckoEvent::COMPOSITOR_PAUSE: michael@0: case AndroidGeckoEvent::COMPOSITOR_RESUME: michael@0: // Give priority to these events, but maintain their order wrt each other. michael@0: { michael@0: uint32_t i = 0; michael@0: while (i < mEventQueue.Length() && michael@0: (mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_CREATE || michael@0: mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_PAUSE || michael@0: mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_RESUME)) { michael@0: i++; michael@0: } michael@0: EVLOG("nsAppShell: Inserting compositor event %d at position %d to maintain priority order", ae->Type(), i); michael@0: mEventQueue.InsertElementAt(i, ae); michael@0: } michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::DRAW: michael@0: if (mQueuedDrawEvent) { michael@0: #if defined(DEBUG) || defined(FORCE_ALOG) michael@0: // coalesce this new draw event with the one already in the queue michael@0: const nsIntRect& oldRect = mQueuedDrawEvent->Rect(); michael@0: const nsIntRect& newRect = ae->Rect(); michael@0: nsIntRect combinedRect = oldRect.Union(newRect); michael@0: michael@0: // XXX We may want to consider using regions instead of rectangles. michael@0: // Print an error if we're upload a lot more than we would michael@0: // if we handled this as two separate events. michael@0: int combinedArea = (oldRect.width * oldRect.height) + michael@0: (newRect.width * newRect.height); michael@0: int boundsArea = combinedRect.width * combinedRect.height; michael@0: if (boundsArea > combinedArea * 8) michael@0: ALOG("nsAppShell: Area of bounds greatly exceeds combined area: %d > %d", michael@0: boundsArea, combinedArea); michael@0: #endif michael@0: michael@0: // coalesce into the new draw event rather than the queued one because michael@0: // it is not always safe to move draws earlier in the queue; there may michael@0: // be events between the two draws that affect scroll position or something. michael@0: ae->UnionRect(mQueuedDrawEvent->Rect()); michael@0: michael@0: EVLOG("nsAppShell: Coalescing previous DRAW event at %p into new DRAW event %p", mQueuedDrawEvent, ae); michael@0: mEventQueue.RemoveElement(mQueuedDrawEvent); michael@0: delete mQueuedDrawEvent; michael@0: } michael@0: michael@0: if (!mAllowCoalescingNextDraw) { michael@0: // if we're not allowing coalescing of this draw event, then michael@0: // don't set mQueuedDrawEvent to point to this; that way the michael@0: // next draw event that comes in won't kill this one. michael@0: mAllowCoalescingNextDraw = true; michael@0: mQueuedDrawEvent = nullptr; michael@0: } else { michael@0: mQueuedDrawEvent = ae; michael@0: } michael@0: michael@0: allowCoalescingNextViewport = true; michael@0: michael@0: mEventQueue.AppendElement(ae); michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::VIEWPORT: michael@0: if (mQueuedViewportEvent) { michael@0: // drop the previous viewport event now that we have a new one michael@0: EVLOG("nsAppShell: Dropping old viewport event at %p in favour of new VIEWPORT event %p", mQueuedViewportEvent, ae); michael@0: mEventQueue.RemoveElement(mQueuedViewportEvent); michael@0: delete mQueuedViewportEvent; michael@0: } michael@0: mQueuedViewportEvent = ae; michael@0: // temporarily turn off draw-coalescing, so that we process a draw michael@0: // event as soon as possible after a viewport change michael@0: mAllowCoalescingNextDraw = false; michael@0: allowCoalescingNextViewport = true; michael@0: michael@0: mEventQueue.AppendElement(ae); michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::MOTION_EVENT: michael@0: if (ae->Action() == AndroidMotionEvent::ACTION_MOVE && mAllowCoalescingTouches) { michael@0: int len = mEventQueue.Length(); michael@0: if (len > 0) { michael@0: AndroidGeckoEvent* event = mEventQueue[len - 1]; michael@0: if (event->Type() == AndroidGeckoEvent::MOTION_EVENT && event->Action() == AndroidMotionEvent::ACTION_MOVE) { michael@0: // consecutive motion-move events; drop the last one before adding the new one michael@0: EVLOG("nsAppShell: Dropping old move event at %p in favour of new move event %p", event, ae); michael@0: mEventQueue.RemoveElementAt(len - 1); michael@0: delete event; michael@0: } michael@0: } michael@0: } michael@0: mEventQueue.AppendElement(ae); michael@0: break; michael@0: michael@0: case AndroidGeckoEvent::NATIVE_POKE: michael@0: allowCoalescingNextViewport = true; michael@0: // fall through michael@0: michael@0: default: michael@0: mEventQueue.AppendElement(ae); michael@0: break; michael@0: } michael@0: michael@0: // if the event wasn't on our whitelist then reset mQueuedViewportEvent michael@0: // so that we don't coalesce future viewport events into the last viewport michael@0: // event we added michael@0: if (!allowCoalescingNextViewport) michael@0: mQueuedViewportEvent = nullptr; michael@0: } michael@0: NotifyNativeEvent(); michael@0: } michael@0: michael@0: void michael@0: nsAppShell::OnResume() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver) michael@0: { michael@0: NS_ASSERTION(aObserver != nullptr, "nsAppShell::AddObserver: aObserver is null!"); michael@0: mObserversHash.Put(aObserverKey, aObserver); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Used by IPC code michael@0: namespace mozilla { michael@0: michael@0: bool ProcessNextEvent() michael@0: { michael@0: return nsAppShell::gAppShell->ProcessNextNativeEvent(true) ? true : false; michael@0: } michael@0: michael@0: void NotifyEvent() michael@0: { michael@0: nsAppShell::gAppShell->NotifyNativeEvent(); michael@0: } michael@0: michael@0: }