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