quick launch

Entering Quick Launch on Startup
  • Ignore startup prefs 
  • Don't open a visible browser window; preload 
  • Suppress the splash screen
  • Launching While in Quick Launch
  • Respect startup prefs 
  • Show the profile picker if necessary
  • "Quitting" While Staying in Quick Launch
  • Prevent actually quitting 
  • Notify user of quicklaunch 
  • Simulate quit to flush private data
  • Entering Quick Launch on Relaunch
  • Respect startup prefs 
  • Enter quicklaunch internally 
  • Open a visible window; don't preload
  • Toggling Quick Launch Dynamically
  • Content coming soon
  • Content coming soon
  • Content coming soon
  • Really Shutting Down Quick Launch
  • Shutting down from the systray icon menu
  • Entering Quick Launch on Startup

    We use the standard registry key Software\\Microsoft\\Windows\\CurrentVersion\\Run to load Mozilla when the computer starts. In this scenario, we want Mozilla to be silent and invisible, because the user has not indicated that he wishes to use the app yet. There are a number of things we need to do to accomplish this.

    Ignore startup prefs

    We don't want to respect the application startup prefs in the Appearance panel. main1() in nsAppRunner.cpp contains the following code:
      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.

    Don't open a visible browser window; preload

    We do, of course, need to open a browser window quickly to do preloading -- that's how quicklaunch improves performance. But we don't want the user to notice anything when he's starting his computer. nsNativeAppSupportWin::StartServerMode() handles this magic; we call it from nsAppRunner.cpp's Ensure1Window() and then bail if we've been instructed not to show UI:
      // If starting up in server mode, then we do things differently.
      nsCOMPtr 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;
          }
      }
    GetIsServerMode returns true because we set nsNativeAppSupportWin::mServerMode previously in nsNativeAppSupportWin::CheckConsole():
      } 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;
      }

    Suppress the splash screen

    In nsNativeAppSupportWin::CheckConsole(), we check for the -turbo command line argument and "replace" it internally with the -nosplash argument:
      } 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().

    Entering Quick Launch on Relaunch

    This case is fundamentally different from that described above. Here, Mozilla is being voluntarily run after the computer has already booted, after we've already completed the above steps, and after the app has been fully exited -- that is, the user chose Exit Mozilla from the systray icon or otherwise shutdown the app process. In this case, preloading isn't possible because the user wants to use Mozilla right away. We just want to enter quicklaunch internally so that when the last window is closed, the process remains open, so subsequent openings will be fast. We also want to respect the application startup prefs, unlike above, and show the systray icon.

    Respect startup prefs

    Once again, the second argument in this call in nsAppRunner.cpp's main1() determines whether we respect the application startup prefs:
      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).

    Enter quicklaunch internally

    Unlike in the "On startup" case above, Mozilla will not be started with the -turbo command line argument. We use the existence of our value in the registry key Software\\Microsoft\\Windows\\CurrentVersion\\Run to determine whether the user has quicklaunch turned on. We check for this in nsNativeAppSupportWin::CheckConsole():
      // 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.

    Open a visible window; don't preload

    nsAppRunner.cpp's Ensure1Window() is responsible for opening the window, but unlike in the "On startup" case above, nsNativeAppSupportWin::GetShouldShowUI() returns true, so we don't bail early: Once again, nsNativeAppSupportWin::StartServerMode(), called from nsAppRunner.cpp's Ensure1Window(), is responsible for setting up the system tray icon. However, because mShouldShowUI is now true, the function will bail before preloading:
      // If starting up in server mode, then we do things differently.
      nsCOMPtr 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;
          }
      }
    We still call nsNativeAppSupportWin::StartServerMode(), but now that function will just setup the systray icon and bail early, without preloading, because mShouldShowUI is true:
      // Turn on system tray icon.
      SetupSysTrayIcon();
    
      if (mShouldShowUI) {
          // We dont have to anything anymore. The native UI
          // will create the window
          return NS_OK;
      }

    Launching While in Quick Launch

    When Mozilla is running in quicklaunch but there are no windows open, the user probably expects launching the application to behave much like it would if Quick Launch weren't on. Thus, we want to respect application startup prefs and show the profile picker if there are multiple profiles.

    Respect startup prefs

    Because Mozilla is already running, nsAppRunner.cpp's main1() is not called. Instead, we handle the task of respecting application startup prefs in nsNativeAppSupportWin::HandleRequest():
      PRBool heedStartupPrefs = PR_FALSE;
      PRInt32 argc = 0;
      args->GetArgc( &argc );
      if ( argc <= 1 ) {
          // Use startup prefs iff there are no windows currently open.
          nsCOMPtr win;
          GetMostRecentWindow( 0, getter_AddRefs( win ) );
          if ( !win ) {
              heedStartupPrefs = PR_TRUE;
          }
      }
    
      // Process command line options.
      rv = DoCommandLines( args, heedStartupPrefs, &windowOpened );

    Show the profile picker if necessary

    The following code in nsNativeAppSupportWin::HandleRequest() ensures that we have a profile to use (it will thus show the profile picker if there multiple profiles exist and the -P "" command line argument wasn't specified):
      // 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;

    Toggling Quick Launch Dynamically

    <content pending since this area just got rewritten>

    "Quitting" While Staying in Quick Launch

    When the user is in quicklaunch and he closed the last window, we will actually quit the current Mozilla process and then restart a new one.  The new process will be in the state as the process that was launched on startup.  That is, it will be silent and invisible because the user has not yet indicated that he wants to use the app.

    Doing such a complete shutdown and restart will ensure that session history is cleared.

    Prevent actually quitting

    This section is no longer relevant.  It existed in the old turbo model when we didn't actually quite the Mozilla process when the user closed the last window.  The code presented in this section is still in the product (it's not clear why this isn't causing a problem) so it will be described here.

    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();

    Notify user of quicklaunch

    It's important to notify the user that the app hasn't actually been shut down, and to give instructions about how to fully shut down. In nsNativeAppSupportWin::OnLastWindowClosing(), called when the last window is closed while in quicklaunch (see above code snippet), we throw a dialog (the FE for which is in turboDialog.xul/js):
      if ( !mShownTurboDialog ) {
          PRBool showDialog = PR_TRUE;
          nsCOMPtr 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 ) );
          }
      }
    The 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:
      // If the last window closed is our special "turbo" window made
      // in StartServerMode(), don't do anything.
      nsCOMPtr docShell;
      ( void )aWindow->GetDocShell( getter_AddRefs( docShell ) );
      nsCOMPtr domWindow( do_GetInterface( docShell ) );
      if ( domWindow == mInitialWindow ) {
          mInitialWindow = nsnull;
          return NS_OK;
      }
    mInitialWindow is set in nsNativeAppSupportWin::StartServerMode(), where the "special" browser window is created and opened.

    Quiting and restarting the process

    The new "restart" code grabs the mutex semaphore and doesn't release it till process termination (see the code below). As a consequence, the restarted process, when it attempts to obtain this mutex, will get it due to WAIT_ABANDONED. So code was added to the Lock method of Mutex struct in nsNativeAppSupportWin to accept WAIT_ABANDONED as a successful return value (meaning, we obtained exclusive access).

    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;
        }
     


    Really Shutting Down Quick Launch

    The standard way of exiting quicklaunch is choosing Exit Mozilla from the systray icon menu.

    Shutting down from the systray icon menu

    We handle the selection of the Exit Mozilla menuitem (and all of the items in the menu) in nsNativeAppSupportWin.cpp's WindowProc(). nsNativeAppSupportWin::HandleRequest() is called with a request of "mozilla -kill":
      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.
        nsCOMPtr appShell =
          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();
          }
        }

    Blake Ross