Bug 835381 - Update libnestegg to 38c83d9d4c0c5c84373aa285bd30094a12d6b6f6. r=kinetik
[gecko.git] / widget / android / nsAppShell.cpp
blobbba4963d1075ccdf79053e11a49749e3e55ea790
1 /* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // Make sure the order of included headers
7 #include "base/basictypes.h"
8 #include "nspr/prtypes.h"
9 #include "base/message_loop.h"
10 #include "base/task.h"
12 #include "mozilla/Hal.h"
13 #include "nsAppShell.h"
14 #include "nsWindow.h"
15 #include "nsThreadUtils.h"
16 #include "nsICommandLineRunner.h"
17 #include "nsIObserverService.h"
18 #include "nsIAppStartup.h"
19 #include "nsIGeolocationProvider.h"
20 #include "nsCacheService.h"
21 #include "nsIDOMEventListener.h"
22 #include "nsDOMNotifyPaintEvent.h"
23 #include "nsIDOMClientRectList.h"
24 #include "nsIDOMClientRect.h"
25 #include "nsIDOMWakeLockListener.h"
26 #include "nsIPowerManagerService.h"
27 #include "nsFrameManager.h"
29 #include "mozilla/Services.h"
30 #include "mozilla/unused.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/Hal.h"
33 #include "prenv.h"
35 #include "AndroidBridge.h"
36 #include <android/log.h>
37 #include <pthread.h>
38 #include <wchar.h>
40 #include "mozilla/dom/ScreenOrientation.h"
42 #include "sampler.h"
43 #ifdef MOZ_ANDROID_HISTORY
44 #include "nsNetUtil.h"
45 #include "IHistory.h"
46 #endif
48 #ifdef MOZ_LOGGING
49 #define FORCE_PR_LOG
50 #include "prlog.h"
51 #endif
53 #ifdef DEBUG_ANDROID_EVENTS
54 #define EVLOG(args...) ALOG(args)
55 #else
56 #define EVLOG(args...) do { } while (0)
57 #endif
59 using namespace mozilla;
61 #ifdef PR_LOGGING
62 PRLogModuleInfo *gWidgetLog = nullptr;
63 #endif
65 nsIGeolocationUpdate *gLocationCallback = nullptr;
66 nsAutoPtr<mozilla::AndroidGeckoEvent> gLastSizeChange;
68 nsAppShell *nsAppShell::gAppShell = nullptr;
70 NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver)
72 class ThumbnailRunnable : public nsRunnable {
73 public:
74 ThumbnailRunnable(nsIAndroidBrowserApp* aBrowserApp, int aTabId,
75 const nsTArray<nsIntPoint>& aPoints, RefCountedJavaObject* aBuffer):
76 mBrowserApp(aBrowserApp), mPoints(aPoints), mTabId(aTabId), mBuffer(aBuffer) {}
78 virtual nsresult Run() {
79 jobject buffer = mBuffer->GetObject();
80 nsCOMPtr<nsIDOMWindow> domWindow;
81 nsCOMPtr<nsIBrowserTab> tab;
82 mBrowserApp->GetBrowserTab(mTabId, getter_AddRefs(tab));
83 if (!tab) {
84 AndroidBridge::Bridge()->SendThumbnail(buffer, mTabId, false);
85 return NS_ERROR_FAILURE;
88 tab->GetWindow(getter_AddRefs(domWindow));
89 if (!domWindow) {
90 AndroidBridge::Bridge()->SendThumbnail(buffer, mTabId, false);
91 return NS_ERROR_FAILURE;
94 NS_ASSERTION(mPoints.Length() == 1, "Thumbnail event does not have enough coordinates");
96 nsresult rv = AndroidBridge::Bridge()->CaptureThumbnail(domWindow, mPoints[0].x, mPoints[0].y, mTabId, buffer);
97 AndroidBridge::Bridge()->SendThumbnail(buffer, mTabId, NS_SUCCEEDED(rv));
98 return rv;
100 private:
101 nsCOMPtr<nsIAndroidBrowserApp> mBrowserApp;
102 nsTArray<nsIntPoint> mPoints;
103 int mTabId;
104 nsRefPtr<RefCountedJavaObject> mBuffer;
107 class WakeLockListener : public nsIDOMMozWakeLockListener {
108 public:
109 NS_DECL_ISUPPORTS;
111 nsresult Callback(const nsAString& topic, const nsAString& state) {
112 AndroidBridge::Bridge()->NotifyWakeLockChanged(topic, state);
113 return NS_OK;
117 NS_IMPL_ISUPPORTS1(WakeLockListener, nsIDOMMozWakeLockListener)
118 nsCOMPtr<nsIPowerManagerService> sPowerManagerService = nullptr;
119 nsCOMPtr<nsIDOMMozWakeLockListener> sWakeLockListener = nullptr;
121 nsAppShell::nsAppShell()
122 : mQueueLock("nsAppShell.mQueueLock"),
123 mCondLock("nsAppShell.mCondLock"),
124 mQueueCond(mCondLock, "nsAppShell.mQueueCond"),
125 mQueuedDrawEvent(nullptr),
126 mQueuedViewportEvent(nullptr),
127 mAllowCoalescingNextDraw(false)
129 gAppShell = this;
131 if (XRE_GetProcessType() != GeckoProcessType_Default) {
132 return;
135 sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
137 if (sPowerManagerService) {
138 sWakeLockListener = new WakeLockListener();
139 } else {
140 NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
145 nsAppShell::~nsAppShell()
147 gAppShell = nullptr;
149 if (sPowerManagerService) {
150 sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
152 sPowerManagerService = nullptr;
153 sWakeLockListener = nullptr;
157 void
158 nsAppShell::NotifyNativeEvent()
160 MutexAutoLock lock(mCondLock);
161 mQueueCond.Notify();
164 #define PREFNAME_MATCH_OS "intl.locale.matchOS"
165 #define PREFNAME_UA_LOCALE "general.useragent.locale"
166 #define PREFNAME_COALESCE_TOUCHES "dom.event.touch.coalescing.enabled"
167 static const char* kObservedPrefs[] = {
168 PREFNAME_MATCH_OS,
169 PREFNAME_UA_LOCALE,
170 PREFNAME_COALESCE_TOUCHES,
171 nullptr
174 nsresult
175 nsAppShell::Init()
177 #ifdef PR_LOGGING
178 if (!gWidgetLog)
179 gWidgetLog = PR_NewLogModule("Widget");
180 #endif
182 mObserversHash.Init();
184 nsresult rv = nsBaseAppShell::Init();
185 AndroidBridge* bridge = AndroidBridge::Bridge();
187 nsCOMPtr<nsIObserverService> obsServ =
188 mozilla::services::GetObserverService();
189 if (obsServ) {
190 obsServ->AddObserver(this, "xpcom-shutdown", false);
193 if (sPowerManagerService)
194 sPowerManagerService->AddWakeLockListener(sWakeLockListener);
196 if (!bridge)
197 return rv;
199 Preferences::AddStrongObservers(this, kObservedPrefs);
201 bool match;
202 rv = Preferences::GetBool(PREFNAME_MATCH_OS, &match);
203 NS_ENSURE_SUCCESS(rv, rv);
205 if (match) {
206 bridge->SetSelectedLocale(EmptyString());
207 return NS_OK;
210 nsAutoString locale;
211 rv = Preferences::GetLocalizedString(PREFNAME_UA_LOCALE, &locale);
212 if (NS_FAILED(rv)) {
213 rv = Preferences::GetString(PREFNAME_UA_LOCALE, &locale);
216 bridge->SetSelectedLocale(locale);
217 mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
218 return rv;
221 NS_IMETHODIMP
222 nsAppShell::Observe(nsISupports* aSubject,
223 const char* aTopic,
224 const PRUnichar* aData)
226 if (!strcmp(aTopic, "xpcom-shutdown")) {
227 // We need to ensure no observers stick around after XPCOM shuts down
228 // or we'll see crashes, as the app shell outlives XPConnect.
229 mObserversHash.Clear();
230 return nsBaseAppShell::Observe(aSubject, aTopic, aData);
231 } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) && aData && (
232 nsDependentString(aData).Equals(
233 NS_LITERAL_STRING(PREFNAME_UA_LOCALE)) ||
234 nsDependentString(aData).Equals(
235 NS_LITERAL_STRING(PREFNAME_COALESCE_TOUCHES)) ||
236 nsDependentString(aData).Equals(
237 NS_LITERAL_STRING(PREFNAME_MATCH_OS)))) {
238 AndroidBridge* bridge = AndroidBridge::Bridge();
239 if (!bridge) {
240 return NS_OK;
243 bool match;
244 nsresult rv = Preferences::GetBool(PREFNAME_MATCH_OS, &match);
245 NS_ENSURE_SUCCESS(rv, rv);
247 if (match) {
248 bridge->SetSelectedLocale(EmptyString());
249 return NS_OK;
252 nsAutoString locale;
253 if (NS_FAILED(Preferences::GetLocalizedString(PREFNAME_UA_LOCALE,
254 &locale))) {
255 locale = Preferences::GetString(PREFNAME_UA_LOCALE);
258 bridge->SetSelectedLocale(locale);
260 mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
261 return NS_OK;
263 return NS_OK;
266 void
267 nsAppShell::ScheduleNativeEventCallback()
269 EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread());
271 // this is valid to be called from any thread, so do so.
272 PostEvent(new AndroidGeckoEvent(AndroidGeckoEvent::NATIVE_POKE));
275 bool
276 nsAppShell::ProcessNextNativeEvent(bool mayWait)
278 EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait);
280 SAMPLE_LABEL("nsAppShell", "ProcessNextNativeEvent");
281 nsAutoPtr<AndroidGeckoEvent> curEvent;
283 MutexAutoLock lock(mCondLock);
285 curEvent = PopNextEvent();
286 if (!curEvent && mayWait) {
287 SAMPLE_LABEL("nsAppShell::ProcessNextNativeEvent", "Wait");
288 // hmm, should we really hardcode this 10s?
289 #if defined(DEBUG_ANDROID_EVENTS)
290 PRTime t0, t1;
291 EVLOG("nsAppShell: waiting on mQueueCond");
292 t0 = PR_Now();
293 mQueueCond.Wait(PR_MillisecondsToInterval(10000));
294 t1 = PR_Now();
295 EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000);
296 #else
297 mQueueCond.Wait();
298 #endif
300 curEvent = PopNextEvent();
304 if (!curEvent)
305 return false;
307 EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type());
309 switch (curEvent->Type()) {
310 case AndroidGeckoEvent::NATIVE_POKE:
311 NativeEventCallback();
312 break;
314 case AndroidGeckoEvent::SENSOR_EVENT:
316 InfallibleTArray<float> values;
317 mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags();
319 switch (type) {
320 case hal::SENSOR_ORIENTATION:
321 case hal::SENSOR_LINEAR_ACCELERATION:
322 case hal::SENSOR_ACCELERATION:
323 case hal::SENSOR_GYROSCOPE:
324 case hal::SENSOR_PROXIMITY:
325 values.AppendElement(curEvent->X());
326 values.AppendElement(curEvent->Y());
327 values.AppendElement(curEvent->Z());
328 break;
330 case hal::SENSOR_LIGHT:
331 values.AppendElement(curEvent->X());
332 break;
334 default:
335 __android_log_print(ANDROID_LOG_ERROR,
336 "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d",
337 type);
340 const hal::SensorAccuracyType &accuracy = (hal::SensorAccuracyType) curEvent->MetaState();
341 hal::SensorData sdata(type, PR_Now(), values, accuracy);
342 hal::NotifySensorChange(sdata);
344 break;
346 case AndroidGeckoEvent::LOCATION_EVENT: {
347 if (!gLocationCallback)
348 break;
350 nsGeoPosition* p = curEvent->GeoPosition();
351 if (p)
352 gLocationCallback->Update(curEvent->GeoPosition());
353 else
354 NS_WARNING("Received location event without geoposition!");
355 break;
358 case AndroidGeckoEvent::ACTIVITY_STOPPING: {
359 if (curEvent->Flags() > 0)
360 break;
362 nsCOMPtr<nsIObserverService> obsServ =
363 mozilla::services::GetObserverService();
364 NS_NAMED_LITERAL_STRING(minimize, "heap-minimize");
365 obsServ->NotifyObservers(nullptr, "memory-pressure", minimize.get());
366 obsServ->NotifyObservers(nullptr, "application-background", nullptr);
368 break;
371 case AndroidGeckoEvent::ACTIVITY_SHUTDOWN: {
372 nsCOMPtr<nsIObserverService> obsServ =
373 mozilla::services::GetObserverService();
374 NS_NAMED_LITERAL_STRING(context, "shutdown-persist");
375 obsServ->NotifyObservers(nullptr, "quit-application-granted", nullptr);
376 obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr);
377 obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context.get());
378 obsServ->NotifyObservers(nullptr, "profile-change-teardown", context.get());
379 obsServ->NotifyObservers(nullptr, "profile-before-change", context.get());
380 nsCOMPtr<nsIAppStartup> appSvc = do_GetService("@mozilla.org/toolkit/app-startup;1");
381 if (appSvc)
382 appSvc->Quit(nsIAppStartup::eForceQuit);
383 break;
386 case AndroidGeckoEvent::ACTIVITY_PAUSING: {
387 if (curEvent->Flags() == 0) {
388 // We aren't transferring to one of our own activities, so set
389 // background status
390 nsCOMPtr<nsIObserverService> obsServ =
391 mozilla::services::GetObserverService();
392 obsServ->NotifyObservers(nullptr, "application-background", nullptr);
394 // If we are OOM killed with the disk cache enabled, the entire
395 // cache will be cleared (bug 105843), so shut down the cache here
396 // and re-init on resume
397 if (nsCacheService::GlobalInstance())
398 nsCacheService::GlobalInstance()->Shutdown();
401 // We really want to send a notification like profile-before-change,
402 // but profile-before-change ends up shutting some things down instead
403 // of flushing data
404 nsIPrefService* prefs = Preferences::GetService();
405 if (prefs) {
406 // reset the crash loop state
407 nsCOMPtr<nsIPrefBranch> prefBranch;
408 prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch));
409 if (prefBranch)
410 prefBranch->SetIntPref("recent_crashes", 0);
412 prefs->SavePrefFile(nullptr);
415 break;
418 case AndroidGeckoEvent::ACTIVITY_START: {
419 if (curEvent->Flags() > 0)
420 break;
422 nsCOMPtr<nsIObserverService> obsServ =
423 mozilla::services::GetObserverService();
424 obsServ->NotifyObservers(nullptr, "application-foreground", nullptr);
426 break;
429 case AndroidGeckoEvent::THUMBNAIL: {
430 if (!mBrowserApp)
431 break;
433 AndroidBridge* bridge = AndroidBridge::Bridge();
434 if (!bridge)
435 break;
437 int32_t tabId = curEvent->MetaState();
438 const nsTArray<nsIntPoint>& points = curEvent->Points();
439 RefCountedJavaObject* buffer = curEvent->ByteBuffer();
440 nsCOMPtr<ThumbnailRunnable> sr = new ThumbnailRunnable(mBrowserApp, tabId, points, buffer);
441 MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableMethod(sr.get(), &ThumbnailRunnable::Run));
442 break;
445 case AndroidGeckoEvent::VIEWPORT:
446 case AndroidGeckoEvent::BROADCAST: {
448 if (curEvent->Characters().Length() == 0)
449 break;
451 nsCOMPtr<nsIObserverService> obsServ =
452 mozilla::services::GetObserverService();
454 const NS_ConvertUTF16toUTF8 topic(curEvent->Characters());
455 const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra());
457 obsServ->NotifyObservers(nullptr, topic.get(), data.get());
458 break;
461 case AndroidGeckoEvent::LOAD_URI: {
462 nsCOMPtr<nsICommandLineRunner> cmdline
463 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
464 if (!cmdline)
465 break;
467 if (curEvent->Characters().Length() == 0)
468 break;
470 char *uri = ToNewUTF8String(curEvent->Characters());
471 if (!uri)
472 break;
474 char *flag = ToNewUTF8String(curEvent->CharactersExtra());
476 const char *argv[4] = {
477 "dummyappname",
478 "-url",
479 uri,
480 flag ? flag : ""
482 nsresult rv = cmdline->Init(4, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO);
483 if (NS_SUCCEEDED(rv))
484 cmdline->Run();
485 nsMemory::Free(uri);
486 if (flag)
487 nsMemory::Free(flag);
488 break;
491 case AndroidGeckoEvent::SIZE_CHANGED: {
492 // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event
493 if (curEvent != gLastSizeChange) {
494 gLastSizeChange = new AndroidGeckoEvent(curEvent);
496 nsWindow::OnGlobalAndroidEvent(curEvent);
497 break;
500 case AndroidGeckoEvent::VISITED: {
501 #ifdef MOZ_ANDROID_HISTORY
502 nsCOMPtr<IHistory> history = services::GetHistoryService();
503 nsCOMPtr<nsIURI> visitedURI;
504 if (history &&
505 NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI),
506 nsString(curEvent->Characters())))) {
507 history->NotifyVisited(visitedURI);
509 #endif
510 break;
513 case AndroidGeckoEvent::NETWORK_CHANGED: {
514 hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->Bandwidth(),
515 curEvent->CanBeMetered()));
516 break;
519 case AndroidGeckoEvent::ACTIVITY_RESUMING: {
520 if (curEvent->Flags() == 0) {
521 // If we are OOM killed with the disk cache enabled, the entire
522 // cache will be cleared (bug 105843), so shut down cache on pause
523 // and re-init here
524 if (nsCacheService::GlobalInstance())
525 nsCacheService::GlobalInstance()->Init();
527 // We didn't return from one of our own activities, so restore
528 // to foreground status
529 nsCOMPtr<nsIObserverService> obsServ =
530 mozilla::services::GetObserverService();
531 obsServ->NotifyObservers(nullptr, "application-foreground", nullptr);
533 break;
536 case AndroidGeckoEvent::SCREENORIENTATION_CHANGED: {
537 nsresult rv;
538 nsCOMPtr<nsIScreenManager> screenMgr =
539 do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
540 if (NS_FAILED(rv)) {
541 NS_ERROR("Can't find nsIScreenManager!");
542 break;
545 nsIntRect rect;
546 int32_t colorDepth, pixelDepth;
547 dom::ScreenOrientation orientation;
548 nsCOMPtr<nsIScreen> screen;
550 screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
551 screen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
552 screen->GetColorDepth(&colorDepth);
553 screen->GetPixelDepth(&pixelDepth);
554 orientation =
555 static_cast<dom::ScreenOrientation>(curEvent->ScreenOrientation());
557 hal::NotifyScreenConfigurationChange(
558 hal::ScreenConfiguration(rect, orientation, colorDepth, pixelDepth));
559 break;
562 default:
563 nsWindow::OnGlobalAndroidEvent(curEvent);
566 EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type());
568 return true;
571 void
572 nsAppShell::ResendLastResizeEvent(nsWindow* aDest) {
573 if (gLastSizeChange) {
574 nsWindow::OnGlobalAndroidEvent(gLastSizeChange);
578 AndroidGeckoEvent*
579 nsAppShell::PopNextEvent()
581 AndroidGeckoEvent *ae = nullptr;
582 MutexAutoLock lock(mQueueLock);
583 if (mEventQueue.Length()) {
584 ae = mEventQueue[0];
585 mEventQueue.RemoveElementAt(0);
586 if (mQueuedDrawEvent == ae) {
587 mQueuedDrawEvent = nullptr;
588 } else if (mQueuedViewportEvent == ae) {
589 mQueuedViewportEvent = nullptr;
593 return ae;
596 AndroidGeckoEvent*
597 nsAppShell::PeekNextEvent()
599 AndroidGeckoEvent *ae = nullptr;
600 MutexAutoLock lock(mQueueLock);
601 if (mEventQueue.Length()) {
602 ae = mEventQueue[0];
605 return ae;
608 void
609 nsAppShell::PostEvent(AndroidGeckoEvent *ae)
612 // set this to true when inserting events that we can coalesce
613 // viewport events across. this is effectively maintaining a whitelist
614 // of events that are unaffected by viewport changes.
615 bool allowCoalescingNextViewport = false;
617 MutexAutoLock lock(mQueueLock);
618 EVLOG("nsAppShell::PostEvent %p %d", ae, ae->Type());
619 switch (ae->Type()) {
620 case AndroidGeckoEvent::SURFACE_DESTROYED:
621 // Give priority to this event, and discard any pending
622 // SURFACE_CREATED events.
623 mEventQueue.InsertElementAt(0, ae);
624 AndroidGeckoEvent *event;
625 for (int i = mEventQueue.Length() - 1; i >= 1; i--) {
626 event = mEventQueue[i];
627 if (event->Type() == AndroidGeckoEvent::SURFACE_CREATED) {
628 EVLOG("nsAppShell: Dropping old SURFACE_CREATED event at %p %d", event, i);
629 mEventQueue.RemoveElementAt(i);
630 delete event;
633 break;
635 case AndroidGeckoEvent::COMPOSITOR_PAUSE:
636 case AndroidGeckoEvent::COMPOSITOR_RESUME:
637 // Give priority to these events, but maintain their order wrt each other.
639 uint32_t i = 0;
640 while (i < mEventQueue.Length() &&
641 (mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_PAUSE ||
642 mEventQueue[i]->Type() == AndroidGeckoEvent::COMPOSITOR_RESUME)) {
643 i++;
645 EVLOG("nsAppShell: Inserting compositor event %d at position %d to maintain priority order", ae->Type(), i);
646 mEventQueue.InsertElementAt(i, ae);
648 break;
650 case AndroidGeckoEvent::DRAW:
651 if (mQueuedDrawEvent) {
652 // coalesce this new draw event with the one already in the queue
653 const nsIntRect& oldRect = mQueuedDrawEvent->Rect();
654 const nsIntRect& newRect = ae->Rect();
655 nsIntRect combinedRect = oldRect.Union(newRect);
657 #if defined(DEBUG) || defined(FORCE_ALOG)
658 // XXX We may want to consider using regions instead of rectangles.
659 // Print an error if we're upload a lot more than we would
660 // if we handled this as two separate events.
661 int combinedArea = (oldRect.width * oldRect.height) +
662 (newRect.width * newRect.height);
663 int boundsArea = combinedRect.width * combinedRect.height;
664 if (boundsArea > combinedArea * 8)
665 ALOG("nsAppShell: Area of bounds greatly exceeds combined area: %d > %d",
666 boundsArea, combinedArea);
667 #endif
669 // coalesce into the new draw event rather than the queued one because
670 // it is not always safe to move draws earlier in the queue; there may
671 // be events between the two draws that affect scroll position or something.
672 ae->Init(AndroidGeckoEvent::DRAW, combinedRect);
674 EVLOG("nsAppShell: Coalescing previous DRAW event at %p into new DRAW event %p", mQueuedDrawEvent, ae);
675 mEventQueue.RemoveElement(mQueuedDrawEvent);
676 delete mQueuedDrawEvent;
679 if (!mAllowCoalescingNextDraw) {
680 // if we're not allowing coalescing of this draw event, then
681 // don't set mQueuedDrawEvent to point to this; that way the
682 // next draw event that comes in won't kill this one.
683 mAllowCoalescingNextDraw = true;
684 mQueuedDrawEvent = nullptr;
685 } else {
686 mQueuedDrawEvent = ae;
689 allowCoalescingNextViewport = true;
691 mEventQueue.AppendElement(ae);
692 break;
694 case AndroidGeckoEvent::VIEWPORT:
695 if (mQueuedViewportEvent) {
696 // drop the previous viewport event now that we have a new one
697 EVLOG("nsAppShell: Dropping old viewport event at %p in favour of new VIEWPORT event %p", mQueuedViewportEvent, ae);
698 mEventQueue.RemoveElement(mQueuedViewportEvent);
699 delete mQueuedViewportEvent;
701 mQueuedViewportEvent = ae;
702 // temporarily turn off draw-coalescing, so that we process a draw
703 // event as soon as possible after a viewport change
704 mAllowCoalescingNextDraw = false;
705 allowCoalescingNextViewport = true;
707 mEventQueue.AppendElement(ae);
708 break;
710 case AndroidGeckoEvent::MOTION_EVENT:
711 if (ae->Action() == AndroidMotionEvent::ACTION_MOVE && mAllowCoalescingTouches) {
712 int len = mEventQueue.Length();
713 if (len > 0) {
714 AndroidGeckoEvent* event = mEventQueue[len - 1];
715 if (event->Type() == AndroidGeckoEvent::MOTION_EVENT && event->Action() == AndroidMotionEvent::ACTION_MOVE) {
716 // consecutive motion-move events; drop the last one before adding the new one
717 EVLOG("nsAppShell: Dropping old move event at %p in favour of new move event %p", event, ae);
718 mEventQueue.RemoveElementAt(len - 1);
719 delete event;
723 mEventQueue.AppendElement(ae);
724 break;
726 case AndroidGeckoEvent::NATIVE_POKE:
727 allowCoalescingNextViewport = true;
728 // fall through
730 default:
731 mEventQueue.AppendElement(ae);
732 break;
735 // if the event wasn't on our whitelist then reset mQueuedViewportEvent
736 // so that we don't coalesce future viewport events into the last viewport
737 // event we added
738 if (!allowCoalescingNextViewport)
739 mQueuedViewportEvent = nullptr;
741 NotifyNativeEvent();
744 void
745 nsAppShell::OnResume()
749 nsresult
750 nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver)
752 NS_ASSERTION(aObserver != nullptr, "nsAppShell::AddObserver: aObserver is null!");
753 mObserversHash.Put(aObserverKey, aObserver);
754 return NS_OK;
758 * The XPCOM event that will call the observer on the main thread.
760 class ObserverCaller : public nsRunnable {
761 public:
762 ObserverCaller(nsIObserver *aObserver, const char *aTopic, const PRUnichar *aData) :
763 mObserver(aObserver), mTopic(aTopic), mData(aData) {
764 NS_ASSERTION(aObserver != nullptr, "ObserverCaller: aObserver is null!");
767 NS_IMETHOD Run() {
768 ALOG("ObserverCaller::Run: observer = %p, topic = '%s')",
769 (nsIObserver*)mObserver, mTopic.get());
770 mObserver->Observe(nullptr, mTopic.get(), mData.get());
771 return NS_OK;
774 private:
775 nsCOMPtr<nsIObserver> mObserver;
776 nsCString mTopic;
777 nsString mData;
780 void
781 nsAppShell::CallObserver(const nsAString &aObserverKey, const nsAString &aTopic, const nsAString &aData)
783 nsCOMPtr<nsIObserver> observer;
784 mObserversHash.Get(aObserverKey, getter_AddRefs(observer));
786 if (!observer) {
787 ALOG("nsAppShell::CallObserver: Observer was not found!");
788 return;
791 const NS_ConvertUTF16toUTF8 sTopic(aTopic);
792 const nsPromiseFlatString& sData = PromiseFlatString(aData);
794 if (NS_IsMainThread()) {
795 // This branch will unlikely be hit, have it just in case
796 observer->Observe(nullptr, sTopic.get(), sData.get());
797 } else {
798 // Java is not running on main thread, so we have to use NS_DispatchToMainThread
799 nsCOMPtr<nsIRunnable> observerCaller = new ObserverCaller(observer, sTopic.get(), sData.get());
800 nsresult rv = NS_DispatchToMainThread(observerCaller);
801 ALOG("NS_DispatchToMainThread result: %d", rv);
802 unused << rv;
806 void
807 nsAppShell::RemoveObserver(const nsAString &aObserverKey)
809 mObserversHash.Remove(aObserverKey);
812 // NotifyObservers support. NotifyObservers only works on main thread.
814 class NotifyObserversCaller : public nsRunnable {
815 public:
816 NotifyObserversCaller(nsISupports *aSupports,
817 const char *aTopic, const PRUnichar *aData) :
818 mSupports(aSupports), mTopic(aTopic), mData(aData) {
821 NS_IMETHOD Run() {
822 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
823 if (os)
824 os->NotifyObservers(mSupports, mTopic.get(), mData.get());
826 return NS_OK;
829 private:
830 nsCOMPtr<nsISupports> mSupports;
831 nsCString mTopic;
832 nsString mData;
835 void
836 nsAppShell::NotifyObservers(nsISupports *aSupports,
837 const char *aTopic,
838 const PRUnichar *aData)
840 // This isn't main thread, so post this to main thread
841 nsCOMPtr<nsIRunnable> caller =
842 new NotifyObserversCaller(aSupports, aTopic, aData);
843 NS_DispatchToMainThread(caller);
846 // Used by IPC code
847 namespace mozilla {
849 bool ProcessNextEvent()
851 return nsAppShell::gAppShell->ProcessNextNativeEvent(true) ? true : false;
854 void NotifyEvent()
856 nsAppShell::gAppShell->NotifyNativeEvent();