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"
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"
35 #include "AndroidBridge.h"
36 #include <android/log.h>
40 #include "mozilla/dom/ScreenOrientation.h"
43 #ifdef MOZ_ANDROID_HISTORY
44 #include "nsNetUtil.h"
53 #ifdef DEBUG_ANDROID_EVENTS
54 #define EVLOG(args...) ALOG(args)
56 #define EVLOG(args...) do { } while (0)
59 using namespace mozilla
;
62 PRLogModuleInfo
*gWidgetLog
= nullptr;
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
{
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
));
84 AndroidBridge::Bridge()->SendThumbnail(buffer
, mTabId
, false);
85 return NS_ERROR_FAILURE
;
88 tab
->GetWindow(getter_AddRefs(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
));
101 nsCOMPtr
<nsIAndroidBrowserApp
> mBrowserApp
;
102 nsTArray
<nsIntPoint
> mPoints
;
104 nsRefPtr
<RefCountedJavaObject
> mBuffer
;
107 class WakeLockListener
: public nsIDOMMozWakeLockListener
{
111 nsresult
Callback(const nsAString
& topic
, const nsAString
& state
) {
112 AndroidBridge::Bridge()->NotifyWakeLockChanged(topic
, state
);
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)
131 if (XRE_GetProcessType() != GeckoProcessType_Default
) {
135 sPowerManagerService
= do_GetService(POWERMANAGERSERVICE_CONTRACTID
);
137 if (sPowerManagerService
) {
138 sWakeLockListener
= new WakeLockListener();
140 NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
145 nsAppShell::~nsAppShell()
149 if (sPowerManagerService
) {
150 sPowerManagerService
->RemoveWakeLockListener(sWakeLockListener
);
152 sPowerManagerService
= nullptr;
153 sWakeLockListener
= nullptr;
158 nsAppShell::NotifyNativeEvent()
160 MutexAutoLock
lock(mCondLock
);
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
[] = {
170 PREFNAME_COALESCE_TOUCHES
,
179 gWidgetLog
= PR_NewLogModule("Widget");
182 mObserversHash
.Init();
184 nsresult rv
= nsBaseAppShell::Init();
185 AndroidBridge
* bridge
= AndroidBridge::Bridge();
187 nsCOMPtr
<nsIObserverService
> obsServ
=
188 mozilla::services::GetObserverService();
190 obsServ
->AddObserver(this, "xpcom-shutdown", false);
193 if (sPowerManagerService
)
194 sPowerManagerService
->AddWakeLockListener(sWakeLockListener
);
199 Preferences::AddStrongObservers(this, kObservedPrefs
);
202 rv
= Preferences::GetBool(PREFNAME_MATCH_OS
, &match
);
203 NS_ENSURE_SUCCESS(rv
, rv
);
206 bridge
->SetSelectedLocale(EmptyString());
211 rv
= Preferences::GetLocalizedString(PREFNAME_UA_LOCALE
, &locale
);
213 rv
= Preferences::GetString(PREFNAME_UA_LOCALE
, &locale
);
216 bridge
->SetSelectedLocale(locale
);
217 mAllowCoalescingTouches
= Preferences::GetBool(PREFNAME_COALESCE_TOUCHES
, true);
222 nsAppShell::Observe(nsISupports
* aSubject
,
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();
244 nsresult rv
= Preferences::GetBool(PREFNAME_MATCH_OS
, &match
);
245 NS_ENSURE_SUCCESS(rv
, rv
);
248 bridge
->SetSelectedLocale(EmptyString());
253 if (NS_FAILED(Preferences::GetLocalizedString(PREFNAME_UA_LOCALE
,
255 locale
= Preferences::GetString(PREFNAME_UA_LOCALE
);
258 bridge
->SetSelectedLocale(locale
);
260 mAllowCoalescingTouches
= Preferences::GetBool(PREFNAME_COALESCE_TOUCHES
, true);
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
));
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)
291 EVLOG("nsAppShell: waiting on mQueueCond");
293 mQueueCond
.Wait(PR_MillisecondsToInterval(10000));
295 EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1
-t0
)/1000);
300 curEvent
= PopNextEvent();
307 EVLOG("nsAppShell: event %p %d", (void*)curEvent
.get(), curEvent
->Type());
309 switch (curEvent
->Type()) {
310 case AndroidGeckoEvent::NATIVE_POKE
:
311 NativeEventCallback();
314 case AndroidGeckoEvent::SENSOR_EVENT
:
316 InfallibleTArray
<float> values
;
317 mozilla::hal::SensorType type
= (mozilla::hal::SensorType
) curEvent
->Flags();
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());
330 case hal::SENSOR_LIGHT
:
331 values
.AppendElement(curEvent
->X());
335 __android_log_print(ANDROID_LOG_ERROR
,
336 "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d",
340 const hal::SensorAccuracyType
&accuracy
= (hal::SensorAccuracyType
) curEvent
->MetaState();
341 hal::SensorData
sdata(type
, PR_Now(), values
, accuracy
);
342 hal::NotifySensorChange(sdata
);
346 case AndroidGeckoEvent::LOCATION_EVENT
: {
347 if (!gLocationCallback
)
350 nsGeoPosition
* p
= curEvent
->GeoPosition();
352 gLocationCallback
->Update(curEvent
->GeoPosition());
354 NS_WARNING("Received location event without geoposition!");
358 case AndroidGeckoEvent::ACTIVITY_STOPPING
: {
359 if (curEvent
->Flags() > 0)
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);
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");
382 appSvc
->Quit(nsIAppStartup::eForceQuit
);
386 case AndroidGeckoEvent::ACTIVITY_PAUSING
: {
387 if (curEvent
->Flags() == 0) {
388 // We aren't transferring to one of our own activities, so set
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
404 nsIPrefService
* prefs
= Preferences::GetService();
406 // reset the crash loop state
407 nsCOMPtr
<nsIPrefBranch
> prefBranch
;
408 prefs
->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch
));
410 prefBranch
->SetIntPref("recent_crashes", 0);
412 prefs
->SavePrefFile(nullptr);
418 case AndroidGeckoEvent::ACTIVITY_START
: {
419 if (curEvent
->Flags() > 0)
422 nsCOMPtr
<nsIObserverService
> obsServ
=
423 mozilla::services::GetObserverService();
424 obsServ
->NotifyObservers(nullptr, "application-foreground", nullptr);
429 case AndroidGeckoEvent::THUMBNAIL
: {
433 AndroidBridge
* bridge
= AndroidBridge::Bridge();
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
));
445 case AndroidGeckoEvent::VIEWPORT
:
446 case AndroidGeckoEvent::BROADCAST
: {
448 if (curEvent
->Characters().Length() == 0)
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());
461 case AndroidGeckoEvent::LOAD_URI
: {
462 nsCOMPtr
<nsICommandLineRunner
> cmdline
463 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
467 if (curEvent
->Characters().Length() == 0)
470 char *uri
= ToNewUTF8String(curEvent
->Characters());
474 char *flag
= ToNewUTF8String(curEvent
->CharactersExtra());
476 const char *argv
[4] = {
482 nsresult rv
= cmdline
->Init(4, argv
, nullptr, nsICommandLine::STATE_REMOTE_AUTO
);
483 if (NS_SUCCEEDED(rv
))
487 nsMemory::Free(flag
);
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
);
500 case AndroidGeckoEvent::VISITED
: {
501 #ifdef MOZ_ANDROID_HISTORY
502 nsCOMPtr
<IHistory
> history
= services::GetHistoryService();
503 nsCOMPtr
<nsIURI
> visitedURI
;
505 NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI
),
506 nsString(curEvent
->Characters())))) {
507 history
->NotifyVisited(visitedURI
);
513 case AndroidGeckoEvent::NETWORK_CHANGED
: {
514 hal::NotifyNetworkChange(hal::NetworkInformation(curEvent
->Bandwidth(),
515 curEvent
->CanBeMetered()));
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
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);
536 case AndroidGeckoEvent::SCREENORIENTATION_CHANGED
: {
538 nsCOMPtr
<nsIScreenManager
> screenMgr
=
539 do_GetService("@mozilla.org/gfx/screenmanager;1", &rv
);
541 NS_ERROR("Can't find nsIScreenManager!");
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
);
555 static_cast<dom::ScreenOrientation
>(curEvent
->ScreenOrientation());
557 hal::NotifyScreenConfigurationChange(
558 hal::ScreenConfiguration(rect
, orientation
, colorDepth
, pixelDepth
));
563 nsWindow::OnGlobalAndroidEvent(curEvent
);
566 EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent
.get(), curEvent
->Type());
572 nsAppShell::ResendLastResizeEvent(nsWindow
* aDest
) {
573 if (gLastSizeChange
) {
574 nsWindow::OnGlobalAndroidEvent(gLastSizeChange
);
579 nsAppShell::PopNextEvent()
581 AndroidGeckoEvent
*ae
= nullptr;
582 MutexAutoLock
lock(mQueueLock
);
583 if (mEventQueue
.Length()) {
585 mEventQueue
.RemoveElementAt(0);
586 if (mQueuedDrawEvent
== ae
) {
587 mQueuedDrawEvent
= nullptr;
588 } else if (mQueuedViewportEvent
== ae
) {
589 mQueuedViewportEvent
= nullptr;
597 nsAppShell::PeekNextEvent()
599 AndroidGeckoEvent
*ae
= nullptr;
600 MutexAutoLock
lock(mQueueLock
);
601 if (mEventQueue
.Length()) {
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
);
635 case AndroidGeckoEvent::COMPOSITOR_PAUSE
:
636 case AndroidGeckoEvent::COMPOSITOR_RESUME
:
637 // Give priority to these events, but maintain their order wrt each other.
640 while (i
< mEventQueue
.Length() &&
641 (mEventQueue
[i
]->Type() == AndroidGeckoEvent::COMPOSITOR_PAUSE
||
642 mEventQueue
[i
]->Type() == AndroidGeckoEvent::COMPOSITOR_RESUME
)) {
645 EVLOG("nsAppShell: Inserting compositor event %d at position %d to maintain priority order", ae
->Type(), i
);
646 mEventQueue
.InsertElementAt(i
, ae
);
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
);
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;
686 mQueuedDrawEvent
= ae
;
689 allowCoalescingNextViewport
= true;
691 mEventQueue
.AppendElement(ae
);
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
);
710 case AndroidGeckoEvent::MOTION_EVENT
:
711 if (ae
->Action() == AndroidMotionEvent::ACTION_MOVE
&& mAllowCoalescingTouches
) {
712 int len
= mEventQueue
.Length();
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);
723 mEventQueue
.AppendElement(ae
);
726 case AndroidGeckoEvent::NATIVE_POKE
:
727 allowCoalescingNextViewport
= true;
731 mEventQueue
.AppendElement(ae
);
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
738 if (!allowCoalescingNextViewport
)
739 mQueuedViewportEvent
= nullptr;
745 nsAppShell::OnResume()
750 nsAppShell::AddObserver(const nsAString
&aObserverKey
, nsIObserver
*aObserver
)
752 NS_ASSERTION(aObserver
!= nullptr, "nsAppShell::AddObserver: aObserver is null!");
753 mObserversHash
.Put(aObserverKey
, aObserver
);
758 * The XPCOM event that will call the observer on the main thread.
760 class ObserverCaller
: public nsRunnable
{
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!");
768 ALOG("ObserverCaller::Run: observer = %p, topic = '%s')",
769 (nsIObserver
*)mObserver
, mTopic
.get());
770 mObserver
->Observe(nullptr, mTopic
.get(), mData
.get());
775 nsCOMPtr
<nsIObserver
> mObserver
;
781 nsAppShell::CallObserver(const nsAString
&aObserverKey
, const nsAString
&aTopic
, const nsAString
&aData
)
783 nsCOMPtr
<nsIObserver
> observer
;
784 mObserversHash
.Get(aObserverKey
, getter_AddRefs(observer
));
787 ALOG("nsAppShell::CallObserver: Observer was not found!");
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());
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
);
807 nsAppShell::RemoveObserver(const nsAString
&aObserverKey
)
809 mObserversHash
.Remove(aObserverKey
);
812 // NotifyObservers support. NotifyObservers only works on main thread.
814 class NotifyObserversCaller
: public nsRunnable
{
816 NotifyObserversCaller(nsISupports
*aSupports
,
817 const char *aTopic
, const PRUnichar
*aData
) :
818 mSupports(aSupports
), mTopic(aTopic
), mData(aData
) {
822 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
824 os
->NotifyObservers(mSupports
, mTopic
.get(), mData
.get());
830 nsCOMPtr
<nsISupports
> mSupports
;
836 nsAppShell::NotifyObservers(nsISupports
*aSupports
,
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
);
849 bool ProcessNextEvent()
851 return nsAppShell::gAppShell
->ProcessNextNativeEvent(true) ? true : false;
856 nsAppShell::gAppShell
->NotifyNativeEvent();