widget/android/nsAppShell.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
     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 "nsIScreen.h"
    13 #include "nsIScreenManager.h"
    14 #include "nsWindow.h"
    15 #include "nsThreadUtils.h"
    16 #include "nsICommandLineRunner.h"
    17 #include "nsIObserverService.h"
    18 #include "nsIAppStartup.h"
    19 #include "nsIGeolocationProvider.h"
    20 #include "nsCacheService.h"
    21 #include "nsIDOMEventListener.h"
    22 #include "nsIDOMClientRectList.h"
    23 #include "nsIDOMClientRect.h"
    24 #include "nsIDOMWakeLockListener.h"
    25 #include "nsIPowerManagerService.h"
    26 #include "nsFrameManager.h"
    27 #include "nsINetworkLinkService.h"
    29 #include "mozilla/Services.h"
    30 #include "mozilla/unused.h"
    31 #include "mozilla/Preferences.h"
    32 #include "mozilla/Hal.h"
    33 #include "prenv.h"
    35 #include "AndroidBridge.h"
    36 #include "AndroidBridgeUtilities.h"
    37 #include <android/log.h>
    38 #include <pthread.h>
    39 #include <wchar.h>
    41 #include "mozilla/dom/ScreenOrientation.h"
    43 #include "GeckoProfiler.h"
    44 #ifdef MOZ_ANDROID_HISTORY
    45 #include "nsNetUtil.h"
    46 #include "IHistory.h"
    47 #endif
    49 #ifdef MOZ_LOGGING
    50 #define FORCE_PR_LOG
    51 #include "prlog.h"
    52 #endif
    54 #ifdef DEBUG_ANDROID_EVENTS
    55 #define EVLOG(args...)  ALOG(args)
    56 #else
    57 #define EVLOG(args...) do { } while (0)
    58 #endif
    60 using namespace mozilla;
    62 #ifdef PR_LOGGING
    63 PRLogModuleInfo *gWidgetLog = nullptr;
    64 #endif
    66 nsIGeolocationUpdate *gLocationCallback = nullptr;
    67 nsAutoPtr<mozilla::AndroidGeckoEvent> gLastSizeChange;
    69 nsAppShell *nsAppShell::gAppShell = nullptr;
    71 NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver)
    73 class ThumbnailRunnable : public nsRunnable {
    74 public:
    75     ThumbnailRunnable(nsIAndroidBrowserApp* aBrowserApp, int aTabId,
    76                        const nsTArray<nsIntPoint>& aPoints, RefCountedJavaObject* aBuffer):
    77         mBrowserApp(aBrowserApp), mPoints(aPoints), mTabId(aTabId), mBuffer(aBuffer) {}
    79     virtual nsresult Run() {
    80         jobject buffer = mBuffer->GetObject();
    81         nsCOMPtr<nsIDOMWindow> domWindow;
    82         nsCOMPtr<nsIBrowserTab> tab;
    83         mBrowserApp->GetBrowserTab(mTabId, getter_AddRefs(tab));
    84         if (!tab) {
    85             mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, false);
    86             return NS_ERROR_FAILURE;
    87         }
    89         tab->GetWindow(getter_AddRefs(domWindow));
    90         if (!domWindow) {
    91             mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, false);
    92             return NS_ERROR_FAILURE;
    93         }
    95         NS_ASSERTION(mPoints.Length() == 1, "Thumbnail event does not have enough coordinates");
    97         nsresult rv = AndroidBridge::Bridge()->CaptureThumbnail(domWindow, mPoints[0].x, mPoints[0].y, mTabId, buffer);
    98         mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, NS_SUCCEEDED(rv));
    99         return rv;
   100     }
   101 private:
   102     nsCOMPtr<nsIAndroidBrowserApp> mBrowserApp;
   103     nsTArray<nsIntPoint> mPoints;
   104     int mTabId;
   105     nsRefPtr<RefCountedJavaObject> mBuffer;
   106 };
   108 class WakeLockListener MOZ_FINAL : public nsIDOMMozWakeLockListener {
   109  public:
   110   NS_DECL_ISUPPORTS;
   112   nsresult Callback(const nsAString& topic, const nsAString& state) {
   113     mozilla::widget::android::GeckoAppShell::NotifyWakeLockChanged(topic, state);
   114     return NS_OK;
   115   }
   116 };
   118 NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
   119 nsCOMPtr<nsIPowerManagerService> sPowerManagerService = nullptr;
   120 StaticRefPtr<WakeLockListener> sWakeLockListener;
   122 nsAppShell::nsAppShell()
   123     : mQueueLock("nsAppShell.mQueueLock"),
   124       mCondLock("nsAppShell.mCondLock"),
   125       mQueueCond(mCondLock, "nsAppShell.mQueueCond"),
   126       mQueuedDrawEvent(nullptr),
   127       mQueuedViewportEvent(nullptr),
   128       mAllowCoalescingNextDraw(false)
   129 {
   130     gAppShell = this;
   132     if (XRE_GetProcessType() != GeckoProcessType_Default) {
   133         return;
   134     }
   136     sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
   138     if (sPowerManagerService) {
   139         sWakeLockListener = new WakeLockListener();
   140     } else {
   141         NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
   142     }
   144 }
   146 nsAppShell::~nsAppShell()
   147 {
   148     gAppShell = nullptr;
   150     if (sPowerManagerService) {
   151         sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
   153         sPowerManagerService = nullptr;
   154         sWakeLockListener = nullptr;
   155     }
   156 }
   158 void
   159 nsAppShell::NotifyNativeEvent()
   160 {
   161     MutexAutoLock lock(mCondLock);
   162     mQueueCond.Notify();
   163 }
   165 #define PREFNAME_COALESCE_TOUCHES "dom.event.touch.coalescing.enabled"
   166 static const char* kObservedPrefs[] = {
   167   PREFNAME_COALESCE_TOUCHES,
   168   nullptr
   169 };
   171 nsresult
   172 nsAppShell::Init()
   173 {
   174 #ifdef PR_LOGGING
   175     if (!gWidgetLog)
   176         gWidgetLog = PR_NewLogModule("Widget");
   177 #endif
   179     nsresult rv = nsBaseAppShell::Init();
   180     nsCOMPtr<nsIObserverService> obsServ =
   181         mozilla::services::GetObserverService();
   182     if (obsServ) {
   183         obsServ->AddObserver(this, "xpcom-shutdown", false);
   184     }
   186     if (sPowerManagerService)
   187         sPowerManagerService->AddWakeLockListener(sWakeLockListener);
   189     Preferences::AddStrongObservers(this, kObservedPrefs);
   190     mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
   191     return rv;
   192 }
   194 NS_IMETHODIMP
   195 nsAppShell::Observe(nsISupports* aSubject,
   196                     const char* aTopic,
   197                     const char16_t* aData)
   198 {
   199     if (!strcmp(aTopic, "xpcom-shutdown")) {
   200         // We need to ensure no observers stick around after XPCOM shuts down
   201         // or we'll see crashes, as the app shell outlives XPConnect.
   202         mObserversHash.Clear();
   203         return nsBaseAppShell::Observe(aSubject, aTopic, aData);
   204     } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
   205                aData &&
   206                nsDependentString(aData).Equals(NS_LITERAL_STRING(PREFNAME_COALESCE_TOUCHES))) {
   207         mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
   208         return NS_OK;
   209     }
   210     return NS_OK;
   211 }
   213 void
   214 nsAppShell::ScheduleNativeEventCallback()
   215 {
   216     EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread());
   218     // this is valid to be called from any thread, so do so.
   219     PostEvent(AndroidGeckoEvent::MakeNativePoke());
   220 }
   222 bool
   223 nsAppShell::ProcessNextNativeEvent(bool mayWait)
   224 {
   225     EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait);
   227     PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent");
   228     nsAutoPtr<AndroidGeckoEvent> curEvent;
   229     {
   230         MutexAutoLock lock(mCondLock);
   232         curEvent = PopNextEvent();
   233         if (!curEvent && mayWait) {
   234             PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent", "Wait");
   235             // hmm, should we really hardcode this 10s?
   236 #if defined(DEBUG_ANDROID_EVENTS)
   237             PRTime t0, t1;
   238             EVLOG("nsAppShell: waiting on mQueueCond");
   239             t0 = PR_Now();
   240             mQueueCond.Wait(PR_MillisecondsToInterval(10000));
   241             t1 = PR_Now();
   242             EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000);
   243 #else
   244             mQueueCond.Wait();
   245 #endif
   247             curEvent = PopNextEvent();
   248         }
   249     }
   251     if (!curEvent)
   252         return false;
   254     EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type());
   256     switch (curEvent->Type()) {
   257     case AndroidGeckoEvent::NATIVE_POKE:
   258         NativeEventCallback();
   259         break;
   261     case AndroidGeckoEvent::SENSOR_EVENT: {
   262         InfallibleTArray<float> values;
   263         mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags();
   265         switch (type) {
   266           // Bug 938035, transfer HAL data for orientation sensor to meet w3c
   267           // spec, ex: HAL report alpha=90 means East but alpha=90 means West
   268           // in w3c spec
   269           case hal::SENSOR_ORIENTATION:
   270             values.AppendElement(360 -curEvent->X());
   271             values.AppendElement(-curEvent->Y());
   272             values.AppendElement(-curEvent->Z());
   273             break;
   274           case hal::SENSOR_LINEAR_ACCELERATION:
   275           case hal::SENSOR_ACCELERATION:
   276           case hal::SENSOR_GYROSCOPE:
   277           case hal::SENSOR_PROXIMITY:
   278             values.AppendElement(curEvent->X());
   279             values.AppendElement(curEvent->Y());
   280             values.AppendElement(curEvent->Z());
   281             break;
   283         case hal::SENSOR_LIGHT:
   284             values.AppendElement(curEvent->X());
   285             break;
   287         default:
   288             __android_log_print(ANDROID_LOG_ERROR,
   289                                 "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d",
   290                                 type);
   291         }
   293         const hal::SensorAccuracyType &accuracy = (hal::SensorAccuracyType) curEvent->MetaState();
   294         hal::SensorData sdata(type, PR_Now(), values, accuracy);
   295         hal::NotifySensorChange(sdata);
   296       }
   297       break;
   299     case AndroidGeckoEvent::LOCATION_EVENT: {
   300         if (!gLocationCallback)
   301             break;
   303         nsGeoPosition* p = curEvent->GeoPosition();
   304         if (p)
   305             gLocationCallback->Update(curEvent->GeoPosition());
   306         else
   307             NS_WARNING("Received location event without geoposition!");
   308         break;
   309     }
   311     case AndroidGeckoEvent::APP_BACKGROUNDING: {
   312         nsCOMPtr<nsIObserverService> obsServ =
   313             mozilla::services::GetObserverService();
   314         obsServ->NotifyObservers(nullptr, "application-background", nullptr);
   316         NS_NAMED_LITERAL_STRING(minimize, "heap-minimize");
   317         obsServ->NotifyObservers(nullptr, "memory-pressure", minimize.get());
   319         // If we are OOM killed with the disk cache enabled, the entire
   320         // cache will be cleared (bug 105843), so shut down the cache here
   321         // and re-init on foregrounding
   322         if (nsCacheService::GlobalInstance()) {
   323             nsCacheService::GlobalInstance()->Shutdown();
   324         }
   326         // We really want to send a notification like profile-before-change,
   327         // but profile-before-change ends up shutting some things down instead
   328         // of flushing data
   329         nsIPrefService* prefs = Preferences::GetService();
   330         if (prefs) {
   331             // reset the crash loop state
   332             nsCOMPtr<nsIPrefBranch> prefBranch;
   333             prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch));
   334             if (prefBranch)
   335                 prefBranch->SetIntPref("recent_crashes", 0);
   337             prefs->SavePrefFile(nullptr);
   338         }
   339         break;
   340     }
   342     case AndroidGeckoEvent::APP_FOREGROUNDING: {
   343         // If we are OOM killed with the disk cache enabled, the entire
   344         // cache will be cleared (bug 105843), so shut down cache on backgrounding
   345         // and re-init here
   346         if (nsCacheService::GlobalInstance()) {
   347             nsCacheService::GlobalInstance()->Init();
   348         }
   350         // We didn't return from one of our own activities, so restore
   351         // to foreground status
   352         nsCOMPtr<nsIObserverService> obsServ =
   353             mozilla::services::GetObserverService();
   354         obsServ->NotifyObservers(nullptr, "application-foreground", nullptr);
   355         break;
   356     }
   358     case AndroidGeckoEvent::THUMBNAIL: {
   359         if (!mBrowserApp)
   360             break;
   362         int32_t tabId = curEvent->MetaState();
   363         const nsTArray<nsIntPoint>& points = curEvent->Points();
   364         RefCountedJavaObject* buffer = curEvent->ByteBuffer();
   365         nsRefPtr<ThumbnailRunnable> sr = new ThumbnailRunnable(mBrowserApp, tabId, points, buffer);
   366         MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableMethod(sr.get(), &ThumbnailRunnable::Run));
   367         break;
   368     }
   370     case AndroidGeckoEvent::VIEWPORT:
   371     case AndroidGeckoEvent::BROADCAST: {
   372         if (curEvent->Characters().Length() == 0)
   373             break;
   375         nsCOMPtr<nsIObserverService> obsServ =
   376             mozilla::services::GetObserverService();
   378         const NS_ConvertUTF16toUTF8 topic(curEvent->Characters());
   379         const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra());
   381         obsServ->NotifyObservers(nullptr, topic.get(), data.get());
   382         break;
   383     }
   385     case AndroidGeckoEvent::TELEMETRY_UI_SESSION_STOP: {
   386         if (curEvent->Characters().Length() == 0)
   387             break;
   389         nsCOMPtr<nsIUITelemetryObserver> obs;
   390         mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
   391         if (!obs)
   392             break;
   394         obs->StopSession(
   395                 nsString(curEvent->Characters()).get(),
   396                 nsString(curEvent->CharactersExtra()).get(),
   397                 curEvent->Time()
   398                 );
   399         break;
   400     }
   402     case AndroidGeckoEvent::TELEMETRY_UI_SESSION_START: {
   403         if (curEvent->Characters().Length() == 0)
   404             break;
   406         nsCOMPtr<nsIUITelemetryObserver> obs;
   407         mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
   408         if (!obs)
   409             break;
   411         obs->StartSession(
   412                 nsString(curEvent->Characters()).get(),
   413                 curEvent->Time()
   414                 );
   415         break;
   416     }
   418     case AndroidGeckoEvent::TELEMETRY_UI_EVENT: {
   419         if (curEvent->Data().Length() == 0)
   420             break;
   422         nsCOMPtr<nsIUITelemetryObserver> obs;
   423         mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
   424         if (!obs)
   425             break;
   427         obs->AddEvent(
   428                 nsString(curEvent->Data()).get(),
   429                 nsString(curEvent->Characters()).get(),
   430                 curEvent->Time(),
   431                 nsString(curEvent->CharactersExtra()).get()
   432                 );
   433         break;
   434     }
   436     case AndroidGeckoEvent::LOAD_URI: {
   437         nsCOMPtr<nsICommandLineRunner> cmdline
   438             (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
   439         if (!cmdline)
   440             break;
   442         if (curEvent->Characters().Length() == 0)
   443             break;
   445         char *uri = ToNewUTF8String(curEvent->Characters());
   446         if (!uri)
   447             break;
   449         char *flag = ToNewUTF8String(curEvent->CharactersExtra());
   451         const char *argv[4] = {
   452             "dummyappname",
   453             "-url",
   454             uri,
   455             flag ? flag : ""
   456         };
   457         nsresult rv = cmdline->Init(4, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO);
   458         if (NS_SUCCEEDED(rv))
   459             cmdline->Run();
   460         nsMemory::Free(uri);
   461         if (flag)
   462             nsMemory::Free(flag);
   463         break;
   464     }
   466     case AndroidGeckoEvent::SIZE_CHANGED: {
   467         // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event
   468         if (curEvent != gLastSizeChange) {
   469             gLastSizeChange = AndroidGeckoEvent::CopyResizeEvent(curEvent);
   470         }
   471         nsWindow::OnGlobalAndroidEvent(curEvent);
   472         break;
   473     }
   475     case AndroidGeckoEvent::VISITED: {
   476 #ifdef MOZ_ANDROID_HISTORY
   477         nsCOMPtr<IHistory> history = services::GetHistoryService();
   478         nsCOMPtr<nsIURI> visitedURI;
   479         if (history &&
   480             NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI),
   481                                    nsString(curEvent->Characters())))) {
   482             history->NotifyVisited(visitedURI);
   483         }
   484 #endif
   485         break;
   486     }
   488     case AndroidGeckoEvent::NETWORK_CHANGED: {
   489         hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->ConnectionType(),
   490                                                          curEvent->IsWifi(),
   491                                                          curEvent->DHCPGateway()));
   492         break;
   493     }
   495     case AndroidGeckoEvent::SCREENORIENTATION_CHANGED: {
   496         nsresult rv;
   497         nsCOMPtr<nsIScreenManager> screenMgr =
   498             do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
   499         if (NS_FAILED(rv)) {
   500             NS_ERROR("Can't find nsIScreenManager!");
   501             break;
   502         }
   504         nsIntRect rect;
   505         int32_t colorDepth, pixelDepth;
   506         dom::ScreenOrientation orientation;
   507         nsCOMPtr<nsIScreen> screen;
   509         screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
   510         screen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
   511         screen->GetColorDepth(&colorDepth);
   512         screen->GetPixelDepth(&pixelDepth);
   513         orientation =
   514             static_cast<dom::ScreenOrientation>(curEvent->ScreenOrientation());
   516         hal::NotifyScreenConfigurationChange(
   517             hal::ScreenConfiguration(rect, orientation, colorDepth, pixelDepth));
   518         break;
   519     }
   521     case AndroidGeckoEvent::CALL_OBSERVER:
   522     {
   523         nsCOMPtr<nsIObserver> observer;
   524         mObserversHash.Get(curEvent->Characters(), getter_AddRefs(observer));
   526         if (observer) {
   527             observer->Observe(nullptr, NS_ConvertUTF16toUTF8(curEvent->CharactersExtra()).get(),
   528                               nsString(curEvent->Data()).get());
   529         } else {
   530             ALOG("Call_Observer event: Observer was not found!");
   531         }
   533         break;
   534     }
   536     case AndroidGeckoEvent::REMOVE_OBSERVER:
   537         mObserversHash.Remove(curEvent->Characters());
   538         break;
   540     case AndroidGeckoEvent::ADD_OBSERVER:
   541         AddObserver(curEvent->Characters(), curEvent->Observer());
   542         break;
   544     case AndroidGeckoEvent::PREFERENCES_GET:
   545     case AndroidGeckoEvent::PREFERENCES_OBSERVE: {
   546         const nsTArray<nsString> &prefNames = curEvent->PrefNames();
   547         size_t count = prefNames.Length();
   548         nsAutoArrayPtr<const char16_t*> prefNamePtrs(new const char16_t*[count]);
   549         for (size_t i = 0; i < count; ++i) {
   550             prefNamePtrs[i] = prefNames[i].get();
   551         }
   553         if (curEvent->Type() == AndroidGeckoEvent::PREFERENCES_GET) {
   554             mBrowserApp->GetPreferences(curEvent->RequestId(), prefNamePtrs, count);
   555         } else {
   556             mBrowserApp->ObservePreferences(curEvent->RequestId(), prefNamePtrs, count);
   557         }
   558         break;
   559     }
   561     case AndroidGeckoEvent::PREFERENCES_REMOVE_OBSERVERS:
   562         mBrowserApp->RemovePreferenceObservers(curEvent->RequestId());
   563         break;
   565     case AndroidGeckoEvent::LOW_MEMORY:
   566         // TODO hook in memory-reduction stuff for different levels here
   567         if (curEvent->MetaState() >= AndroidGeckoEvent::MEMORY_PRESSURE_MEDIUM) {
   568             nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   569             if (os) {
   570                 os->NotifyObservers(nullptr,
   571                                     "memory-pressure",
   572                                     MOZ_UTF16("low-memory"));
   573             }
   574         }
   575         break;
   577     case AndroidGeckoEvent::NETWORK_LINK_CHANGE:
   578     {
   579         nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   580         if (os) {
   581             os->NotifyObservers(nullptr,
   582                                 NS_NETWORK_LINK_TOPIC,
   583                                 nsString(curEvent->Characters()).get());
   584         }
   585         break;
   586     }
   588     case AndroidGeckoEvent::TELEMETRY_HISTOGRAM_ADD:
   589         Telemetry::Accumulate(NS_ConvertUTF16toUTF8(curEvent->Characters()).get(),
   590                               curEvent->Count());
   591         break;
   593     case AndroidGeckoEvent::NOOP:
   594         break;
   596     default:
   597         nsWindow::OnGlobalAndroidEvent(curEvent);
   598         break;
   599     }
   601     if (curEvent->AckNeeded()) {
   602         mozilla::widget::android::GeckoAppShell::AcknowledgeEvent();
   603     }
   605     EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type());
   607     return true;
   608 }
   610 void
   611 nsAppShell::ResendLastResizeEvent(nsWindow* aDest) {
   612     if (gLastSizeChange) {
   613         nsWindow::OnGlobalAndroidEvent(gLastSizeChange);
   614     }
   615 }
   617 AndroidGeckoEvent*
   618 nsAppShell::PopNextEvent()
   619 {
   620     AndroidGeckoEvent *ae = nullptr;
   621     MutexAutoLock lock(mQueueLock);
   622     if (mEventQueue.Length()) {
   623         ae = mEventQueue[0];
   624         mEventQueue.RemoveElementAt(0);
   625         if (mQueuedDrawEvent == ae) {
   626             mQueuedDrawEvent = nullptr;
   627         } else if (mQueuedViewportEvent == ae) {
   628             mQueuedViewportEvent = nullptr;
   629         }
   630     }
   632     return ae;
   633 }
   635 AndroidGeckoEvent*
   636 nsAppShell::PeekNextEvent()
   637 {
   638     AndroidGeckoEvent *ae = nullptr;
   639     MutexAutoLock lock(mQueueLock);
   640     if (mEventQueue.Length()) {
   641         ae = mEventQueue[0];
   642     }
   644     return ae;
   645 }
   647 void
   648 nsAppShell::PostEvent(AndroidGeckoEvent *ae)
   649 {
   650     {
   651         // set this to true when inserting events that we can coalesce
   652         // viewport events across. this is effectively maintaining a whitelist
   653         // of events that are unaffected by viewport changes.
   654         bool allowCoalescingNextViewport = false;
   656         MutexAutoLock lock(mQueueLock);
   657         EVLOG("nsAppShell::PostEvent %p %d", ae, ae->Type());
   658         switch (ae->Type()) {
   659         case AndroidGeckoEvent::COMPOSITOR_CREATE:
   660         case AndroidGeckoEvent::COMPOSITOR_PAUSE:
   661         case AndroidGeckoEvent::COMPOSITOR_RESUME:
   662             // Give priority to these events, but maintain their order wrt each other.
   663             {
   664                 uint32_t i = 0;
   665                 while (i < mEventQueue.Length() &&
   666                        (mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_CREATE ||
   667                         mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_PAUSE ||
   668                         mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_RESUME)) {
   669                     i++;
   670                 }
   671                 EVLOG("nsAppShell: Inserting compositor event %d at position %d to maintain priority order", ae->Type(), i);
   672                 mEventQueue.InsertElementAt(i, ae);
   673             }
   674             break;
   676         case AndroidGeckoEvent::DRAW:
   677             if (mQueuedDrawEvent) {
   678 #if defined(DEBUG) || defined(FORCE_ALOG)
   679                 // coalesce this new draw event with the one already in the queue
   680                 const nsIntRect& oldRect = mQueuedDrawEvent->Rect();
   681                 const nsIntRect& newRect = ae->Rect();
   682                 nsIntRect combinedRect = oldRect.Union(newRect);
   684                 // XXX We may want to consider using regions instead of rectangles.
   685                 //     Print an error if we're upload a lot more than we would
   686                 //     if we handled this as two separate events.
   687                 int combinedArea = (oldRect.width * oldRect.height) +
   688                                    (newRect.width * newRect.height);
   689                 int boundsArea = combinedRect.width * combinedRect.height;
   690                 if (boundsArea > combinedArea * 8)
   691                     ALOG("nsAppShell: Area of bounds greatly exceeds combined area: %d > %d",
   692                          boundsArea, combinedArea);
   693 #endif
   695                 // coalesce into the new draw event rather than the queued one because
   696                 // it is not always safe to move draws earlier in the queue; there may
   697                 // be events between the two draws that affect scroll position or something.
   698                 ae->UnionRect(mQueuedDrawEvent->Rect());
   700                 EVLOG("nsAppShell: Coalescing previous DRAW event at %p into new DRAW event %p", mQueuedDrawEvent, ae);
   701                 mEventQueue.RemoveElement(mQueuedDrawEvent);
   702                 delete mQueuedDrawEvent;
   703             }
   705             if (!mAllowCoalescingNextDraw) {
   706                 // if we're not allowing coalescing of this draw event, then
   707                 // don't set mQueuedDrawEvent to point to this; that way the
   708                 // next draw event that comes in won't kill this one.
   709                 mAllowCoalescingNextDraw = true;
   710                 mQueuedDrawEvent = nullptr;
   711             } else {
   712                 mQueuedDrawEvent = ae;
   713             }
   715             allowCoalescingNextViewport = true;
   717             mEventQueue.AppendElement(ae);
   718             break;
   720         case AndroidGeckoEvent::VIEWPORT:
   721             if (mQueuedViewportEvent) {
   722                 // drop the previous viewport event now that we have a new one
   723                 EVLOG("nsAppShell: Dropping old viewport event at %p in favour of new VIEWPORT event %p", mQueuedViewportEvent, ae);
   724                 mEventQueue.RemoveElement(mQueuedViewportEvent);
   725                 delete mQueuedViewportEvent;
   726             }
   727             mQueuedViewportEvent = ae;
   728             // temporarily turn off draw-coalescing, so that we process a draw
   729             // event as soon as possible after a viewport change
   730             mAllowCoalescingNextDraw = false;
   731             allowCoalescingNextViewport = true;
   733             mEventQueue.AppendElement(ae);
   734             break;
   736         case AndroidGeckoEvent::MOTION_EVENT:
   737             if (ae->Action() == AndroidMotionEvent::ACTION_MOVE && mAllowCoalescingTouches) {
   738                 int len = mEventQueue.Length();
   739                 if (len > 0) {
   740                     AndroidGeckoEvent* event = mEventQueue[len - 1];
   741                     if (event->Type() == AndroidGeckoEvent::MOTION_EVENT && event->Action() == AndroidMotionEvent::ACTION_MOVE) {
   742                         // consecutive motion-move events; drop the last one before adding the new one
   743                         EVLOG("nsAppShell: Dropping old move event at %p in favour of new move event %p", event, ae);
   744                         mEventQueue.RemoveElementAt(len - 1);
   745                         delete event;
   746                     }
   747                 }
   748             }
   749             mEventQueue.AppendElement(ae);
   750             break;
   752         case AndroidGeckoEvent::NATIVE_POKE:
   753             allowCoalescingNextViewport = true;
   754             // fall through
   756         default:
   757             mEventQueue.AppendElement(ae);
   758             break;
   759         }
   761         // if the event wasn't on our whitelist then reset mQueuedViewportEvent
   762         // so that we don't coalesce future viewport events into the last viewport
   763         // event we added
   764         if (!allowCoalescingNextViewport)
   765             mQueuedViewportEvent = nullptr;
   766     }
   767     NotifyNativeEvent();
   768 }
   770 void
   771 nsAppShell::OnResume()
   772 {
   773 }
   775 nsresult
   776 nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver)
   777 {
   778     NS_ASSERTION(aObserver != nullptr, "nsAppShell::AddObserver: aObserver is null!");
   779     mObserversHash.Put(aObserverKey, aObserver);
   780     return NS_OK;
   781 }
   783 // Used by IPC code
   784 namespace mozilla {
   786 bool ProcessNextEvent()
   787 {
   788     return nsAppShell::gAppShell->ProcessNextNativeEvent(true) ? true : false;
   789 }
   791 void NotifyEvent()
   792 {
   793     nsAppShell::gAppShell->NotifyNativeEvent();
   794 }
   796 }

mercurial