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 +}