rv = DoCommandLines( cmdLineArgs, (argc == 1), &windowOpened );DoCommandLine's second parameter is a boolean value indicating whether to respect the startup prefs. Because argc == 1 is only true when there are no command line arguments, we are guaranteed to ignore the startup prefs: -turbo must be specified in this mode.
// If starting up in server mode, then we do things differently. nsCOMPtrGetIsServerMode returns true because we set nsNativeAppSupportWin::mServerMode previously in nsNativeAppSupportWin::CheckConsole():nativeApp; rv = GetNativeAppSupport(getter_AddRefs(nativeApp)); if (NS_SUCCEEDED(rv)) { PRBool isServerMode = PR_FALSE; nativeApp->GetIsServerMode(&isServerMode); if (isServerMode) { nativeApp->StartServerMode(); } PRBool shouldShowUI = PR_TRUE; nativeApp->GetShouldShowUI(&shouldShowUI); if (!shouldShowUI) { return NS_OK; } }
} else if ( strcmp( "-turbo", __argv[i] ) == 0 || strcmp( "/turbo", __argv[i] ) == 0) { // Start in server mode (and suppress splash screen). mServerMode = PR_TRUE; ... }nsNativeAppSupportWin::StartServerMode() calls nsNativeAppSupportWin::SetupSysTrayIcon() to setup the quicklaunch systray icon, and then opens a browser window. It reparents the window to ensure that it can't be seen. It passes about:blank as the window argument indicating what start page to use, as we don't want to do any networking. It also passes the turbo=yes argument to indicate that the window should be closed immediately, as we're only using it to preload. navigator.js' Startup() function, called when a browser window loads, checks for this argument and handles the closing:
} else if (window.arguments[1].indexOf("turbo=yes") != -1) { turboMode = true; } ... // Close the window now, if it's for turbo mode startup. if ( turboMode ) { // Set "command line used" flag. If we don't do this, then when a cmd line url // for a "real* invocation comes in, we will override it with the "cmd line url" // from the turbo-mode process (i.e., the home page). appCore.cmdLineURLUsed = true; // For some reason, window.close() directly doesn't work, so do it in the future. window.setTimeout( "window.close()", 100 ); return; }
} else if ( strcmp( "-turbo", __argv[i] ) == 0 || strcmp( "/turbo", __argv[i] ) == 0 ) { ... __argv[i] = "-nosplash"; // Bit of a hack, but it works! // Ignore other args. break; }The -nosplash argument is handled in nsAppRunner.cpp's GetWantSplashScreen().
rv = DoCommandLines( cmdLineArgs, (argc == 1), &windowOpened );However, this time the -turbo argument will not be specified, so argc == 1 will be true (provided no other arguments are specified).
// check if this is a restart of the browser after quiting from // the servermoded browser instance. if (!mServerMode ) { HKEY key; LONG result = ::RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_QUERY_VALUE, &key ); if ( result == ERROR_SUCCESS ) { BYTE regvalue[_MAX_PATH]; DWORD type, len = sizeof(regvalue); result = ::RegQueryValueEx( key, NS_QUICKLAUNCH_RUN_KEY, NULL, &type, regvalue, &len); ::RegCloseKey( key ); if ( result == ERROR_SUCCESS && len > 0 ) { // Make sure the filename in the quicklaunch command matches us char fileName[_MAX_PATH]; int rv = ::GetModuleFileName( NULL, fileName, sizeof fileName ); if (rv && !PL_strncasecmp(fileName, (const char *)regvalue, PR_MIN(len, strlen(fileName)))) { mServerMode = PR_TRUE; mShouldShowUI = PR_TRUE; } } } }Note that now mShouldShowUI is set to true: the user is voluntarily starting the app, so we must show UI.
// If starting up in server mode, then we do things differently. nsCOMPtrWe still call nsNativeAppSupportWin::StartServerMode(), but now that function will just setup the systray icon and bail early, without preloading, because mShouldShowUI is true:nativeApp; rv = GetNativeAppSupport(getter_AddRefs(nativeApp)); if (NS_SUCCEEDED(rv)) { PRBool isServerMode = PR_FALSE; nativeApp->GetIsServerMode(&isServerMode); if (isServerMode) { nativeApp->StartServerMode(); } PRBool shouldShowUI = PR_TRUE; nativeApp->GetShouldShowUI(&shouldShowUI); if (!shouldShowUI) { return NS_OK; } }
// Turn on system tray icon. SetupSysTrayIcon(); if (mShouldShowUI) { // We dont have to anything anymore. The native UI // will create the window return NS_OK; }
PRBool heedStartupPrefs = PR_FALSE; PRInt32 argc = 0; args->GetArgc( &argc ); if ( argc <= 1 ) { // Use startup prefs iff there are no windows currently open. nsCOMPtrwin; GetMostRecentWindow( 0, getter_AddRefs( win ) ); if ( !win ) { heedStartupPrefs = PR_TRUE; } } // Process command line options. rv = DoCommandLines( args, heedStartupPrefs, &windowOpened );
// Need profile before opening windows. rv = nativeApp->EnsureProfile(args); if (NS_FAILED(rv)) return;We had set the mForceProfileStartup flag to true in nsNativeAppSupportWin::OnLastWindowClosing() to indicate that the current "session" was over, and thus we *must* again prompt for profile choice on next launch. So nsNativeAppSupportWin::EnsureProfile() checks this flag and calls nsIAppShellService::DoProfileStartup() to display the profile picker if it's true:
PRBool haveProfile; rv = profileMgr->IsCurrentProfileAvailable(&haveProfile); if (!mForceProfileStartup && NS_SUCCEEDED(rv) && haveProfile) return NS_OK; // If the profile selection is happening, fail. PRBool doingProfileStartup; rv = profileMgr->GetIsStartingUp(&doingProfileStartup); if (NS_FAILED(rv) || doingProfileStartup) return NS_ERROR_FAILURE; rv = appShell->DoProfileStartup(args, PR_TRUE); mForceProfileStartup = PR_FALSE;
Doing such a complete shutdown and restart will ensure that session
history is cleared.
When there are no remaining windows in nsIAppShellService::UnregisterTopLevelWindow(), before calling nsIAppShellService::Quit(), we check for quicklaunch and bail early if it's on:
// Check to see if we should quit in this case. if (mNativeAppSupport) { PRBool serverMode = PR_FALSE; mNativeAppSupport->GetIsServerMode(&serverMode); if (serverMode) { mNativeAppSupport->OnLastWindowClosing(aWindow); return NS_OK; } } Quit();
if ( !mShownTurboDialog ) { PRBool showDialog = PR_TRUE; nsCOMPtrThe mShownTurboDialog flag prevents recursion: OnLastWindowClosing() would be called upon closure of the dialog. Note that we only throw the dialog if the browser.turbo.showDialog pref is true; users who don't wish to be constantly reminded of quicklaunch can check the dialog's "Don't show me this again" checkbox. It is important that we don't throw this dialog if the window being closed is our "special" window, that is, the hidden one that we open briefly to preload. A guard at the top of OnLastWindowClosing() prevents this:prefService ( do_GetService( NS_PREF_CONTRACTID, &rv ) ); if ( NS_SUCCEEDED( rv ) ) prefService->GetBoolPref( "browser.turbo.showDialog", &showDialog ); nsCOMPtr domWindowInt ( do_GetInterface( docShell ) ); if ( showDialog && domWindowInt ) { nsCOMPtr newWindow; mShownTurboDialog = PR_TRUE; domWindowInt->OpenDialog( NS_LITERAL_STRING( "chrome://navigator/content/turboDialog.xul" ), NS_LITERAL_STRING( "_blank" ), NS_LITERAL_STRING( "chrome,modal,titlebar,centerscreen,dialog" ), nsnull, getter_AddRefs( newWindow ) ); } }
// If the last window closed is our special "turbo" window made // in StartServerMode(), don't do anything. nsCOMPtrmInitialWindow is set in nsNativeAppSupportWin::StartServerMode(), where the "special" browser window is created and opened.docShell; ( void )aWindow->GetDocShell( getter_AddRefs( docShell ) ); nsCOMPtr domWindow( do_GetInterface( docShell ) ); if ( domWindow == mInitialWindow ) { mInitialWindow = nsnull; return NS_OK; }
That is certainly true (we do have exclusive access). We can ignore the distinction between WAIT_ABANDONED and WAIT_OBJECT_O because being abandoned doesn't put us at any risk and we certainly don't want to prevent starting Mozilla for the rest of eternity.
Note that we could get into this predicament ("abandoned" state) if Mozilla had ever crashed while it owned the semaphore (which is very unlikely, BTW).
BOOL Lock( DWORD timeout ) {
if ( mHandle ) {
...
mState = WaitForSingleObject( mHandle, timeout );
...
return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED;
} else {
return FALSE;
}
}
In the private section of class nsNativeAppSupportWin is a static data member to hold the calculated mutex semaphore name. This is needed so that we can use that name in the restart logic to grab the mutex before quitting.
static char mMutexName[];
Here is where that new static data member is defined and initialized
char nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
Here is the code that calculates the mutex semaphore name. It now stores
that name into the static data member instead of a local
char buffer.
// Build mutex name from app name.
::_snprintf( mMutexName, sizeof mMutexName, "%s%s",
nameBuffer, MOZ_STARTUP_MUTEX_NAME );
Mutex startupLock = Mutex( mMutexName );
Here's the bulk of the code to perform the shutdown and restart. Just read the comments.
Note the bit at the beginning that grabs the mutex semaphore. There is some risk that if we don't subsequently terminate the application this semaphore will remain owned and no other mozilla process will be able to start. That shouldn't happen, but it is a risk.
We ensure the mutex is freed as soon as we're done destroying the MessageWindow. The reason is that the mutex is effectively protecting the lookup logic in MessageWindow::MessageWindow that searches for an existing MessageWindow. We need to block that code in the second process until we've destroyed the window. But once it is destroyed, there is no reason to hold the mutex. Letting the second process run at that point will not affect how it runs.
Technically, there is some risk that the code that runs after we do the DestroyWindow here may access the same resources as code that runs during startup of the second process. But because shutdown is reasonably quick, so the risk is not too great.
Alert readers may wonder whether we could just dispense with the mutex completely, since we'd be releasing it immediately after we launch the process anyway. But we can't because the user might accidently cause yet another process to run and we need to protect against *that* process talking to us (via our MessageWindow) while we're shutting down.
nsCOMPtr<nsIAppShellService> appShell =
do_GetService( "@mozilla.org/appshell/appShellService;1",
&rv);
if ( NS_SUCCEEDED( rv ) ) {
// Instead of staying alive,
launch a new instance of the application and then
// terminate for real.
We take steps to ensure that the new instance will run
// as a "server process"
and not try to pawn off its request back on this
// instance.
// Grab mutex. We
need to allocate an instance and not free it because otherwise the
// Mutex object's destructor
will release it. Instead, we want process termination to
// release it.
(new Mutex( mMutexName ))->Lock(
-1 );
// Turn off MessageWindow
so the other process can't see us.
DWORD rc = ::DestroyWindow(
(HWND)MessageWindow() );
// Launch another instance.
char buffer[ _MAX_PATH ];
// Same application as this
one.
::GetModuleFileName( 0,
buffer, sizeof buffer );
// Clean up name so we don't
have to worry about enclosing it in quotes.
::GetShortPathName( buffer,
buffer, sizeof buffer );
nsCAutoString cmdLine( buffer
);
// The new process must
run in turbo mode (no splash screen, no window, etc.).
cmdLine.Append( " -turbo"
);
// Now do the Win32 stuff...
STARTUPINFO startupInfo;
::GetStartupInfo( &startupInfo
);
PROCESS_INFORMATION processInfo;
rc = ::CreateProcess( 0,
(LPTSTR)cmdLine.get(),
0,
0,
0,
0,
0,
0,
&startupInfo,
&processInfo );
// Turn off turbo mode and
quit the application.
SetIsServerMode( PR_FALSE
);
appShell->Quit();
// Done. This app will
now commence shutdown.
return NS_OK;
}
case TURBO_EXIT: (void)nsNativeAppSupportWin::HandleRequest( (LPBYTE)"mozilla -kill" ); break;HandleRequest's handling of this request is straightforward. It shuts down quicklaunch and, if there are no windows open and nsIAppShellService instructs us to quit on last window closing, quits Mozilla:
// try for the "-kill" argument, to shut down the server rv = args->GetCmdLineValue( "-kill", getter_Copies(arg)); if ( NS_SUCCEEDED(rv) && (const char*)arg ) { // Turn off server mode. nsCOMPtrappShell = do_GetService( "@mozilla.org/appshell/appShellService;1", &rv); if (NS_FAILED(rv)) return; nsCOMPtr native; rv = appShell->GetNativeAppSupport( getter_AddRefs( native )); if (NS_SUCCEEDED(rv)) { native->SetIsServerMode( PR_FALSE ); // close app if there are no more top-level windows. PRBool quitIfNoWindows = PR_FALSE; appShell->GetQuitOnLastWindowClosing( &quitIfNoWindows ); if (quitIfNoWindows) { nsCOMPtr win; GetMostRecentWindow( 0, getter_AddRefs( win ) ); if (!win) appShell->Quit(); } }