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