toolkit/components/startup/nsAppStartup.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsAppStartup.h"
     8 #include "nsIAppShellService.h"
     9 #include "nsPIDOMWindow.h"
    10 #include "nsIInterfaceRequestor.h"
    11 #include "nsIFile.h"
    12 #include "nsIObserverService.h"
    13 #include "nsIPrefBranch.h"
    14 #include "nsIPrefService.h"
    15 #include "nsIPromptService.h"
    16 #include "nsIStringBundle.h"
    17 #include "nsISupportsPrimitives.h"
    18 #include "nsIWebBrowserChrome.h"
    19 #include "nsIWindowMediator.h"
    20 #include "nsIWindowWatcher.h"
    21 #include "nsIXULRuntime.h"
    22 #include "nsIXULWindow.h"
    23 #include "nsNativeCharsetUtils.h"
    24 #include "nsThreadUtils.h"
    25 #include "nsAutoPtr.h"
    26 #include "nsString.h"
    27 #include "mozilla/Preferences.h"
    28 #include "GeckoProfiler.h"
    30 #include "prprf.h"
    31 #include "nsIInterfaceRequestorUtils.h"
    32 #include "nsWidgetsCID.h"
    33 #include "nsAppShellCID.h"
    34 #include "nsXPCOMCIDInternal.h"
    35 #include "mozilla/Services.h"
    36 #include "nsIXPConnect.h"
    37 #include "jsapi.h"
    38 #include "prenv.h"
    39 #include "nsAppDirectoryServiceDefs.h"
    41 #if defined(XP_WIN)
    42 // Prevent collisions with nsAppStartup::GetStartupInfo()
    43 #undef GetStartupInfo
    44 #endif
    46 #include "mozilla/IOInterposer.h"
    47 #include "mozilla/Telemetry.h"
    48 #include "mozilla/StartupTimeline.h"
    50 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
    52 #define kPrefLastSuccess "toolkit.startup.last_success"
    53 #define kPrefMaxResumedCrashes "toolkit.startup.max_resumed_crashes"
    54 #define kPrefRecentCrashes "toolkit.startup.recent_crashes"
    55 #define kPrefAlwaysUseSafeMode "toolkit.startup.always_use_safe_mode"
    57 #if defined(XP_WIN)
    58 #include "mozilla/perfprobe.h"
    59 /**
    60  * Events sent to the system for profiling purposes
    61  */
    62 //Keep them syncronized with the .mof file
    64 //Process-wide GUID, used by the OS to differentiate sources
    65 // {509962E0-406B-46F4-99BA-5A009F8D2225}
    66 //Keep it synchronized with the .mof file
    67 #define NS_APPLICATION_TRACING_CID \
    68   { 0x509962E0, 0x406B, 0x46F4, \
    69   { 0x99, 0xBA, 0x5A, 0x00, 0x9F, 0x8D, 0x22, 0x25} }
    71 //Event-specific GUIDs, used by the OS to differentiate events
    72 // {A3DA04E0-57D7-482A-A1C1-61DA5F95BACB}
    73 #define NS_PLACES_INIT_COMPLETE_EVENT_CID \
    74   { 0xA3DA04E0, 0x57D7, 0x482A, \
    75   { 0xA1, 0xC1, 0x61, 0xDA, 0x5F, 0x95, 0xBA, 0xCB} }
    76 // {917B96B1-ECAD-4DAB-A760-8D49027748AE}
    77 #define NS_SESSION_STORE_WINDOW_RESTORED_EVENT_CID \
    78   { 0x917B96B1, 0xECAD, 0x4DAB, \
    79   { 0xA7, 0x60, 0x8D, 0x49, 0x02, 0x77, 0x48, 0xAE} }
    80 // {26D1E091-0AE7-4F49-A554-4214445C505C}
    81 #define NS_XPCOM_SHUTDOWN_EVENT_CID \
    82   { 0x26D1E091, 0x0AE7, 0x4F49, \
    83   { 0xA5, 0x54, 0x42, 0x14, 0x44, 0x5C, 0x50, 0x5C} }
    85 static NS_DEFINE_CID(kApplicationTracingCID,
    86   NS_APPLICATION_TRACING_CID);
    87 static NS_DEFINE_CID(kPlacesInitCompleteCID,
    88   NS_PLACES_INIT_COMPLETE_EVENT_CID);
    89 static NS_DEFINE_CID(kSessionStoreWindowRestoredCID,
    90   NS_SESSION_STORE_WINDOW_RESTORED_EVENT_CID);
    91 static NS_DEFINE_CID(kXPCOMShutdownCID,
    92   NS_XPCOM_SHUTDOWN_EVENT_CID);  
    93 #endif //defined(XP_WIN)
    95 using namespace mozilla;
    97 uint32_t gRestartMode = 0;
    99 class nsAppExitEvent : public nsRunnable {
   100 private:
   101   nsRefPtr<nsAppStartup> mService;
   103 public:
   104   nsAppExitEvent(nsAppStartup *service) : mService(service) {}
   106   NS_IMETHOD Run() {
   107     // Tell the appshell to exit
   108     mService->mAppShell->Exit();
   110     mService->mRunning = false;
   111     return NS_OK;
   112   }
   113 };
   115 /**
   116  * Computes an approximation of the absolute time represented by @a stamp
   117  * which is comparable to those obtained via PR_Now(). If the current absolute
   118  * time varies a lot (e.g. DST adjustments) since the first call then the
   119  * resulting times may be inconsistent.
   120  *
   121  * @param stamp The timestamp to be converted
   122  * @returns The converted timestamp
   123  */
   124 uint64_t ComputeAbsoluteTimestamp(PRTime prnow, TimeStamp now, TimeStamp stamp)
   125 {
   126   static PRTime sAbsoluteNow = PR_Now();
   127   static TimeStamp sMonotonicNow = TimeStamp::Now();
   129   return sAbsoluteNow - (sMonotonicNow - stamp).ToMicroseconds();
   130 }
   132 //
   133 // nsAppStartup
   134 //
   136 nsAppStartup::nsAppStartup() :
   137   mConsiderQuitStopper(0),
   138   mRunning(false),
   139   mShuttingDown(false),
   140   mStartingUp(true),
   141   mAttemptingQuit(false),
   142   mRestart(false),
   143   mInterrupted(false),
   144   mIsSafeModeNecessary(false),
   145   mStartupCrashTrackingEnded(false),
   146   mRestartTouchEnvironment(false)
   147 { }
   150 nsresult
   151 nsAppStartup::Init()
   152 {
   153   nsresult rv;
   155   // Create widget application shell
   156   mAppShell = do_GetService(kAppShellCID, &rv);
   157   NS_ENSURE_SUCCESS(rv, rv);
   159   nsCOMPtr<nsIObserverService> os =
   160     mozilla::services::GetObserverService();
   161   if (!os)
   162     return NS_ERROR_FAILURE;
   164   os->AddObserver(this, "quit-application-forced", true);
   165   os->AddObserver(this, "sessionstore-init-started", true);
   166   os->AddObserver(this, "sessionstore-windows-restored", true);
   167   os->AddObserver(this, "profile-change-teardown", true);
   168   os->AddObserver(this, "xul-window-registered", true);
   169   os->AddObserver(this, "xul-window-destroyed", true);
   170   os->AddObserver(this, "xpcom-shutdown", true);
   172 #if defined(XP_WIN)
   173   os->AddObserver(this, "places-init-complete", true);
   174   // This last event is only interesting to us for xperf-based measures
   176   // Initialize interaction with profiler
   177   mProbesManager =
   178     new ProbeManager(
   179                      kApplicationTracingCID,
   180                      NS_LITERAL_CSTRING("Application startup probe"));
   181   // Note: The operation is meant mostly for in-house profiling.
   182   // Therefore, we do not warn if probes manager cannot be initialized
   184   if (mProbesManager) {
   185     mPlacesInitCompleteProbe =
   186       mProbesManager->
   187       GetProbe(kPlacesInitCompleteCID,
   188                NS_LITERAL_CSTRING("places-init-complete"));
   189     NS_WARN_IF_FALSE(mPlacesInitCompleteProbe,
   190                      "Cannot initialize probe 'places-init-complete'");
   192     mSessionWindowRestoredProbe =
   193       mProbesManager->
   194       GetProbe(kSessionStoreWindowRestoredCID,
   195                NS_LITERAL_CSTRING("sessionstore-windows-restored"));
   196     NS_WARN_IF_FALSE(mSessionWindowRestoredProbe,
   197                      "Cannot initialize probe 'sessionstore-windows-restored'");
   199     mXPCOMShutdownProbe =
   200       mProbesManager->
   201       GetProbe(kXPCOMShutdownCID,
   202                NS_LITERAL_CSTRING("xpcom-shutdown"));
   203     NS_WARN_IF_FALSE(mXPCOMShutdownProbe,
   204                      "Cannot initialize probe 'xpcom-shutdown'");
   206     rv = mProbesManager->StartSession();
   207     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
   208                      "Cannot initialize system probe manager");
   209   }
   210 #endif //defined(XP_WIN)
   212   return NS_OK;
   213 }
   216 //
   217 // nsAppStartup->nsISupports
   218 //
   220 NS_IMPL_ISUPPORTS(nsAppStartup,
   221                   nsIAppStartup,
   222                   nsIWindowCreator,
   223                   nsIWindowCreator2,
   224                   nsIObserver,
   225                   nsISupportsWeakReference)
   228 //
   229 // nsAppStartup->nsIAppStartup
   230 //
   232 NS_IMETHODIMP
   233 nsAppStartup::CreateHiddenWindow()
   234 {
   235 #ifdef MOZ_WIDGET_GONK
   236   return NS_OK;
   237 #else
   238   nsCOMPtr<nsIAppShellService> appShellService
   239     (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
   240   NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE);
   242   return appShellService->CreateHiddenWindow();
   243 #endif
   244 }
   247 NS_IMETHODIMP
   248 nsAppStartup::DestroyHiddenWindow()
   249 {
   250 #ifdef MOZ_WIDGET_GONK
   251   return NS_OK;
   252 #else
   253   nsCOMPtr<nsIAppShellService> appShellService
   254     (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
   255   NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE);
   257   return appShellService->DestroyHiddenWindow();
   258 #endif
   259 }
   261 NS_IMETHODIMP
   262 nsAppStartup::Run(void)
   263 {
   264   NS_ASSERTION(!mRunning, "Reentrant appstartup->Run()");
   266   // If we have no windows open and no explicit calls to
   267   // enterLastWindowClosingSurvivalArea, or somebody has explicitly called
   268   // quit, don't bother running the event loop which would probably leave us
   269   // with a zombie process.
   271   if (!mShuttingDown && mConsiderQuitStopper != 0) {
   272 #ifdef XP_MACOSX
   273     EnterLastWindowClosingSurvivalArea();
   274 #endif
   276     mRunning = true;
   278     nsresult rv = mAppShell->Run();
   279     if (NS_FAILED(rv))
   280       return rv;
   281   }
   283   nsresult retval = NS_OK;
   284   if (mRestartTouchEnvironment) {
   285     retval = NS_SUCCESS_RESTART_METRO_APP;
   286   } else if (mRestart) {
   287     retval = NS_SUCCESS_RESTART_APP;
   288   }
   290   return retval;
   291 }
   295 NS_IMETHODIMP
   296 nsAppStartup::Quit(uint32_t aMode)
   297 {
   298   uint32_t ferocity = (aMode & 0xF);
   300   // Quit the application. We will asynchronously call the appshell's
   301   // Exit() method via nsAppExitEvent to allow one last pass
   302   // through any events in the queue. This guarantees a tidy cleanup.
   303   nsresult rv = NS_OK;
   304   bool postedExitEvent = false;
   306   if (mShuttingDown)
   307     return NS_OK;
   309   // If we're considering quitting, we will only do so if:
   310   if (ferocity == eConsiderQuit) {
   311 #ifdef XP_MACOSX
   312     nsCOMPtr<nsIAppShellService> appShell
   313       (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
   314     bool hasHiddenPrivateWindow = false;
   315     if (appShell) {
   316       appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow);
   317     }
   318     int32_t suspiciousCount = hasHiddenPrivateWindow ? 2 : 1;
   319 #endif
   321     if (mConsiderQuitStopper == 0) {
   322       // there are no windows...
   323       ferocity = eAttemptQuit;
   324     }
   325 #ifdef XP_MACOSX
   326     else if (mConsiderQuitStopper == suspiciousCount) {
   327       // ... or there is only a hiddenWindow left, and it's useless:
   329       // Failure shouldn't be fatal, but will abort quit attempt:
   330       if (!appShell)
   331         return NS_OK;
   333       bool usefulHiddenWindow;
   334       appShell->GetApplicationProvidedHiddenWindow(&usefulHiddenWindow);
   335       nsCOMPtr<nsIXULWindow> hiddenWindow;
   336       appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
   337       // If the remaining windows are useful, we won't quit:
   338       nsCOMPtr<nsIXULWindow> hiddenPrivateWindow;
   339       if (hasHiddenPrivateWindow) {
   340         appShell->GetHiddenPrivateWindow(getter_AddRefs(hiddenPrivateWindow));
   341         if ((!hiddenWindow && !hiddenPrivateWindow) || usefulHiddenWindow)
   342           return NS_OK;
   343       } else if (!hiddenWindow || usefulHiddenWindow) {
   344         return NS_OK;
   345       }
   347       ferocity = eAttemptQuit;
   348     }
   349 #endif
   350   }
   352   nsCOMPtr<nsIObserverService> obsService;
   353   if (ferocity == eAttemptQuit || ferocity == eForceQuit) {
   355     nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
   356     nsCOMPtr<nsIWindowMediator> mediator (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
   357     if (mediator) {
   358       mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
   359       if (windowEnumerator) {
   360         bool more;
   361         while (windowEnumerator->HasMoreElements(&more), more) {
   362           nsCOMPtr<nsISupports> window;
   363           windowEnumerator->GetNext(getter_AddRefs(window));
   364           nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(window));
   365           if (domWindow) {
   366             if (!domWindow->CanClose())
   367               return NS_OK;
   368           }
   369         }
   370       }
   371     }
   373     PROFILER_MARKER("Shutdown start");
   374     mozilla::RecordShutdownStartTimeStamp();
   375     mShuttingDown = true;
   376     if (!mRestart) {
   377       mRestart = (aMode & eRestart) != 0;
   378       gRestartMode = (aMode & 0xF0);
   379     }
   381     if (!mRestartTouchEnvironment) {
   382       mRestartTouchEnvironment = (aMode & eRestartTouchEnvironment) != 0;
   383       gRestartMode = (aMode & 0xF0);
   384     }
   386     if (mRestart || mRestartTouchEnvironment) {
   387       // Mark the next startup as a restart.
   388       PR_SetEnv("MOZ_APP_RESTART=1");
   390       /* Firefox-restarts reuse the process so regular process start-time isn't
   391          a useful indicator of startup time anymore. */
   392       TimeStamp::RecordProcessRestart();
   393     }
   395     obsService = mozilla::services::GetObserverService();
   397     if (!mAttemptingQuit) {
   398       mAttemptingQuit = true;
   399 #ifdef XP_MACOSX
   400       // now even the Mac wants to quit when the last window is closed
   401       ExitLastWindowClosingSurvivalArea();
   402 #endif
   403       if (obsService)
   404         obsService->NotifyObservers(nullptr, "quit-application-granted", nullptr);
   405     }
   407     /* Enumerate through each open window and close it. It's important to do
   408        this before we forcequit because this can control whether we really quit
   409        at all. e.g. if one of these windows has an unload handler that
   410        opens a new window. Ugh. I know. */
   411     CloseAllWindows();
   413     if (mediator) {
   414       if (ferocity == eAttemptQuit) {
   415         ferocity = eForceQuit; // assume success
   417         /* Were we able to immediately close all windows? if not, eAttemptQuit
   418            failed. This could happen for a variety of reasons; in fact it's
   419            very likely. Perhaps we're being called from JS and the window->Close
   420            method hasn't had a chance to wrap itself up yet. So give up.
   421            We'll return (with eConsiderQuit) as the remaining windows are
   422            closed. */
   423         mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
   424         if (windowEnumerator) {
   425           bool more;
   426           while (windowEnumerator->HasMoreElements(&more), more) {
   427             /* we can't quit immediately. we'll try again as the last window
   428                finally closes. */
   429             ferocity = eAttemptQuit;
   430             nsCOMPtr<nsISupports> window;
   431             windowEnumerator->GetNext(getter_AddRefs(window));
   432             nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(window);
   433             if (domWindow) {
   434               bool closed = false;
   435               domWindow->GetClosed(&closed);
   436               if (!closed) {
   437                 rv = NS_ERROR_FAILURE;
   438                 break;
   439               }
   440             }
   441           }
   442         }
   443       }
   444     }
   445   }
   447   if (ferocity == eForceQuit) {
   448     // do it!
   450     // No chance of the shutdown being cancelled from here on; tell people
   451     // we're shutting down for sure while all services are still available.
   452     if (obsService) {
   453       NS_NAMED_LITERAL_STRING(shutdownStr, "shutdown");
   454       NS_NAMED_LITERAL_STRING(restartStr, "restart");
   455       obsService->NotifyObservers(nullptr, "quit-application",
   456         (mRestart || mRestartTouchEnvironment) ?
   457          restartStr.get() : shutdownStr.get());
   458     }
   460     if (!mRunning) {
   461       postedExitEvent = true;
   462     }
   463     else {
   464       // no matter what, make sure we send the exit event.  If
   465       // worst comes to worst, we'll do a leaky shutdown but we WILL
   466       // shut down. Well, assuming that all *this* stuff works ;-).
   467       nsCOMPtr<nsIRunnable> event = new nsAppExitEvent(this);
   468       rv = NS_DispatchToCurrentThread(event);
   469       if (NS_SUCCEEDED(rv)) {
   470         postedExitEvent = true;
   471       }
   472       else {
   473         NS_WARNING("failed to dispatch nsAppExitEvent");
   474       }
   475     }
   476   }
   478   // turn off the reentrancy check flag, but not if we have
   479   // more asynchronous work to do still.
   480   if (!postedExitEvent)
   481     mShuttingDown = false;
   482   return rv;
   483 }
   486 void
   487 nsAppStartup::CloseAllWindows()
   488 {
   489   nsCOMPtr<nsIWindowMediator> mediator
   490     (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
   492   nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
   494   mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator));
   496   if (!windowEnumerator)
   497     return;
   499   bool more;
   500   while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) {
   501     nsCOMPtr<nsISupports> isupports;
   502     if (NS_FAILED(windowEnumerator->GetNext(getter_AddRefs(isupports))))
   503       break;
   505     nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(isupports);
   506     NS_ASSERTION(window, "not an nsPIDOMWindow");
   507     if (window)
   508       window->ForceClose();
   509   }
   510 }
   512 NS_IMETHODIMP
   513 nsAppStartup::EnterLastWindowClosingSurvivalArea(void)
   514 {
   515   ++mConsiderQuitStopper;
   516   return NS_OK;
   517 }
   520 NS_IMETHODIMP
   521 nsAppStartup::ExitLastWindowClosingSurvivalArea(void)
   522 {
   523   NS_ASSERTION(mConsiderQuitStopper > 0, "consider quit stopper out of bounds");
   524   --mConsiderQuitStopper;
   526   if (mRunning)
   527     Quit(eConsiderQuit);
   529   return NS_OK;
   530 }
   532 //
   533 // nsAppStartup->nsIAppStartup2
   534 //
   536 NS_IMETHODIMP
   537 nsAppStartup::GetShuttingDown(bool *aResult)
   538 {
   539   *aResult = mShuttingDown;
   540   return NS_OK;
   541 }
   543 NS_IMETHODIMP
   544 nsAppStartup::GetStartingUp(bool *aResult)
   545 {
   546   *aResult = mStartingUp;
   547   return NS_OK;
   548 }
   550 NS_IMETHODIMP
   551 nsAppStartup::DoneStartingUp()
   552 {
   553   // This must be called once at most
   554   MOZ_ASSERT(mStartingUp);
   556   mStartingUp = false;
   557   return NS_OK;
   558 }
   560 NS_IMETHODIMP
   561 nsAppStartup::GetRestarting(bool *aResult)
   562 {
   563   *aResult = mRestart;
   564   return NS_OK;
   565 }
   567 NS_IMETHODIMP
   568 nsAppStartup::GetWasRestarted(bool *aResult)
   569 {
   570   char *mozAppRestart = PR_GetEnv("MOZ_APP_RESTART");
   572   /* When calling PR_SetEnv() with an empty value the existing variable may
   573    * be unset or set to the empty string depending on the underlying platform
   574    * thus we have to check if the variable is present and not empty. */
   575   *aResult = mozAppRestart && (strcmp(mozAppRestart, "") != 0);
   577   return NS_OK;
   578 }
   580 NS_IMETHODIMP
   581 nsAppStartup::GetRestartingTouchEnvironment(bool *aResult)
   582 {
   583   NS_ENSURE_ARG_POINTER(aResult);
   584   *aResult = mRestartTouchEnvironment;
   585   return NS_OK;
   586 }
   588 NS_IMETHODIMP
   589 nsAppStartup::SetInterrupted(bool aInterrupted)
   590 {
   591   mInterrupted = aInterrupted;
   592   return NS_OK;
   593 }
   595 NS_IMETHODIMP
   596 nsAppStartup::GetInterrupted(bool *aInterrupted)
   597 {
   598   *aInterrupted = mInterrupted;
   599   return NS_OK;
   600 }
   602 //
   603 // nsAppStartup->nsIWindowCreator
   604 //
   606 NS_IMETHODIMP
   607 nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent,
   608                                  uint32_t aChromeFlags,
   609                                  nsIWebBrowserChrome **_retval)
   610 {
   611   bool cancel;
   612   return CreateChromeWindow2(aParent, aChromeFlags, 0, 0, &cancel, _retval);
   613 }
   616 //
   617 // nsAppStartup->nsIWindowCreator2
   618 //
   620 NS_IMETHODIMP
   621 nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
   622                                   uint32_t aChromeFlags,
   623                                   uint32_t aContextFlags,
   624                                   nsIURI *aURI,
   625                                   bool *aCancel,
   626                                   nsIWebBrowserChrome **_retval)
   627 {
   628   NS_ENSURE_ARG_POINTER(aCancel);
   629   NS_ENSURE_ARG_POINTER(_retval);
   630   *aCancel = false;
   631   *_retval = 0;
   633   // Non-modal windows cannot be opened if we are attempting to quit
   634   if (mAttemptingQuit && (aChromeFlags & nsIWebBrowserChrome::CHROME_MODAL) == 0)
   635     return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
   637   nsCOMPtr<nsIXULWindow> newWindow;
   639   if (aParent) {
   640     nsCOMPtr<nsIXULWindow> xulParent(do_GetInterface(aParent));
   641     NS_ASSERTION(xulParent, "window created using non-XUL parent. that's unexpected, but may work.");
   643     if (xulParent)
   644       xulParent->CreateNewWindow(aChromeFlags, getter_AddRefs(newWindow));
   645     // And if it fails, don't try again without a parent. It could fail
   646     // intentionally (bug 115969).
   647   } else { // try using basic methods:
   648     /* You really shouldn't be making dependent windows without a parent.
   649       But unparented modal (and therefore dependent) windows happen
   650       in our codebase, so we allow it after some bellyaching: */
   651     if (aChromeFlags & nsIWebBrowserChrome::CHROME_DEPENDENT)
   652       NS_WARNING("dependent window created without a parent");
   654     nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
   655     if (!appShell)
   656       return NS_ERROR_FAILURE;
   658     appShell->CreateTopLevelWindow(0, 0, aChromeFlags,
   659                                    nsIAppShellService::SIZE_TO_CONTENT,
   660                                    nsIAppShellService::SIZE_TO_CONTENT,
   661                                    getter_AddRefs(newWindow));
   662   }
   664   // if anybody gave us anything to work with, use it
   665   if (newWindow) {
   666     newWindow->SetContextFlags(aContextFlags);
   667     nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(newWindow));
   668     if (thing)
   669       CallGetInterface(thing.get(), _retval);
   670   }
   672   return *_retval ? NS_OK : NS_ERROR_FAILURE;
   673 }
   676 //
   677 // nsAppStartup->nsIObserver
   678 //
   680 NS_IMETHODIMP
   681 nsAppStartup::Observe(nsISupports *aSubject,
   682                       const char *aTopic, const char16_t *aData)
   683 {
   684   NS_ASSERTION(mAppShell, "appshell service notified before appshell built");
   685   if (!strcmp(aTopic, "quit-application-forced")) {
   686     mShuttingDown = true;
   687   }
   688   else if (!strcmp(aTopic, "profile-change-teardown")) {
   689     if (!mShuttingDown) {
   690       EnterLastWindowClosingSurvivalArea();
   691       CloseAllWindows();
   692       ExitLastWindowClosingSurvivalArea();
   693     }
   694   } else if (!strcmp(aTopic, "xul-window-registered")) {
   695     EnterLastWindowClosingSurvivalArea();
   696   } else if (!strcmp(aTopic, "xul-window-destroyed")) {
   697     ExitLastWindowClosingSurvivalArea();
   698   } else if (!strcmp(aTopic, "sessionstore-windows-restored")) {
   699     StartupTimeline::Record(StartupTimeline::SESSION_RESTORED);
   700     IOInterposer::EnteringNextStage();
   701 #if defined(XP_WIN)
   702     if (mSessionWindowRestoredProbe) {
   703       mSessionWindowRestoredProbe->Trigger();
   704     }
   705   } else if (!strcmp(aTopic, "places-init-complete")) {
   706     if (mPlacesInitCompleteProbe) {
   707       mPlacesInitCompleteProbe->Trigger();
   708     }
   709 #endif //defined(XP_WIN)
   710   } else if (!strcmp(aTopic, "sessionstore-init-started")) {
   711     StartupTimeline::Record(StartupTimeline::SESSION_RESTORE_INIT);
   712   } else if (!strcmp(aTopic, "xpcom-shutdown")) {
   713     IOInterposer::EnteringNextStage();
   714 #if defined(XP_WIN)
   715     if (mXPCOMShutdownProbe) {
   716       mXPCOMShutdownProbe->Trigger();
   717     }
   718 #endif // defined(XP_WIN)
   719   } else {
   720     NS_ERROR("Unexpected observer topic.");
   721   }
   723   return NS_OK;
   724 }
   726 NS_IMETHODIMP
   727 nsAppStartup::GetStartupInfo(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval)
   728 {
   729   JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
   731   aRetval.setObject(*obj);
   733   TimeStamp procTime = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
   734   TimeStamp now = TimeStamp::Now();
   735   PRTime absNow = PR_Now();
   737   if (procTime.IsNull()) {
   738     bool error = false;
   740     procTime = TimeStamp::ProcessCreation(error);
   742     if (error) {
   743       Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS,
   744         StartupTimeline::PROCESS_CREATION);
   745     }
   747     StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, procTime);
   748   }
   750   for (int i = StartupTimeline::PROCESS_CREATION;
   751        i < StartupTimeline::MAX_EVENT_ID;
   752        ++i)
   753   {
   754     StartupTimeline::Event ev = static_cast<StartupTimeline::Event>(i);
   755     TimeStamp stamp = StartupTimeline::Get(ev);
   757     if (stamp.IsNull() && (ev == StartupTimeline::MAIN)) {
   758       // Always define main to aid with bug 689256.
   759       stamp = procTime;
   760       MOZ_ASSERT(!stamp.IsNull());
   761       Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS,
   762         StartupTimeline::MAIN);
   763     }
   765     if (!stamp.IsNull()) {
   766       if (stamp >= procTime) {
   767         PRTime prStamp = ComputeAbsoluteTimestamp(absNow, now, stamp)
   768           / PR_USEC_PER_MSEC;
   769         JS::Rooted<JSObject*> date(aCx, JS_NewDateObjectMsec(aCx, prStamp));
   770         JS_DefineProperty(aCx, obj, StartupTimeline::Describe(ev), date, JSPROP_ENUMERATE);
   771       } else {
   772         Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, ev);
   773       }
   774     }
   775   }
   777   return NS_OK;
   778 }
   780 NS_IMETHODIMP
   781 nsAppStartup::GetAutomaticSafeModeNecessary(bool *_retval)
   782 {
   783   NS_ENSURE_ARG_POINTER(_retval);
   785   bool alwaysSafe = false;
   786   Preferences::GetBool(kPrefAlwaysUseSafeMode, &alwaysSafe);
   788   if (!alwaysSafe) {
   789 #if DEBUG
   790     mIsSafeModeNecessary = false;
   791 #else
   792     mIsSafeModeNecessary &= !PR_GetEnv("MOZ_DISABLE_AUTO_SAFE_MODE");
   793 #endif
   794   }
   796   *_retval = mIsSafeModeNecessary;
   797   return NS_OK;
   798 }
   800 NS_IMETHODIMP
   801 nsAppStartup::TrackStartupCrashBegin(bool *aIsSafeModeNecessary)
   802 {
   803   const int32_t MAX_TIME_SINCE_STARTUP = 6 * 60 * 60 * 1000;
   804   const int32_t MAX_STARTUP_BUFFER = 10;
   805   nsresult rv;
   807   mStartupCrashTrackingEnded = false;
   809   StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_BEGIN);
   811   bool hasLastSuccess = Preferences::HasUserValue(kPrefLastSuccess);
   812   if (!hasLastSuccess) {
   813     // Clear so we don't get stuck with SafeModeNecessary returning true if we
   814     // have had too many recent crashes and the last success pref is missing.
   815     Preferences::ClearUser(kPrefRecentCrashes);
   816     return NS_ERROR_NOT_AVAILABLE;
   817   }
   819   bool inSafeMode = false;
   820   nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
   821   NS_ENSURE_TRUE(xr, NS_ERROR_FAILURE);
   823   xr->GetInSafeMode(&inSafeMode);
   825   PRTime replacedLockTime;
   826   rv = xr->GetReplacedLockTime(&replacedLockTime);
   828   if (NS_FAILED(rv) || !replacedLockTime) {
   829     if (!inSafeMode)
   830       Preferences::ClearUser(kPrefRecentCrashes);
   831     GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
   832     return NS_OK;
   833   }
   835   // check whether safe mode is necessary
   836   int32_t maxResumedCrashes = -1;
   837   rv = Preferences::GetInt(kPrefMaxResumedCrashes, &maxResumedCrashes);
   838   NS_ENSURE_SUCCESS(rv, NS_OK);
   840   int32_t recentCrashes = 0;
   841   Preferences::GetInt(kPrefRecentCrashes, &recentCrashes);
   842   mIsSafeModeNecessary = (recentCrashes > maxResumedCrashes && maxResumedCrashes != -1);
   844   // Bug 731613 - Don't check if the last startup was a crash if XRE_PROFILE_PATH is set.  After
   845   // profile manager, the profile lock's mod. time has been changed so can't be used on this startup.
   846   // After a restart, it's safe to assume the last startup was successful.
   847   char *xreProfilePath = PR_GetEnv("XRE_PROFILE_PATH");
   848   if (xreProfilePath) {
   849     GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
   850     return NS_ERROR_NOT_AVAILABLE;
   851   }
   853   // time of last successful startup
   854   int32_t lastSuccessfulStartup;
   855   rv = Preferences::GetInt(kPrefLastSuccess, &lastSuccessfulStartup);
   856   NS_ENSURE_SUCCESS(rv, rv);
   858   int32_t lockSeconds = (int32_t)(replacedLockTime / PR_MSEC_PER_SEC);
   860   // started close enough to good startup so call it good
   861   if (lockSeconds <= lastSuccessfulStartup + MAX_STARTUP_BUFFER
   862       && lockSeconds >= lastSuccessfulStartup - MAX_STARTUP_BUFFER) {
   863     GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
   864     return NS_OK;
   865   }
   867   // sanity check that the pref set at last success is not greater than the current time
   868   if (PR_Now() / PR_USEC_PER_SEC <= lastSuccessfulStartup)
   869     return NS_ERROR_FAILURE;
   871   // The last startup was a crash so include it in the count regardless of when it happened.
   872   Telemetry::Accumulate(Telemetry::STARTUP_CRASH_DETECTED, true);
   874   if (inSafeMode) {
   875     GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
   876     return NS_OK;
   877   }
   879   PRTime now = (PR_Now() / PR_USEC_PER_MSEC);
   880   // if the last startup attempt which crashed was in the last 6 hours
   881   if (replacedLockTime >= now - MAX_TIME_SINCE_STARTUP) {
   882     NS_WARNING("Last startup was detected as a crash.");
   883     recentCrashes++;
   884     rv = Preferences::SetInt(kPrefRecentCrashes, recentCrashes);
   885   } else {
   886     // Otherwise ignore that crash and all previous since it may not be applicable anymore
   887     // and we don't want someone to get stuck in safe mode if their prefs are read-only.
   888     rv = Preferences::ClearUser(kPrefRecentCrashes);
   889   }
   890   NS_ENSURE_SUCCESS(rv, rv);
   892   // recalculate since recent crashes count may have changed above
   893   mIsSafeModeNecessary = (recentCrashes > maxResumedCrashes && maxResumedCrashes != -1);
   895   nsCOMPtr<nsIPrefService> prefs = Preferences::GetService();
   896   rv = prefs->SavePrefFile(nullptr); // flush prefs to disk since we are tracking crashes
   897   NS_ENSURE_SUCCESS(rv, rv);
   899   GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
   900   return rv;
   901 }
   903 NS_IMETHODIMP
   904 nsAppStartup::TrackStartupCrashEnd()
   905 {
   906   bool inSafeMode = false;
   907   nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
   908   if (xr)
   909     xr->GetInSafeMode(&inSafeMode);
   911   // return if we already ended or we're restarting into safe mode
   912   if (mStartupCrashTrackingEnded || (mIsSafeModeNecessary && !inSafeMode))
   913     return NS_OK;
   914   mStartupCrashTrackingEnded = true;
   916   StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_END);
   918   // Use the timestamp of XRE_main as an approximation for the lock file timestamp.
   919   // See MAX_STARTUP_BUFFER for the buffer time period.
   920   TimeStamp mainTime = StartupTimeline::Get(StartupTimeline::MAIN);
   921   TimeStamp now = TimeStamp::Now();
   922   PRTime prNow = PR_Now();
   923   nsresult rv;
   925   if (mainTime.IsNull()) {
   926     NS_WARNING("Could not get StartupTimeline::MAIN time.");
   927   } else {
   928     uint64_t lockFileTime = ComputeAbsoluteTimestamp(prNow, now, mainTime);
   930     rv = Preferences::SetInt(kPrefLastSuccess,
   931       (int32_t)(lockFileTime / PR_USEC_PER_SEC));
   933     if (NS_FAILED(rv))
   934       NS_WARNING("Could not set startup crash detection pref.");
   935   }
   937   if (inSafeMode && mIsSafeModeNecessary) {
   938     // On a successful startup in automatic safe mode, allow the user one more crash
   939     // in regular mode before returning to safe mode.
   940     int32_t maxResumedCrashes = 0;
   941     int32_t prefType;
   942     rv = Preferences::GetDefaultRootBranch()->GetPrefType(kPrefMaxResumedCrashes, &prefType);
   943     NS_ENSURE_SUCCESS(rv, rv);
   944     if (prefType == nsIPrefBranch::PREF_INT) {
   945       rv = Preferences::GetInt(kPrefMaxResumedCrashes, &maxResumedCrashes);
   946       NS_ENSURE_SUCCESS(rv, rv);
   947     }
   948     rv = Preferences::SetInt(kPrefRecentCrashes, maxResumedCrashes);
   949     NS_ENSURE_SUCCESS(rv, rv);
   950   } else if (!inSafeMode) {
   951     // clear the count of recent crashes after a succesful startup when not in safe mode
   952     rv = Preferences::ClearUser(kPrefRecentCrashes);
   953     if (NS_FAILED(rv)) NS_WARNING("Could not clear startup crash count.");
   954   }
   955   nsCOMPtr<nsIPrefService> prefs = Preferences::GetService();
   956   rv = prefs->SavePrefFile(nullptr); // flush prefs to disk since we are tracking crashes
   958   return rv;
   959 }
   961 NS_IMETHODIMP
   962 nsAppStartup::RestartInSafeMode(uint32_t aQuitMode)
   963 {
   964   PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
   965   this->Quit(aQuitMode | nsIAppStartup::eRestart);
   967   return NS_OK;
   968 }

mercurial