|
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/. */ |
|
5 |
|
6 #include "nsAppShell.h" |
|
7 |
|
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" |
|
28 |
|
29 #include "mozilla/Services.h" |
|
30 #include "mozilla/unused.h" |
|
31 #include "mozilla/Preferences.h" |
|
32 #include "mozilla/Hal.h" |
|
33 #include "prenv.h" |
|
34 |
|
35 #include "AndroidBridge.h" |
|
36 #include "AndroidBridgeUtilities.h" |
|
37 #include <android/log.h> |
|
38 #include <pthread.h> |
|
39 #include <wchar.h> |
|
40 |
|
41 #include "mozilla/dom/ScreenOrientation.h" |
|
42 |
|
43 #include "GeckoProfiler.h" |
|
44 #ifdef MOZ_ANDROID_HISTORY |
|
45 #include "nsNetUtil.h" |
|
46 #include "IHistory.h" |
|
47 #endif |
|
48 |
|
49 #ifdef MOZ_LOGGING |
|
50 #define FORCE_PR_LOG |
|
51 #include "prlog.h" |
|
52 #endif |
|
53 |
|
54 #ifdef DEBUG_ANDROID_EVENTS |
|
55 #define EVLOG(args...) ALOG(args) |
|
56 #else |
|
57 #define EVLOG(args...) do { } while (0) |
|
58 #endif |
|
59 |
|
60 using namespace mozilla; |
|
61 |
|
62 #ifdef PR_LOGGING |
|
63 PRLogModuleInfo *gWidgetLog = nullptr; |
|
64 #endif |
|
65 |
|
66 nsIGeolocationUpdate *gLocationCallback = nullptr; |
|
67 nsAutoPtr<mozilla::AndroidGeckoEvent> gLastSizeChange; |
|
68 |
|
69 nsAppShell *nsAppShell::gAppShell = nullptr; |
|
70 |
|
71 NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver) |
|
72 |
|
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) {} |
|
78 |
|
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 } |
|
88 |
|
89 tab->GetWindow(getter_AddRefs(domWindow)); |
|
90 if (!domWindow) { |
|
91 mozilla::widget::android::ThumbnailHelper::SendThumbnail(buffer, mTabId, false); |
|
92 return NS_ERROR_FAILURE; |
|
93 } |
|
94 |
|
95 NS_ASSERTION(mPoints.Length() == 1, "Thumbnail event does not have enough coordinates"); |
|
96 |
|
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 }; |
|
107 |
|
108 class WakeLockListener MOZ_FINAL : public nsIDOMMozWakeLockListener { |
|
109 public: |
|
110 NS_DECL_ISUPPORTS; |
|
111 |
|
112 nsresult Callback(const nsAString& topic, const nsAString& state) { |
|
113 mozilla::widget::android::GeckoAppShell::NotifyWakeLockChanged(topic, state); |
|
114 return NS_OK; |
|
115 } |
|
116 }; |
|
117 |
|
118 NS_IMPL_ISUPPORTS(WakeLockListener, nsIDOMMozWakeLockListener) |
|
119 nsCOMPtr<nsIPowerManagerService> sPowerManagerService = nullptr; |
|
120 StaticRefPtr<WakeLockListener> sWakeLockListener; |
|
121 |
|
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; |
|
131 |
|
132 if (XRE_GetProcessType() != GeckoProcessType_Default) { |
|
133 return; |
|
134 } |
|
135 |
|
136 sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID); |
|
137 |
|
138 if (sPowerManagerService) { |
|
139 sWakeLockListener = new WakeLockListener(); |
|
140 } else { |
|
141 NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!"); |
|
142 } |
|
143 |
|
144 } |
|
145 |
|
146 nsAppShell::~nsAppShell() |
|
147 { |
|
148 gAppShell = nullptr; |
|
149 |
|
150 if (sPowerManagerService) { |
|
151 sPowerManagerService->RemoveWakeLockListener(sWakeLockListener); |
|
152 |
|
153 sPowerManagerService = nullptr; |
|
154 sWakeLockListener = nullptr; |
|
155 } |
|
156 } |
|
157 |
|
158 void |
|
159 nsAppShell::NotifyNativeEvent() |
|
160 { |
|
161 MutexAutoLock lock(mCondLock); |
|
162 mQueueCond.Notify(); |
|
163 } |
|
164 |
|
165 #define PREFNAME_COALESCE_TOUCHES "dom.event.touch.coalescing.enabled" |
|
166 static const char* kObservedPrefs[] = { |
|
167 PREFNAME_COALESCE_TOUCHES, |
|
168 nullptr |
|
169 }; |
|
170 |
|
171 nsresult |
|
172 nsAppShell::Init() |
|
173 { |
|
174 #ifdef PR_LOGGING |
|
175 if (!gWidgetLog) |
|
176 gWidgetLog = PR_NewLogModule("Widget"); |
|
177 #endif |
|
178 |
|
179 nsresult rv = nsBaseAppShell::Init(); |
|
180 nsCOMPtr<nsIObserverService> obsServ = |
|
181 mozilla::services::GetObserverService(); |
|
182 if (obsServ) { |
|
183 obsServ->AddObserver(this, "xpcom-shutdown", false); |
|
184 } |
|
185 |
|
186 if (sPowerManagerService) |
|
187 sPowerManagerService->AddWakeLockListener(sWakeLockListener); |
|
188 |
|
189 Preferences::AddStrongObservers(this, kObservedPrefs); |
|
190 mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true); |
|
191 return rv; |
|
192 } |
|
193 |
|
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 } |
|
212 |
|
213 void |
|
214 nsAppShell::ScheduleNativeEventCallback() |
|
215 { |
|
216 EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread()); |
|
217 |
|
218 // this is valid to be called from any thread, so do so. |
|
219 PostEvent(AndroidGeckoEvent::MakeNativePoke()); |
|
220 } |
|
221 |
|
222 bool |
|
223 nsAppShell::ProcessNextNativeEvent(bool mayWait) |
|
224 { |
|
225 EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait); |
|
226 |
|
227 PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent"); |
|
228 nsAutoPtr<AndroidGeckoEvent> curEvent; |
|
229 { |
|
230 MutexAutoLock lock(mCondLock); |
|
231 |
|
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 |
|
246 |
|
247 curEvent = PopNextEvent(); |
|
248 } |
|
249 } |
|
250 |
|
251 if (!curEvent) |
|
252 return false; |
|
253 |
|
254 EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type()); |
|
255 |
|
256 switch (curEvent->Type()) { |
|
257 case AndroidGeckoEvent::NATIVE_POKE: |
|
258 NativeEventCallback(); |
|
259 break; |
|
260 |
|
261 case AndroidGeckoEvent::SENSOR_EVENT: { |
|
262 InfallibleTArray<float> values; |
|
263 mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); |
|
264 |
|
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; |
|
282 |
|
283 case hal::SENSOR_LIGHT: |
|
284 values.AppendElement(curEvent->X()); |
|
285 break; |
|
286 |
|
287 default: |
|
288 __android_log_print(ANDROID_LOG_ERROR, |
|
289 "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d", |
|
290 type); |
|
291 } |
|
292 |
|
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; |
|
298 |
|
299 case AndroidGeckoEvent::LOCATION_EVENT: { |
|
300 if (!gLocationCallback) |
|
301 break; |
|
302 |
|
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 } |
|
310 |
|
311 case AndroidGeckoEvent::APP_BACKGROUNDING: { |
|
312 nsCOMPtr<nsIObserverService> obsServ = |
|
313 mozilla::services::GetObserverService(); |
|
314 obsServ->NotifyObservers(nullptr, "application-background", nullptr); |
|
315 |
|
316 NS_NAMED_LITERAL_STRING(minimize, "heap-minimize"); |
|
317 obsServ->NotifyObservers(nullptr, "memory-pressure", minimize.get()); |
|
318 |
|
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 } |
|
325 |
|
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); |
|
336 |
|
337 prefs->SavePrefFile(nullptr); |
|
338 } |
|
339 break; |
|
340 } |
|
341 |
|
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 } |
|
349 |
|
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 } |
|
357 |
|
358 case AndroidGeckoEvent::THUMBNAIL: { |
|
359 if (!mBrowserApp) |
|
360 break; |
|
361 |
|
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 } |
|
369 |
|
370 case AndroidGeckoEvent::VIEWPORT: |
|
371 case AndroidGeckoEvent::BROADCAST: { |
|
372 if (curEvent->Characters().Length() == 0) |
|
373 break; |
|
374 |
|
375 nsCOMPtr<nsIObserverService> obsServ = |
|
376 mozilla::services::GetObserverService(); |
|
377 |
|
378 const NS_ConvertUTF16toUTF8 topic(curEvent->Characters()); |
|
379 const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra()); |
|
380 |
|
381 obsServ->NotifyObservers(nullptr, topic.get(), data.get()); |
|
382 break; |
|
383 } |
|
384 |
|
385 case AndroidGeckoEvent::TELEMETRY_UI_SESSION_STOP: { |
|
386 if (curEvent->Characters().Length() == 0) |
|
387 break; |
|
388 |
|
389 nsCOMPtr<nsIUITelemetryObserver> obs; |
|
390 mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); |
|
391 if (!obs) |
|
392 break; |
|
393 |
|
394 obs->StopSession( |
|
395 nsString(curEvent->Characters()).get(), |
|
396 nsString(curEvent->CharactersExtra()).get(), |
|
397 curEvent->Time() |
|
398 ); |
|
399 break; |
|
400 } |
|
401 |
|
402 case AndroidGeckoEvent::TELEMETRY_UI_SESSION_START: { |
|
403 if (curEvent->Characters().Length() == 0) |
|
404 break; |
|
405 |
|
406 nsCOMPtr<nsIUITelemetryObserver> obs; |
|
407 mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); |
|
408 if (!obs) |
|
409 break; |
|
410 |
|
411 obs->StartSession( |
|
412 nsString(curEvent->Characters()).get(), |
|
413 curEvent->Time() |
|
414 ); |
|
415 break; |
|
416 } |
|
417 |
|
418 case AndroidGeckoEvent::TELEMETRY_UI_EVENT: { |
|
419 if (curEvent->Data().Length() == 0) |
|
420 break; |
|
421 |
|
422 nsCOMPtr<nsIUITelemetryObserver> obs; |
|
423 mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); |
|
424 if (!obs) |
|
425 break; |
|
426 |
|
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 } |
|
435 |
|
436 case AndroidGeckoEvent::LOAD_URI: { |
|
437 nsCOMPtr<nsICommandLineRunner> cmdline |
|
438 (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); |
|
439 if (!cmdline) |
|
440 break; |
|
441 |
|
442 if (curEvent->Characters().Length() == 0) |
|
443 break; |
|
444 |
|
445 char *uri = ToNewUTF8String(curEvent->Characters()); |
|
446 if (!uri) |
|
447 break; |
|
448 |
|
449 char *flag = ToNewUTF8String(curEvent->CharactersExtra()); |
|
450 |
|
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 } |
|
465 |
|
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 } |
|
474 |
|
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 } |
|
487 |
|
488 case AndroidGeckoEvent::NETWORK_CHANGED: { |
|
489 hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->ConnectionType(), |
|
490 curEvent->IsWifi(), |
|
491 curEvent->DHCPGateway())); |
|
492 break; |
|
493 } |
|
494 |
|
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 } |
|
503 |
|
504 nsIntRect rect; |
|
505 int32_t colorDepth, pixelDepth; |
|
506 dom::ScreenOrientation orientation; |
|
507 nsCOMPtr<nsIScreen> screen; |
|
508 |
|
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()); |
|
515 |
|
516 hal::NotifyScreenConfigurationChange( |
|
517 hal::ScreenConfiguration(rect, orientation, colorDepth, pixelDepth)); |
|
518 break; |
|
519 } |
|
520 |
|
521 case AndroidGeckoEvent::CALL_OBSERVER: |
|
522 { |
|
523 nsCOMPtr<nsIObserver> observer; |
|
524 mObserversHash.Get(curEvent->Characters(), getter_AddRefs(observer)); |
|
525 |
|
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 } |
|
532 |
|
533 break; |
|
534 } |
|
535 |
|
536 case AndroidGeckoEvent::REMOVE_OBSERVER: |
|
537 mObserversHash.Remove(curEvent->Characters()); |
|
538 break; |
|
539 |
|
540 case AndroidGeckoEvent::ADD_OBSERVER: |
|
541 AddObserver(curEvent->Characters(), curEvent->Observer()); |
|
542 break; |
|
543 |
|
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 } |
|
552 |
|
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 } |
|
560 |
|
561 case AndroidGeckoEvent::PREFERENCES_REMOVE_OBSERVERS: |
|
562 mBrowserApp->RemovePreferenceObservers(curEvent->RequestId()); |
|
563 break; |
|
564 |
|
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; |
|
576 |
|
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 } |
|
587 |
|
588 case AndroidGeckoEvent::TELEMETRY_HISTOGRAM_ADD: |
|
589 Telemetry::Accumulate(NS_ConvertUTF16toUTF8(curEvent->Characters()).get(), |
|
590 curEvent->Count()); |
|
591 break; |
|
592 |
|
593 case AndroidGeckoEvent::NOOP: |
|
594 break; |
|
595 |
|
596 default: |
|
597 nsWindow::OnGlobalAndroidEvent(curEvent); |
|
598 break; |
|
599 } |
|
600 |
|
601 if (curEvent->AckNeeded()) { |
|
602 mozilla::widget::android::GeckoAppShell::AcknowledgeEvent(); |
|
603 } |
|
604 |
|
605 EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type()); |
|
606 |
|
607 return true; |
|
608 } |
|
609 |
|
610 void |
|
611 nsAppShell::ResendLastResizeEvent(nsWindow* aDest) { |
|
612 if (gLastSizeChange) { |
|
613 nsWindow::OnGlobalAndroidEvent(gLastSizeChange); |
|
614 } |
|
615 } |
|
616 |
|
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 } |
|
631 |
|
632 return ae; |
|
633 } |
|
634 |
|
635 AndroidGeckoEvent* |
|
636 nsAppShell::PeekNextEvent() |
|
637 { |
|
638 AndroidGeckoEvent *ae = nullptr; |
|
639 MutexAutoLock lock(mQueueLock); |
|
640 if (mEventQueue.Length()) { |
|
641 ae = mEventQueue[0]; |
|
642 } |
|
643 |
|
644 return ae; |
|
645 } |
|
646 |
|
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; |
|
655 |
|
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; |
|
675 |
|
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); |
|
683 |
|
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 |
|
694 |
|
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()); |
|
699 |
|
700 EVLOG("nsAppShell: Coalescing previous DRAW event at %p into new DRAW event %p", mQueuedDrawEvent, ae); |
|
701 mEventQueue.RemoveElement(mQueuedDrawEvent); |
|
702 delete mQueuedDrawEvent; |
|
703 } |
|
704 |
|
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 } |
|
714 |
|
715 allowCoalescingNextViewport = true; |
|
716 |
|
717 mEventQueue.AppendElement(ae); |
|
718 break; |
|
719 |
|
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; |
|
732 |
|
733 mEventQueue.AppendElement(ae); |
|
734 break; |
|
735 |
|
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; |
|
751 |
|
752 case AndroidGeckoEvent::NATIVE_POKE: |
|
753 allowCoalescingNextViewport = true; |
|
754 // fall through |
|
755 |
|
756 default: |
|
757 mEventQueue.AppendElement(ae); |
|
758 break; |
|
759 } |
|
760 |
|
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 } |
|
769 |
|
770 void |
|
771 nsAppShell::OnResume() |
|
772 { |
|
773 } |
|
774 |
|
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 } |
|
782 |
|
783 // Used by IPC code |
|
784 namespace mozilla { |
|
785 |
|
786 bool ProcessNextEvent() |
|
787 { |
|
788 return nsAppShell::gAppShell->ProcessNextNativeEvent(true) ? true : false; |
|
789 } |
|
790 |
|
791 void NotifyEvent() |
|
792 { |
|
793 nsAppShell::gAppShell->NotifyNativeEvent(); |
|
794 } |
|
795 |
|
796 } |