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