toolkit/components/startup/nsAppStartup.cpp

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

mercurial