1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code. This file was split
16 : * from xpfe/appshell/src/nsAppShellService.cpp
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : * Robert O'Callahan <roc+moz@cs.cmu.edu>
26 : * Benjamin Smedberg <bsmedberg@covad.net>
27 : * Daniel Brooks <db48x@db48x.net>
28 : * Taras Glek <tglek@mozilla.com>
29 : * Landry Breuil <landry@openbsd.org>
30 : * David Rajchenbach-Teller <dteller@mozilla.com>
31 : *
32 : * Alternatively, the contents of this file may be used under the terms of
33 : * either of the GNU General Public License Version 2 or later (the "GPL"),
34 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 : * in which case the provisions of the GPL or the LGPL are applicable instead
36 : * of those above. If you wish to allow use of your version of this file only
37 : * under the terms of either the GPL or the LGPL, and not to allow others to
38 : * use your version of this file under the terms of the MPL, indicate your
39 : * decision by deleting the provisions above and replace them with the notice
40 : * and other provisions required by the GPL or the LGPL. If you do not delete
41 : * the provisions above, a recipient may use your version of this file under
42 : * the terms of any one of the MPL, the GPL or the LGPL.
43 : *
44 : * ***** END LICENSE BLOCK ***** */
45 :
46 : #include "nsAppStartup.h"
47 :
48 : #include "nsIAppShellService.h"
49 : #include "nsPIDOMWindow.h"
50 : #include "nsIInterfaceRequestor.h"
51 : #include "nsILocalFile.h"
52 : #include "nsIObserverService.h"
53 : #include "nsIPrefBranch.h"
54 : #include "nsIPrefService.h"
55 : #include "nsIProfileChangeStatus.h"
56 : #include "nsIPromptService.h"
57 : #include "nsIStringBundle.h"
58 : #include "nsISupportsPrimitives.h"
59 : #include "nsIWebBrowserChrome.h"
60 : #include "nsIWindowMediator.h"
61 : #include "nsIWindowWatcher.h"
62 : #include "nsIXULRuntime.h"
63 : #include "nsIXULWindow.h"
64 : #include "nsNativeCharsetUtils.h"
65 : #include "nsThreadUtils.h"
66 : #include "nsAutoPtr.h"
67 : #include "nsStringGlue.h"
68 : #include "mozilla/Preferences.h"
69 :
70 : #include "prprf.h"
71 : #include "nsCRT.h"
72 : #include "nsIInterfaceRequestorUtils.h"
73 : #include "nsWidgetsCID.h"
74 : #include "nsAppShellCID.h"
75 : #include "nsXPCOMCIDInternal.h"
76 : #include "mozilla/Services.h"
77 : #include "mozilla/FunctionTimer.h"
78 : #include "nsIXPConnect.h"
79 : #include "jsapi.h"
80 : #include "prenv.h"
81 :
82 : #if defined(XP_WIN)
83 : #include <windows.h>
84 : // windows.h can go to hell
85 : #undef GetStartupInfo
86 : #elif defined(XP_UNIX)
87 : #include <unistd.h>
88 : #include <sys/syscall.h>
89 : #endif
90 :
91 : #ifdef XP_MACOSX
92 : #include <sys/sysctl.h>
93 : #endif
94 :
95 : #ifdef __OpenBSD__
96 : #include <sys/param.h>
97 : #include <sys/sysctl.h>
98 : #endif
99 :
100 : #include "mozilla/Telemetry.h"
101 : #include "mozilla/StartupTimeline.h"
102 :
103 : static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
104 :
105 : #define kPrefLastSuccess "toolkit.startup.last_success"
106 : #define kPrefMaxResumedCrashes "toolkit.startup.max_resumed_crashes"
107 : #define kPrefRecentCrashes "toolkit.startup.recent_crashes"
108 :
109 : #if defined(XP_WIN)
110 : #include "mozilla/perfprobe.h"
111 : /**
112 : * Events sent to the system for profiling purposes
113 : */
114 : //Keep them syncronized with the .mof file
115 :
116 : //Process-wide GUID, used by the OS to differentiate sources
117 : // {509962E0-406B-46F4-99BA-5A009F8D2225}
118 : //Keep it synchronized with the .mof file
119 : #define NS_APPLICATION_TRACING_CID \
120 : { 0x509962E0, 0x406B, 0x46F4, \
121 : { 0x99, 0xBA, 0x5A, 0x00, 0x9F, 0x8D, 0x22, 0x25} }
122 :
123 : //Event-specific GUIDs, used by the OS to differentiate events
124 : // {A3DA04E0-57D7-482A-A1C1-61DA5F95BACB}
125 : #define NS_PLACES_INIT_COMPLETE_EVENT_CID \
126 : { 0xA3DA04E0, 0x57D7, 0x482A, \
127 : { 0xA1, 0xC1, 0x61, 0xDA, 0x5F, 0x95, 0xBA, 0xCB} }
128 : // {917B96B1-ECAD-4DAB-A760-8D49027748AE}
129 : #define NS_SESSION_STORE_WINDOW_RESTORED_EVENT_CID \
130 : { 0x917B96B1, 0xECAD, 0x4DAB, \
131 : { 0xA7, 0x60, 0x8D, 0x49, 0x02, 0x77, 0x48, 0xAE} }
132 :
133 : static NS_DEFINE_CID(kApplicationTracingCID,
134 : NS_APPLICATION_TRACING_CID);
135 : static NS_DEFINE_CID(kPlacesInitCompleteCID,
136 : NS_PLACES_INIT_COMPLETE_EVENT_CID);
137 : static NS_DEFINE_CID(kSessionStoreWindowRestoredCID,
138 : NS_SESSION_STORE_WINDOW_RESTORED_EVENT_CID);
139 : #endif //defined(XP_WIN)
140 :
141 : using namespace mozilla;
142 :
143 : PRUint32 gRestartMode = 0;
144 :
145 0 : class nsAppExitEvent : public nsRunnable {
146 : private:
147 : nsRefPtr<nsAppStartup> mService;
148 :
149 : public:
150 0 : nsAppExitEvent(nsAppStartup *service) : mService(service) {}
151 :
152 0 : NS_IMETHOD Run() {
153 : // Tell the appshell to exit
154 0 : mService->mAppShell->Exit();
155 :
156 : // We're done "shutting down".
157 0 : mService->mShuttingDown = false;
158 0 : mService->mRunning = false;
159 0 : return NS_OK;
160 : }
161 : };
162 :
163 : //
164 : // nsAppStartup
165 : //
166 :
167 16 : nsAppStartup::nsAppStartup() :
168 : mConsiderQuitStopper(0),
169 : mRunning(false),
170 : mShuttingDown(false),
171 : mAttemptingQuit(false),
172 : mRestart(false),
173 : mInterrupted(false),
174 : mIsSafeModeNecessary(false),
175 16 : mStartupCrashTrackingEnded(false)
176 16 : { }
177 :
178 :
179 : nsresult
180 16 : nsAppStartup::Init()
181 : {
182 : NS_TIME_FUNCTION;
183 : nsresult rv;
184 :
185 : // Create widget application shell
186 16 : mAppShell = do_GetService(kAppShellCID, &rv);
187 16 : NS_ENSURE_SUCCESS(rv, rv);
188 :
189 : NS_TIME_FUNCTION_MARK("Got AppShell service");
190 :
191 : nsCOMPtr<nsIObserverService> os =
192 32 : mozilla::services::GetObserverService();
193 16 : if (!os)
194 0 : return NS_ERROR_FAILURE;
195 :
196 : NS_TIME_FUNCTION_MARK("Got Observer service");
197 :
198 16 : os->AddObserver(this, "quit-application-forced", true);
199 16 : os->AddObserver(this, "sessionstore-windows-restored", true);
200 16 : os->AddObserver(this, "profile-change-teardown", true);
201 16 : os->AddObserver(this, "xul-window-registered", true);
202 16 : os->AddObserver(this, "xul-window-destroyed", true);
203 :
204 : #if defined(XP_WIN)
205 : os->AddObserver(this, "places-init-complete", true);
206 : // This last event is only interesting to us for xperf-based measures
207 :
208 : // Initialize interaction with profiler
209 : mProbesManager =
210 : new ProbeManager(
211 : kApplicationTracingCID,
212 : NS_LITERAL_CSTRING("Application startup probe"));
213 : // Note: The operation is meant mostly for in-house profiling.
214 : // Therefore, we do not warn if probes manager cannot be initialized
215 :
216 : if (mProbesManager) {
217 : mPlacesInitCompleteProbe =
218 : mProbesManager->
219 : GetProbe(kPlacesInitCompleteCID,
220 : NS_LITERAL_CSTRING("places-init-complete"));
221 : NS_WARN_IF_FALSE(mPlacesInitCompleteProbe,
222 : "Cannot initialize probe 'places-init-complete'");
223 :
224 : mSessionWindowRestoredProbe =
225 : mProbesManager->
226 : GetProbe(kSessionStoreWindowRestoredCID,
227 : NS_LITERAL_CSTRING("sessionstore-windows-restored"));
228 : NS_WARN_IF_FALSE(mSessionWindowRestoredProbe,
229 : "Cannot initialize probe 'sessionstore-windows-restored'");
230 :
231 : rv = mProbesManager->StartSession();
232 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
233 : "Cannot initialize system probe manager");
234 : }
235 : #endif //defined(XP_WIN)
236 :
237 16 : return NS_OK;
238 : }
239 :
240 :
241 : //
242 : // nsAppStartup->nsISupports
243 : //
244 :
245 758 : NS_IMPL_THREADSAFE_ISUPPORTS5(nsAppStartup,
246 : nsIAppStartup,
247 : nsIWindowCreator,
248 : nsIWindowCreator2,
249 : nsIObserver,
250 : nsISupportsWeakReference)
251 :
252 :
253 : //
254 : // nsAppStartup->nsIAppStartup
255 : //
256 :
257 : NS_IMETHODIMP
258 0 : nsAppStartup::CreateHiddenWindow()
259 : {
260 : nsCOMPtr<nsIAppShellService> appShellService
261 0 : (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
262 0 : NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE);
263 :
264 0 : return appShellService->CreateHiddenWindow();
265 : }
266 :
267 :
268 : NS_IMETHODIMP
269 0 : nsAppStartup::DestroyHiddenWindow()
270 : {
271 : nsCOMPtr<nsIAppShellService> appShellService
272 0 : (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
273 0 : NS_ENSURE_TRUE(appShellService, NS_ERROR_FAILURE);
274 :
275 0 : return appShellService->DestroyHiddenWindow();
276 : }
277 :
278 : NS_IMETHODIMP
279 0 : nsAppStartup::Run(void)
280 : {
281 0 : NS_ASSERTION(!mRunning, "Reentrant appstartup->Run()");
282 :
283 : // If we have no windows open and no explicit calls to
284 : // enterLastWindowClosingSurvivalArea, or somebody has explicitly called
285 : // quit, don't bother running the event loop which would probably leave us
286 : // with a zombie process.
287 :
288 0 : if (!mShuttingDown && mConsiderQuitStopper != 0) {
289 : #ifdef XP_MACOSX
290 : EnterLastWindowClosingSurvivalArea();
291 : #endif
292 :
293 0 : mRunning = true;
294 :
295 0 : nsresult rv = mAppShell->Run();
296 0 : if (NS_FAILED(rv))
297 0 : return rv;
298 : }
299 :
300 0 : return mRestart ? NS_SUCCESS_RESTART_APP : NS_OK;
301 : }
302 :
303 :
304 : NS_IMETHODIMP
305 0 : nsAppStartup::Quit(PRUint32 aMode)
306 : {
307 0 : PRUint32 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 0 : if (mConsiderQuitStopper == 0) {
321 : // there are no windows...
322 0 : ferocity = eAttemptQuit;
323 : }
324 : #ifdef XP_MACOSX
325 : else if (mConsiderQuitStopper == 1) {
326 : // ... or there is only a hiddenWindow left, and it's useless:
327 : nsCOMPtr<nsIAppShellService> appShell
328 : (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
329 :
330 : // Failure shouldn't be fatal, but will abort quit attempt:
331 : if (!appShell)
332 : return NS_OK;
333 :
334 : bool usefulHiddenWindow;
335 : appShell->GetApplicationProvidedHiddenWindow(&usefulHiddenWindow);
336 : nsCOMPtr<nsIXULWindow> hiddenWindow;
337 : appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
338 : // If the one window is useful, we won't quit:
339 : if (!hiddenWindow || usefulHiddenWindow)
340 : return NS_OK;
341 :
342 : ferocity = eAttemptQuit;
343 : }
344 : #endif
345 : }
346 :
347 0 : nsCOMPtr<nsIObserverService> obsService;
348 0 : if (ferocity == eAttemptQuit || ferocity == eForceQuit) {
349 :
350 0 : nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
351 0 : nsCOMPtr<nsIWindowMediator> mediator (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
352 0 : if (mediator) {
353 0 : mediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
354 0 : if (windowEnumerator) {
355 : bool more;
356 0 : while (windowEnumerator->HasMoreElements(&more), more) {
357 0 : nsCOMPtr<nsISupports> window;
358 0 : windowEnumerator->GetNext(getter_AddRefs(window));
359 0 : nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(window));
360 0 : if (domWindow) {
361 0 : if (!domWindow->CanClose())
362 0 : return NS_OK;
363 : }
364 : }
365 : }
366 : }
367 :
368 0 : mShuttingDown = true;
369 0 : if (!mRestart) {
370 0 : mRestart = (aMode & eRestart) != 0;
371 0 : gRestartMode = (aMode & 0xF0);
372 : }
373 :
374 0 : if (mRestart) {
375 : // Firefox-restarts reuse the process. Process start-time isn't a useful indicator of startup time
376 0 : PR_SetEnv(PR_smprintf("MOZ_APP_RESTART=%lld", (PRInt64) PR_Now() / PR_USEC_PER_MSEC));
377 : }
378 :
379 0 : obsService = mozilla::services::GetObserverService();
380 :
381 0 : if (!mAttemptingQuit) {
382 0 : mAttemptingQuit = true;
383 : #ifdef XP_MACOSX
384 : // now even the Mac wants to quit when the last window is closed
385 : ExitLastWindowClosingSurvivalArea();
386 : #endif
387 0 : if (obsService)
388 0 : obsService->NotifyObservers(nsnull, "quit-application-granted", nsnull);
389 : }
390 :
391 : /* Enumerate through each open window and close it. It's important to do
392 : this before we forcequit because this can control whether we really quit
393 : at all. e.g. if one of these windows has an unload handler that
394 : opens a new window. Ugh. I know. */
395 0 : CloseAllWindows();
396 :
397 0 : if (mediator) {
398 0 : if (ferocity == eAttemptQuit) {
399 0 : ferocity = eForceQuit; // assume success
400 :
401 : /* Were we able to immediately close all windows? if not, eAttemptQuit
402 : failed. This could happen for a variety of reasons; in fact it's
403 : very likely. Perhaps we're being called from JS and the window->Close
404 : method hasn't had a chance to wrap itself up yet. So give up.
405 : We'll return (with eConsiderQuit) as the remaining windows are
406 : closed. */
407 0 : mediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
408 0 : if (windowEnumerator) {
409 : bool more;
410 0 : while (windowEnumerator->HasMoreElements(&more), more) {
411 : /* we can't quit immediately. we'll try again as the last window
412 : finally closes. */
413 0 : ferocity = eAttemptQuit;
414 0 : nsCOMPtr<nsISupports> window;
415 0 : windowEnumerator->GetNext(getter_AddRefs(window));
416 0 : nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(window);
417 0 : if (domWindow) {
418 0 : bool closed = false;
419 0 : domWindow->GetClosed(&closed);
420 0 : if (!closed) {
421 0 : rv = NS_ERROR_FAILURE;
422 : break;
423 : }
424 : }
425 : }
426 : }
427 : }
428 : }
429 : }
430 :
431 0 : if (ferocity == eForceQuit) {
432 : // do it!
433 :
434 : // No chance of the shutdown being cancelled from here on; tell people
435 : // we're shutting down for sure while all services are still available.
436 0 : if (obsService) {
437 0 : NS_NAMED_LITERAL_STRING(shutdownStr, "shutdown");
438 0 : NS_NAMED_LITERAL_STRING(restartStr, "restart");
439 0 : obsService->NotifyObservers(nsnull, "quit-application",
440 0 : mRestart ? restartStr.get() : shutdownStr.get());
441 : }
442 :
443 0 : if (!mRunning) {
444 0 : postedExitEvent = true;
445 : }
446 : else {
447 : // no matter what, make sure we send the exit event. If
448 : // worst comes to worst, we'll do a leaky shutdown but we WILL
449 : // shut down. Well, assuming that all *this* stuff works ;-).
450 0 : nsCOMPtr<nsIRunnable> event = new nsAppExitEvent(this);
451 0 : rv = NS_DispatchToCurrentThread(event);
452 0 : if (NS_SUCCEEDED(rv)) {
453 0 : postedExitEvent = true;
454 : }
455 : else {
456 0 : NS_WARNING("failed to dispatch nsAppExitEvent");
457 : }
458 : }
459 : }
460 :
461 : // turn off the reentrancy check flag, but not if we have
462 : // more asynchronous work to do still.
463 0 : if (!postedExitEvent)
464 0 : mShuttingDown = false;
465 0 : return rv;
466 : }
467 :
468 :
469 : void
470 15 : nsAppStartup::CloseAllWindows()
471 : {
472 : nsCOMPtr<nsIWindowMediator> mediator
473 30 : (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
474 :
475 30 : nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
476 :
477 15 : mediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
478 :
479 15 : if (!windowEnumerator)
480 : return;
481 :
482 : bool more;
483 30 : while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) {
484 0 : nsCOMPtr<nsISupports> isupports;
485 0 : if (NS_FAILED(windowEnumerator->GetNext(getter_AddRefs(isupports))))
486 : break;
487 :
488 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(isupports);
489 0 : NS_ASSERTION(window, "not an nsPIDOMWindow");
490 0 : if (window)
491 0 : window->ForceClose();
492 : }
493 : }
494 :
495 : NS_IMETHODIMP
496 15 : nsAppStartup::EnterLastWindowClosingSurvivalArea(void)
497 : {
498 15 : ++mConsiderQuitStopper;
499 15 : return NS_OK;
500 : }
501 :
502 :
503 : NS_IMETHODIMP
504 15 : nsAppStartup::ExitLastWindowClosingSurvivalArea(void)
505 : {
506 15 : NS_ASSERTION(mConsiderQuitStopper > 0, "consider quit stopper out of bounds");
507 15 : --mConsiderQuitStopper;
508 :
509 15 : if (mRunning)
510 0 : Quit(eConsiderQuit);
511 :
512 15 : return NS_OK;
513 : }
514 :
515 : //
516 : // nsAppStartup->nsIAppStartup2
517 : //
518 :
519 : NS_IMETHODIMP
520 0 : nsAppStartup::GetShuttingDown(bool *aResult)
521 : {
522 0 : *aResult = mShuttingDown;
523 0 : return NS_OK;
524 : }
525 :
526 : NS_IMETHODIMP
527 17 : nsAppStartup::SetInterrupted(bool aInterrupted)
528 : {
529 17 : mInterrupted = aInterrupted;
530 17 : return NS_OK;
531 : }
532 :
533 : NS_IMETHODIMP
534 13 : nsAppStartup::GetInterrupted(bool *aInterrupted)
535 : {
536 13 : *aInterrupted = mInterrupted;
537 13 : return NS_OK;
538 : }
539 :
540 : //
541 : // nsAppStartup->nsIWindowCreator
542 : //
543 :
544 : NS_IMETHODIMP
545 0 : nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent,
546 : PRUint32 aChromeFlags,
547 : nsIWebBrowserChrome **_retval)
548 : {
549 : bool cancel;
550 0 : return CreateChromeWindow2(aParent, aChromeFlags, 0, 0, &cancel, _retval);
551 : }
552 :
553 :
554 : //
555 : // nsAppStartup->nsIWindowCreator2
556 : //
557 :
558 : NS_IMETHODIMP
559 0 : nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
560 : PRUint32 aChromeFlags,
561 : PRUint32 aContextFlags,
562 : nsIURI *aURI,
563 : bool *aCancel,
564 : nsIWebBrowserChrome **_retval)
565 : {
566 0 : NS_ENSURE_ARG_POINTER(aCancel);
567 0 : NS_ENSURE_ARG_POINTER(_retval);
568 0 : *aCancel = false;
569 0 : *_retval = 0;
570 :
571 : // Non-modal windows cannot be opened if we are attempting to quit
572 0 : if (mAttemptingQuit && (aChromeFlags & nsIWebBrowserChrome::CHROME_MODAL) == 0)
573 0 : return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
574 :
575 0 : nsCOMPtr<nsIXULWindow> newWindow;
576 :
577 0 : if (aParent) {
578 0 : nsCOMPtr<nsIXULWindow> xulParent(do_GetInterface(aParent));
579 0 : NS_ASSERTION(xulParent, "window created using non-XUL parent. that's unexpected, but may work.");
580 :
581 0 : if (xulParent)
582 0 : xulParent->CreateNewWindow(aChromeFlags, getter_AddRefs(newWindow));
583 : // And if it fails, don't try again without a parent. It could fail
584 : // intentionally (bug 115969).
585 : } else { // try using basic methods:
586 : /* You really shouldn't be making dependent windows without a parent.
587 : But unparented modal (and therefore dependent) windows happen
588 : in our codebase, so we allow it after some bellyaching: */
589 0 : if (aChromeFlags & nsIWebBrowserChrome::CHROME_DEPENDENT)
590 0 : NS_WARNING("dependent window created without a parent");
591 :
592 0 : nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
593 0 : if (!appShell)
594 0 : return NS_ERROR_FAILURE;
595 :
596 0 : appShell->CreateTopLevelWindow(0, 0, aChromeFlags,
597 : nsIAppShellService::SIZE_TO_CONTENT,
598 : nsIAppShellService::SIZE_TO_CONTENT,
599 0 : getter_AddRefs(newWindow));
600 : }
601 :
602 : // if anybody gave us anything to work with, use it
603 0 : if (newWindow) {
604 0 : newWindow->SetContextFlags(aContextFlags);
605 0 : nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(newWindow));
606 0 : if (thing)
607 0 : CallGetInterface(thing.get(), _retval);
608 : }
609 :
610 0 : return *_retval ? NS_OK : NS_ERROR_FAILURE;
611 : }
612 :
613 :
614 : //
615 : // nsAppStartup->nsIObserver
616 : //
617 :
618 : NS_IMETHODIMP
619 15 : nsAppStartup::Observe(nsISupports *aSubject,
620 : const char *aTopic, const PRUnichar *aData)
621 : {
622 15 : NS_ASSERTION(mAppShell, "appshell service notified before appshell built");
623 15 : if (!strcmp(aTopic, "quit-application-forced")) {
624 0 : mShuttingDown = true;
625 : }
626 15 : else if (!strcmp(aTopic, "profile-change-teardown")) {
627 15 : if (!mShuttingDown) {
628 15 : EnterLastWindowClosingSurvivalArea();
629 15 : CloseAllWindows();
630 15 : ExitLastWindowClosingSurvivalArea();
631 : }
632 0 : } else if (!strcmp(aTopic, "xul-window-registered")) {
633 0 : EnterLastWindowClosingSurvivalArea();
634 0 : } else if (!strcmp(aTopic, "xul-window-destroyed")) {
635 0 : ExitLastWindowClosingSurvivalArea();
636 0 : } else if (!strcmp(aTopic, "sessionstore-windows-restored")) {
637 0 : StartupTimeline::Record(StartupTimeline::SESSION_RESTORED);
638 : #if defined(XP_WIN)
639 : if (mSessionWindowRestoredProbe) {
640 : mSessionWindowRestoredProbe->Trigger();
641 : }
642 : } else if (!strcmp(aTopic, "places-init-complete")) {
643 : if (mPlacesInitCompleteProbe) {
644 : mPlacesInitCompleteProbe->Trigger();
645 : }
646 : #endif //defined(XP_WIN)
647 : } else {
648 0 : NS_ERROR("Unexpected observer topic.");
649 : }
650 :
651 15 : return NS_OK;
652 : }
653 :
654 : #if defined(LINUX) || defined(ANDROID)
655 : static PRUint64
656 18 : JiffiesSinceBoot(const char *file)
657 : {
658 : char stat[512];
659 18 : FILE *f = fopen(file, "r");
660 18 : if (!f)
661 0 : return 0;
662 18 : int n = fread(&stat, 1, sizeof(stat) - 1, f);
663 18 : fclose(f);
664 18 : if (n <= 0)
665 0 : return 0;
666 18 : stat[n] = 0;
667 :
668 18 : long long unsigned starttime = 0; // instead of PRUint64 to keep GCC quiet
669 :
670 18 : char *s = strrchr(stat, ')');
671 18 : if (!s)
672 0 : return 0;
673 : int ret = sscanf(s + 2,
674 : "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
675 : "%*u %*u %*u %*u %*u %*d %*d %*d %*d %llu",
676 18 : &starttime);
677 18 : if (ret != 1 || !starttime)
678 0 : return 0;
679 18 : return starttime;
680 : }
681 :
682 : static void
683 9 : ThreadedCalculateProcessCreationTimestamp(void *aClosure)
684 : {
685 9 : PRTime now = PR_Now();
686 9 : long hz = sysconf(_SC_CLK_TCK);
687 9 : if (!hz)
688 0 : return;
689 :
690 : char thread_stat[40];
691 9 : sprintf(thread_stat, "/proc/self/task/%d/stat", (pid_t) syscall(__NR_gettid));
692 :
693 9 : PRUint64 thread_jiffies = JiffiesSinceBoot(thread_stat);
694 9 : PRUint64 self_jiffies = JiffiesSinceBoot("/proc/self/stat");
695 :
696 9 : if (!thread_jiffies || !self_jiffies)
697 0 : return;
698 :
699 9 : PRTime interval = (thread_jiffies - self_jiffies) * PR_USEC_PER_SEC / hz;
700 9 : StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, now - interval);
701 : }
702 :
703 : static PRTime
704 9 : CalculateProcessCreationTimestamp()
705 : {
706 : PRThread *thread = PR_CreateThread(PR_USER_THREAD,
707 : ThreadedCalculateProcessCreationTimestamp,
708 : NULL,
709 : PR_PRIORITY_NORMAL,
710 : PR_LOCAL_THREAD,
711 : PR_JOINABLE_THREAD,
712 9 : 0);
713 :
714 9 : PR_JoinThread(thread);
715 9 : return StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
716 : }
717 : #elif defined(XP_WIN)
718 : static PRTime
719 : CalculateProcessCreationTimestamp()
720 : {
721 : FILETIME start, foo, bar, baz;
722 : bool success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz);
723 : if (!success)
724 : return 0;
725 : // copied from NSPR _PR_FileTimeToPRTime
726 : PRUint64 timestamp = 0;
727 : CopyMemory(×tamp, &start, sizeof(PRTime));
728 : #ifdef __GNUC__
729 : timestamp = (timestamp - 116444736000000000LL) / 10LL;
730 : #else
731 : timestamp = (timestamp - 116444736000000000i64) / 10i64;
732 : #endif
733 : return timestamp;
734 : }
735 : #elif defined(XP_MACOSX)
736 : static PRTime
737 : CalculateProcessCreationTimestamp()
738 : {
739 : int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
740 : size_t buffer_size;
741 : if (sysctl(mib, 4, NULL, &buffer_size, NULL, 0))
742 : return 0;
743 :
744 : struct kinfo_proc *proc = (kinfo_proc*) malloc(buffer_size);
745 : if (sysctl(mib, 4, proc, &buffer_size, NULL, 0)) {
746 : free(proc);
747 : return 0;
748 : }
749 : PRTime starttime = static_cast<PRTime>(proc->kp_proc.p_un.__p_starttime.tv_sec) * PR_USEC_PER_SEC;
750 : starttime += proc->kp_proc.p_un.__p_starttime.tv_usec;
751 : free(proc);
752 : return starttime;
753 : }
754 : #elif defined(__OpenBSD__)
755 : static PRTime
756 : CalculateProcessCreationTimestamp()
757 : {
758 : int mib[6] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid(), sizeof(struct kinfo_proc), 1 };
759 : size_t buffer_size;
760 : if (sysctl(mib, 6, NULL, &buffer_size, NULL, 0))
761 : return 0;
762 :
763 : struct kinfo_proc *proc = (struct kinfo_proc*) malloc(buffer_size);
764 : if (sysctl(mib, 6, proc, &buffer_size, NULL, 0)) {
765 : free(proc);
766 : return 0;
767 : }
768 : PRTime starttime = static_cast<PRTime>(proc->p_ustart_sec) * PR_USEC_PER_SEC;
769 : starttime += proc->p_ustart_usec;
770 : free(proc);
771 : return starttime;
772 : }
773 : #else
774 : static PRTime
775 : CalculateProcessCreationTimestamp()
776 : {
777 : return 0;
778 : }
779 : #endif
780 :
781 : NS_IMETHODIMP
782 15 : nsAppStartup::GetStartupInfo(JSContext* aCx, JS::Value* aRetval)
783 : {
784 15 : JSObject *obj = JS_NewObject(aCx, NULL, NULL, NULL);
785 15 : *aRetval = OBJECT_TO_JSVAL(obj);
786 :
787 15 : PRTime ProcessCreationTimestamp = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
788 :
789 15 : if (!ProcessCreationTimestamp) {
790 9 : char *moz_app_restart = PR_GetEnv("MOZ_APP_RESTART");
791 9 : if (moz_app_restart) {
792 0 : ProcessCreationTimestamp = nsCRT::atoll(moz_app_restart) * PR_USEC_PER_MSEC;
793 : } else {
794 9 : ProcessCreationTimestamp = CalculateProcessCreationTimestamp();
795 : }
796 : // Bug 670008: Avoid obviously invalid process creation times
797 9 : if (PR_Now() <= ProcessCreationTimestamp) {
798 0 : ProcessCreationTimestamp = -1;
799 0 : Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, StartupTimeline::PROCESS_CREATION);
800 : }
801 9 : StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, ProcessCreationTimestamp);
802 : }
803 :
804 165 : for (int i = StartupTimeline::PROCESS_CREATION; i < StartupTimeline::MAX_EVENT_ID; ++i) {
805 150 : StartupTimeline::Event ev = static_cast<StartupTimeline::Event>(i);
806 150 : if (StartupTimeline::Get(ev) > 0) {
807 : // always define main to aid with bug 689256
808 30 : if ((ev != StartupTimeline::MAIN) &&
809 15 : (StartupTimeline::Get(ev) < StartupTimeline::Get(StartupTimeline::PROCESS_CREATION))) {
810 0 : Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, i);
811 0 : StartupTimeline::Record(ev, -1);
812 : } else {
813 15 : JSObject *date = JS_NewDateObjectMsec(aCx, StartupTimeline::Get(ev) / PR_USEC_PER_MSEC);
814 15 : JS_DefineProperty(aCx, obj, StartupTimeline::Describe(ev), OBJECT_TO_JSVAL(date), NULL, NULL, JSPROP_ENUMERATE);
815 : }
816 : }
817 : }
818 :
819 15 : return NS_OK;
820 : }
821 :
822 : NS_IMETHODIMP
823 52 : nsAppStartup::GetAutomaticSafeModeNecessary(bool *_retval)
824 : {
825 52 : NS_ENSURE_ARG_POINTER(_retval);
826 :
827 52 : *_retval = mIsSafeModeNecessary;
828 52 : return NS_OK;
829 : }
830 :
831 : NS_IMETHODIMP
832 43 : nsAppStartup::TrackStartupCrashBegin(bool *aIsSafeModeNecessary)
833 : {
834 43 : const PRInt32 MAX_TIME_SINCE_STARTUP = 6 * 60 * 60 * 1000;
835 43 : const PRInt32 MAX_STARTUP_BUFFER = 10;
836 : nsresult rv;
837 :
838 43 : mStartupCrashTrackingEnded = false;
839 :
840 43 : StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_BEGIN);
841 :
842 43 : bool hasLastSuccess = Preferences::HasUserValue(kPrefLastSuccess);
843 43 : if (!hasLastSuccess) {
844 : // Clear so we don't get stuck with SafeModeNecessary returning true if we
845 : // have had too many recent crashes and the last success pref is missing.
846 10 : Preferences::ClearUser(kPrefRecentCrashes);
847 10 : return NS_ERROR_NOT_AVAILABLE;
848 : }
849 :
850 33 : bool inSafeMode = false;
851 66 : nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
852 33 : NS_ENSURE_TRUE(xr, NS_ERROR_FAILURE);
853 :
854 33 : xr->GetInSafeMode(&inSafeMode);
855 :
856 : PRInt64 replacedLockTime;
857 33 : rv = xr->GetReplacedLockTime(&replacedLockTime);
858 :
859 33 : if (NS_FAILED(rv) || !replacedLockTime) {
860 0 : if (!inSafeMode)
861 0 : Preferences::ClearUser(kPrefRecentCrashes);
862 0 : GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
863 0 : return NS_OK;
864 : }
865 :
866 : // check whether safe mode is necessary
867 33 : PRInt32 maxResumedCrashes = -1;
868 33 : rv = Preferences::GetInt(kPrefMaxResumedCrashes, &maxResumedCrashes);
869 33 : NS_ENSURE_SUCCESS(rv, NS_OK);
870 :
871 33 : PRInt32 recentCrashes = 0;
872 33 : Preferences::GetInt(kPrefRecentCrashes, &recentCrashes);
873 33 : mIsSafeModeNecessary = (recentCrashes > maxResumedCrashes && maxResumedCrashes != -1);
874 :
875 : // Bug 731613 - Don't check if the last startup was a crash if XRE_PROFILE_PATH is set. After
876 : // profile manager, the profile lock's mod. time has been changed so can't be used on this startup.
877 : // After a restart, it's safe to assume the last startup was successful.
878 33 : char *xreProfilePath = PR_GetEnv("XRE_PROFILE_PATH");
879 33 : if (xreProfilePath) {
880 0 : GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
881 0 : return NS_ERROR_NOT_AVAILABLE;
882 : }
883 :
884 : // time of last successful startup
885 : PRInt32 lastSuccessfulStartup;
886 33 : rv = Preferences::GetInt(kPrefLastSuccess, &lastSuccessfulStartup);
887 33 : NS_ENSURE_SUCCESS(rv, rv);
888 :
889 33 : PRInt32 lockSeconds = (PRInt32)(replacedLockTime / PR_MSEC_PER_SEC);
890 :
891 : // started close enough to good startup so call it good
892 33 : if (lockSeconds <= lastSuccessfulStartup + MAX_STARTUP_BUFFER
893 : && lockSeconds >= lastSuccessfulStartup - MAX_STARTUP_BUFFER) {
894 18 : GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
895 18 : return NS_OK;
896 : }
897 :
898 : // sanity check that the pref set at last success is not greater than the current time
899 15 : if (PR_Now() / PR_USEC_PER_SEC <= lastSuccessfulStartup)
900 0 : return NS_ERROR_FAILURE;
901 :
902 : // The last startup was a crash so include it in the count regardless of when it happened.
903 15 : Telemetry::Accumulate(Telemetry::STARTUP_CRASH_DETECTED, true);
904 :
905 15 : if (inSafeMode) {
906 5 : GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
907 5 : return NS_OK;
908 : }
909 :
910 10 : PRTime now = (PR_Now() / PR_USEC_PER_MSEC);
911 : // if the last startup attempt which crashed was in the last 6 hours
912 10 : if (replacedLockTime >= now - MAX_TIME_SINCE_STARTUP) {
913 6 : NS_WARNING("Last startup was detected as a crash.");
914 6 : recentCrashes++;
915 6 : rv = Preferences::SetInt(kPrefRecentCrashes, recentCrashes);
916 : } else {
917 : // Otherwise ignore that crash and all previous since it may not be applicable anymore
918 : // and we don't want someone to get stuck in safe mode if their prefs are read-only.
919 4 : rv = Preferences::ClearUser(kPrefRecentCrashes);
920 : }
921 10 : NS_ENSURE_SUCCESS(rv, rv);
922 :
923 : // recalculate since recent crashes count may have changed above
924 10 : mIsSafeModeNecessary = (recentCrashes > maxResumedCrashes && maxResumedCrashes != -1);
925 :
926 20 : nsCOMPtr<nsIPrefService> prefs = Preferences::GetService();
927 10 : rv = prefs->SavePrefFile(nsnull); // flush prefs to disk since we are tracking crashes
928 10 : NS_ENSURE_SUCCESS(rv, rv);
929 :
930 10 : GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
931 10 : return rv;
932 : }
933 :
934 : NS_IMETHODIMP
935 5 : nsAppStartup::TrackStartupCrashEnd()
936 : {
937 5 : bool inSafeMode = false;
938 10 : nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
939 5 : if (xr)
940 5 : xr->GetInSafeMode(&inSafeMode);
941 :
942 : // return if we already ended or we're restarting into safe mode
943 5 : if (mStartupCrashTrackingEnded || (mIsSafeModeNecessary && !inSafeMode))
944 0 : return NS_OK;
945 5 : mStartupCrashTrackingEnded = true;
946 :
947 5 : StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_END);
948 :
949 : // Use the timestamp of XRE_main as an approximation for the lock file timestamp.
950 : // See MAX_STARTUP_BUFFER for the buffer time period.
951 : nsresult rv;
952 5 : PRTime mainTime = StartupTimeline::Get(StartupTimeline::MAIN);
953 5 : if (mainTime <= 0) {
954 5 : NS_WARNING("Could not get StartupTimeline::MAIN time.");
955 : } else {
956 0 : PRInt32 lockFileTime = (PRInt32)(mainTime / PR_USEC_PER_SEC);
957 0 : rv = Preferences::SetInt(kPrefLastSuccess, lockFileTime);
958 0 : if (NS_FAILED(rv)) NS_WARNING("Could not set startup crash detection pref.");
959 : }
960 :
961 5 : if (inSafeMode && mIsSafeModeNecessary) {
962 : // On a successful startup in automatic safe mode, allow the user one more crash
963 : // in regular mode before returning to safe mode.
964 1 : PRInt32 maxResumedCrashes = 0;
965 : PRInt32 prefType;
966 1 : rv = Preferences::GetDefaultRootBranch()->GetPrefType(kPrefMaxResumedCrashes, &prefType);
967 1 : NS_ENSURE_SUCCESS(rv, rv);
968 1 : if (prefType == nsIPrefBranch::PREF_INT) {
969 1 : rv = Preferences::GetInt(kPrefMaxResumedCrashes, &maxResumedCrashes);
970 1 : NS_ENSURE_SUCCESS(rv, rv);
971 : }
972 1 : rv = Preferences::SetInt(kPrefRecentCrashes, maxResumedCrashes);
973 1 : NS_ENSURE_SUCCESS(rv, rv);
974 4 : } else if (!inSafeMode) {
975 : // clear the count of recent crashes after a succesful startup when not in safe mode
976 3 : rv = Preferences::ClearUser(kPrefRecentCrashes);
977 3 : if (NS_FAILED(rv)) NS_WARNING("Could not clear startup crash count.");
978 : }
979 10 : nsCOMPtr<nsIPrefService> prefs = Preferences::GetService();
980 5 : rv = prefs->SavePrefFile(nsnull); // flush prefs to disk since we are tracking crashes
981 :
982 5 : return rv;
983 : }
984 :
985 : NS_IMETHODIMP
986 0 : nsAppStartup::RestartInSafeMode(PRUint32 aQuitMode)
987 : {
988 0 : PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
989 0 : this->Quit(aQuitMode | nsIAppStartup::eRestart);
990 :
991 0 : return NS_OK;
992 : }
|