Source code

Revision control

Other Tools

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