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"
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"
27 #include "mozilla/Preferences.h"
28 #include "GeckoProfiler.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"
39 #include "nsAppDirectoryServiceDefs.h"
42 // Prevent collisions with nsAppStartup::GetStartupInfo()
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"
58 #include "mozilla/perfprobe.h"
60 * Events sent to the system for profiling purposes
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
{
101 nsRefPtr
<nsAppStartup
> mService
;
104 nsAppExitEvent(nsAppStartup
*service
) : mService(service
) {}
107 // Tell the appshell to exit
108 mService
->mAppShell
->Exit();
110 mService
->mRunning
= false;
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.
121 * @param stamp The timestamp to be converted
122 * @returns The converted timestamp
124 uint64_t ComputeAbsoluteTimestamp(PRTime prnow
, TimeStamp now
, TimeStamp stamp
)
126 static PRTime sAbsoluteNow
= PR_Now();
127 static TimeStamp sMonotonicNow
= TimeStamp::Now();
129 return sAbsoluteNow
- (sMonotonicNow
- stamp
).ToMicroseconds();
136 nsAppStartup::nsAppStartup() :
137 mConsiderQuitStopper(0),
139 mShuttingDown(false),
141 mAttemptingQuit(false),
144 mIsSafeModeNecessary(false),
145 mStartupCrashTrackingEnded(false),
146 mRestartTouchEnvironment(false)
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();
162 return NS_ERROR_FAILURE
;
164 os
->AddObserver(this, "quit-application", true);
165 os
->AddObserver(this, "quit-application-forced", true);
166 os
->AddObserver(this, "sessionstore-init-started", true);
167 os
->AddObserver(this, "sessionstore-windows-restored", true);
168 os
->AddObserver(this, "profile-change-teardown", true);
169 os
->AddObserver(this, "xul-window-registered", true);
170 os
->AddObserver(this, "xul-window-destroyed", true);
171 os
->AddObserver(this, "profile-before-change", true);
172 os
->AddObserver(this, "xpcom-shutdown", true);
175 os
->AddObserver(this, "places-init-complete", true);
176 // This last event is only interesting to us for xperf-based measures
178 // Initialize interaction with profiler
181 kApplicationTracingCID
,
182 NS_LITERAL_CSTRING("Application startup probe"));
183 // Note: The operation is meant mostly for in-house profiling.
184 // Therefore, we do not warn if probes manager cannot be initialized
186 if (mProbesManager
) {
187 mPlacesInitCompleteProbe
=
189 GetProbe(kPlacesInitCompleteCID
,
190 NS_LITERAL_CSTRING("places-init-complete"));
191 NS_WARN_IF_FALSE(mPlacesInitCompleteProbe
,
192 "Cannot initialize probe 'places-init-complete'");
194 mSessionWindowRestoredProbe
=
196 GetProbe(kSessionStoreWindowRestoredCID
,
197 NS_LITERAL_CSTRING("sessionstore-windows-restored"));
198 NS_WARN_IF_FALSE(mSessionWindowRestoredProbe
,
199 "Cannot initialize probe 'sessionstore-windows-restored'");
201 mXPCOMShutdownProbe
=
203 GetProbe(kXPCOMShutdownCID
,
204 NS_LITERAL_CSTRING("xpcom-shutdown"));
205 NS_WARN_IF_FALSE(mXPCOMShutdownProbe
,
206 "Cannot initialize probe 'xpcom-shutdown'");
208 rv
= mProbesManager
->StartSession();
209 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
),
210 "Cannot initialize system probe manager");
212 #endif //defined(XP_WIN)
219 // nsAppStartup->nsISupports
222 NS_IMPL_ISUPPORTS(nsAppStartup
,
227 nsISupportsWeakReference
)
231 // nsAppStartup->nsIAppStartup
235 nsAppStartup::CreateHiddenWindow()
237 #ifdef MOZ_WIDGET_GONK
240 nsCOMPtr
<nsIAppShellService
> appShellService
241 (do_GetService(NS_APPSHELLSERVICE_CONTRACTID
));
242 NS_ENSURE_TRUE(appShellService
, NS_ERROR_FAILURE
);
244 return appShellService
->CreateHiddenWindow();
250 nsAppStartup::DestroyHiddenWindow()
252 #ifdef MOZ_WIDGET_GONK
255 nsCOMPtr
<nsIAppShellService
> appShellService
256 (do_GetService(NS_APPSHELLSERVICE_CONTRACTID
));
257 NS_ENSURE_TRUE(appShellService
, NS_ERROR_FAILURE
);
259 return appShellService
->DestroyHiddenWindow();
264 nsAppStartup::Run(void)
266 NS_ASSERTION(!mRunning
, "Reentrant appstartup->Run()");
268 // If we have no windows open and no explicit calls to
269 // enterLastWindowClosingSurvivalArea, or somebody has explicitly called
270 // quit, don't bother running the event loop which would probably leave us
271 // with a zombie process.
273 if (!mShuttingDown
&& mConsiderQuitStopper
!= 0) {
275 EnterLastWindowClosingSurvivalArea();
280 nsresult rv
= mAppShell
->Run();
285 nsresult retval
= NS_OK
;
286 if (mRestartTouchEnvironment
) {
287 retval
= NS_SUCCESS_RESTART_METRO_APP
;
288 } else if (mRestart
) {
289 retval
= NS_SUCCESS_RESTART_APP
;
298 nsAppStartup::Quit(uint32_t aMode
)
300 uint32_t ferocity
= (aMode
& 0xF);
302 // Quit the application. We will asynchronously call the appshell's
303 // Exit() method via nsAppExitEvent to allow one last pass
304 // through any events in the queue. This guarantees a tidy cleanup.
306 bool postedExitEvent
= false;
311 // If we're considering quitting, we will only do so if:
312 if (ferocity
== eConsiderQuit
) {
314 nsCOMPtr
<nsIAppShellService
> appShell
315 (do_GetService(NS_APPSHELLSERVICE_CONTRACTID
));
316 bool hasHiddenPrivateWindow
= false;
318 appShell
->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow
);
320 int32_t suspiciousCount
= hasHiddenPrivateWindow
? 2 : 1;
323 if (mConsiderQuitStopper
== 0) {
324 // there are no windows...
325 ferocity
= eAttemptQuit
;
328 else if (mConsiderQuitStopper
== suspiciousCount
) {
329 // ... or there is only a hiddenWindow left, and it's useless:
331 // Failure shouldn't be fatal, but will abort quit attempt:
335 bool usefulHiddenWindow
;
336 appShell
->GetApplicationProvidedHiddenWindow(&usefulHiddenWindow
);
337 nsCOMPtr
<nsIXULWindow
> hiddenWindow
;
338 appShell
->GetHiddenWindow(getter_AddRefs(hiddenWindow
));
339 // If the remaining windows are useful, we won't quit:
340 nsCOMPtr
<nsIXULWindow
> hiddenPrivateWindow
;
341 if (hasHiddenPrivateWindow
) {
342 appShell
->GetHiddenPrivateWindow(getter_AddRefs(hiddenPrivateWindow
));
343 if ((!hiddenWindow
&& !hiddenPrivateWindow
) || usefulHiddenWindow
)
345 } else if (!hiddenWindow
|| usefulHiddenWindow
) {
349 ferocity
= eAttemptQuit
;
354 nsCOMPtr
<nsIObserverService
> obsService
;
355 if (ferocity
== eAttemptQuit
|| ferocity
== eForceQuit
) {
357 nsCOMPtr
<nsISimpleEnumerator
> windowEnumerator
;
358 nsCOMPtr
<nsIWindowMediator
> mediator (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
360 mediator
->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator
));
361 if (windowEnumerator
) {
363 while (windowEnumerator
->HasMoreElements(&more
), more
) {
364 nsCOMPtr
<nsISupports
> window
;
365 windowEnumerator
->GetNext(getter_AddRefs(window
));
366 nsCOMPtr
<nsPIDOMWindow
> domWindow(do_QueryInterface(window
));
368 MOZ_ASSERT(domWindow
->IsOuterWindow());
369 if (!domWindow
->CanClose())
376 PROFILER_MARKER("Shutdown start");
377 mozilla::RecordShutdownStartTimeStamp();
378 mShuttingDown
= true;
380 mRestart
= (aMode
& eRestart
) != 0;
381 gRestartMode
= (aMode
& 0xF0);
384 if (!mRestartTouchEnvironment
) {
385 mRestartTouchEnvironment
= (aMode
& eRestartTouchEnvironment
) != 0;
386 gRestartMode
= (aMode
& 0xF0);
389 if (mRestart
|| mRestartTouchEnvironment
) {
390 // Mark the next startup as a restart.
391 PR_SetEnv("MOZ_APP_RESTART=1");
393 /* Firefox-restarts reuse the process so regular process start-time isn't
394 a useful indicator of startup time anymore. */
395 TimeStamp::RecordProcessRestart();
398 obsService
= mozilla::services::GetObserverService();
400 if (!mAttemptingQuit
) {
401 mAttemptingQuit
= true;
403 // now even the Mac wants to quit when the last window is closed
404 ExitLastWindowClosingSurvivalArea();
407 obsService
->NotifyObservers(nullptr, "quit-application-granted", nullptr);
410 /* Enumerate through each open window and close it. It's important to do
411 this before we forcequit because this can control whether we really quit
412 at all. e.g. if one of these windows has an unload handler that
413 opens a new window. Ugh. I know. */
417 if (ferocity
== eAttemptQuit
) {
418 ferocity
= eForceQuit
; // assume success
420 /* Were we able to immediately close all windows? if not, eAttemptQuit
421 failed. This could happen for a variety of reasons; in fact it's
422 very likely. Perhaps we're being called from JS and the window->Close
423 method hasn't had a chance to wrap itself up yet. So give up.
424 We'll return (with eConsiderQuit) as the remaining windows are
426 mediator
->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator
));
427 if (windowEnumerator
) {
429 while (windowEnumerator
->HasMoreElements(&more
), more
) {
430 /* we can't quit immediately. we'll try again as the last window
432 ferocity
= eAttemptQuit
;
433 nsCOMPtr
<nsISupports
> window
;
434 windowEnumerator
->GetNext(getter_AddRefs(window
));
435 nsCOMPtr
<nsIDOMWindow
> domWindow
= do_QueryInterface(window
);
438 domWindow
->GetClosed(&closed
);
440 rv
= NS_ERROR_FAILURE
;
450 if (ferocity
== eForceQuit
) {
453 // No chance of the shutdown being cancelled from here on; tell people
454 // we're shutting down for sure while all services are still available.
456 NS_NAMED_LITERAL_STRING(shutdownStr
, "shutdown");
457 NS_NAMED_LITERAL_STRING(restartStr
, "restart");
458 obsService
->NotifyObservers(nullptr, "quit-application",
459 (mRestart
|| mRestartTouchEnvironment
) ?
460 restartStr
.get() : shutdownStr
.get());
464 postedExitEvent
= true;
467 // no matter what, make sure we send the exit event. If
468 // worst comes to worst, we'll do a leaky shutdown but we WILL
469 // shut down. Well, assuming that all *this* stuff works ;-).
470 nsCOMPtr
<nsIRunnable
> event
= new nsAppExitEvent(this);
471 rv
= NS_DispatchToCurrentThread(event
);
472 if (NS_SUCCEEDED(rv
)) {
473 postedExitEvent
= true;
476 NS_WARNING("failed to dispatch nsAppExitEvent");
481 // turn off the reentrancy check flag, but not if we have
482 // more asynchronous work to do still.
483 if (!postedExitEvent
)
484 mShuttingDown
= false;
490 nsAppStartup::CloseAllWindows()
492 nsCOMPtr
<nsIWindowMediator
> mediator
493 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
495 nsCOMPtr
<nsISimpleEnumerator
> windowEnumerator
;
497 mediator
->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator
));
499 if (!windowEnumerator
)
503 while (NS_SUCCEEDED(windowEnumerator
->HasMoreElements(&more
)) && more
) {
504 nsCOMPtr
<nsISupports
> isupports
;
505 if (NS_FAILED(windowEnumerator
->GetNext(getter_AddRefs(isupports
))))
508 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(isupports
);
509 NS_ASSERTION(window
, "not an nsPIDOMWindow");
511 MOZ_ASSERT(window
->IsOuterWindow());
512 window
->ForceClose();
518 nsAppStartup::EnterLastWindowClosingSurvivalArea(void)
520 ++mConsiderQuitStopper
;
526 nsAppStartup::ExitLastWindowClosingSurvivalArea(void)
528 NS_ASSERTION(mConsiderQuitStopper
> 0, "consider quit stopper out of bounds");
529 --mConsiderQuitStopper
;
538 // nsAppStartup->nsIAppStartup2
542 nsAppStartup::GetShuttingDown(bool *aResult
)
544 *aResult
= mShuttingDown
;
549 nsAppStartup::GetStartingUp(bool *aResult
)
551 *aResult
= mStartingUp
;
556 nsAppStartup::DoneStartingUp()
558 // This must be called once at most
559 MOZ_ASSERT(mStartingUp
);
566 nsAppStartup::GetRestarting(bool *aResult
)
573 nsAppStartup::GetWasRestarted(bool *aResult
)
575 char *mozAppRestart
= PR_GetEnv("MOZ_APP_RESTART");
577 /* When calling PR_SetEnv() with an empty value the existing variable may
578 * be unset or set to the empty string depending on the underlying platform
579 * thus we have to check if the variable is present and not empty. */
580 *aResult
= mozAppRestart
&& (strcmp(mozAppRestart
, "") != 0);
586 nsAppStartup::GetRestartingTouchEnvironment(bool *aResult
)
588 NS_ENSURE_ARG_POINTER(aResult
);
589 *aResult
= mRestartTouchEnvironment
;
594 nsAppStartup::SetInterrupted(bool aInterrupted
)
596 mInterrupted
= aInterrupted
;
601 nsAppStartup::GetInterrupted(bool *aInterrupted
)
603 *aInterrupted
= mInterrupted
;
608 // nsAppStartup->nsIWindowCreator
612 nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome
*aParent
,
613 uint32_t aChromeFlags
,
614 nsIWebBrowserChrome
**_retval
)
617 return CreateChromeWindow2(aParent
, aChromeFlags
, 0, 0, nullptr, &cancel
, _retval
);
622 // nsAppStartup->nsIWindowCreator2
626 nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome
*aParent
,
627 uint32_t aChromeFlags
,
628 uint32_t aContextFlags
,
630 nsITabParent
*aOpeningTab
,
632 nsIWebBrowserChrome
**_retval
)
634 NS_ENSURE_ARG_POINTER(aCancel
);
635 NS_ENSURE_ARG_POINTER(_retval
);
639 // Non-modal windows cannot be opened if we are attempting to quit
640 if (mAttemptingQuit
&& (aChromeFlags
& nsIWebBrowserChrome::CHROME_MODAL
) == 0)
641 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN
;
643 nsCOMPtr
<nsIXULWindow
> newWindow
;
646 nsCOMPtr
<nsIXULWindow
> xulParent(do_GetInterface(aParent
));
647 NS_ASSERTION(xulParent
, "window created using non-XUL parent. that's unexpected, but may work.");
650 xulParent
->CreateNewWindow(aChromeFlags
, aOpeningTab
, getter_AddRefs(newWindow
));
651 // And if it fails, don't try again without a parent. It could fail
652 // intentionally (bug 115969).
653 } else { // try using basic methods:
654 /* You really shouldn't be making dependent windows without a parent.
655 But unparented modal (and therefore dependent) windows happen
656 in our codebase, so we allow it after some bellyaching: */
657 if (aChromeFlags
& nsIWebBrowserChrome::CHROME_DEPENDENT
)
658 NS_WARNING("dependent window created without a parent");
660 nsCOMPtr
<nsIAppShellService
> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID
));
662 return NS_ERROR_FAILURE
;
664 appShell
->CreateTopLevelWindow(0, 0, aChromeFlags
,
665 nsIAppShellService::SIZE_TO_CONTENT
,
666 nsIAppShellService::SIZE_TO_CONTENT
,
668 getter_AddRefs(newWindow
));
671 // if anybody gave us anything to work with, use it
673 newWindow
->SetContextFlags(aContextFlags
);
674 nsCOMPtr
<nsIInterfaceRequestor
> thing(do_QueryInterface(newWindow
));
676 CallGetInterface(thing
.get(), _retval
);
679 return *_retval
? NS_OK
: NS_ERROR_FAILURE
;
684 // nsAppStartup->nsIObserver
688 nsAppStartup::Observe(nsISupports
*aSubject
,
689 const char *aTopic
, const char16_t
*aData
)
691 NS_ASSERTION(mAppShell
, "appshell service notified before appshell built");
692 if (!strcmp(aTopic
, "quit-application-forced")) {
693 mShuttingDown
= true;
695 else if (!strcmp(aTopic
, "profile-change-teardown")) {
696 if (!mShuttingDown
) {
697 EnterLastWindowClosingSurvivalArea();
699 ExitLastWindowClosingSurvivalArea();
701 } else if (!strcmp(aTopic
, "xul-window-registered")) {
702 EnterLastWindowClosingSurvivalArea();
703 } else if (!strcmp(aTopic
, "xul-window-destroyed")) {
704 ExitLastWindowClosingSurvivalArea();
705 } else if (!strcmp(aTopic
, "sessionstore-windows-restored")) {
706 StartupTimeline::Record(StartupTimeline::SESSION_RESTORED
);
707 IOInterposer::EnteringNextStage();
709 if (mSessionWindowRestoredProbe
) {
710 mSessionWindowRestoredProbe
->Trigger();
712 } else if (!strcmp(aTopic
, "places-init-complete")) {
713 if (mPlacesInitCompleteProbe
) {
714 mPlacesInitCompleteProbe
->Trigger();
716 #endif //defined(XP_WIN)
717 } else if (!strcmp(aTopic
, "sessionstore-init-started")) {
718 StartupTimeline::Record(StartupTimeline::SESSION_RESTORE_INIT
);
719 } else if (!strcmp(aTopic
, "xpcom-shutdown")) {
720 IOInterposer::EnteringNextStage();
722 if (mXPCOMShutdownProbe
) {
723 mXPCOMShutdownProbe
->Trigger();
725 #endif // defined(XP_WIN)
726 } else if (!strcmp(aTopic
, "quit-application")) {
727 StartupTimeline::Record(StartupTimeline::QUIT_APPLICATION
);
728 } else if (!strcmp(aTopic
, "profile-before-change")) {
729 StartupTimeline::Record(StartupTimeline::PROFILE_BEFORE_CHANGE
);
731 NS_ERROR("Unexpected observer topic.");
738 nsAppStartup::GetStartupInfo(JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aRetval
)
740 JS::Rooted
<JSObject
*> obj(aCx
, JS_NewObject(aCx
, nullptr, JS::NullPtr(), JS::NullPtr()));
742 aRetval
.setObject(*obj
);
744 TimeStamp procTime
= StartupTimeline::Get(StartupTimeline::PROCESS_CREATION
);
745 TimeStamp now
= TimeStamp::Now();
746 PRTime absNow
= PR_Now();
748 if (procTime
.IsNull()) {
751 procTime
= TimeStamp::ProcessCreation(error
);
754 Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS
,
755 StartupTimeline::PROCESS_CREATION
);
758 StartupTimeline::Record(StartupTimeline::PROCESS_CREATION
, procTime
);
761 for (int i
= StartupTimeline::PROCESS_CREATION
;
762 i
< StartupTimeline::MAX_EVENT_ID
;
765 StartupTimeline::Event ev
= static_cast<StartupTimeline::Event
>(i
);
766 TimeStamp stamp
= StartupTimeline::Get(ev
);
768 if (stamp
.IsNull() && (ev
== StartupTimeline::MAIN
)) {
769 // Always define main to aid with bug 689256.
771 MOZ_ASSERT(!stamp
.IsNull());
772 Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS
,
773 StartupTimeline::MAIN
);
776 if (!stamp
.IsNull()) {
777 if (stamp
>= procTime
) {
778 PRTime prStamp
= ComputeAbsoluteTimestamp(absNow
, now
, stamp
)
780 JS::Rooted
<JSObject
*> date(aCx
, JS_NewDateObjectMsec(aCx
, prStamp
));
781 JS_DefineProperty(aCx
, obj
, StartupTimeline::Describe(ev
), date
, JSPROP_ENUMERATE
);
783 Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS
, ev
);
792 nsAppStartup::GetAutomaticSafeModeNecessary(bool *_retval
)
794 NS_ENSURE_ARG_POINTER(_retval
);
796 bool alwaysSafe
= false;
797 Preferences::GetBool(kPrefAlwaysUseSafeMode
, &alwaysSafe
);
801 mIsSafeModeNecessary
= false;
803 mIsSafeModeNecessary
&= !PR_GetEnv("MOZ_DISABLE_AUTO_SAFE_MODE");
807 *_retval
= mIsSafeModeNecessary
;
812 nsAppStartup::TrackStartupCrashBegin(bool *aIsSafeModeNecessary
)
814 const int32_t MAX_TIME_SINCE_STARTUP
= 6 * 60 * 60 * 1000;
815 const int32_t MAX_STARTUP_BUFFER
= 10;
818 mStartupCrashTrackingEnded
= false;
820 StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_BEGIN
);
822 bool hasLastSuccess
= Preferences::HasUserValue(kPrefLastSuccess
);
823 if (!hasLastSuccess
) {
824 // Clear so we don't get stuck with SafeModeNecessary returning true if we
825 // have had too many recent crashes and the last success pref is missing.
826 Preferences::ClearUser(kPrefRecentCrashes
);
827 return NS_ERROR_NOT_AVAILABLE
;
830 bool inSafeMode
= false;
831 nsCOMPtr
<nsIXULRuntime
> xr
= do_GetService(XULRUNTIME_SERVICE_CONTRACTID
);
832 NS_ENSURE_TRUE(xr
, NS_ERROR_FAILURE
);
834 xr
->GetInSafeMode(&inSafeMode
);
836 PRTime replacedLockTime
;
837 rv
= xr
->GetReplacedLockTime(&replacedLockTime
);
839 if (NS_FAILED(rv
) || !replacedLockTime
) {
841 Preferences::ClearUser(kPrefRecentCrashes
);
842 GetAutomaticSafeModeNecessary(aIsSafeModeNecessary
);
846 // check whether safe mode is necessary
847 int32_t maxResumedCrashes
= -1;
848 rv
= Preferences::GetInt(kPrefMaxResumedCrashes
, &maxResumedCrashes
);
849 NS_ENSURE_SUCCESS(rv
, NS_OK
);
851 int32_t recentCrashes
= 0;
852 Preferences::GetInt(kPrefRecentCrashes
, &recentCrashes
);
853 mIsSafeModeNecessary
= (recentCrashes
> maxResumedCrashes
&& maxResumedCrashes
!= -1);
855 // Bug 731613 - Don't check if the last startup was a crash if XRE_PROFILE_PATH is set. After
856 // profile manager, the profile lock's mod. time has been changed so can't be used on this startup.
857 // After a restart, it's safe to assume the last startup was successful.
858 char *xreProfilePath
= PR_GetEnv("XRE_PROFILE_PATH");
859 if (xreProfilePath
) {
860 GetAutomaticSafeModeNecessary(aIsSafeModeNecessary
);
861 return NS_ERROR_NOT_AVAILABLE
;
864 // time of last successful startup
865 int32_t lastSuccessfulStartup
;
866 rv
= Preferences::GetInt(kPrefLastSuccess
, &lastSuccessfulStartup
);
867 NS_ENSURE_SUCCESS(rv
, rv
);
869 int32_t lockSeconds
= (int32_t)(replacedLockTime
/ PR_MSEC_PER_SEC
);
871 // started close enough to good startup so call it good
872 if (lockSeconds
<= lastSuccessfulStartup
+ MAX_STARTUP_BUFFER
873 && lockSeconds
>= lastSuccessfulStartup
- MAX_STARTUP_BUFFER
) {
874 GetAutomaticSafeModeNecessary(aIsSafeModeNecessary
);
878 // sanity check that the pref set at last success is not greater than the current time
879 if (PR_Now() / PR_USEC_PER_SEC
<= lastSuccessfulStartup
)
880 return NS_ERROR_FAILURE
;
882 // The last startup was a crash so include it in the count regardless of when it happened.
883 Telemetry::Accumulate(Telemetry::STARTUP_CRASH_DETECTED
, true);
886 GetAutomaticSafeModeNecessary(aIsSafeModeNecessary
);
890 PRTime now
= (PR_Now() / PR_USEC_PER_MSEC
);
891 // if the last startup attempt which crashed was in the last 6 hours
892 if (replacedLockTime
>= now
- MAX_TIME_SINCE_STARTUP
) {
893 NS_WARNING("Last startup was detected as a crash.");
895 rv
= Preferences::SetInt(kPrefRecentCrashes
, recentCrashes
);
897 // Otherwise ignore that crash and all previous since it may not be applicable anymore
898 // and we don't want someone to get stuck in safe mode if their prefs are read-only.
899 rv
= Preferences::ClearUser(kPrefRecentCrashes
);
901 NS_ENSURE_SUCCESS(rv
, rv
);
903 // recalculate since recent crashes count may have changed above
904 mIsSafeModeNecessary
= (recentCrashes
> maxResumedCrashes
&& maxResumedCrashes
!= -1);
906 nsCOMPtr
<nsIPrefService
> prefs
= Preferences::GetService();
907 rv
= prefs
->SavePrefFile(nullptr); // flush prefs to disk since we are tracking crashes
908 NS_ENSURE_SUCCESS(rv
, rv
);
910 GetAutomaticSafeModeNecessary(aIsSafeModeNecessary
);
915 nsAppStartup::TrackStartupCrashEnd()
917 bool inSafeMode
= false;
918 nsCOMPtr
<nsIXULRuntime
> xr
= do_GetService(XULRUNTIME_SERVICE_CONTRACTID
);
920 xr
->GetInSafeMode(&inSafeMode
);
922 // return if we already ended or we're restarting into safe mode
923 if (mStartupCrashTrackingEnded
|| (mIsSafeModeNecessary
&& !inSafeMode
))
925 mStartupCrashTrackingEnded
= true;
927 StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_END
);
929 // Use the timestamp of XRE_main as an approximation for the lock file timestamp.
930 // See MAX_STARTUP_BUFFER for the buffer time period.
931 TimeStamp mainTime
= StartupTimeline::Get(StartupTimeline::MAIN
);
932 TimeStamp now
= TimeStamp::Now();
933 PRTime prNow
= PR_Now();
936 if (mainTime
.IsNull()) {
937 NS_WARNING("Could not get StartupTimeline::MAIN time.");
939 uint64_t lockFileTime
= ComputeAbsoluteTimestamp(prNow
, now
, mainTime
);
941 rv
= Preferences::SetInt(kPrefLastSuccess
,
942 (int32_t)(lockFileTime
/ PR_USEC_PER_SEC
));
945 NS_WARNING("Could not set startup crash detection pref.");
948 if (inSafeMode
&& mIsSafeModeNecessary
) {
949 // On a successful startup in automatic safe mode, allow the user one more crash
950 // in regular mode before returning to safe mode.
951 int32_t maxResumedCrashes
= 0;
953 rv
= Preferences::GetDefaultRootBranch()->GetPrefType(kPrefMaxResumedCrashes
, &prefType
);
954 NS_ENSURE_SUCCESS(rv
, rv
);
955 if (prefType
== nsIPrefBranch::PREF_INT
) {
956 rv
= Preferences::GetInt(kPrefMaxResumedCrashes
, &maxResumedCrashes
);
957 NS_ENSURE_SUCCESS(rv
, rv
);
959 rv
= Preferences::SetInt(kPrefRecentCrashes
, maxResumedCrashes
);
960 NS_ENSURE_SUCCESS(rv
, rv
);
961 } else if (!inSafeMode
) {
962 // clear the count of recent crashes after a succesful startup when not in safe mode
963 rv
= Preferences::ClearUser(kPrefRecentCrashes
);
964 if (NS_FAILED(rv
)) NS_WARNING("Could not clear startup crash count.");
966 nsCOMPtr
<nsIPrefService
> prefs
= Preferences::GetService();
967 rv
= prefs
->SavePrefFile(nullptr); // flush prefs to disk since we are tracking crashes
973 nsAppStartup::RestartInSafeMode(uint32_t aQuitMode
)
975 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
976 this->Quit(aQuitMode
| nsIAppStartup::eRestart
);