Back to index

lightning-sunbird  0.9+nobinonly
nsAppStartup.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code. This file was split
00016  * from xpfe/appshell/src/nsAppShellService.cpp
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *   Robert O'Callahan <roc+moz@cs.cmu.edu>
00026  *   Benjamin Smedberg <bsmedberg@covad.net>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsAppStartup.h"
00043 
00044 #include "nsIAppShellService.h"
00045 #include "nsICloseAllWindows.h"
00046 #include "nsICmdLineService.h"
00047 #include "nsIDOMWindowInternal.h"
00048 #include "nsIEventQueue.h"
00049 #include "nsIEventQueueService.h"
00050 #include "nsIInterfaceRequestor.h"
00051 #include "nsILocalFile.h"
00052 #include "nsIObserverService.h"
00053 #include "nsIPrefBranch.h"
00054 #include "nsIPrefService.h"
00055 #include "nsIProfileChangeStatus.h"
00056 #include "nsIProfileInternal.h"
00057 #include "nsIPromptService.h"
00058 #include "nsIStringBundle.h"
00059 #include "nsISupportsPrimitives.h"
00060 #include "nsITimelineService.h"
00061 #include "nsIWebBrowserChrome.h"
00062 #include "nsIWindowMediator.h"
00063 #include "nsIWindowWatcher.h"
00064 #include "nsIXULWindow.h"
00065 #include "nsNativeCharsetUtils.h"
00066 
00067 #include "prprf.h"
00068 #include "nsCRT.h"
00069 #include "nsIInterfaceRequestorUtils.h"
00070 #include "nsWidgetsCID.h"
00071 #include "nsAppShellCID.h"
00072 #include "nsXPFEComponentsCID.h"
00073 #include "nsEmbedCID.h"
00074 
00075 NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
00076 
00077 //
00078 // nsAppStartup
00079 //
00080 
00081 nsAppStartup::nsAppStartup() :
00082   mConsiderQuitStopper(0),
00083   mShuttingDown(PR_FALSE),
00084   mAttemptingQuit(PR_FALSE)
00085 { }
00086 
00087 
00088 //
00089 // nsAppStartup->nsISupports
00090 //
00091 
00092 NS_IMPL_ISUPPORTS5(nsAppStartup,
00093                    nsIAppStartup,
00094                    nsIWindowCreator,
00095                    nsIWindowCreator2,
00096                    nsIObserver,
00097                    nsISupportsWeakReference)
00098 
00099 
00100 //
00101 // nsAppStartup->nsIAppStartup
00102 //
00103 
00104 NS_IMETHODIMP
00105 nsAppStartup::Initialize(nsISupports *aNativeAppSupportOrSplashScreen)
00106 {
00107   nsresult rv;
00108 
00109   // Remember where the native app support lives.
00110   mNativeAppSupport = do_QueryInterface(aNativeAppSupportOrSplashScreen);
00111 
00112   // Or, remember the splash screen (for backward compatibility).
00113   if (!mNativeAppSupport)
00114     mSplashScreen = do_QueryInterface(aNativeAppSupportOrSplashScreen);
00115 
00116   // Create widget application shell
00117   mAppShell = do_CreateInstance(kAppShellCID, &rv);
00118   NS_ENSURE_SUCCESS(rv, rv);
00119 
00120   rv = mAppShell->Create(nsnull, nsnull);
00121   NS_ENSURE_SUCCESS(rv, rv);
00122 
00123   // listen to EventQueues' comings and goings. do this after the appshell
00124   // has been created, but after the event queue has been created. that
00125   // latter bit is unfortunate, but we deal with it.
00126   nsCOMPtr<nsIObserverService> os
00127     (do_GetService("@mozilla.org/observer-service;1", &rv));
00128   NS_ENSURE_SUCCESS(rv, rv);
00129 
00130   os->AddObserver(this, "nsIEventQueueActivated", PR_TRUE);
00131   os->AddObserver(this, "nsIEventQueueDestroyed", PR_TRUE);
00132   os->AddObserver(this, "skin-selected", PR_TRUE);
00133   os->AddObserver(this, "locale-selected", PR_TRUE);
00134   os->AddObserver(this, "xpinstall-restart", PR_TRUE);
00135   os->AddObserver(this, "profile-change-teardown", PR_TRUE);
00136   os->AddObserver(this, "profile-initial-state", PR_TRUE);
00137   os->AddObserver(this, "xul-window-registered", PR_TRUE);
00138   os->AddObserver(this, "xul-window-destroyed", PR_TRUE);
00139   os->AddObserver(this, "xul-window-visible", PR_TRUE);
00140 
00141   return NS_OK;
00142 }
00143 
00144 
00145 NS_IMETHODIMP
00146 nsAppStartup::CreateHiddenWindow()
00147 {
00148   nsCOMPtr<nsIAppShellService> appShellService
00149     (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
00150   NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE);
00151 
00152   return appShellService->CreateHiddenWindow(mAppShell);
00153 }
00154 
00155 
00156 NS_IMETHODIMP
00157 nsAppStartup::DoProfileStartup(nsICmdLineService *aCmdLineService,
00158                                PRBool canInteract)
00159 {
00160   nsresult rv;
00161 
00162   nsCOMPtr<nsIProfileInternal> profileMgr
00163     (do_GetService(NS_PROFILE_CONTRACTID, &rv));
00164   NS_ENSURE_SUCCESS(rv,rv);
00165 
00166   EnterLastWindowClosingSurvivalArea();
00167 
00168   // If we are being launched in turbo mode, profile mgr cannot show UI
00169   rv = profileMgr->StartupWithArgs(aCmdLineService, canInteract);
00170   if (!canInteract && rv == NS_ERROR_PROFILE_REQUIRES_INTERACTION) {
00171     NS_WARNING("nsIProfileInternal::StartupWithArgs returned NS_ERROR_PROFILE_REQUIRES_INTERACTION");       
00172     rv = NS_OK;
00173   }
00174 
00175   if (NS_SUCCEEDED(rv)) {
00176     rv = CheckAndRemigrateDefunctProfile();
00177     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to check and remigrate profile");
00178     rv = NS_OK;
00179   }
00180 
00181   ExitLastWindowClosingSurvivalArea();
00182 
00183   // if Quit() was called while we were starting up we have a failure situation...
00184   if (mShuttingDown)
00185     return NS_ERROR_FAILURE;
00186 
00187   return rv;
00188 }
00189 
00190 
00191 NS_IMETHODIMP
00192 nsAppStartup::GetNativeAppSupport(nsINativeAppSupport **aResult)
00193 {
00194   NS_PRECONDITION(aResult, "Null out param");
00195 
00196   if (!mNativeAppSupport)
00197     return NS_ERROR_FAILURE;
00198 
00199   NS_ADDREF(*aResult = mNativeAppSupport);
00200   return NS_OK;
00201 }
00202 
00203 
00204 NS_IMETHODIMP
00205 nsAppStartup::Run(void)
00206 {
00207   return mAppShell->Run();
00208 }
00209 
00210 
00211 NS_IMETHODIMP
00212 nsAppStartup::Quit(PRUint32 aFerocity)
00213 {
00214   // Quit the application. We will asynchronously call the appshell's
00215   // Exit() method via the ExitCallback() to allow one last pass
00216   // through any events in the queue. This guarantees a tidy cleanup.
00217   nsresult rv = NS_OK;
00218   PRBool postedExitEvent = PR_FALSE;
00219 
00220   if (mShuttingDown)
00221     return NS_OK;
00222 
00223   /* eForceQuit doesn't actually work; it can cause a subtle crash if
00224      there are windows open which have unload handlers which open
00225      new windows. Use eAttemptQuit for now. */
00226   if (aFerocity == eForceQuit) {
00227     NS_WARNING("attempted to force quit");
00228     // it will be treated the same as eAttemptQuit, below
00229   }
00230 
00231   mShuttingDown = PR_TRUE;
00232 
00233   nsCOMPtr<nsIWindowMediator> mediator
00234     (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
00235 
00236   if (aFerocity == eConsiderQuit && mConsiderQuitStopper == 0) {
00237     // attempt quit if the last window has been unregistered/closed
00238 
00239     PRBool windowsRemain = PR_TRUE;
00240 
00241     if (mediator) {
00242       nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
00243       mediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
00244       if (windowEnumerator)
00245         windowEnumerator->HasMoreElements(&windowsRemain);
00246     }
00247     if (!windowsRemain) {
00248       aFerocity = eAttemptQuit;
00249 
00250       // Check to see if we should quit in this case.
00251       if (mNativeAppSupport) {
00252         PRBool serverMode = PR_FALSE;
00253         mNativeAppSupport->GetIsServerMode(&serverMode);
00254         if (serverMode) {
00255           // stop! give control to server mode
00256           mShuttingDown = PR_FALSE;
00257           mNativeAppSupport->OnLastWindowClosing();
00258           return NS_OK;
00259         }
00260       }
00261     }
00262   }
00263 
00264   /* Currently aFerocity can never have the value of eForceQuit here.
00265      That's temporary (in an unscheduled kind of way) and logically
00266      this code is part of the eForceQuit case, so I'm checking against
00267      that value anyway. Reviewers made me add this comment. */
00268   if (aFerocity == eAttemptQuit || aFerocity == eForceQuit) {
00269 
00270     AttemptingQuit(PR_TRUE);
00271 
00272     /* Enumerate through each open window and close it. It's important to do
00273        this before we forcequit because this can control whether we really quit
00274        at all. e.g. if one of these windows has an unload handler that
00275        opens a new window. Ugh. I know. */
00276     if (mediator) {
00277       nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
00278 
00279       mediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
00280 
00281       if (windowEnumerator) {
00282 
00283         while (1) {
00284           PRBool more;
00285           if (NS_FAILED(rv = windowEnumerator->HasMoreElements(&more)) || !more)
00286             break;
00287 
00288           nsCOMPtr<nsISupports> isupports;
00289           rv = windowEnumerator->GetNext(getter_AddRefs(isupports));
00290           if (NS_FAILED(rv))
00291             break;
00292 
00293           nsCOMPtr<nsIDOMWindowInternal> window = do_QueryInterface(isupports);
00294           NS_ASSERTION(window, "not an nsIDOMWindowInternal");
00295           if (!window)
00296             continue;
00297 
00298           window->Close();
00299         }
00300       }
00301 
00302       if (aFerocity == eAttemptQuit) {
00303 
00304         aFerocity = eForceQuit; // assume success
00305 
00306         /* Were we able to immediately close all windows? if not, eAttemptQuit
00307            failed. This could happen for a variety of reasons; in fact it's
00308            very likely. Perhaps we're being called from JS and the window->Close
00309            method hasn't had a chance to wrap itself up yet. So give up.
00310            We'll return (with eConsiderQuit) as the remaining windows are
00311            closed. */
00312         mediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
00313         if (windowEnumerator) {
00314           PRBool more;
00315           while (windowEnumerator->HasMoreElements(&more), more) {
00316             /* we can't quit immediately. we'll try again as the last window
00317                finally closes. */
00318             aFerocity = eAttemptQuit;
00319             nsCOMPtr<nsISupports> window;
00320             windowEnumerator->GetNext(getter_AddRefs(window));
00321             nsCOMPtr<nsIDOMWindowInternal> domWindow(do_QueryInterface(window));
00322             if (domWindow) {
00323               PRBool closed = PR_FALSE;
00324               domWindow->GetClosed(&closed);
00325               if (!closed) {
00326                 rv = NS_ERROR_FAILURE;
00327                 break;
00328               }
00329             }
00330           }
00331         }
00332       }
00333     }
00334   }
00335 
00336   if (aFerocity == eForceQuit) {
00337     // do it!
00338 
00339     // No chance of the shutdown being cancelled from here on; tell people
00340     // we're shutting down for sure while all services are still available.
00341     nsCOMPtr<nsIObserverService> obsService = do_GetService("@mozilla.org/observer-service;1", &rv);
00342     obsService->NotifyObservers(nsnull, "quit-application", nsnull);
00343 
00344     // first shutdown native app support; doing this first will prevent new
00345     // requests to open additional windows coming in.
00346     if (mNativeAppSupport) {
00347       mNativeAppSupport->Quit();
00348       mNativeAppSupport = 0;
00349     }
00350 
00351     nsCOMPtr<nsIAppShellService> appShellService
00352       (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
00353     if (appShellService)
00354       appShellService->DestroyHiddenWindow();
00355     
00356     // no matter what, make sure we send the exit event.  If
00357     // worst comes to worst, we'll do a leaky shutdown but we WILL
00358     // shut down. Well, assuming that all *this* stuff works ;-).
00359     nsCOMPtr<nsIEventQueueService> svc = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
00360     if (NS_SUCCEEDED(rv)) {
00361 
00362       nsCOMPtr<nsIEventQueue> queue;
00363       rv = svc->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(queue));
00364       if (NS_SUCCEEDED(rv)) {
00365 
00366         PLEvent* event = new PLEvent;
00367         if (event) {
00368           NS_ADDREF_THIS();
00369           PL_InitEvent(event,
00370                        this,
00371                        HandleExitEvent,
00372                        DestroyExitEvent);
00373 
00374           rv = queue->PostEvent(event);
00375           if (NS_SUCCEEDED(rv)) {
00376             postedExitEvent = PR_TRUE;
00377           }
00378           else {
00379             PL_DestroyEvent(event);
00380           }
00381         }
00382         else {
00383           rv = NS_ERROR_OUT_OF_MEMORY;
00384         }
00385       }
00386     }
00387   }
00388 
00389   // turn off the reentrancy check flag, but not if we have
00390   // more asynchronous work to do still.
00391   if (!postedExitEvent)
00392     mShuttingDown = PR_FALSE;
00393   return rv;
00394 }
00395 
00396 
00397 /* We know we're trying to quit the app, but may not be able to do so
00398    immediately. Enter a state where we're more ready to quit.
00399    (Does useful work only on the Mac.) */
00400 void
00401 nsAppStartup::AttemptingQuit(PRBool aAttempt)
00402 {
00403 #if defined(XP_MAC) || defined(XP_MACOSX)
00404   if (aAttempt) {
00405     // now even the Mac wants to quit when the last window is closed
00406     if (!mAttemptingQuit)
00407       ExitLastWindowClosingSurvivalArea();
00408     mAttemptingQuit = PR_TRUE;
00409   } else {
00410     // changed our mind. back to normal.
00411     if (mAttemptingQuit)
00412       EnterLastWindowClosingSurvivalArea();
00413     mAttemptingQuit = PR_FALSE;
00414   }
00415 #else
00416   mAttemptingQuit = aAttempt;
00417 #endif
00418 }
00419 
00420 nsresult
00421 nsAppStartup::CheckAndRemigrateDefunctProfile()
00422 {
00423   nsresult rv;
00424 
00425   nsCOMPtr<nsIPrefBranch> prefBranch;
00426   nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00427   NS_ENSURE_SUCCESS(rv,rv);
00428   rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
00429   NS_ENSURE_SUCCESS(rv,rv);
00430 
00431   PRInt32 secondsBeforeDefunct;
00432   rv = prefBranch->GetIntPref("profile.seconds_until_defunct", &secondsBeforeDefunct);
00433   NS_ENSURE_SUCCESS(rv,rv);
00434 
00435   // -1 is the value for "never go defunct"
00436   // if the pref is set to -1, we'll never prompt the user to remigrate
00437   // see all.js (and all-ns.js)
00438   if (secondsBeforeDefunct == -1)
00439     return NS_OK;
00440 
00441   // used for converting
00442   // seconds -> millisecs
00443   // and microsecs -> millisecs
00444   PRInt64 oneThousand = LL_INIT(0, 1000);
00445   
00446   PRInt64 defunctInterval;
00447   // Init as seconds
00448   LL_I2L(defunctInterval, secondsBeforeDefunct);
00449   // Convert secs to millisecs
00450   LL_MUL(defunctInterval, defunctInterval, oneThousand);
00451         
00452   nsCOMPtr<nsIProfileInternal> profileMgr(do_GetService(NS_PROFILE_CONTRACTID, &rv));
00453   NS_ENSURE_SUCCESS(rv,rv);
00454 
00455   nsXPIDLString profileName;
00456   PRInt64 lastModTime;
00457   profileMgr->GetCurrentProfile(getter_Copies(profileName));
00458   rv = profileMgr->GetProfileLastModTime(profileName.get(), &lastModTime);
00459   NS_ENSURE_SUCCESS(rv,rv);
00460 
00461   // convert "now" from microsecs to millisecs
00462   PRInt64 nowInMilliSecs = PR_Now(); 
00463   LL_DIV(nowInMilliSecs, nowInMilliSecs, oneThousand);
00464   
00465   // determine (using the pref value) when the profile would be considered defunct
00466   PRInt64 defunctIntervalAgo;
00467   LL_SUB(defunctIntervalAgo, nowInMilliSecs, defunctInterval);
00468 
00469   // if we've used our current 6.x / mozilla profile more recently than
00470   // when we'd consider it defunct, don't remigrate
00471   if (LL_CMP(lastModTime, >, defunctIntervalAgo))
00472     return NS_OK;
00473   
00474   nsCOMPtr<nsILocalFile> origProfileDir;
00475   rv = profileMgr->GetOriginalProfileDir(profileName, getter_AddRefs(origProfileDir));
00476   // if this fails
00477   // then the current profile is a new one (not from 4.x) 
00478   // so we are done.
00479   if (NS_FAILED(rv))
00480     return NS_OK;
00481   
00482   // Now, we know that a matching 4.x profile exists
00483   // See if it has any newer files in it than our defunct profile.
00484   nsCOMPtr<nsISimpleEnumerator> dirEnum;
00485   rv = origProfileDir->GetDirectoryEntries(getter_AddRefs(dirEnum));
00486   NS_ENSURE_SUCCESS(rv,rv);
00487   
00488   PRBool promptForRemigration = PR_FALSE;
00489   PRBool hasMore;
00490   while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
00491     nsCOMPtr<nsILocalFile> currElem;
00492     rv = dirEnum->GetNext(getter_AddRefs(currElem));
00493     NS_ENSURE_SUCCESS(rv,rv);
00494     
00495     PRInt64 currElemModTime;
00496     rv = currElem->GetLastModifiedTime(&currElemModTime);
00497     NS_ENSURE_SUCCESS(rv,rv);
00498     // if this file in our 4.x profile is more recent than when we last used our mozilla / 6.x profile
00499     // we should prompt for re-migration
00500     if (LL_CMP(currElemModTime, >, lastModTime)) {
00501       promptForRemigration = PR_TRUE;
00502       break;
00503     }
00504   }
00505   
00506   // If nothing in the 4.x dir is newer than our defunct profile, return.
00507   if (!promptForRemigration)
00508     return NS_OK;
00509  
00510   nsCOMPtr<nsIStringBundleService> stringBundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
00511   NS_ENSURE_SUCCESS(rv,rv);
00512 
00513   nsCOMPtr<nsIStringBundle> migrationBundle, brandBundle;
00514   rv = stringBundleService->CreateBundle("chrome://communicator/locale/profile/migration.properties", getter_AddRefs(migrationBundle));
00515   NS_ENSURE_SUCCESS(rv,rv);
00516 
00517   rv = stringBundleService->CreateBundle("chrome://branding/locale/brand.properties", getter_AddRefs(brandBundle));
00518   NS_ENSURE_SUCCESS(rv,rv);
00519 
00520   nsXPIDLString brandName;
00521   rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(), getter_Copies(brandName));
00522   NS_ENSURE_SUCCESS(rv,rv);
00523  
00524   nsXPIDLString dialogText;
00525   rv = migrationBundle->GetStringFromName(NS_LITERAL_STRING("confirmRemigration").get(), getter_Copies(dialogText));
00526   NS_ENSURE_SUCCESS(rv,rv);
00527 
00528   nsCOMPtr<nsIPromptService> promptService(do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
00529   NS_ENSURE_SUCCESS(rv,rv);
00530   PRInt32 buttonPressed;
00531   rv = promptService->ConfirmEx(nsnull, brandName.get(),
00532     dialogText.get(),
00533     (nsIPromptService::BUTTON_POS_0 * 
00534     nsIPromptService::BUTTON_TITLE_YES) + 
00535     (nsIPromptService::BUTTON_POS_1 * 
00536     nsIPromptService::BUTTON_TITLE_NO),
00537     nsnull, nsnull, nsnull, nsnull, nsnull, &buttonPressed);
00538   NS_ENSURE_SUCCESS(rv,rv);
00539   
00540   if (buttonPressed == 0) {
00541     // Need to shut down the current profile before remigrating it
00542     profileMgr->ShutDownCurrentProfile(nsIProfile::SHUTDOWN_PERSIST);
00543     // If this fails, it will restore what was there.
00544     rv = profileMgr->RemigrateProfile(profileName.get());
00545     NS_ASSERTION(NS_SUCCEEDED(rv), "Remigration of profile failed.");
00546     // Whether or not we succeeded or failed, need to reset this.
00547     profileMgr->SetCurrentProfile(profileName.get());
00548   }
00549   return NS_OK;
00550 }   
00551 
00552 
00553 NS_IMETHODIMP
00554 nsAppStartup::EnterLastWindowClosingSurvivalArea(void)
00555 {
00556   ++mConsiderQuitStopper;
00557   return NS_OK;
00558 }
00559 
00560 
00561 NS_IMETHODIMP
00562 nsAppStartup::ExitLastWindowClosingSurvivalArea(void)
00563 {
00564   NS_ASSERTION(mConsiderQuitStopper > 0, "consider quit stopper out of bounds");
00565   --mConsiderQuitStopper;
00566   return NS_OK;
00567 }
00568 
00569 
00570 NS_IMETHODIMP
00571 nsAppStartup::HideSplashScreen()
00572 {
00573   // Hide the splash screen.
00574   if ( mNativeAppSupport ) {
00575     mNativeAppSupport->HideSplashScreen();
00576   } else if ( mSplashScreen ) {
00577     mSplashScreen->Hide();
00578   }
00579   return NS_OK;
00580 }
00581 
00582 
00583 NS_IMETHODIMP
00584 nsAppStartup::CreateStartupState(PRInt32 aWindowWidth, PRInt32 aWindowHeight,
00585                                  PRBool *_retval)
00586 {
00587   NS_ENSURE_ARG_POINTER(_retval);
00588   *_retval = PR_FALSE;
00589   nsresult rv;
00590   
00591   // If starting up in server mode, then we do things differently.
00592   nsCOMPtr<nsINativeAppSupport> nativeApp;
00593   rv = GetNativeAppSupport(getter_AddRefs(nativeApp));
00594   if (NS_SUCCEEDED(rv)) {
00595       PRBool isServerMode = PR_FALSE;
00596       nativeApp->GetIsServerMode(&isServerMode);
00597       if (isServerMode) {
00598           nativeApp->StartServerMode();
00599       }
00600       PRBool shouldShowUI = PR_TRUE;
00601       nativeApp->GetShouldShowUI(&shouldShowUI);
00602       if (!shouldShowUI) {
00603           return NS_OK;
00604       }
00605   }  
00606 
00607   nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID));
00608   if (!prefService)
00609     return NS_ERROR_FAILURE;
00610   nsCOMPtr<nsIPrefBranch> startupBranch;
00611   prefService->GetBranch(PREF_STARTUP_PREFIX, getter_AddRefs(startupBranch));
00612   if (!startupBranch)
00613     return NS_ERROR_FAILURE;
00614   
00615   PRUint32 childCount;
00616   char **childArray = nsnull;
00617   rv = startupBranch->GetChildList("", &childCount, &childArray);
00618   if (NS_FAILED(rv))
00619     return rv;
00620     
00621   for (PRUint32 i = 0; i < childCount; i++) {
00622     PRBool prefValue;
00623     rv = startupBranch->GetBoolPref(childArray[i], &prefValue);
00624     if (NS_SUCCEEDED(rv) && prefValue) {
00625       PRBool windowOpened;
00626       rv = LaunchTask(childArray[i], aWindowHeight, aWindowWidth, &windowOpened);
00627       if (NS_SUCCEEDED(rv) && windowOpened)
00628         *_retval = PR_TRUE;
00629     }
00630   }
00631   
00632   NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(childCount, childArray);
00633   
00634   return NS_OK;
00635 }
00636 
00637 
00638 NS_IMETHODIMP
00639 nsAppStartup::Ensure1Window(nsICmdLineService *aCmdLineService)
00640 {
00641   nsresult rv;
00642  
00643   nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
00644   if (NS_FAILED(rv))
00645     return rv;
00646 
00647   nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
00648   if (NS_SUCCEEDED(windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator))))
00649   {
00650     PRBool more;
00651     windowEnumerator->HasMoreElements(&more);
00652     if (!more)
00653     {
00654       // No window exists so lets create a browser one
00655       PRInt32 height = nsIAppShellService::SIZE_TO_CONTENT;
00656       PRInt32 width  = nsIAppShellService::SIZE_TO_CONTENT;
00657                             
00658       // Get the value of -width option
00659       nsXPIDLCString tempString;
00660       rv = aCmdLineService->GetCmdLineValue("-width", getter_Copies(tempString));
00661       if (NS_SUCCEEDED(rv) && !tempString.IsEmpty())
00662         PR_sscanf(tempString.get(), "%d", &width);
00663 
00664       // Get the value of -height option
00665       rv = aCmdLineService->GetCmdLineValue("-height", getter_Copies(tempString));
00666       if (NS_SUCCEEDED(rv) && !tempString.IsEmpty())
00667         PR_sscanf(tempString.get(), "%d", &height);
00668 
00669       rv = OpenBrowserWindow(height, width);
00670     }
00671   }
00672   return rv;
00673 }
00674 
00675 
00676 void* PR_CALLBACK
00677 nsAppStartup::HandleExitEvent(PLEvent* aEvent)
00678 {
00679   nsAppStartup *service =
00680     NS_REINTERPRET_CAST(nsAppStartup*, aEvent->owner);
00681 
00682   // Tell the appshell to exit
00683   service->mAppShell->Exit();
00684 
00685   // We're done "shutting down".
00686   service->mShuttingDown = PR_FALSE;
00687 
00688   return nsnull;
00689 }
00690 
00691 void PR_CALLBACK
00692 nsAppStartup::DestroyExitEvent(PLEvent* aEvent)
00693 {
00694   nsAppStartup *service =
00695     NS_REINTERPRET_CAST(nsAppStartup*, aEvent->owner);
00696   NS_RELEASE(service);
00697   delete aEvent;
00698 }
00699 
00700 
00701 nsresult
00702 nsAppStartup::LaunchTask(const char *aParam, PRInt32 height, PRInt32 width, PRBool *windowOpened)
00703 {
00704   nsresult rv;
00705 
00706   nsCOMPtr <nsICmdLineService> cmdLine =
00707     do_GetService(NS_COMMANDLINESERVICE_CONTRACTID, &rv);
00708   if (NS_FAILED(rv)) return rv;
00709 
00710   nsCOMPtr <nsICmdLineHandler> handler;
00711   rv = cmdLine->GetHandlerForParam(aParam, getter_AddRefs(handler));
00712   if (NS_FAILED(rv)) return rv;
00713 
00714   nsXPIDLCString chromeUrlForTask;
00715   rv = handler->GetChromeUrlForTask(getter_Copies(chromeUrlForTask));
00716   if (NS_FAILED(rv)) return rv;
00717 
00718   PRBool handlesArgs = PR_FALSE;
00719   rv = handler->GetHandlesArgs(&handlesArgs);
00720   if (handlesArgs) {
00721     nsXPIDLString defaultArgs;
00722     rv = handler->GetDefaultArgs(getter_Copies(defaultArgs));
00723     if (NS_FAILED(rv)) return rv;
00724     rv = OpenWindow(chromeUrlForTask, defaultArgs,
00725                     nsIAppShellService::SIZE_TO_CONTENT,
00726                     nsIAppShellService::SIZE_TO_CONTENT);
00727   }
00728   else {
00729     rv = OpenWindow(chromeUrlForTask, EmptyString(), width, height);
00730   }
00731   
00732   // If we get here without an error, then a window was opened OK.
00733   if (NS_SUCCEEDED(rv)) {
00734     *windowOpened = PR_TRUE;
00735   }
00736 
00737   return rv;
00738 }
00739 
00740 nsresult
00741 nsAppStartup::OpenWindow(const nsAFlatCString& aChromeURL,
00742                          const nsAFlatString& aAppArgs,
00743                          PRInt32 aWidth, PRInt32 aHeight)
00744 {
00745   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
00746   nsCOMPtr<nsISupportsString> sarg(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
00747   if (!wwatch || !sarg)
00748     return NS_ERROR_FAILURE;
00749 
00750   // Make sure a profile is selected.
00751 
00752   // We need the native app support object. If this fails, we still proceed.
00753   // That's because some platforms don't have a native app
00754   // support implementation.  On those platforms, "ensuring a
00755   // profile" is moot (because they don't support "-turbo",
00756   // basically).  Specifically, because they don't do turbo, they will
00757   // *always* have a profile selected.
00758   nsCOMPtr<nsINativeAppSupport> nativeApp;
00759   if (NS_SUCCEEDED(GetNativeAppSupport(getter_AddRefs(nativeApp))))
00760   {
00761     nsCOMPtr <nsICmdLineService> cmdLine =
00762       do_GetService(NS_COMMANDLINESERVICE_CONTRACTID);
00763 
00764     if (cmdLine) {
00765       // Make sure profile has been selected.
00766       // At this point, we have to look for failure.  That
00767       // handles the case where the user chooses "Exit" on
00768       // the profile manager window.
00769       if (NS_FAILED(nativeApp->EnsureProfile(cmdLine)))
00770         return NS_ERROR_NOT_INITIALIZED;
00771     }
00772   }
00773 
00774   sarg->SetData(aAppArgs);
00775 
00776   nsCAutoString features("chrome,dialog=no,all");
00777   if (aHeight != nsIAppShellService::SIZE_TO_CONTENT) {
00778     features.Append(",height=");
00779     features.AppendInt(aHeight);
00780   }
00781   if (aWidth != nsIAppShellService::SIZE_TO_CONTENT) {
00782     features.Append(",width=");
00783     features.AppendInt(aWidth);
00784   }
00785 
00786   nsCOMPtr<nsIDOMWindow> newWindow;
00787   return wwatch->OpenWindow(0, aChromeURL.get(), "_blank",
00788                             features.get(), sarg,
00789                             getter_AddRefs(newWindow));
00790 }
00791 
00792 
00793 nsresult
00794 nsAppStartup::OpenBrowserWindow(PRInt32 height, PRInt32 width)
00795 {
00796   nsresult rv;
00797   nsCOMPtr<nsICmdLineHandler> handler(
00798     do_GetService("@mozilla.org/commandlinehandler/general-startup;1?type=browser", &rv));
00799   if (NS_FAILED(rv)) return rv;
00800 
00801   nsXPIDLCString chromeUrlForTask;
00802   rv = handler->GetChromeUrlForTask(getter_Copies(chromeUrlForTask));
00803   if (NS_FAILED(rv)) return rv;
00804 
00805   nsCOMPtr <nsICmdLineService> cmdLine = do_GetService(NS_COMMANDLINESERVICE_CONTRACTID, &rv);
00806   if (NS_FAILED(rv)) return rv;
00807 
00808   nsXPIDLCString urlToLoad;
00809   rv = cmdLine->GetURLToLoad(getter_Copies(urlToLoad));
00810   if (!urlToLoad.IsEmpty()) {
00811 
00812 #ifdef DEBUG_CMD_LINE
00813     printf("url to load: %s\n", urlToLoad.get());
00814 #endif /* DEBUG_CMD_LINE */
00815 
00816     nsAutoString url; 
00817     // convert the cmdLine URL to Unicode
00818     rv = NS_CopyNativeToUnicode(nsDependentCString(urlToLoad), url);
00819     if (NS_FAILED(rv)) {
00820       NS_ERROR("Failed to convert commandline url to unicode");
00821       return rv;
00822     }
00823     rv = OpenWindow(chromeUrlForTask, url, width, height);
00824 
00825   } else {
00826 
00827     nsXPIDLString defaultArgs;
00828     rv = handler->GetDefaultArgs(getter_Copies(defaultArgs));
00829     if (NS_FAILED(rv)) return rv;
00830 
00831 #ifdef DEBUG_CMD_LINE
00832     printf("default args: %s\n", NS_ConvertUTF16toUTF8(defaultArgs).get());
00833 #endif /* DEBUG_CMD_LINE */
00834 
00835     rv = OpenWindow(chromeUrlForTask, defaultArgs, width, height);
00836   }
00837 
00838   return rv;
00839 }
00840 
00841 
00842 //
00843 // nsAppStartup->nsIWindowCreator
00844 //
00845 
00846 NS_IMETHODIMP
00847 nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent,
00848                                  PRUint32 aChromeFlags,
00849                                  nsIWebBrowserChrome **_retval)
00850 {
00851   PRBool cancel;
00852   return CreateChromeWindow2(aParent, aChromeFlags, 0, 0, &cancel, _retval);
00853 }
00854 
00855 
00856 //
00857 // nsAppStartup->nsIWindowCreator2
00858 //
00859 
00860 NS_IMETHODIMP
00861 nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
00862                                   PRUint32 aChromeFlags,
00863                                   PRUint32 aContextFlags,
00864                                   nsIURI *aURI,
00865                                   PRBool *aCancel,
00866                                   nsIWebBrowserChrome **_retval)
00867 {
00868   NS_ENSURE_ARG_POINTER(aCancel);
00869   NS_ENSURE_ARG_POINTER(_retval);
00870   *aCancel = PR_FALSE;
00871   *_retval = 0;
00872 
00873   nsCOMPtr<nsIXULWindow> newWindow;
00874 
00875   if (aParent) {
00876     nsCOMPtr<nsIXULWindow> xulParent(do_GetInterface(aParent));
00877     NS_ASSERTION(xulParent, "window created using non-XUL parent. that's unexpected, but may work.");
00878 
00879     if (xulParent)
00880       xulParent->CreateNewWindow(aChromeFlags, mAppShell, getter_AddRefs(newWindow));
00881     // And if it fails, don't try again without a parent. It could fail
00882     // intentionally (bug 115969).
00883   } else { // try using basic methods:
00884     /* You really shouldn't be making dependent windows without a parent.
00885       But unparented modal (and therefore dependent) windows happen
00886       in our codebase, so we allow it after some bellyaching: */
00887     if (aChromeFlags & nsIWebBrowserChrome::CHROME_DEPENDENT)
00888       NS_WARNING("dependent window created without a parent");
00889 
00890     nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
00891     if (!appShell)
00892       return NS_ERROR_FAILURE;
00893     
00894     appShell->CreateTopLevelWindow(0, 0, aChromeFlags,
00895                                    nsIAppShellService::SIZE_TO_CONTENT,
00896                                    nsIAppShellService::SIZE_TO_CONTENT,
00897                                    mAppShell, getter_AddRefs(newWindow));
00898   }
00899 
00900   // if anybody gave us anything to work with, use it
00901   if (newWindow) {
00902     newWindow->SetContextFlags(aContextFlags);
00903     nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(newWindow));
00904     if (thing)
00905       CallGetInterface(thing.get(), _retval);
00906   }
00907 
00908   return *_retval ? NS_OK : NS_ERROR_FAILURE;
00909 }
00910 
00911 
00912 //
00913 // nsAppStartup->nsIObserver
00914 //
00915 
00916 NS_IMETHODIMP
00917 nsAppStartup::Observe(nsISupports *aSubject,
00918                       const char *aTopic, const PRUnichar *aData)
00919 {
00920   NS_ASSERTION(mAppShell, "appshell service notified before appshell built");
00921   if (!strcmp(aTopic, "nsIEventQueueActivated")) {
00922     nsCOMPtr<nsIEventQueue> eq(do_QueryInterface(aSubject));
00923     if (eq) {
00924       PRBool isNative = PR_TRUE;
00925       // we only add native event queues to the appshell
00926       eq->IsQueueNative(&isNative);
00927       if (isNative)
00928         mAppShell->ListenToEventQueue(eq, PR_TRUE);
00929     }
00930   } else if (!strcmp(aTopic, "nsIEventQueueDestroyed")) {
00931     nsCOMPtr<nsIEventQueue> eq(do_QueryInterface(aSubject));
00932     if (eq) {
00933       PRBool isNative = PR_TRUE;
00934       // we only remove native event queues from the appshell
00935       eq->IsQueueNative(&isNative);
00936       if (isNative)
00937         mAppShell->ListenToEventQueue(eq, PR_FALSE);
00938     }
00939   } else if (!strcmp(aTopic, "skin-selected") ||
00940              !strcmp(aTopic, "locale-selected") ||
00941              !strcmp(aTopic, "xpinstall-restart")) {
00942     if (mNativeAppSupport)
00943       mNativeAppSupport->SetIsServerMode(PR_FALSE);
00944   } else if (!strcmp(aTopic, "profile-change-teardown")) {
00945     nsresult rv;
00946     EnterLastWindowClosingSurvivalArea();
00947     // NOTE: No early error exits because we need to execute the
00948     // balancing ExitLastWindowClosingSurvivalArea().
00949     nsCOMPtr<nsICloseAllWindows> closer =
00950             do_CreateInstance("@mozilla.org/appshell/closeallwindows;1", &rv);
00951     NS_ASSERTION(closer, "Failed to create nsICloseAllWindows impl.");
00952     PRBool proceedWithSwitch = PR_FALSE;
00953     if (closer)
00954       rv = closer->CloseAll(PR_TRUE, &proceedWithSwitch);
00955     if (NS_FAILED(rv) || !proceedWithSwitch) {
00956       nsCOMPtr<nsIProfileChangeStatus> changeStatus(do_QueryInterface(aSubject));
00957       if (changeStatus)
00958         changeStatus->VetoChange();
00959     }
00960     ExitLastWindowClosingSurvivalArea();
00961   } else if (!strcmp(aTopic, "profile-initial-state")) {
00962     if (nsDependentString(aData).EqualsLiteral("switch")) {
00963       // Now, establish the startup state according to the new prefs.
00964       PRBool openedWindow;
00965       CreateStartupState(nsIAppShellService::SIZE_TO_CONTENT,
00966                          nsIAppShellService::SIZE_TO_CONTENT, &openedWindow);
00967       if (!openedWindow)
00968         OpenBrowserWindow(nsIAppShellService::SIZE_TO_CONTENT,
00969                           nsIAppShellService::SIZE_TO_CONTENT);
00970     }
00971   } else if (!strcmp(aTopic, "xul-window-registered")) {
00972     AttemptingQuit(PR_FALSE);
00973   } else if (!strcmp(aTopic, "xul-window-destroyed")) {
00974     Quit(eConsiderQuit);
00975   } else if (!strcmp(aTopic, "xul-window-visible")) {
00976     // Hide splash screen (if there is one).
00977     static PRBool splashScreenGone = PR_FALSE;
00978     if(!splashScreenGone) {
00979       HideSplashScreen();
00980       splashScreenGone = PR_TRUE;
00981     }
00982   } else {
00983     NS_ERROR("Unexpected observer topic.");
00984   }
00985 
00986   return NS_OK;
00987 }
00988