Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / toolkit / components / startup / src / nsAppStartup.cpp
blob93038bfb6e9fbf4d54796882816e0d92edf43230
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 Communicator client code. This file was split
16 * from xpfe/appshell/src/nsAppShellService.cpp
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Robert O'Callahan <roc+moz@cs.cmu.edu>
26 * Benjamin Smedberg <bsmedberg@covad.net>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsAppStartup.h"
44 #include "nsIAppShellService.h"
45 #include "nsIDOMWindowInternal.h"
46 #include "nsIInterfaceRequestor.h"
47 #include "nsILocalFile.h"
48 #include "nsIObserverService.h"
49 #include "nsIPrefBranch.h"
50 #include "nsIPrefService.h"
51 #include "nsIProfileChangeStatus.h"
52 #include "nsIPromptService.h"
53 #include "nsIStringBundle.h"
54 #include "nsISupportsPrimitives.h"
55 #include "nsITimelineService.h"
56 #include "nsIWebBrowserChrome.h"
57 #include "nsIWindowMediator.h"
58 #include "nsIWindowWatcher.h"
59 #include "nsIXULWindow.h"
60 #include "nsNativeCharsetUtils.h"
61 #include "nsThreadUtils.h"
62 #include "nsAutoPtr.h"
63 #include "nsStringGlue.h"
65 #include "prprf.h"
66 #include "nsCRT.h"
67 #include "nsIInterfaceRequestorUtils.h"
68 #include "nsWidgetsCID.h"
69 #include "nsAppShellCID.h"
70 #include "nsXPFEComponentsCID.h"
72 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
74 class nsAppExitEvent : public nsRunnable {
75 private:
76 nsRefPtr<nsAppStartup> mService;
78 public:
79 nsAppExitEvent(nsAppStartup *service) : mService(service) {}
81 NS_IMETHOD Run() {
82 // Tell the appshell to exit
83 mService->mAppShell->Exit();
85 // We're done "shutting down".
86 mService->mShuttingDown = PR_FALSE;
87 mService->mRunning = PR_FALSE;
88 return NS_OK;
93 // nsAppStartup
96 nsAppStartup::nsAppStartup() :
97 mConsiderQuitStopper(0),
98 mRunning(PR_FALSE),
99 mShuttingDown(PR_FALSE),
100 mAttemptingQuit(PR_FALSE),
101 mRestart(PR_FALSE)
105 nsresult
106 nsAppStartup::Init()
108 nsresult rv;
110 // Create widget application shell
111 mAppShell = do_GetService(kAppShellCID, &rv);
112 NS_ENSURE_SUCCESS(rv, rv);
114 nsCOMPtr<nsIObserverService> os
115 (do_GetService("@mozilla.org/observer-service;1", &rv));
116 NS_ENSURE_SUCCESS(rv, rv);
118 os->AddObserver(this, "quit-application-forced", PR_TRUE);
119 os->AddObserver(this, "profile-change-teardown", PR_TRUE);
120 os->AddObserver(this, "xul-window-registered", PR_TRUE);
121 os->AddObserver(this, "xul-window-destroyed", PR_TRUE);
123 return NS_OK;
128 // nsAppStartup->nsISupports
131 NS_IMPL_THREADSAFE_ISUPPORTS5(nsAppStartup,
132 nsIAppStartup,
133 nsIWindowCreator,
134 nsIWindowCreator2,
135 nsIObserver,
136 nsISupportsWeakReference)
140 // nsAppStartup->nsIAppStartup
143 NS_IMETHODIMP
144 nsAppStartup::CreateHiddenWindow()
146 nsCOMPtr<nsIAppShellService> appShellService
147 (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
148 NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE);
150 return appShellService->CreateHiddenWindow(mAppShell);
154 NS_IMETHODIMP
155 nsAppStartup::DestroyHiddenWindow()
157 nsCOMPtr<nsIAppShellService> appShellService
158 (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
159 NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE);
161 return appShellService->DestroyHiddenWindow();
164 PRInt32
165 nsAppStartup::RealQuitStoppers()
167 #ifdef XP_MACOSX
168 // When attempting quit is set we must subtract the hidden window
169 return mConsiderQuitStopper - (mAttemptingQuit ? 0 : 1);
170 #else
171 return mConsiderQuitStopper;
172 #endif
175 NS_IMETHODIMP
176 nsAppStartup::Run(void)
178 NS_ASSERTION(!mRunning, "Reentrant appstartup->Run()");
180 // If we have no windows open and no explicit calls to
181 // enterLastWindowClosingSurvivalArea, or somebody has explicitly called
182 // quit, don't bother running the event loop which would probably leave us
183 // with a zombie process.
185 if (!mShuttingDown && mConsiderQuitStopper != 0) {
186 #ifdef XP_MACOSX
187 EnterLastWindowClosingSurvivalArea();
188 #endif
190 mRunning = PR_TRUE;
192 nsresult rv = mAppShell->Run();
193 if (NS_FAILED(rv))
194 return rv;
197 return mRestart ? NS_SUCCESS_RESTART_APP : NS_OK;
201 NS_IMETHODIMP
202 nsAppStartup::Quit(PRUint32 aMode)
204 PRUint32 ferocity = (aMode & 0xF);
206 // Quit the application. We will asynchronously call the appshell's
207 // Exit() method via nsAppExitEvent to allow one last pass
208 // through any events in the queue. This guarantees a tidy cleanup.
209 nsresult rv = NS_OK;
210 PRBool postedExitEvent = PR_FALSE;
212 if (mShuttingDown)
213 return NS_OK;
215 // If we're considering quitting, we will only do so if:
216 if (ferocity == eConsiderQuit) {
217 if (mConsiderQuitStopper == 0) {
218 // there are no windows...
219 ferocity = eAttemptQuit;
221 #ifdef XP_MACOSX
222 else if (mConsiderQuitStopper == 1) {
223 // ... or there is only a hiddenWindow left, and it's useless:
224 nsCOMPtr<nsIAppShellService> appShell
225 (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
227 // Failure shouldn't be fatal, but will abort quit attempt:
228 if (!appShell)
229 return NS_OK;
231 PRBool usefulHiddenWindow;
232 appShell->GetApplicationProvidedHiddenWindow(&usefulHiddenWindow);
233 nsCOMPtr<nsIXULWindow> hiddenWindow;
234 appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
235 // If the one window is useful, we won't quit:
236 if (!hiddenWindow || usefulHiddenWindow)
237 return NS_OK;
239 ferocity = eAttemptQuit;
241 #endif
244 mShuttingDown = PR_TRUE;
245 if (!mRestart)
246 mRestart = (aMode & eRestart) != 0;
248 nsCOMPtr<nsIObserverService> obsService;
249 if (ferocity == eAttemptQuit || ferocity == eForceQuit) {
251 obsService = do_GetService("@mozilla.org/observer-service;1");
252 if (obsService)
253 obsService->NotifyObservers(nsnull, "quit-application-granted", nsnull);
255 AttemptingQuit(PR_TRUE);
257 /* Enumerate through each open window and close it. It's important to do
258 this before we forcequit because this can control whether we really quit
259 at all. e.g. if one of these windows has an unload handler that
260 opens a new window. Ugh. I know. */
261 CloseAllWindows();
263 nsCOMPtr<nsIWindowMediator> mediator
264 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
265 if (mediator) {
266 if (ferocity == eAttemptQuit) {
267 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
269 ferocity = eForceQuit; // assume success
271 /* Were we able to immediately close all windows? if not, eAttemptQuit
272 failed. This could happen for a variety of reasons; in fact it's
273 very likely. Perhaps we're being called from JS and the window->Close
274 method hasn't had a chance to wrap itself up yet. So give up.
275 We'll return (with eConsiderQuit) as the remaining windows are
276 closed. */
277 mediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
278 if (windowEnumerator) {
279 PRBool more;
280 while (windowEnumerator->HasMoreElements(&more), more) {
281 /* we can't quit immediately. we'll try again as the last window
282 finally closes. */
283 ferocity = eAttemptQuit;
284 nsCOMPtr<nsISupports> window;
285 windowEnumerator->GetNext(getter_AddRefs(window));
286 nsCOMPtr<nsIDOMWindowInternal> domWindow(do_QueryInterface(window));
287 if (domWindow) {
288 PRBool closed = PR_FALSE;
289 domWindow->GetClosed(&closed);
290 if (!closed) {
291 rv = NS_ERROR_FAILURE;
292 break;
301 if (ferocity == eForceQuit) {
302 // do it!
304 // No chance of the shutdown being cancelled from here on; tell people
305 // we're shutting down for sure while all services are still available.
306 if (obsService) {
307 NS_NAMED_LITERAL_STRING(shutdownStr, "shutdown");
308 NS_NAMED_LITERAL_STRING(restartStr, "restart");
309 obsService->NotifyObservers(nsnull, "quit-application",
310 mRestart ? restartStr.get() : shutdownStr.get());
313 if (!mRunning) {
314 postedExitEvent = PR_TRUE;
316 else {
317 // no matter what, make sure we send the exit event. If
318 // worst comes to worst, we'll do a leaky shutdown but we WILL
319 // shut down. Well, assuming that all *this* stuff works ;-).
320 nsCOMPtr<nsIRunnable> event = new nsAppExitEvent(this);
321 rv = NS_DispatchToCurrentThread(event);
322 if (NS_SUCCEEDED(rv)) {
323 postedExitEvent = PR_TRUE;
325 else {
326 NS_WARNING("failed to dispatch nsAppExitEvent");
331 // turn off the reentrancy check flag, but not if we have
332 // more asynchronous work to do still.
333 if (!postedExitEvent)
334 mShuttingDown = PR_FALSE;
335 return rv;
339 /* We know we're trying to quit the app, but may not be able to do so
340 immediately. Enter a state where we're more ready to quit.
341 (Does useful work only on the Mac.) */
342 void
343 nsAppStartup::AttemptingQuit(PRBool aAttempt)
345 #ifdef XP_MACOSX
346 if (aAttempt) {
347 // now even the Mac wants to quit when the last window is closed
348 if (!mAttemptingQuit)
349 ExitLastWindowClosingSurvivalArea();
350 } else {
351 // changed our mind. back to normal.
352 if (mAttemptingQuit)
353 EnterLastWindowClosingSurvivalArea();
355 #endif
357 mAttemptingQuit = aAttempt;
360 void
361 nsAppStartup::CloseAllWindows()
363 nsCOMPtr<nsIWindowMediator> mediator
364 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
366 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
368 mediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
370 if (!windowEnumerator)
371 return;
373 PRBool more;
374 while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) {
375 nsCOMPtr<nsISupports> isupports;
376 if (NS_FAILED(windowEnumerator->GetNext(getter_AddRefs(isupports))))
377 break;
379 nsCOMPtr<nsIDOMWindowInternal> window = do_QueryInterface(isupports);
380 NS_ASSERTION(window, "not an nsIDOMWindowInternal");
381 if (window) {
382 #ifdef XP_MACOSX
383 PRInt32 quitStoppers = RealQuitStoppers();
384 #endif
385 window->Close();
386 #ifdef XP_MACOSX
387 if (!mAttemptingQuit) {
388 PRInt32 currentQuitStoppers = RealQuitStoppers();
389 // If the current number of windows is smaller or same then the number
390 // recorded before window close, we must re-attempt quit.
391 // 'Or same' condition is here because the actual window deregisters
392 // later asynchronously.
393 if (currentQuitStoppers <= quitStoppers)
394 AttemptingQuit(PR_TRUE);
396 #endif
401 NS_IMETHODIMP
402 nsAppStartup::EnterLastWindowClosingSurvivalArea(void)
404 ++mConsiderQuitStopper;
405 return NS_OK;
409 NS_IMETHODIMP
410 nsAppStartup::ExitLastWindowClosingSurvivalArea(void)
412 NS_ASSERTION(mConsiderQuitStopper > 0, "consider quit stopper out of bounds");
413 --mConsiderQuitStopper;
415 #ifdef XP_MACOSX
416 if (!mShuttingDown && mRunning && (mConsiderQuitStopper <= 1))
417 Quit(eConsiderQuit);
418 #else
419 if (!mShuttingDown && mRunning && (mConsiderQuitStopper == 0))
420 Quit(eConsiderQuit);
421 #endif
423 return NS_OK;
427 // nsAppStartup->nsIWindowCreator
430 NS_IMETHODIMP
431 nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent,
432 PRUint32 aChromeFlags,
433 nsIWebBrowserChrome **_retval)
435 PRBool cancel;
436 return CreateChromeWindow2(aParent, aChromeFlags, 0, 0, &cancel, _retval);
441 // nsAppStartup->nsIWindowCreator2
444 NS_IMETHODIMP
445 nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
446 PRUint32 aChromeFlags,
447 PRUint32 aContextFlags,
448 nsIURI *aURI,
449 PRBool *aCancel,
450 nsIWebBrowserChrome **_retval)
452 NS_ENSURE_ARG_POINTER(aCancel);
453 NS_ENSURE_ARG_POINTER(_retval);
454 *aCancel = PR_FALSE;
455 *_retval = 0;
457 nsCOMPtr<nsIXULWindow> newWindow;
459 if (aParent) {
460 nsCOMPtr<nsIXULWindow> xulParent(do_GetInterface(aParent));
461 NS_ASSERTION(xulParent, "window created using non-XUL parent. that's unexpected, but may work.");
463 if (xulParent)
464 xulParent->CreateNewWindow(aChromeFlags, mAppShell, getter_AddRefs(newWindow));
465 // And if it fails, don't try again without a parent. It could fail
466 // intentionally (bug 115969).
467 } else { // try using basic methods:
468 /* You really shouldn't be making dependent windows without a parent.
469 But unparented modal (and therefore dependent) windows happen
470 in our codebase, so we allow it after some bellyaching: */
471 if (aChromeFlags & nsIWebBrowserChrome::CHROME_DEPENDENT)
472 NS_WARNING("dependent window created without a parent");
474 nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
475 if (!appShell)
476 return NS_ERROR_FAILURE;
478 appShell->CreateTopLevelWindow(0, 0, aChromeFlags,
479 nsIAppShellService::SIZE_TO_CONTENT,
480 nsIAppShellService::SIZE_TO_CONTENT,
481 mAppShell, getter_AddRefs(newWindow));
484 // if anybody gave us anything to work with, use it
485 if (newWindow) {
486 newWindow->SetContextFlags(aContextFlags);
487 nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(newWindow));
488 if (thing)
489 CallGetInterface(thing.get(), _retval);
492 return *_retval ? NS_OK : NS_ERROR_FAILURE;
497 // nsAppStartup->nsIObserver
500 NS_IMETHODIMP
501 nsAppStartup::Observe(nsISupports *aSubject,
502 const char *aTopic, const PRUnichar *aData)
504 NS_ASSERTION(mAppShell, "appshell service notified before appshell built");
505 if (!strcmp(aTopic, "quit-application-forced")) {
506 mShuttingDown = PR_TRUE;
508 else if (!strcmp(aTopic, "profile-change-teardown")) {
509 if (!mShuttingDown) {
510 EnterLastWindowClosingSurvivalArea();
511 CloseAllWindows();
512 ExitLastWindowClosingSurvivalArea();
514 } else if (!strcmp(aTopic, "xul-window-registered")) {
515 EnterLastWindowClosingSurvivalArea();
516 AttemptingQuit(PR_FALSE);
517 } else if (!strcmp(aTopic, "xul-window-destroyed")) {
518 ExitLastWindowClosingSurvivalArea();
519 } else {
520 NS_ERROR("Unexpected observer topic.");
523 return NS_OK;