CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / android / nsAppShell.cpp
blobf6e9b1c4bd0ce4fdc923f7f3d399a1c8b121e6a5
1 /* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Mozilla Foundation
19 * Portions created by the Initial Developer are Copyright (C) 2009-2010
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Vladimir Vukicevic <vladimir@pobox.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsAppShell.h"
40 #include "nsWindow.h"
41 #include "nsThreadUtils.h"
42 #include "nsICommandLineRunner.h"
43 #include "nsIObserverService.h"
44 #include "nsIAppStartup.h"
45 #include "nsIGeolocationProvider.h"
46 #include "nsIPrefService.h"
48 #include "mozilla/Services.h"
49 #include "mozilla/unused.h"
50 #include "prenv.h"
52 #include "AndroidBridge.h"
53 #include "nsAccelerometerSystem.h"
54 #include <android/log.h>
55 #include <pthread.h>
57 #ifdef MOZ_LOGGING
58 #define FORCE_PR_LOG
59 #include "prlog.h"
60 #endif
62 #ifdef ANDROID_DEBUG_EVENTS
63 #define EVLOG(args...) ALOG(args)
64 #else
65 #define EVLOG(args...) do { } while (0)
66 #endif
68 using namespace mozilla;
70 #ifdef PR_LOGGING
71 PRLogModuleInfo *gWidgetLog = nsnull;
72 #endif
74 nsAccelerometerSystem *gAccel = nsnull;
75 nsIGeolocationUpdate *gLocationCallback = nsnull;
77 nsAppShell *nsAppShell::gAppShell = nsnull;
79 NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver)
81 nsAppShell::nsAppShell()
82 : mQueueLock(nsnull),
83 mCondLock(nsnull),
84 mQueueCond(nsnull),
85 mNumDraws(0)
87 gAppShell = this;
90 nsAppShell::~nsAppShell()
92 gAppShell = nsnull;
95 void
96 nsAppShell::NotifyNativeEvent()
98 PR_Lock(mCondLock);
99 PR_NotifyCondVar(mQueueCond);
100 PR_Unlock(mCondLock);
103 nsresult
104 nsAppShell::Init()
106 #ifdef PR_LOGGING
107 if (!gWidgetLog)
108 gWidgetLog = PR_NewLogModule("Widget");
109 #endif
111 mQueueLock = PR_NewLock();
112 mCondLock = PR_NewLock();
113 mQueueCond = PR_NewCondVar(mCondLock);
115 mObserversHash.Init();
117 nsresult rv = nsBaseAppShell::Init();
118 if (AndroidBridge::Bridge())
119 AndroidBridge::Bridge()->NotifyAppShellReady();
121 nsCOMPtr<nsIObserverService> obsServ =
122 mozilla::services::GetObserverService();
123 if (obsServ) {
124 obsServ->AddObserver(this, "xpcom-shutdown", PR_FALSE);
126 return rv;
129 NS_IMETHODIMP
130 nsAppShell::Observe(nsISupports* aSubject,
131 const char* aTopic,
132 const PRUnichar* aData)
134 if (!strcmp(aTopic, "xpcom-shutdown")) {
135 // We need to ensure no observers stick around after XPCOM shuts down
136 // or we'll see crashes, as the app shell outlives XPConnect.
137 mObserversHash.Clear();
139 return nsBaseAppShell::Observe(aSubject, aTopic, aData);
142 void
143 nsAppShell::ScheduleNativeEventCallback()
145 EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread());
147 // this is valid to be called from any thread, so do so.
148 PostEvent(new AndroidGeckoEvent(AndroidGeckoEvent::NATIVE_POKE));
151 PRBool
152 nsAppShell::ProcessNextNativeEvent(PRBool mayWait)
154 EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait);
156 PR_Lock(mCondLock);
158 nsAutoPtr<AndroidGeckoEvent> curEvent;
159 AndroidGeckoEvent *nextEvent;
161 curEvent = GetNextEvent();
162 if (!curEvent && mayWait) {
163 // hmm, should we really hardcode this 10s?
164 #if defined(ANDROID_DEBUG_EVENTS)
165 PRTime t0, t1;
166 EVLOG("nsAppShell: waiting on mQueueCond");
167 t0 = PR_Now();
169 PR_WaitCondVar(mQueueCond, PR_MillisecondsToInterval(10000));
170 t1 = PR_Now();
171 EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000);
172 #else
173 PR_WaitCondVar(mQueueCond, PR_INTERVAL_NO_TIMEOUT);
174 #endif
176 curEvent = GetNextEvent();
179 PR_Unlock(mCondLock);
181 if (!curEvent)
182 return false;
184 // Combine subsequent events of the same type
186 nextEvent = PeekNextEvent();
188 while (nextEvent) {
189 int curType = curEvent->Type();
190 int nextType = nextEvent->Type();
192 while (nextType == AndroidGeckoEvent::DRAW &&
193 mNumDraws > 1)
195 // skip this draw, since there's a later one already in the queue.. this will let us
196 // deal with sequences that look like:
197 // MOVE DRAW MOVE DRAW MOVE DRAW
198 // and end up with just
199 // MOVE DRAW
200 // when we process all the events.
201 RemoveNextEvent();
202 delete nextEvent;
204 #if defined(ANDROID_DEBUG_EVENTS)
205 ALOG("# Removing DRAW event (%d outstanding)", mNumDraws);
206 #endif
208 nextEvent = PeekNextEvent();
209 nextType = nextEvent->Type();
212 // If the next type of event isn't the same as the current type,
213 // we don't coalesce.
214 if (nextType != curType)
215 break;
217 // Can only coalesce motion move events, for motion events
218 if (curType != AndroidGeckoEvent::MOTION_EVENT)
219 break;
221 if (!(curEvent->Action() == AndroidMotionEvent::ACTION_MOVE &&
222 nextEvent->Action() == AndroidMotionEvent::ACTION_MOVE))
223 break;
225 #if defined(ANDROID_DEBUG_EVENTS)
226 ALOG("# Removing % 2d event", curType);
227 #endif
229 RemoveNextEvent();
230 curEvent = nextEvent;
231 nextEvent = PeekNextEvent();
234 EVLOG("nsAppShell: event %p %d [ndraws %d]", (void*)curEvent.get(), curEvent->Type(), mNumDraws);
236 switch (curEvent->Type()) {
237 case AndroidGeckoEvent::NATIVE_POKE:
238 NativeEventCallback();
239 break;
241 case AndroidGeckoEvent::SENSOR_EVENT:
242 gAccel->AccelerationChanged(-curEvent->X(), curEvent->Y(), curEvent->Z());
243 break;
245 case AndroidGeckoEvent::LOCATION_EVENT:
246 if (!gLocationCallback)
247 break;
249 if (curEvent->GeoPosition())
250 gLocationCallback->Update(curEvent->GeoPosition());
251 else
252 NS_WARNING("Received location event without geoposition!");
253 break;
255 case AndroidGeckoEvent::ACTIVITY_STOPPING: {
256 nsCOMPtr<nsIObserverService> obsServ =
257 mozilla::services::GetObserverService();
258 NS_NAMED_LITERAL_STRING(minimize, "heap-minimize");
259 obsServ->NotifyObservers(nsnull, "memory-pressure", minimize.get());
261 break;
264 case AndroidGeckoEvent::ACTIVITY_SHUTDOWN: {
265 nsCOMPtr<nsIObserverService> obsServ =
266 mozilla::services::GetObserverService();
267 NS_NAMED_LITERAL_STRING(context, "shutdown-persist");
268 obsServ->NotifyObservers(nsnull, "quit-application-granted", nsnull);
269 obsServ->NotifyObservers(nsnull, "quit-application-forced", nsnull);
270 obsServ->NotifyObservers(nsnull, "profile-change-net-teardown", context.get());
271 obsServ->NotifyObservers(nsnull, "profile-change-teardown", context.get());
272 obsServ->NotifyObservers(nsnull, "profile-before-change", context.get());
273 nsCOMPtr<nsIAppStartup> appSvc = do_GetService("@mozilla.org/toolkit/app-startup;1");
274 if (appSvc)
275 appSvc->Quit(nsIAppStartup::eForceQuit);
276 break;
279 case AndroidGeckoEvent::ACTIVITY_PAUSING: {
280 // We really want to send a notification like profile-before-change,
281 // but profile-before-change ends up shutting some things down instead
282 // of flushing data
283 nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
284 if (prefs)
285 prefs->SavePrefFile(nsnull);
287 break;
290 case AndroidGeckoEvent::LOAD_URI: {
291 nsCOMPtr<nsICommandLineRunner> cmdline
292 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
293 if (!cmdline)
294 break;
296 char *uri = ToNewUTF8String(curEvent->Characters());
297 if (!uri)
298 break;
300 const char *argv[3] = {
301 "dummyappname",
302 "-remote",
305 nsresult rv = cmdline->Init(3, const_cast<char **>(argv), nsnull, nsICommandLine::STATE_REMOTE_AUTO);
306 if (NS_SUCCEEDED(rv))
307 cmdline->Run();
308 nsMemory::Free(uri);
309 break;
312 default:
313 nsWindow::OnGlobalAndroidEvent(curEvent);
316 EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type());
318 return true;
321 AndroidGeckoEvent*
322 nsAppShell::GetNextEvent()
324 AndroidGeckoEvent *ae = nsnull;
325 PR_Lock(mQueueLock);
326 if (mEventQueue.Length()) {
327 ae = mEventQueue[0];
328 mEventQueue.RemoveElementAt(0);
329 if (ae->Type() == AndroidGeckoEvent::DRAW) {
330 mNumDraws--;
333 PR_Unlock(mQueueLock);
335 return ae;
338 AndroidGeckoEvent*
339 nsAppShell::PeekNextEvent()
341 AndroidGeckoEvent *ae = nsnull;
342 PR_Lock(mQueueLock);
343 if (mEventQueue.Length()) {
344 ae = mEventQueue[0];
346 PR_Unlock(mQueueLock);
348 return ae;
351 void
352 nsAppShell::PostEvent(AndroidGeckoEvent *ae)
354 PR_Lock(mQueueLock);
355 mEventQueue.AppendElement(ae);
356 if (ae->Type() == AndroidGeckoEvent::DRAW) {
357 mNumDraws++;
359 PR_Unlock(mQueueLock);
360 NotifyNativeEvent();
363 void
364 nsAppShell::RemoveNextEvent()
366 AndroidGeckoEvent *ae = nsnull;
367 PR_Lock(mQueueLock);
368 if (mEventQueue.Length()) {
369 ae = mEventQueue[0];
370 mEventQueue.RemoveElementAt(0);
371 if (ae->Type() == AndroidGeckoEvent::DRAW) {
372 mNumDraws--;
375 PR_Unlock(mQueueLock);
378 void
379 nsAppShell::OnResume()
383 nsresult
384 nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver)
386 NS_ASSERTION(aObserver != nsnull, "nsAppShell::AddObserver: aObserver is null!");
387 return mObserversHash.Put(aObserverKey, aObserver) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
391 * The XPCOM event that will call the observer on the main thread.
393 class ObserverCaller : public nsRunnable {
394 public:
395 ObserverCaller(nsIObserver *aObserver, const char *aTopic, const PRUnichar *aData) :
396 mObserver(aObserver), mTopic(aTopic), mData(aData) {
397 NS_ASSERTION(aObserver != nsnull, "ObserverCaller: aObserver is null!");
400 NS_IMETHOD Run() {
401 ALOG("ObserverCaller::Run: observer = %p, topic = '%s')",
402 (nsIObserver*)mObserver, mTopic.get());
403 mObserver->Observe(nsnull, mTopic.get(), mData.get());
404 return NS_OK;
407 private:
408 nsCOMPtr<nsIObserver> mObserver;
409 nsCString mTopic;
410 nsString mData;
413 void
414 nsAppShell::CallObserver(const nsAString &aObserverKey, const nsAString &aTopic, const nsAString &aData)
416 nsCOMPtr<nsIObserver> observer;
417 mObserversHash.Get(aObserverKey, getter_AddRefs(observer));
419 if (!observer) {
420 ALOG("nsAppShell::CallObserver: Observer was not found!");
421 return;
424 const NS_ConvertUTF16toUTF8 sTopic(aTopic);
425 const nsPromiseFlatString& sData = PromiseFlatString(aData);
427 if (NS_IsMainThread()) {
428 // This branch will unlikely be hit, have it just in case
429 observer->Observe(nsnull, sTopic.get(), sData.get());
430 } else {
431 // Java is not running on main thread, so we have to use NS_DispatchToMainThread
432 nsCOMPtr<nsIRunnable> observerCaller = new ObserverCaller(observer, sTopic.get(), sData.get());
433 nsresult rv = NS_DispatchToMainThread(observerCaller);
434 ALOG("NS_DispatchToMainThread result: %d", rv);
435 unused << rv;
439 void
440 nsAppShell::RemoveObserver(const nsAString &aObserverKey)
442 mObserversHash.Remove(aObserverKey);
445 // NotifyObservers support. NotifyObservers only works on main thread.
447 class NotifyObserversCaller : public nsRunnable {
448 public:
449 NotifyObserversCaller(nsISupports *aSupports,
450 const char *aTopic, const PRUnichar *aData) :
451 mSupports(aSupports), mTopic(aTopic), mData(aData) {
454 NS_IMETHOD Run() {
455 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
456 if (os)
457 os->NotifyObservers(mSupports, mTopic.get(), mData.get());
459 return NS_OK;
462 private:
463 nsCOMPtr<nsISupports> mSupports;
464 nsCString mTopic;
465 nsString mData;
468 void
469 nsAppShell::NotifyObservers(nsISupports *aSupports,
470 const char *aTopic,
471 const PRUnichar *aData)
473 // This isn't main thread, so post this to main thread
474 nsCOMPtr<nsIRunnable> caller =
475 new NotifyObserversCaller(aSupports, aTopic, aData);
476 NS_DispatchToMainThread(caller);
479 // Used by IPC code
480 namespace mozilla {
482 bool ProcessNextEvent()
484 return nsAppShell::gAppShell->ProcessNextNativeEvent(PR_TRUE) ? true : false;
487 void NotifyEvent()
489 nsAppShell::gAppShell->NotifyNativeEvent();