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