widget/android/nsAppShell.cpp

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

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

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

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 }

mercurial