1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/startup/nsAppStartup.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,968 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsAppStartup.h" 1.10 + 1.11 +#include "nsIAppShellService.h" 1.12 +#include "nsPIDOMWindow.h" 1.13 +#include "nsIInterfaceRequestor.h" 1.14 +#include "nsIFile.h" 1.15 +#include "nsIObserverService.h" 1.16 +#include "nsIPrefBranch.h" 1.17 +#include "nsIPrefService.h" 1.18 +#include "nsIPromptService.h" 1.19 +#include "nsIStringBundle.h" 1.20 +#include "nsISupportsPrimitives.h" 1.21 +#include "nsIWebBrowserChrome.h" 1.22 +#include "nsIWindowMediator.h" 1.23 +#include "nsIWindowWatcher.h" 1.24 +#include "nsIXULRuntime.h" 1.25 +#include "nsIXULWindow.h" 1.26 +#include "nsNativeCharsetUtils.h" 1.27 +#include "nsThreadUtils.h" 1.28 +#include "nsAutoPtr.h" 1.29 +#include "nsString.h" 1.30 +#include "mozilla/Preferences.h" 1.31 +#include "GeckoProfiler.h" 1.32 + 1.33 +#include "prprf.h" 1.34 +#include "nsIInterfaceRequestorUtils.h" 1.35 +#include "nsWidgetsCID.h" 1.36 +#include "nsAppShellCID.h" 1.37 +#include "nsXPCOMCIDInternal.h" 1.38 +#include "mozilla/Services.h" 1.39 +#include "nsIXPConnect.h" 1.40 +#include "jsapi.h" 1.41 +#include "prenv.h" 1.42 +#include "nsAppDirectoryServiceDefs.h" 1.43 + 1.44 +#if defined(XP_WIN) 1.45 +// Prevent collisions with nsAppStartup::GetStartupInfo() 1.46 +#undef GetStartupInfo 1.47 +#endif 1.48 + 1.49 +#include "mozilla/IOInterposer.h" 1.50 +#include "mozilla/Telemetry.h" 1.51 +#include "mozilla/StartupTimeline.h" 1.52 + 1.53 +static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); 1.54 + 1.55 +#define kPrefLastSuccess "toolkit.startup.last_success" 1.56 +#define kPrefMaxResumedCrashes "toolkit.startup.max_resumed_crashes" 1.57 +#define kPrefRecentCrashes "toolkit.startup.recent_crashes" 1.58 +#define kPrefAlwaysUseSafeMode "toolkit.startup.always_use_safe_mode" 1.59 + 1.60 +#if defined(XP_WIN) 1.61 +#include "mozilla/perfprobe.h" 1.62 +/** 1.63 + * Events sent to the system for profiling purposes 1.64 + */ 1.65 +//Keep them syncronized with the .mof file 1.66 + 1.67 +//Process-wide GUID, used by the OS to differentiate sources 1.68 +// {509962E0-406B-46F4-99BA-5A009F8D2225} 1.69 +//Keep it synchronized with the .mof file 1.70 +#define NS_APPLICATION_TRACING_CID \ 1.71 + { 0x509962E0, 0x406B, 0x46F4, \ 1.72 + { 0x99, 0xBA, 0x5A, 0x00, 0x9F, 0x8D, 0x22, 0x25} } 1.73 + 1.74 +//Event-specific GUIDs, used by the OS to differentiate events 1.75 +// {A3DA04E0-57D7-482A-A1C1-61DA5F95BACB} 1.76 +#define NS_PLACES_INIT_COMPLETE_EVENT_CID \ 1.77 + { 0xA3DA04E0, 0x57D7, 0x482A, \ 1.78 + { 0xA1, 0xC1, 0x61, 0xDA, 0x5F, 0x95, 0xBA, 0xCB} } 1.79 +// {917B96B1-ECAD-4DAB-A760-8D49027748AE} 1.80 +#define NS_SESSION_STORE_WINDOW_RESTORED_EVENT_CID \ 1.81 + { 0x917B96B1, 0xECAD, 0x4DAB, \ 1.82 + { 0xA7, 0x60, 0x8D, 0x49, 0x02, 0x77, 0x48, 0xAE} } 1.83 +// {26D1E091-0AE7-4F49-A554-4214445C505C} 1.84 +#define NS_XPCOM_SHUTDOWN_EVENT_CID \ 1.85 + { 0x26D1E091, 0x0AE7, 0x4F49, \ 1.86 + { 0xA5, 0x54, 0x42, 0x14, 0x44, 0x5C, 0x50, 0x5C} } 1.87 + 1.88 +static NS_DEFINE_CID(kApplicationTracingCID, 1.89 + NS_APPLICATION_TRACING_CID); 1.90 +static NS_DEFINE_CID(kPlacesInitCompleteCID, 1.91 + NS_PLACES_INIT_COMPLETE_EVENT_CID); 1.92 +static NS_DEFINE_CID(kSessionStoreWindowRestoredCID, 1.93 + NS_SESSION_STORE_WINDOW_RESTORED_EVENT_CID); 1.94 +static NS_DEFINE_CID(kXPCOMShutdownCID, 1.95 + NS_XPCOM_SHUTDOWN_EVENT_CID); 1.96 +#endif //defined(XP_WIN) 1.97 + 1.98 +using namespace mozilla; 1.99 + 1.100 +uint32_t gRestartMode = 0; 1.101 + 1.102 +class nsAppExitEvent : public nsRunnable { 1.103 +private: 1.104 + nsRefPtr<nsAppStartup> mService; 1.105 + 1.106 +public: 1.107 + nsAppExitEvent(nsAppStartup *service) : mService(service) {} 1.108 + 1.109 + NS_IMETHOD Run() { 1.110 + // Tell the appshell to exit 1.111 + mService->mAppShell->Exit(); 1.112 + 1.113 + mService->mRunning = false; 1.114 + return NS_OK; 1.115 + } 1.116 +}; 1.117 + 1.118 +/** 1.119 + * Computes an approximation of the absolute time represented by @a stamp 1.120 + * which is comparable to those obtained via PR_Now(). If the current absolute 1.121 + * time varies a lot (e.g. DST adjustments) since the first call then the 1.122 + * resulting times may be inconsistent. 1.123 + * 1.124 + * @param stamp The timestamp to be converted 1.125 + * @returns The converted timestamp 1.126 + */ 1.127 +uint64_t ComputeAbsoluteTimestamp(PRTime prnow, TimeStamp now, TimeStamp stamp) 1.128 +{ 1.129 + static PRTime sAbsoluteNow = PR_Now(); 1.130 + static TimeStamp sMonotonicNow = TimeStamp::Now(); 1.131 + 1.132 + return sAbsoluteNow - (sMonotonicNow - stamp).ToMicroseconds(); 1.133 +} 1.134 + 1.135 +// 1.136 +// nsAppStartup 1.137 +// 1.138 + 1.139 +nsAppStartup::nsAppStartup() : 1.140 + mConsiderQuitStopper(0), 1.141 + mRunning(false), 1.142 + mShuttingDown(false), 1.143 + mStartingUp(true), 1.144 + mAttemptingQuit(false), 1.145 + mRestart(false), 1.146 + mInterrupted(false), 1.147 + mIsSafeModeNecessary(false), 1.148 + mStartupCrashTrackingEnded(false), 1.149 + mRestartTouchEnvironment(false) 1.150 +{ } 1.151 + 1.152 + 1.153 +nsresult 1.154 +nsAppStartup::Init() 1.155 +{ 1.156 + nsresult rv; 1.157 + 1.158 + // Create widget application shell 1.159 + mAppShell = do_GetService(kAppShellCID, &rv); 1.160 + NS_ENSURE_SUCCESS(rv, rv); 1.161 + 1.162 + nsCOMPtr<nsIObserverService> os = 1.163 + mozilla::services::GetObserverService(); 1.164 + if (!os) 1.165 + return NS_ERROR_FAILURE; 1.166 + 1.167 + os->AddObserver(this, "quit-application-forced", true); 1.168 + os->AddObserver(this, "sessionstore-init-started", true); 1.169 + os->AddObserver(this, "sessionstore-windows-restored", true); 1.170 + os->AddObserver(this, "profile-change-teardown", true); 1.171 + os->AddObserver(this, "xul-window-registered", true); 1.172 + os->AddObserver(this, "xul-window-destroyed", true); 1.173 + os->AddObserver(this, "xpcom-shutdown", true); 1.174 + 1.175 +#if defined(XP_WIN) 1.176 + os->AddObserver(this, "places-init-complete", true); 1.177 + // This last event is only interesting to us for xperf-based measures 1.178 + 1.179 + // Initialize interaction with profiler 1.180 + mProbesManager = 1.181 + new ProbeManager( 1.182 + kApplicationTracingCID, 1.183 + NS_LITERAL_CSTRING("Application startup probe")); 1.184 + // Note: The operation is meant mostly for in-house profiling. 1.185 + // Therefore, we do not warn if probes manager cannot be initialized 1.186 + 1.187 + if (mProbesManager) { 1.188 + mPlacesInitCompleteProbe = 1.189 + mProbesManager-> 1.190 + GetProbe(kPlacesInitCompleteCID, 1.191 + NS_LITERAL_CSTRING("places-init-complete")); 1.192 + NS_WARN_IF_FALSE(mPlacesInitCompleteProbe, 1.193 + "Cannot initialize probe 'places-init-complete'"); 1.194 + 1.195 + mSessionWindowRestoredProbe = 1.196 + mProbesManager-> 1.197 + GetProbe(kSessionStoreWindowRestoredCID, 1.198 + NS_LITERAL_CSTRING("sessionstore-windows-restored")); 1.199 + NS_WARN_IF_FALSE(mSessionWindowRestoredProbe, 1.200 + "Cannot initialize probe 'sessionstore-windows-restored'"); 1.201 + 1.202 + mXPCOMShutdownProbe = 1.203 + mProbesManager-> 1.204 + GetProbe(kXPCOMShutdownCID, 1.205 + NS_LITERAL_CSTRING("xpcom-shutdown")); 1.206 + NS_WARN_IF_FALSE(mXPCOMShutdownProbe, 1.207 + "Cannot initialize probe 'xpcom-shutdown'"); 1.208 + 1.209 + rv = mProbesManager->StartSession(); 1.210 + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), 1.211 + "Cannot initialize system probe manager"); 1.212 + } 1.213 +#endif //defined(XP_WIN) 1.214 + 1.215 + return NS_OK; 1.216 +} 1.217 + 1.218 + 1.219 +// 1.220 +// nsAppStartup->nsISupports 1.221 +// 1.222 + 1.223 +NS_IMPL_ISUPPORTS(nsAppStartup, 1.224 + nsIAppStartup, 1.225 + nsIWindowCreator, 1.226 + nsIWindowCreator2, 1.227 + nsIObserver, 1.228 + nsISupportsWeakReference) 1.229 + 1.230 + 1.231 +// 1.232 +// nsAppStartup->nsIAppStartup 1.233 +// 1.234 + 1.235 +NS_IMETHODIMP 1.236 +nsAppStartup::CreateHiddenWindow() 1.237 +{ 1.238 +#ifdef MOZ_WIDGET_GONK 1.239 + return NS_OK; 1.240 +#else 1.241 + nsCOMPtr<nsIAppShellService> appShellService 1.242 + (do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); 1.243 + NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE); 1.244 + 1.245 + return appShellService->CreateHiddenWindow(); 1.246 +#endif 1.247 +} 1.248 + 1.249 + 1.250 +NS_IMETHODIMP 1.251 +nsAppStartup::DestroyHiddenWindow() 1.252 +{ 1.253 +#ifdef MOZ_WIDGET_GONK 1.254 + return NS_OK; 1.255 +#else 1.256 + nsCOMPtr<nsIAppShellService> appShellService 1.257 + (do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); 1.258 + NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE); 1.259 + 1.260 + return appShellService->DestroyHiddenWindow(); 1.261 +#endif 1.262 +} 1.263 + 1.264 +NS_IMETHODIMP 1.265 +nsAppStartup::Run(void) 1.266 +{ 1.267 + NS_ASSERTION(!mRunning, "Reentrant appstartup->Run()"); 1.268 + 1.269 + // If we have no windows open and no explicit calls to 1.270 + // enterLastWindowClosingSurvivalArea, or somebody has explicitly called 1.271 + // quit, don't bother running the event loop which would probably leave us 1.272 + // with a zombie process. 1.273 + 1.274 + if (!mShuttingDown && mConsiderQuitStopper != 0) { 1.275 +#ifdef XP_MACOSX 1.276 + EnterLastWindowClosingSurvivalArea(); 1.277 +#endif 1.278 + 1.279 + mRunning = true; 1.280 + 1.281 + nsresult rv = mAppShell->Run(); 1.282 + if (NS_FAILED(rv)) 1.283 + return rv; 1.284 + } 1.285 + 1.286 + nsresult retval = NS_OK; 1.287 + if (mRestartTouchEnvironment) { 1.288 + retval = NS_SUCCESS_RESTART_METRO_APP; 1.289 + } else if (mRestart) { 1.290 + retval = NS_SUCCESS_RESTART_APP; 1.291 + } 1.292 + 1.293 + return retval; 1.294 +} 1.295 + 1.296 + 1.297 + 1.298 +NS_IMETHODIMP 1.299 +nsAppStartup::Quit(uint32_t aMode) 1.300 +{ 1.301 + uint32_t ferocity = (aMode & 0xF); 1.302 + 1.303 + // Quit the application. We will asynchronously call the appshell's 1.304 + // Exit() method via nsAppExitEvent to allow one last pass 1.305 + // through any events in the queue. This guarantees a tidy cleanup. 1.306 + nsresult rv = NS_OK; 1.307 + bool postedExitEvent = false; 1.308 + 1.309 + if (mShuttingDown) 1.310 + return NS_OK; 1.311 + 1.312 + // If we're considering quitting, we will only do so if: 1.313 + if (ferocity == eConsiderQuit) { 1.314 +#ifdef XP_MACOSX 1.315 + nsCOMPtr<nsIAppShellService> appShell 1.316 + (do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); 1.317 + bool hasHiddenPrivateWindow = false; 1.318 + if (appShell) { 1.319 + appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow); 1.320 + } 1.321 + int32_t suspiciousCount = hasHiddenPrivateWindow ? 2 : 1; 1.322 +#endif 1.323 + 1.324 + if (mConsiderQuitStopper == 0) { 1.325 + // there are no windows... 1.326 + ferocity = eAttemptQuit; 1.327 + } 1.328 +#ifdef XP_MACOSX 1.329 + else if (mConsiderQuitStopper == suspiciousCount) { 1.330 + // ... or there is only a hiddenWindow left, and it's useless: 1.331 + 1.332 + // Failure shouldn't be fatal, but will abort quit attempt: 1.333 + if (!appShell) 1.334 + return NS_OK; 1.335 + 1.336 + bool usefulHiddenWindow; 1.337 + appShell->GetApplicationProvidedHiddenWindow(&usefulHiddenWindow); 1.338 + nsCOMPtr<nsIXULWindow> hiddenWindow; 1.339 + appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow)); 1.340 + // If the remaining windows are useful, we won't quit: 1.341 + nsCOMPtr<nsIXULWindow> hiddenPrivateWindow; 1.342 + if (hasHiddenPrivateWindow) { 1.343 + appShell->GetHiddenPrivateWindow(getter_AddRefs(hiddenPrivateWindow)); 1.344 + if ((!hiddenWindow && !hiddenPrivateWindow) || usefulHiddenWindow) 1.345 + return NS_OK; 1.346 + } else if (!hiddenWindow || usefulHiddenWindow) { 1.347 + return NS_OK; 1.348 + } 1.349 + 1.350 + ferocity = eAttemptQuit; 1.351 + } 1.352 +#endif 1.353 + } 1.354 + 1.355 + nsCOMPtr<nsIObserverService> obsService; 1.356 + if (ferocity == eAttemptQuit || ferocity == eForceQuit) { 1.357 + 1.358 + nsCOMPtr<nsISimpleEnumerator> windowEnumerator; 1.359 + nsCOMPtr<nsIWindowMediator> mediator (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); 1.360 + if (mediator) { 1.361 + mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator)); 1.362 + if (windowEnumerator) { 1.363 + bool more; 1.364 + while (windowEnumerator->HasMoreElements(&more), more) { 1.365 + nsCOMPtr<nsISupports> window; 1.366 + windowEnumerator->GetNext(getter_AddRefs(window)); 1.367 + nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(window)); 1.368 + if (domWindow) { 1.369 + if (!domWindow->CanClose()) 1.370 + return NS_OK; 1.371 + } 1.372 + } 1.373 + } 1.374 + } 1.375 + 1.376 + PROFILER_MARKER("Shutdown start"); 1.377 + mozilla::RecordShutdownStartTimeStamp(); 1.378 + mShuttingDown = true; 1.379 + if (!mRestart) { 1.380 + mRestart = (aMode & eRestart) != 0; 1.381 + gRestartMode = (aMode & 0xF0); 1.382 + } 1.383 + 1.384 + if (!mRestartTouchEnvironment) { 1.385 + mRestartTouchEnvironment = (aMode & eRestartTouchEnvironment) != 0; 1.386 + gRestartMode = (aMode & 0xF0); 1.387 + } 1.388 + 1.389 + if (mRestart || mRestartTouchEnvironment) { 1.390 + // Mark the next startup as a restart. 1.391 + PR_SetEnv("MOZ_APP_RESTART=1"); 1.392 + 1.393 + /* Firefox-restarts reuse the process so regular process start-time isn't 1.394 + a useful indicator of startup time anymore. */ 1.395 + TimeStamp::RecordProcessRestart(); 1.396 + } 1.397 + 1.398 + obsService = mozilla::services::GetObserverService(); 1.399 + 1.400 + if (!mAttemptingQuit) { 1.401 + mAttemptingQuit = true; 1.402 +#ifdef XP_MACOSX 1.403 + // now even the Mac wants to quit when the last window is closed 1.404 + ExitLastWindowClosingSurvivalArea(); 1.405 +#endif 1.406 + if (obsService) 1.407 + obsService->NotifyObservers(nullptr, "quit-application-granted", nullptr); 1.408 + } 1.409 + 1.410 + /* Enumerate through each open window and close it. It's important to do 1.411 + this before we forcequit because this can control whether we really quit 1.412 + at all. e.g. if one of these windows has an unload handler that 1.413 + opens a new window. Ugh. I know. */ 1.414 + CloseAllWindows(); 1.415 + 1.416 + if (mediator) { 1.417 + if (ferocity == eAttemptQuit) { 1.418 + ferocity = eForceQuit; // assume success 1.419 + 1.420 + /* Were we able to immediately close all windows? if not, eAttemptQuit 1.421 + failed. This could happen for a variety of reasons; in fact it's 1.422 + very likely. Perhaps we're being called from JS and the window->Close 1.423 + method hasn't had a chance to wrap itself up yet. So give up. 1.424 + We'll return (with eConsiderQuit) as the remaining windows are 1.425 + closed. */ 1.426 + mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator)); 1.427 + if (windowEnumerator) { 1.428 + bool more; 1.429 + while (windowEnumerator->HasMoreElements(&more), more) { 1.430 + /* we can't quit immediately. we'll try again as the last window 1.431 + finally closes. */ 1.432 + ferocity = eAttemptQuit; 1.433 + nsCOMPtr<nsISupports> window; 1.434 + windowEnumerator->GetNext(getter_AddRefs(window)); 1.435 + nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(window); 1.436 + if (domWindow) { 1.437 + bool closed = false; 1.438 + domWindow->GetClosed(&closed); 1.439 + if (!closed) { 1.440 + rv = NS_ERROR_FAILURE; 1.441 + break; 1.442 + } 1.443 + } 1.444 + } 1.445 + } 1.446 + } 1.447 + } 1.448 + } 1.449 + 1.450 + if (ferocity == eForceQuit) { 1.451 + // do it! 1.452 + 1.453 + // No chance of the shutdown being cancelled from here on; tell people 1.454 + // we're shutting down for sure while all services are still available. 1.455 + if (obsService) { 1.456 + NS_NAMED_LITERAL_STRING(shutdownStr, "shutdown"); 1.457 + NS_NAMED_LITERAL_STRING(restartStr, "restart"); 1.458 + obsService->NotifyObservers(nullptr, "quit-application", 1.459 + (mRestart || mRestartTouchEnvironment) ? 1.460 + restartStr.get() : shutdownStr.get()); 1.461 + } 1.462 + 1.463 + if (!mRunning) { 1.464 + postedExitEvent = true; 1.465 + } 1.466 + else { 1.467 + // no matter what, make sure we send the exit event. If 1.468 + // worst comes to worst, we'll do a leaky shutdown but we WILL 1.469 + // shut down. Well, assuming that all *this* stuff works ;-). 1.470 + nsCOMPtr<nsIRunnable> event = new nsAppExitEvent(this); 1.471 + rv = NS_DispatchToCurrentThread(event); 1.472 + if (NS_SUCCEEDED(rv)) { 1.473 + postedExitEvent = true; 1.474 + } 1.475 + else { 1.476 + NS_WARNING("failed to dispatch nsAppExitEvent"); 1.477 + } 1.478 + } 1.479 + } 1.480 + 1.481 + // turn off the reentrancy check flag, but not if we have 1.482 + // more asynchronous work to do still. 1.483 + if (!postedExitEvent) 1.484 + mShuttingDown = false; 1.485 + return rv; 1.486 +} 1.487 + 1.488 + 1.489 +void 1.490 +nsAppStartup::CloseAllWindows() 1.491 +{ 1.492 + nsCOMPtr<nsIWindowMediator> mediator 1.493 + (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); 1.494 + 1.495 + nsCOMPtr<nsISimpleEnumerator> windowEnumerator; 1.496 + 1.497 + mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator)); 1.498 + 1.499 + if (!windowEnumerator) 1.500 + return; 1.501 + 1.502 + bool more; 1.503 + while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) { 1.504 + nsCOMPtr<nsISupports> isupports; 1.505 + if (NS_FAILED(windowEnumerator->GetNext(getter_AddRefs(isupports)))) 1.506 + break; 1.507 + 1.508 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(isupports); 1.509 + NS_ASSERTION(window, "not an nsPIDOMWindow"); 1.510 + if (window) 1.511 + window->ForceClose(); 1.512 + } 1.513 +} 1.514 + 1.515 +NS_IMETHODIMP 1.516 +nsAppStartup::EnterLastWindowClosingSurvivalArea(void) 1.517 +{ 1.518 + ++mConsiderQuitStopper; 1.519 + return NS_OK; 1.520 +} 1.521 + 1.522 + 1.523 +NS_IMETHODIMP 1.524 +nsAppStartup::ExitLastWindowClosingSurvivalArea(void) 1.525 +{ 1.526 + NS_ASSERTION(mConsiderQuitStopper > 0, "consider quit stopper out of bounds"); 1.527 + --mConsiderQuitStopper; 1.528 + 1.529 + if (mRunning) 1.530 + Quit(eConsiderQuit); 1.531 + 1.532 + return NS_OK; 1.533 +} 1.534 + 1.535 +// 1.536 +// nsAppStartup->nsIAppStartup2 1.537 +// 1.538 + 1.539 +NS_IMETHODIMP 1.540 +nsAppStartup::GetShuttingDown(bool *aResult) 1.541 +{ 1.542 + *aResult = mShuttingDown; 1.543 + return NS_OK; 1.544 +} 1.545 + 1.546 +NS_IMETHODIMP 1.547 +nsAppStartup::GetStartingUp(bool *aResult) 1.548 +{ 1.549 + *aResult = mStartingUp; 1.550 + return NS_OK; 1.551 +} 1.552 + 1.553 +NS_IMETHODIMP 1.554 +nsAppStartup::DoneStartingUp() 1.555 +{ 1.556 + // This must be called once at most 1.557 + MOZ_ASSERT(mStartingUp); 1.558 + 1.559 + mStartingUp = false; 1.560 + return NS_OK; 1.561 +} 1.562 + 1.563 +NS_IMETHODIMP 1.564 +nsAppStartup::GetRestarting(bool *aResult) 1.565 +{ 1.566 + *aResult = mRestart; 1.567 + return NS_OK; 1.568 +} 1.569 + 1.570 +NS_IMETHODIMP 1.571 +nsAppStartup::GetWasRestarted(bool *aResult) 1.572 +{ 1.573 + char *mozAppRestart = PR_GetEnv("MOZ_APP_RESTART"); 1.574 + 1.575 + /* When calling PR_SetEnv() with an empty value the existing variable may 1.576 + * be unset or set to the empty string depending on the underlying platform 1.577 + * thus we have to check if the variable is present and not empty. */ 1.578 + *aResult = mozAppRestart && (strcmp(mozAppRestart, "") != 0); 1.579 + 1.580 + return NS_OK; 1.581 +} 1.582 + 1.583 +NS_IMETHODIMP 1.584 +nsAppStartup::GetRestartingTouchEnvironment(bool *aResult) 1.585 +{ 1.586 + NS_ENSURE_ARG_POINTER(aResult); 1.587 + *aResult = mRestartTouchEnvironment; 1.588 + return NS_OK; 1.589 +} 1.590 + 1.591 +NS_IMETHODIMP 1.592 +nsAppStartup::SetInterrupted(bool aInterrupted) 1.593 +{ 1.594 + mInterrupted = aInterrupted; 1.595 + return NS_OK; 1.596 +} 1.597 + 1.598 +NS_IMETHODIMP 1.599 +nsAppStartup::GetInterrupted(bool *aInterrupted) 1.600 +{ 1.601 + *aInterrupted = mInterrupted; 1.602 + return NS_OK; 1.603 +} 1.604 + 1.605 +// 1.606 +// nsAppStartup->nsIWindowCreator 1.607 +// 1.608 + 1.609 +NS_IMETHODIMP 1.610 +nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent, 1.611 + uint32_t aChromeFlags, 1.612 + nsIWebBrowserChrome **_retval) 1.613 +{ 1.614 + bool cancel; 1.615 + return CreateChromeWindow2(aParent, aChromeFlags, 0, 0, &cancel, _retval); 1.616 +} 1.617 + 1.618 + 1.619 +// 1.620 +// nsAppStartup->nsIWindowCreator2 1.621 +// 1.622 + 1.623 +NS_IMETHODIMP 1.624 +nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent, 1.625 + uint32_t aChromeFlags, 1.626 + uint32_t aContextFlags, 1.627 + nsIURI *aURI, 1.628 + bool *aCancel, 1.629 + nsIWebBrowserChrome **_retval) 1.630 +{ 1.631 + NS_ENSURE_ARG_POINTER(aCancel); 1.632 + NS_ENSURE_ARG_POINTER(_retval); 1.633 + *aCancel = false; 1.634 + *_retval = 0; 1.635 + 1.636 + // Non-modal windows cannot be opened if we are attempting to quit 1.637 + if (mAttemptingQuit && (aChromeFlags & nsIWebBrowserChrome::CHROME_MODAL) == 0) 1.638 + return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; 1.639 + 1.640 + nsCOMPtr<nsIXULWindow> newWindow; 1.641 + 1.642 + if (aParent) { 1.643 + nsCOMPtr<nsIXULWindow> xulParent(do_GetInterface(aParent)); 1.644 + NS_ASSERTION(xulParent, "window created using non-XUL parent. that's unexpected, but may work."); 1.645 + 1.646 + if (xulParent) 1.647 + xulParent->CreateNewWindow(aChromeFlags, getter_AddRefs(newWindow)); 1.648 + // And if it fails, don't try again without a parent. It could fail 1.649 + // intentionally (bug 115969). 1.650 + } else { // try using basic methods: 1.651 + /* You really shouldn't be making dependent windows without a parent. 1.652 + But unparented modal (and therefore dependent) windows happen 1.653 + in our codebase, so we allow it after some bellyaching: */ 1.654 + if (aChromeFlags & nsIWebBrowserChrome::CHROME_DEPENDENT) 1.655 + NS_WARNING("dependent window created without a parent"); 1.656 + 1.657 + nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); 1.658 + if (!appShell) 1.659 + return NS_ERROR_FAILURE; 1.660 + 1.661 + appShell->CreateTopLevelWindow(0, 0, aChromeFlags, 1.662 + nsIAppShellService::SIZE_TO_CONTENT, 1.663 + nsIAppShellService::SIZE_TO_CONTENT, 1.664 + getter_AddRefs(newWindow)); 1.665 + } 1.666 + 1.667 + // if anybody gave us anything to work with, use it 1.668 + if (newWindow) { 1.669 + newWindow->SetContextFlags(aContextFlags); 1.670 + nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(newWindow)); 1.671 + if (thing) 1.672 + CallGetInterface(thing.get(), _retval); 1.673 + } 1.674 + 1.675 + return *_retval ? NS_OK : NS_ERROR_FAILURE; 1.676 +} 1.677 + 1.678 + 1.679 +// 1.680 +// nsAppStartup->nsIObserver 1.681 +// 1.682 + 1.683 +NS_IMETHODIMP 1.684 +nsAppStartup::Observe(nsISupports *aSubject, 1.685 + const char *aTopic, const char16_t *aData) 1.686 +{ 1.687 + NS_ASSERTION(mAppShell, "appshell service notified before appshell built"); 1.688 + if (!strcmp(aTopic, "quit-application-forced")) { 1.689 + mShuttingDown = true; 1.690 + } 1.691 + else if (!strcmp(aTopic, "profile-change-teardown")) { 1.692 + if (!mShuttingDown) { 1.693 + EnterLastWindowClosingSurvivalArea(); 1.694 + CloseAllWindows(); 1.695 + ExitLastWindowClosingSurvivalArea(); 1.696 + } 1.697 + } else if (!strcmp(aTopic, "xul-window-registered")) { 1.698 + EnterLastWindowClosingSurvivalArea(); 1.699 + } else if (!strcmp(aTopic, "xul-window-destroyed")) { 1.700 + ExitLastWindowClosingSurvivalArea(); 1.701 + } else if (!strcmp(aTopic, "sessionstore-windows-restored")) { 1.702 + StartupTimeline::Record(StartupTimeline::SESSION_RESTORED); 1.703 + IOInterposer::EnteringNextStage(); 1.704 +#if defined(XP_WIN) 1.705 + if (mSessionWindowRestoredProbe) { 1.706 + mSessionWindowRestoredProbe->Trigger(); 1.707 + } 1.708 + } else if (!strcmp(aTopic, "places-init-complete")) { 1.709 + if (mPlacesInitCompleteProbe) { 1.710 + mPlacesInitCompleteProbe->Trigger(); 1.711 + } 1.712 +#endif //defined(XP_WIN) 1.713 + } else if (!strcmp(aTopic, "sessionstore-init-started")) { 1.714 + StartupTimeline::Record(StartupTimeline::SESSION_RESTORE_INIT); 1.715 + } else if (!strcmp(aTopic, "xpcom-shutdown")) { 1.716 + IOInterposer::EnteringNextStage(); 1.717 +#if defined(XP_WIN) 1.718 + if (mXPCOMShutdownProbe) { 1.719 + mXPCOMShutdownProbe->Trigger(); 1.720 + } 1.721 +#endif // defined(XP_WIN) 1.722 + } else { 1.723 + NS_ERROR("Unexpected observer topic."); 1.724 + } 1.725 + 1.726 + return NS_OK; 1.727 +} 1.728 + 1.729 +NS_IMETHODIMP 1.730 +nsAppStartup::GetStartupInfo(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) 1.731 +{ 1.732 + JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); 1.733 + 1.734 + aRetval.setObject(*obj); 1.735 + 1.736 + TimeStamp procTime = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION); 1.737 + TimeStamp now = TimeStamp::Now(); 1.738 + PRTime absNow = PR_Now(); 1.739 + 1.740 + if (procTime.IsNull()) { 1.741 + bool error = false; 1.742 + 1.743 + procTime = TimeStamp::ProcessCreation(error); 1.744 + 1.745 + if (error) { 1.746 + Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, 1.747 + StartupTimeline::PROCESS_CREATION); 1.748 + } 1.749 + 1.750 + StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, procTime); 1.751 + } 1.752 + 1.753 + for (int i = StartupTimeline::PROCESS_CREATION; 1.754 + i < StartupTimeline::MAX_EVENT_ID; 1.755 + ++i) 1.756 + { 1.757 + StartupTimeline::Event ev = static_cast<StartupTimeline::Event>(i); 1.758 + TimeStamp stamp = StartupTimeline::Get(ev); 1.759 + 1.760 + if (stamp.IsNull() && (ev == StartupTimeline::MAIN)) { 1.761 + // Always define main to aid with bug 689256. 1.762 + stamp = procTime; 1.763 + MOZ_ASSERT(!stamp.IsNull()); 1.764 + Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, 1.765 + StartupTimeline::MAIN); 1.766 + } 1.767 + 1.768 + if (!stamp.IsNull()) { 1.769 + if (stamp >= procTime) { 1.770 + PRTime prStamp = ComputeAbsoluteTimestamp(absNow, now, stamp) 1.771 + / PR_USEC_PER_MSEC; 1.772 + JS::Rooted<JSObject*> date(aCx, JS_NewDateObjectMsec(aCx, prStamp)); 1.773 + JS_DefineProperty(aCx, obj, StartupTimeline::Describe(ev), date, JSPROP_ENUMERATE); 1.774 + } else { 1.775 + Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, ev); 1.776 + } 1.777 + } 1.778 + } 1.779 + 1.780 + return NS_OK; 1.781 +} 1.782 + 1.783 +NS_IMETHODIMP 1.784 +nsAppStartup::GetAutomaticSafeModeNecessary(bool *_retval) 1.785 +{ 1.786 + NS_ENSURE_ARG_POINTER(_retval); 1.787 + 1.788 + bool alwaysSafe = false; 1.789 + Preferences::GetBool(kPrefAlwaysUseSafeMode, &alwaysSafe); 1.790 + 1.791 + if (!alwaysSafe) { 1.792 +#if DEBUG 1.793 + mIsSafeModeNecessary = false; 1.794 +#else 1.795 + mIsSafeModeNecessary &= !PR_GetEnv("MOZ_DISABLE_AUTO_SAFE_MODE"); 1.796 +#endif 1.797 + } 1.798 + 1.799 + *_retval = mIsSafeModeNecessary; 1.800 + return NS_OK; 1.801 +} 1.802 + 1.803 +NS_IMETHODIMP 1.804 +nsAppStartup::TrackStartupCrashBegin(bool *aIsSafeModeNecessary) 1.805 +{ 1.806 + const int32_t MAX_TIME_SINCE_STARTUP = 6 * 60 * 60 * 1000; 1.807 + const int32_t MAX_STARTUP_BUFFER = 10; 1.808 + nsresult rv; 1.809 + 1.810 + mStartupCrashTrackingEnded = false; 1.811 + 1.812 + StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_BEGIN); 1.813 + 1.814 + bool hasLastSuccess = Preferences::HasUserValue(kPrefLastSuccess); 1.815 + if (!hasLastSuccess) { 1.816 + // Clear so we don't get stuck with SafeModeNecessary returning true if we 1.817 + // have had too many recent crashes and the last success pref is missing. 1.818 + Preferences::ClearUser(kPrefRecentCrashes); 1.819 + return NS_ERROR_NOT_AVAILABLE; 1.820 + } 1.821 + 1.822 + bool inSafeMode = false; 1.823 + nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID); 1.824 + NS_ENSURE_TRUE(xr, NS_ERROR_FAILURE); 1.825 + 1.826 + xr->GetInSafeMode(&inSafeMode); 1.827 + 1.828 + PRTime replacedLockTime; 1.829 + rv = xr->GetReplacedLockTime(&replacedLockTime); 1.830 + 1.831 + if (NS_FAILED(rv) || !replacedLockTime) { 1.832 + if (!inSafeMode) 1.833 + Preferences::ClearUser(kPrefRecentCrashes); 1.834 + GetAutomaticSafeModeNecessary(aIsSafeModeNecessary); 1.835 + return NS_OK; 1.836 + } 1.837 + 1.838 + // check whether safe mode is necessary 1.839 + int32_t maxResumedCrashes = -1; 1.840 + rv = Preferences::GetInt(kPrefMaxResumedCrashes, &maxResumedCrashes); 1.841 + NS_ENSURE_SUCCESS(rv, NS_OK); 1.842 + 1.843 + int32_t recentCrashes = 0; 1.844 + Preferences::GetInt(kPrefRecentCrashes, &recentCrashes); 1.845 + mIsSafeModeNecessary = (recentCrashes > maxResumedCrashes && maxResumedCrashes != -1); 1.846 + 1.847 + // Bug 731613 - Don't check if the last startup was a crash if XRE_PROFILE_PATH is set. After 1.848 + // profile manager, the profile lock's mod. time has been changed so can't be used on this startup. 1.849 + // After a restart, it's safe to assume the last startup was successful. 1.850 + char *xreProfilePath = PR_GetEnv("XRE_PROFILE_PATH"); 1.851 + if (xreProfilePath) { 1.852 + GetAutomaticSafeModeNecessary(aIsSafeModeNecessary); 1.853 + return NS_ERROR_NOT_AVAILABLE; 1.854 + } 1.855 + 1.856 + // time of last successful startup 1.857 + int32_t lastSuccessfulStartup; 1.858 + rv = Preferences::GetInt(kPrefLastSuccess, &lastSuccessfulStartup); 1.859 + NS_ENSURE_SUCCESS(rv, rv); 1.860 + 1.861 + int32_t lockSeconds = (int32_t)(replacedLockTime / PR_MSEC_PER_SEC); 1.862 + 1.863 + // started close enough to good startup so call it good 1.864 + if (lockSeconds <= lastSuccessfulStartup + MAX_STARTUP_BUFFER 1.865 + && lockSeconds >= lastSuccessfulStartup - MAX_STARTUP_BUFFER) { 1.866 + GetAutomaticSafeModeNecessary(aIsSafeModeNecessary); 1.867 + return NS_OK; 1.868 + } 1.869 + 1.870 + // sanity check that the pref set at last success is not greater than the current time 1.871 + if (PR_Now() / PR_USEC_PER_SEC <= lastSuccessfulStartup) 1.872 + return NS_ERROR_FAILURE; 1.873 + 1.874 + // The last startup was a crash so include it in the count regardless of when it happened. 1.875 + Telemetry::Accumulate(Telemetry::STARTUP_CRASH_DETECTED, true); 1.876 + 1.877 + if (inSafeMode) { 1.878 + GetAutomaticSafeModeNecessary(aIsSafeModeNecessary); 1.879 + return NS_OK; 1.880 + } 1.881 + 1.882 + PRTime now = (PR_Now() / PR_USEC_PER_MSEC); 1.883 + // if the last startup attempt which crashed was in the last 6 hours 1.884 + if (replacedLockTime >= now - MAX_TIME_SINCE_STARTUP) { 1.885 + NS_WARNING("Last startup was detected as a crash."); 1.886 + recentCrashes++; 1.887 + rv = Preferences::SetInt(kPrefRecentCrashes, recentCrashes); 1.888 + } else { 1.889 + // Otherwise ignore that crash and all previous since it may not be applicable anymore 1.890 + // and we don't want someone to get stuck in safe mode if their prefs are read-only. 1.891 + rv = Preferences::ClearUser(kPrefRecentCrashes); 1.892 + } 1.893 + NS_ENSURE_SUCCESS(rv, rv); 1.894 + 1.895 + // recalculate since recent crashes count may have changed above 1.896 + mIsSafeModeNecessary = (recentCrashes > maxResumedCrashes && maxResumedCrashes != -1); 1.897 + 1.898 + nsCOMPtr<nsIPrefService> prefs = Preferences::GetService(); 1.899 + rv = prefs->SavePrefFile(nullptr); // flush prefs to disk since we are tracking crashes 1.900 + NS_ENSURE_SUCCESS(rv, rv); 1.901 + 1.902 + GetAutomaticSafeModeNecessary(aIsSafeModeNecessary); 1.903 + return rv; 1.904 +} 1.905 + 1.906 +NS_IMETHODIMP 1.907 +nsAppStartup::TrackStartupCrashEnd() 1.908 +{ 1.909 + bool inSafeMode = false; 1.910 + nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID); 1.911 + if (xr) 1.912 + xr->GetInSafeMode(&inSafeMode); 1.913 + 1.914 + // return if we already ended or we're restarting into safe mode 1.915 + if (mStartupCrashTrackingEnded || (mIsSafeModeNecessary && !inSafeMode)) 1.916 + return NS_OK; 1.917 + mStartupCrashTrackingEnded = true; 1.918 + 1.919 + StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_END); 1.920 + 1.921 + // Use the timestamp of XRE_main as an approximation for the lock file timestamp. 1.922 + // See MAX_STARTUP_BUFFER for the buffer time period. 1.923 + TimeStamp mainTime = StartupTimeline::Get(StartupTimeline::MAIN); 1.924 + TimeStamp now = TimeStamp::Now(); 1.925 + PRTime prNow = PR_Now(); 1.926 + nsresult rv; 1.927 + 1.928 + if (mainTime.IsNull()) { 1.929 + NS_WARNING("Could not get StartupTimeline::MAIN time."); 1.930 + } else { 1.931 + uint64_t lockFileTime = ComputeAbsoluteTimestamp(prNow, now, mainTime); 1.932 + 1.933 + rv = Preferences::SetInt(kPrefLastSuccess, 1.934 + (int32_t)(lockFileTime / PR_USEC_PER_SEC)); 1.935 + 1.936 + if (NS_FAILED(rv)) 1.937 + NS_WARNING("Could not set startup crash detection pref."); 1.938 + } 1.939 + 1.940 + if (inSafeMode && mIsSafeModeNecessary) { 1.941 + // On a successful startup in automatic safe mode, allow the user one more crash 1.942 + // in regular mode before returning to safe mode. 1.943 + int32_t maxResumedCrashes = 0; 1.944 + int32_t prefType; 1.945 + rv = Preferences::GetDefaultRootBranch()->GetPrefType(kPrefMaxResumedCrashes, &prefType); 1.946 + NS_ENSURE_SUCCESS(rv, rv); 1.947 + if (prefType == nsIPrefBranch::PREF_INT) { 1.948 + rv = Preferences::GetInt(kPrefMaxResumedCrashes, &maxResumedCrashes); 1.949 + NS_ENSURE_SUCCESS(rv, rv); 1.950 + } 1.951 + rv = Preferences::SetInt(kPrefRecentCrashes, maxResumedCrashes); 1.952 + NS_ENSURE_SUCCESS(rv, rv); 1.953 + } else if (!inSafeMode) { 1.954 + // clear the count of recent crashes after a succesful startup when not in safe mode 1.955 + rv = Preferences::ClearUser(kPrefRecentCrashes); 1.956 + if (NS_FAILED(rv)) NS_WARNING("Could not clear startup crash count."); 1.957 + } 1.958 + nsCOMPtr<nsIPrefService> prefs = Preferences::GetService(); 1.959 + rv = prefs->SavePrefFile(nullptr); // flush prefs to disk since we are tracking crashes 1.960 + 1.961 + return rv; 1.962 +} 1.963 + 1.964 +NS_IMETHODIMP 1.965 +nsAppStartup::RestartInSafeMode(uint32_t aQuitMode) 1.966 +{ 1.967 + PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); 1.968 + this->Quit(aQuitMode | nsIAppStartup::eRestart); 1.969 + 1.970 + return NS_OK; 1.971 +}