widget/android/nsAppShell.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/android/nsAppShell.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,796 @@
     1.4 +/* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsAppShell.h"
    1.10 +
    1.11 +#include "base/basictypes.h"
    1.12 +#include "base/message_loop.h"
    1.13 +#include "base/task.h"
    1.14 +#include "mozilla/Hal.h"
    1.15 +#include "nsIScreen.h"
    1.16 +#include "nsIScreenManager.h"
    1.17 +#include "nsWindow.h"
    1.18 +#include "nsThreadUtils.h"
    1.19 +#include "nsICommandLineRunner.h"
    1.20 +#include "nsIObserverService.h"
    1.21 +#include "nsIAppStartup.h"
    1.22 +#include "nsIGeolocationProvider.h"
    1.23 +#include "nsCacheService.h"
    1.24 +#include "nsIDOMEventListener.h"
    1.25 +#include "nsIDOMClientRectList.h"
    1.26 +#include "nsIDOMClientRect.h"
    1.27 +#include "nsIDOMWakeLockListener.h"
    1.28 +#include "nsIPowerManagerService.h"
    1.29 +#include "nsFrameManager.h"
    1.30 +#include "nsINetworkLinkService.h"
    1.31 +
    1.32 +#include "mozilla/Services.h"
    1.33 +#include "mozilla/unused.h"
    1.34 +#include "mozilla/Preferences.h"
    1.35 +#include "mozilla/Hal.h"
    1.36 +#include "prenv.h"
    1.37 +
    1.38 +#include "AndroidBridge.h"
    1.39 +#include "AndroidBridgeUtilities.h"
    1.40 +#include <android/log.h>
    1.41 +#include <pthread.h>
    1.42 +#include <wchar.h>
    1.43 +
    1.44 +#include "mozilla/dom/ScreenOrientation.h"
    1.45 +
    1.46 +#include "GeckoProfiler.h"
    1.47 +#ifdef MOZ_ANDROID_HISTORY
    1.48 +#include "nsNetUtil.h"
    1.49 +#include "IHistory.h"
    1.50 +#endif
    1.51 +
    1.52 +#ifdef MOZ_LOGGING
    1.53 +#define FORCE_PR_LOG
    1.54 +#include "prlog.h"
    1.55 +#endif
    1.56 +
    1.57 +#ifdef DEBUG_ANDROID_EVENTS
    1.58 +#define EVLOG(args...)  ALOG(args)
    1.59 +#else
    1.60 +#define EVLOG(args...) do { } while (0)
    1.61 +#endif
    1.62 +
    1.63 +using namespace mozilla;
    1.64 +
    1.65 +#ifdef PR_LOGGING
    1.66 +PRLogModuleInfo *gWidgetLog = nullptr;
    1.67 +#endif
    1.68 +
    1.69 +nsIGeolocationUpdate *gLocationCallback = nullptr;
    1.70 +nsAutoPtr<mozilla::AndroidGeckoEvent> gLastSizeChange;
    1.71 +
    1.72 +nsAppShell *nsAppShell::gAppShell = nullptr;
    1.73 +
    1.74 +NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver)
    1.75 +
    1.76 +class ThumbnailRunnable : public nsRunnable {
    1.77 +public:
    1.78 +    ThumbnailRunnable(nsIAndroidBrowserApp* aBrowserApp, int aTabId,
    1.79 +                       const nsTArray<nsIntPoint>& aPoints, RefCountedJavaObject* aBuffer):
    1.80 +        mBrowserApp(aBrowserApp), mPoints(aPoints), mTabId(aTabId), mBuffer(aBuffer) {}
    1.81 +
    1.82 +    virtual nsresult Run() {
    1.83 +        jobject buffer = mBuffer->GetObject();
    1.84 +        nsCOMPtr<nsIDOMWindow> domWindow;
    1.85 +        nsCOMPtr<nsIBrowserTab> tab;
    1.86 +        mBrowserApp->GetBrowserTab(mTabId, getter_AddRefs(tab));
    1.87 +        if (!tab) {
    1.88 +            mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, false);
    1.89 +            return NS_ERROR_FAILURE;
    1.90 +        }
    1.91 +
    1.92 +        tab->GetWindow(getter_AddRefs(domWindow));
    1.93 +        if (!domWindow) {
    1.94 +            mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, false);
    1.95 +            return NS_ERROR_FAILURE;
    1.96 +        }
    1.97 +
    1.98 +        NS_ASSERTION(mPoints.Length() == 1, "Thumbnail event does not have enough coordinates");
    1.99 +
   1.100 +        nsresult rv = AndroidBridge::Bridge()->CaptureThumbnail(domWindow, mPoints[0].x, mPoints[0].y, mTabId, buffer);
   1.101 +        mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, NS_SUCCEEDED(rv));
   1.102 +        return rv;
   1.103 +    }
   1.104 +private:
   1.105 +    nsCOMPtr<nsIAndroidBrowserApp> mBrowserApp;
   1.106 +    nsTArray<nsIntPoint> mPoints;
   1.107 +    int mTabId;
   1.108 +    nsRefPtr<RefCountedJavaObject> mBuffer;
   1.109 +};
   1.110 +
   1.111 +class WakeLockListener MOZ_FINAL : public nsIDOMMozWakeLockListener {
   1.112 + public:
   1.113 +  NS_DECL_ISUPPORTS;
   1.114 +
   1.115 +  nsresult Callback(const nsAString& topic, const nsAString& state) {
   1.116 +    mozilla::widget::android::GeckoAppShell::NotifyWakeLockChanged(topic, state);
   1.117 +    return NS_OK;
   1.118 +  }
   1.119 +};
   1.120 +
   1.121 +NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener)
   1.122 +nsCOMPtr<nsIPowerManagerService> sPowerManagerService = nullptr;
   1.123 +StaticRefPtr<WakeLockListener> sWakeLockListener;
   1.124 +
   1.125 +nsAppShell::nsAppShell()
   1.126 +    : mQueueLock("nsAppShell.mQueueLock"),
   1.127 +      mCondLock("nsAppShell.mCondLock"),
   1.128 +      mQueueCond(mCondLock, "nsAppShell.mQueueCond"),
   1.129 +      mQueuedDrawEvent(nullptr),
   1.130 +      mQueuedViewportEvent(nullptr),
   1.131 +      mAllowCoalescingNextDraw(false)
   1.132 +{
   1.133 +    gAppShell = this;
   1.134 +
   1.135 +    if (XRE_GetProcessType() != GeckoProcessType_Default) {
   1.136 +        return;
   1.137 +    }
   1.138 +
   1.139 +    sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
   1.140 +
   1.141 +    if (sPowerManagerService) {
   1.142 +        sWakeLockListener = new WakeLockListener();
   1.143 +    } else {
   1.144 +        NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
   1.145 +    }
   1.146 +
   1.147 +}
   1.148 +
   1.149 +nsAppShell::~nsAppShell()
   1.150 +{
   1.151 +    gAppShell = nullptr;
   1.152 +
   1.153 +    if (sPowerManagerService) {
   1.154 +        sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
   1.155 +
   1.156 +        sPowerManagerService = nullptr;
   1.157 +        sWakeLockListener = nullptr;
   1.158 +    }
   1.159 +}
   1.160 +
   1.161 +void
   1.162 +nsAppShell::NotifyNativeEvent()
   1.163 +{
   1.164 +    MutexAutoLock lock(mCondLock);
   1.165 +    mQueueCond.Notify();
   1.166 +}
   1.167 +
   1.168 +#define PREFNAME_COALESCE_TOUCHES "dom.event.touch.coalescing.enabled"
   1.169 +static const char* kObservedPrefs[] = {
   1.170 +  PREFNAME_COALESCE_TOUCHES,
   1.171 +  nullptr
   1.172 +};
   1.173 +
   1.174 +nsresult
   1.175 +nsAppShell::Init()
   1.176 +{
   1.177 +#ifdef PR_LOGGING
   1.178 +    if (!gWidgetLog)
   1.179 +        gWidgetLog = PR_NewLogModule("Widget");
   1.180 +#endif
   1.181 +
   1.182 +    nsresult rv = nsBaseAppShell::Init();
   1.183 +    nsCOMPtr<nsIObserverService> obsServ =
   1.184 +        mozilla::services::GetObserverService();
   1.185 +    if (obsServ) {
   1.186 +        obsServ->AddObserver(this, "xpcom-shutdown", false);
   1.187 +    }
   1.188 +
   1.189 +    if (sPowerManagerService)
   1.190 +        sPowerManagerService->AddWakeLockListener(sWakeLockListener);
   1.191 +
   1.192 +    Preferences::AddStrongObservers(this, kObservedPrefs);
   1.193 +    mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
   1.194 +    return rv;
   1.195 +}
   1.196 +
   1.197 +NS_IMETHODIMP
   1.198 +nsAppShell::Observe(nsISupports* aSubject,
   1.199 +                    const char* aTopic,
   1.200 +                    const char16_t* aData)
   1.201 +{
   1.202 +    if (!strcmp(aTopic, "xpcom-shutdown")) {
   1.203 +        // We need to ensure no observers stick around after XPCOM shuts down
   1.204 +        // or we'll see crashes, as the app shell outlives XPConnect.
   1.205 +        mObserversHash.Clear();
   1.206 +        return nsBaseAppShell::Observe(aSubject, aTopic, aData);
   1.207 +    } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
   1.208 +               aData &&
   1.209 +               nsDependentString(aData).Equals(NS_LITERAL_STRING(PREFNAME_COALESCE_TOUCHES))) {
   1.210 +        mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
   1.211 +        return NS_OK;
   1.212 +    }
   1.213 +    return NS_OK;
   1.214 +}
   1.215 +
   1.216 +void
   1.217 +nsAppShell::ScheduleNativeEventCallback()
   1.218 +{
   1.219 +    EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread());
   1.220 +
   1.221 +    // this is valid to be called from any thread, so do so.
   1.222 +    PostEvent(AndroidGeckoEvent::MakeNativePoke());
   1.223 +}
   1.224 +
   1.225 +bool
   1.226 +nsAppShell::ProcessNextNativeEvent(bool mayWait)
   1.227 +{
   1.228 +    EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait);
   1.229 +
   1.230 +    PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent");
   1.231 +    nsAutoPtr<AndroidGeckoEvent> curEvent;
   1.232 +    {
   1.233 +        MutexAutoLock lock(mCondLock);
   1.234 +
   1.235 +        curEvent = PopNextEvent();
   1.236 +        if (!curEvent && mayWait) {
   1.237 +            PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent", "Wait");
   1.238 +            // hmm, should we really hardcode this 10s?
   1.239 +#if defined(DEBUG_ANDROID_EVENTS)
   1.240 +            PRTime t0, t1;
   1.241 +            EVLOG("nsAppShell: waiting on mQueueCond");
   1.242 +            t0 = PR_Now();
   1.243 +            mQueueCond.Wait(PR_MillisecondsToInterval(10000));
   1.244 +            t1 = PR_Now();
   1.245 +            EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000);
   1.246 +#else
   1.247 +            mQueueCond.Wait();
   1.248 +#endif
   1.249 +
   1.250 +            curEvent = PopNextEvent();
   1.251 +        }
   1.252 +    }
   1.253 +
   1.254 +    if (!curEvent)
   1.255 +        return false;
   1.256 +
   1.257 +    EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type());
   1.258 +
   1.259 +    switch (curEvent->Type()) {
   1.260 +    case AndroidGeckoEvent::NATIVE_POKE:
   1.261 +        NativeEventCallback();
   1.262 +        break;
   1.263 +
   1.264 +    case AndroidGeckoEvent::SENSOR_EVENT: {
   1.265 +        InfallibleTArray<float> values;
   1.266 +        mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags();
   1.267 +
   1.268 +        switch (type) {
   1.269 +          // Bug 938035, transfer HAL data for orientation sensor to meet w3c
   1.270 +          // spec, ex: HAL report alpha=90 means East but alpha=90 means West
   1.271 +          // in w3c spec
   1.272 +          case hal::SENSOR_ORIENTATION:
   1.273 +            values.AppendElement(360 -curEvent->X());
   1.274 +            values.AppendElement(-curEvent->Y());
   1.275 +            values.AppendElement(-curEvent->Z());
   1.276 +            break;
   1.277 +          case hal::SENSOR_LINEAR_ACCELERATION:
   1.278 +          case hal::SENSOR_ACCELERATION:
   1.279 +          case hal::SENSOR_GYROSCOPE:
   1.280 +          case hal::SENSOR_PROXIMITY:
   1.281 +            values.AppendElement(curEvent->X());
   1.282 +            values.AppendElement(curEvent->Y());
   1.283 +            values.AppendElement(curEvent->Z());
   1.284 +            break;
   1.285 +
   1.286 +        case hal::SENSOR_LIGHT:
   1.287 +            values.AppendElement(curEvent->X());
   1.288 +            break;
   1.289 +
   1.290 +        default:
   1.291 +            __android_log_print(ANDROID_LOG_ERROR,
   1.292 +                                "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d",
   1.293 +                                type);
   1.294 +        }
   1.295 +
   1.296 +        const hal::SensorAccuracyType &accuracy = (hal::SensorAccuracyType) curEvent->MetaState();
   1.297 +        hal::SensorData sdata(type, PR_Now(), values, accuracy);
   1.298 +        hal::NotifySensorChange(sdata);
   1.299 +      }
   1.300 +      break;
   1.301 +
   1.302 +    case AndroidGeckoEvent::LOCATION_EVENT: {
   1.303 +        if (!gLocationCallback)
   1.304 +            break;
   1.305 +
   1.306 +        nsGeoPosition* p = curEvent->GeoPosition();
   1.307 +        if (p)
   1.308 +            gLocationCallback->Update(curEvent->GeoPosition());
   1.309 +        else
   1.310 +            NS_WARNING("Received location event without geoposition!");
   1.311 +        break;
   1.312 +    }
   1.313 +
   1.314 +    case AndroidGeckoEvent::APP_BACKGROUNDING: {
   1.315 +        nsCOMPtr<nsIObserverService> obsServ =
   1.316 +            mozilla::services::GetObserverService();
   1.317 +        obsServ->NotifyObservers(nullptr, "application-background", nullptr);
   1.318 +
   1.319 +        NS_NAMED_LITERAL_STRING(minimize, "heap-minimize");
   1.320 +        obsServ->NotifyObservers(nullptr, "memory-pressure", minimize.get());
   1.321 +
   1.322 +        // If we are OOM killed with the disk cache enabled, the entire
   1.323 +        // cache will be cleared (bug 105843), so shut down the cache here
   1.324 +        // and re-init on foregrounding
   1.325 +        if (nsCacheService::GlobalInstance()) {
   1.326 +            nsCacheService::GlobalInstance()->Shutdown();
   1.327 +        }
   1.328 +
   1.329 +        // We really want to send a notification like profile-before-change,
   1.330 +        // but profile-before-change ends up shutting some things down instead
   1.331 +        // of flushing data
   1.332 +        nsIPrefService* prefs = Preferences::GetService();
   1.333 +        if (prefs) {
   1.334 +            // reset the crash loop state
   1.335 +            nsCOMPtr<nsIPrefBranch> prefBranch;
   1.336 +            prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch));
   1.337 +            if (prefBranch)
   1.338 +                prefBranch->SetIntPref("recent_crashes", 0);
   1.339 +
   1.340 +            prefs->SavePrefFile(nullptr);
   1.341 +        }
   1.342 +        break;
   1.343 +    }
   1.344 +
   1.345 +    case AndroidGeckoEvent::APP_FOREGROUNDING: {
   1.346 +        // If we are OOM killed with the disk cache enabled, the entire
   1.347 +        // cache will be cleared (bug 105843), so shut down cache on backgrounding
   1.348 +        // and re-init here
   1.349 +        if (nsCacheService::GlobalInstance()) {
   1.350 +            nsCacheService::GlobalInstance()->Init();
   1.351 +        }
   1.352 +
   1.353 +        // We didn't return from one of our own activities, so restore
   1.354 +        // to foreground status
   1.355 +        nsCOMPtr<nsIObserverService> obsServ =
   1.356 +            mozilla::services::GetObserverService();
   1.357 +        obsServ->NotifyObservers(nullptr, "application-foreground", nullptr);
   1.358 +        break;
   1.359 +    }
   1.360 +
   1.361 +    case AndroidGeckoEvent::THUMBNAIL: {
   1.362 +        if (!mBrowserApp)
   1.363 +            break;
   1.364 +
   1.365 +        int32_t tabId = curEvent->MetaState();
   1.366 +        const nsTArray<nsIntPoint>& points = curEvent->Points();
   1.367 +        RefCountedJavaObject* buffer = curEvent->ByteBuffer();
   1.368 +        nsRefPtr<ThumbnailRunnable> sr = new ThumbnailRunnable(mBrowserApp, tabId, points, buffer);
   1.369 +        MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableMethod(sr.get(), &ThumbnailRunnable::Run));
   1.370 +        break;
   1.371 +    }
   1.372 +
   1.373 +    case AndroidGeckoEvent::VIEWPORT:
   1.374 +    case AndroidGeckoEvent::BROADCAST: {
   1.375 +        if (curEvent->Characters().Length() == 0)
   1.376 +            break;
   1.377 +
   1.378 +        nsCOMPtr<nsIObserverService> obsServ =
   1.379 +            mozilla::services::GetObserverService();
   1.380 +
   1.381 +        const NS_ConvertUTF16toUTF8 topic(curEvent->Characters());
   1.382 +        const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra());
   1.383 +
   1.384 +        obsServ->NotifyObservers(nullptr, topic.get(), data.get());
   1.385 +        break;
   1.386 +    }
   1.387 +
   1.388 +    case AndroidGeckoEvent::TELEMETRY_UI_SESSION_STOP: {
   1.389 +        if (curEvent->Characters().Length() == 0)
   1.390 +            break;
   1.391 +
   1.392 +        nsCOMPtr<nsIUITelemetryObserver> obs;
   1.393 +        mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
   1.394 +        if (!obs)
   1.395 +            break;
   1.396 +
   1.397 +        obs->StopSession(
   1.398 +                nsString(curEvent->Characters()).get(),
   1.399 +                nsString(curEvent->CharactersExtra()).get(),
   1.400 +                curEvent->Time()
   1.401 +                );
   1.402 +        break;
   1.403 +    }
   1.404 +
   1.405 +    case AndroidGeckoEvent::TELEMETRY_UI_SESSION_START: {
   1.406 +        if (curEvent->Characters().Length() == 0)
   1.407 +            break;
   1.408 +
   1.409 +        nsCOMPtr<nsIUITelemetryObserver> obs;
   1.410 +        mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
   1.411 +        if (!obs)
   1.412 +            break;
   1.413 +
   1.414 +        obs->StartSession(
   1.415 +                nsString(curEvent->Characters()).get(),
   1.416 +                curEvent->Time()
   1.417 +                );
   1.418 +        break;
   1.419 +    }
   1.420 +
   1.421 +    case AndroidGeckoEvent::TELEMETRY_UI_EVENT: {
   1.422 +        if (curEvent->Data().Length() == 0)
   1.423 +            break;
   1.424 +
   1.425 +        nsCOMPtr<nsIUITelemetryObserver> obs;
   1.426 +        mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
   1.427 +        if (!obs)
   1.428 +            break;
   1.429 +
   1.430 +        obs->AddEvent(
   1.431 +                nsString(curEvent->Data()).get(),
   1.432 +                nsString(curEvent->Characters()).get(),
   1.433 +                curEvent->Time(),
   1.434 +                nsString(curEvent->CharactersExtra()).get()
   1.435 +                );
   1.436 +        break;
   1.437 +    }
   1.438 +
   1.439 +    case AndroidGeckoEvent::LOAD_URI: {
   1.440 +        nsCOMPtr<nsICommandLineRunner> cmdline
   1.441 +            (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
   1.442 +        if (!cmdline)
   1.443 +            break;
   1.444 +
   1.445 +        if (curEvent->Characters().Length() == 0)
   1.446 +            break;
   1.447 +
   1.448 +        char *uri = ToNewUTF8String(curEvent->Characters());
   1.449 +        if (!uri)
   1.450 +            break;
   1.451 +
   1.452 +        char *flag = ToNewUTF8String(curEvent->CharactersExtra());
   1.453 +
   1.454 +        const char *argv[4] = {
   1.455 +            "dummyappname",
   1.456 +            "-url",
   1.457 +            uri,
   1.458 +            flag ? flag : ""
   1.459 +        };
   1.460 +        nsresult rv = cmdline->Init(4, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO);
   1.461 +        if (NS_SUCCEEDED(rv))
   1.462 +            cmdline->Run();
   1.463 +        nsMemory::Free(uri);
   1.464 +        if (flag)
   1.465 +            nsMemory::Free(flag);
   1.466 +        break;
   1.467 +    }
   1.468 +
   1.469 +    case AndroidGeckoEvent::SIZE_CHANGED: {
   1.470 +        // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event
   1.471 +        if (curEvent != gLastSizeChange) {
   1.472 +            gLastSizeChange = AndroidGeckoEvent::CopyResizeEvent(curEvent);
   1.473 +        }
   1.474 +        nsWindow::OnGlobalAndroidEvent(curEvent);
   1.475 +        break;
   1.476 +    }
   1.477 +
   1.478 +    case AndroidGeckoEvent::VISITED: {
   1.479 +#ifdef MOZ_ANDROID_HISTORY
   1.480 +        nsCOMPtr<IHistory> history = services::GetHistoryService();
   1.481 +        nsCOMPtr<nsIURI> visitedURI;
   1.482 +        if (history &&
   1.483 +            NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI),
   1.484 +                                   nsString(curEvent->Characters())))) {
   1.485 +            history->NotifyVisited(visitedURI);
   1.486 +        }
   1.487 +#endif
   1.488 +        break;
   1.489 +    }
   1.490 +
   1.491 +    case AndroidGeckoEvent::NETWORK_CHANGED: {
   1.492 +        hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->ConnectionType(),
   1.493 +                                                         curEvent->IsWifi(),
   1.494 +                                                         curEvent->DHCPGateway()));
   1.495 +        break;
   1.496 +    }
   1.497 +
   1.498 +    case AndroidGeckoEvent::SCREENORIENTATION_CHANGED: {
   1.499 +        nsresult rv;
   1.500 +        nsCOMPtr<nsIScreenManager> screenMgr =
   1.501 +            do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
   1.502 +        if (NS_FAILED(rv)) {
   1.503 +            NS_ERROR("Can't find nsIScreenManager!");
   1.504 +            break;
   1.505 +        }
   1.506 +
   1.507 +        nsIntRect rect;
   1.508 +        int32_t colorDepth, pixelDepth;
   1.509 +        dom::ScreenOrientation orientation;
   1.510 +        nsCOMPtr<nsIScreen> screen;
   1.511 +
   1.512 +        screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
   1.513 +        screen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
   1.514 +        screen->GetColorDepth(&colorDepth);
   1.515 +        screen->GetPixelDepth(&pixelDepth);
   1.516 +        orientation =
   1.517 +            static_cast<dom::ScreenOrientation>(curEvent->ScreenOrientation());
   1.518 +
   1.519 +        hal::NotifyScreenConfigurationChange(
   1.520 +            hal::ScreenConfiguration(rect, orientation, colorDepth, pixelDepth));
   1.521 +        break;
   1.522 +    }
   1.523 +
   1.524 +    case AndroidGeckoEvent::CALL_OBSERVER:
   1.525 +    {
   1.526 +        nsCOMPtr<nsIObserver> observer;
   1.527 +        mObserversHash.Get(curEvent->Characters(), getter_AddRefs(observer));
   1.528 +
   1.529 +        if (observer) {
   1.530 +            observer->Observe(nullptr, NS_ConvertUTF16toUTF8(curEvent->CharactersExtra()).get(),
   1.531 +                              nsString(curEvent->Data()).get());
   1.532 +        } else {
   1.533 +            ALOG("Call_Observer event: Observer was not found!");
   1.534 +        }
   1.535 +
   1.536 +        break;
   1.537 +    }
   1.538 +
   1.539 +    case AndroidGeckoEvent::REMOVE_OBSERVER:
   1.540 +        mObserversHash.Remove(curEvent->Characters());
   1.541 +        break;
   1.542 +
   1.543 +    case AndroidGeckoEvent::ADD_OBSERVER:
   1.544 +        AddObserver(curEvent->Characters(), curEvent->Observer());
   1.545 +        break;
   1.546 +
   1.547 +    case AndroidGeckoEvent::PREFERENCES_GET:
   1.548 +    case AndroidGeckoEvent::PREFERENCES_OBSERVE: {
   1.549 +        const nsTArray<nsString> &prefNames = curEvent->PrefNames();
   1.550 +        size_t count = prefNames.Length();
   1.551 +        nsAutoArrayPtr<const char16_t*> prefNamePtrs(new const char16_t*[count]);
   1.552 +        for (size_t i = 0; i < count; ++i) {
   1.553 +            prefNamePtrs[i] = prefNames[i].get();
   1.554 +        }
   1.555 +
   1.556 +        if (curEvent->Type() == AndroidGeckoEvent::PREFERENCES_GET) {
   1.557 +            mBrowserApp->GetPreferences(curEvent->RequestId(), prefNamePtrs, count);
   1.558 +        } else {
   1.559 +            mBrowserApp->ObservePreferences(curEvent->RequestId(), prefNamePtrs, count);
   1.560 +        }
   1.561 +        break;
   1.562 +    }
   1.563 +
   1.564 +    case AndroidGeckoEvent::PREFERENCES_REMOVE_OBSERVERS:
   1.565 +        mBrowserApp->RemovePreferenceObservers(curEvent->RequestId());
   1.566 +        break;
   1.567 +
   1.568 +    case AndroidGeckoEvent::LOW_MEMORY:
   1.569 +        // TODO hook in memory-reduction stuff for different levels here
   1.570 +        if (curEvent->MetaState() >= AndroidGeckoEvent::MEMORY_PRESSURE_MEDIUM) {
   1.571 +            nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.572 +            if (os) {
   1.573 +                os->NotifyObservers(nullptr,
   1.574 +                                    "memory-pressure",
   1.575 +                                    MOZ_UTF16("low-memory"));
   1.576 +            }
   1.577 +        }
   1.578 +        break;
   1.579 +
   1.580 +    case AndroidGeckoEvent::NETWORK_LINK_CHANGE:
   1.581 +    {
   1.582 +        nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.583 +        if (os) {
   1.584 +            os->NotifyObservers(nullptr,
   1.585 +                                NS_NETWORK_LINK_TOPIC,
   1.586 +                                nsString(curEvent->Characters()).get());
   1.587 +        }
   1.588 +        break;
   1.589 +    }
   1.590 +
   1.591 +    case AndroidGeckoEvent::TELEMETRY_HISTOGRAM_ADD:
   1.592 +        Telemetry::Accumulate(NS_ConvertUTF16toUTF8(curEvent->Characters()).get(),
   1.593 +                              curEvent->Count());
   1.594 +        break;
   1.595 +
   1.596 +    case AndroidGeckoEvent::NOOP:
   1.597 +        break;
   1.598 +
   1.599 +    default:
   1.600 +        nsWindow::OnGlobalAndroidEvent(curEvent);
   1.601 +        break;
   1.602 +    }
   1.603 +
   1.604 +    if (curEvent->AckNeeded()) {
   1.605 +        mozilla::widget::android::GeckoAppShell::AcknowledgeEvent();
   1.606 +    }
   1.607 +
   1.608 +    EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type());
   1.609 +
   1.610 +    return true;
   1.611 +}
   1.612 +
   1.613 +void
   1.614 +nsAppShell::ResendLastResizeEvent(nsWindow* aDest) {
   1.615 +    if (gLastSizeChange) {
   1.616 +        nsWindow::OnGlobalAndroidEvent(gLastSizeChange);
   1.617 +    }
   1.618 +}
   1.619 +
   1.620 +AndroidGeckoEvent*
   1.621 +nsAppShell::PopNextEvent()
   1.622 +{
   1.623 +    AndroidGeckoEvent *ae = nullptr;
   1.624 +    MutexAutoLock lock(mQueueLock);
   1.625 +    if (mEventQueue.Length()) {
   1.626 +        ae = mEventQueue[0];
   1.627 +        mEventQueue.RemoveElementAt(0);
   1.628 +        if (mQueuedDrawEvent == ae) {
   1.629 +            mQueuedDrawEvent = nullptr;
   1.630 +        } else if (mQueuedViewportEvent == ae) {
   1.631 +            mQueuedViewportEvent = nullptr;
   1.632 +        }
   1.633 +    }
   1.634 +
   1.635 +    return ae;
   1.636 +}
   1.637 +
   1.638 +AndroidGeckoEvent*
   1.639 +nsAppShell::PeekNextEvent()
   1.640 +{
   1.641 +    AndroidGeckoEvent *ae = nullptr;
   1.642 +    MutexAutoLock lock(mQueueLock);
   1.643 +    if (mEventQueue.Length()) {
   1.644 +        ae = mEventQueue[0];
   1.645 +    }
   1.646 +
   1.647 +    return ae;
   1.648 +}
   1.649 +
   1.650 +void
   1.651 +nsAppShell::PostEvent(AndroidGeckoEvent *ae)
   1.652 +{
   1.653 +    {
   1.654 +        // set this to true when inserting events that we can coalesce
   1.655 +        // viewport events across. this is effectively maintaining a whitelist
   1.656 +        // of events that are unaffected by viewport changes.
   1.657 +        bool allowCoalescingNextViewport = false;
   1.658 +
   1.659 +        MutexAutoLock lock(mQueueLock);
   1.660 +        EVLOG("nsAppShell::PostEvent %p %d", ae, ae->Type());
   1.661 +        switch (ae->Type()) {
   1.662 +        case AndroidGeckoEvent::COMPOSITOR_CREATE:
   1.663 +        case AndroidGeckoEvent::COMPOSITOR_PAUSE:
   1.664 +        case AndroidGeckoEvent::COMPOSITOR_RESUME:
   1.665 +            // Give priority to these events, but maintain their order wrt each other.
   1.666 +            {
   1.667 +                uint32_t i = 0;
   1.668 +                while (i < mEventQueue.Length() &&
   1.669 +                       (mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_CREATE ||
   1.670 +                        mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_PAUSE ||
   1.671 +                        mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_RESUME)) {
   1.672 +                    i++;
   1.673 +                }
   1.674 +                EVLOG("nsAppShell: Inserting compositor event %d at position %d to maintain priority order", ae->Type(), i);
   1.675 +                mEventQueue.InsertElementAt(i, ae);
   1.676 +            }
   1.677 +            break;
   1.678 +
   1.679 +        case AndroidGeckoEvent::DRAW:
   1.680 +            if (mQueuedDrawEvent) {
   1.681 +#if defined(DEBUG) || defined(FORCE_ALOG)
   1.682 +                // coalesce this new draw event with the one already in the queue
   1.683 +                const nsIntRect& oldRect = mQueuedDrawEvent->Rect();
   1.684 +                const nsIntRect& newRect = ae->Rect();
   1.685 +                nsIntRect combinedRect = oldRect.Union(newRect);
   1.686 +
   1.687 +                // XXX We may want to consider using regions instead of rectangles.
   1.688 +                //     Print an error if we're upload a lot more than we would
   1.689 +                //     if we handled this as two separate events.
   1.690 +                int combinedArea = (oldRect.width * oldRect.height) +
   1.691 +                                   (newRect.width * newRect.height);
   1.692 +                int boundsArea = combinedRect.width * combinedRect.height;
   1.693 +                if (boundsArea > combinedArea * 8)
   1.694 +                    ALOG("nsAppShell: Area of bounds greatly exceeds combined area: %d > %d",
   1.695 +                         boundsArea, combinedArea);
   1.696 +#endif
   1.697 +
   1.698 +                // coalesce into the new draw event rather than the queued one because
   1.699 +                // it is not always safe to move draws earlier in the queue; there may
   1.700 +                // be events between the two draws that affect scroll position or something.
   1.701 +                ae->UnionRect(mQueuedDrawEvent->Rect());
   1.702 +
   1.703 +                EVLOG("nsAppShell: Coalescing previous DRAW event at %p into new DRAW event %p", mQueuedDrawEvent, ae);
   1.704 +                mEventQueue.RemoveElement(mQueuedDrawEvent);
   1.705 +                delete mQueuedDrawEvent;
   1.706 +            }
   1.707 +
   1.708 +            if (!mAllowCoalescingNextDraw) {
   1.709 +                // if we're not allowing coalescing of this draw event, then
   1.710 +                // don't set mQueuedDrawEvent to point to this; that way the
   1.711 +                // next draw event that comes in won't kill this one.
   1.712 +                mAllowCoalescingNextDraw = true;
   1.713 +                mQueuedDrawEvent = nullptr;
   1.714 +            } else {
   1.715 +                mQueuedDrawEvent = ae;
   1.716 +            }
   1.717 +
   1.718 +            allowCoalescingNextViewport = true;
   1.719 +
   1.720 +            mEventQueue.AppendElement(ae);
   1.721 +            break;
   1.722 +
   1.723 +        case AndroidGeckoEvent::VIEWPORT:
   1.724 +            if (mQueuedViewportEvent) {
   1.725 +                // drop the previous viewport event now that we have a new one
   1.726 +                EVLOG("nsAppShell: Dropping old viewport event at %p in favour of new VIEWPORT event %p", mQueuedViewportEvent, ae);
   1.727 +                mEventQueue.RemoveElement(mQueuedViewportEvent);
   1.728 +                delete mQueuedViewportEvent;
   1.729 +            }
   1.730 +            mQueuedViewportEvent = ae;
   1.731 +            // temporarily turn off draw-coalescing, so that we process a draw
   1.732 +            // event as soon as possible after a viewport change
   1.733 +            mAllowCoalescingNextDraw = false;
   1.734 +            allowCoalescingNextViewport = true;
   1.735 +
   1.736 +            mEventQueue.AppendElement(ae);
   1.737 +            break;
   1.738 +
   1.739 +        case AndroidGeckoEvent::MOTION_EVENT:
   1.740 +            if (ae->Action() == AndroidMotionEvent::ACTION_MOVE && mAllowCoalescingTouches) {
   1.741 +                int len = mEventQueue.Length();
   1.742 +                if (len > 0) {
   1.743 +                    AndroidGeckoEvent* event = mEventQueue[len - 1];
   1.744 +                    if (event->Type() == AndroidGeckoEvent::MOTION_EVENT && event->Action() == AndroidMotionEvent::ACTION_MOVE) {
   1.745 +                        // consecutive motion-move events; drop the last one before adding the new one
   1.746 +                        EVLOG("nsAppShell: Dropping old move event at %p in favour of new move event %p", event, ae);
   1.747 +                        mEventQueue.RemoveElementAt(len - 1);
   1.748 +                        delete event;
   1.749 +                    }
   1.750 +                }
   1.751 +            }
   1.752 +            mEventQueue.AppendElement(ae);
   1.753 +            break;
   1.754 +
   1.755 +        case AndroidGeckoEvent::NATIVE_POKE:
   1.756 +            allowCoalescingNextViewport = true;
   1.757 +            // fall through
   1.758 +
   1.759 +        default:
   1.760 +            mEventQueue.AppendElement(ae);
   1.761 +            break;
   1.762 +        }
   1.763 +
   1.764 +        // if the event wasn't on our whitelist then reset mQueuedViewportEvent
   1.765 +        // so that we don't coalesce future viewport events into the last viewport
   1.766 +        // event we added
   1.767 +        if (!allowCoalescingNextViewport)
   1.768 +            mQueuedViewportEvent = nullptr;
   1.769 +    }
   1.770 +    NotifyNativeEvent();
   1.771 +}
   1.772 +
   1.773 +void
   1.774 +nsAppShell::OnResume()
   1.775 +{
   1.776 +}
   1.777 +
   1.778 +nsresult
   1.779 +nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver)
   1.780 +{
   1.781 +    NS_ASSERTION(aObserver != nullptr, "nsAppShell::AddObserver: aObserver is null!");
   1.782 +    mObserversHash.Put(aObserverKey, aObserver);
   1.783 +    return NS_OK;
   1.784 +}
   1.785 +
   1.786 +// Used by IPC code
   1.787 +namespace mozilla {
   1.788 +
   1.789 +bool ProcessNextEvent()
   1.790 +{
   1.791 +    return nsAppShell::gAppShell->ProcessNextNativeEvent(true) ? true : false;
   1.792 +}
   1.793 +
   1.794 +void NotifyEvent()
   1.795 +{
   1.796 +    nsAppShell::gAppShell->NotifyNativeEvent();
   1.797 +}
   1.798 +
   1.799 +}

mercurial