1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chrome_browser_main_posix.h"
12 #include <sys/resource.h>
16 #include "base/check_op.h"
17 #include "base/functional/bind.h"
18 #include "base/notreached.h"
19 #include "build/build_config.h"
20 #include "build/chromeos_buildflags.h"
21 #include "chrome/browser/lifetime/application_lifetime.h"
22 #include "chrome/browser/lifetime/application_lifetime_desktop.h"
23 #include "chrome/browser/sessions/session_restore.h"
24 #include "chrome/browser/shutdown_signal_handlers_posix.h"
25 #include "content/public/browser/browser_task_traits.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/common/result_codes.h"
29 using content::BrowserThread;
33 // See comment in |PreEarlyInitialization()|, where sigaction is called.
34 void SIGCHLDHandler(int signal) {
37 // ExitHandler takes care of servicing an exit (from a signal) at the
38 // appropriate time. Specifically if we get an exit and have not finished
39 // session restore we delay the exit. To do otherwise means we're exiting part
40 // way through startup which causes all sorts of problems.
43 ExitHandler(const ExitHandler&) = delete;
44 ExitHandler& operator=(const ExitHandler&) = delete;
46 // Invokes exit when appropriate.
47 static void ExitWhenPossibleOnUIThread(int signal);
53 // Called when a session restore has finished.
54 void OnSessionRestoreDone(Profile* profile, int num_tabs_restored);
56 // Does the appropriate call to Exit.
59 // Points to the on-session-restored callback that was registered with
60 // SessionRestore's callback list. When objects of this class are destroyed,
61 // the subscription's destructor will automatically unregister the callback in
62 // SessionRestore, so that the callback list does not contain any obsolete
64 base::CallbackListSubscription on_session_restored_callback_subscription_;
68 void ExitHandler::ExitWhenPossibleOnUIThread(int signal) {
69 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
70 if (SessionRestore::IsRestoringSynchronously()) {
71 // ExitHandler takes care of deleting itself.
74 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
75 // of lacros-chrome is complete.
76 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
80 // SIGINT gets sent when the user types Ctrl+C, but the session is
81 // likely not going away, so try to exit gracefully. SIGHUP is sent on
82 // most systems as a first warning of shutdown. If the process takes
83 // too long to quit, the next signal is usually SIGTERM.
87 // SIGTERM is usually sent instead of SIGKILL to gracefully shutdown
88 // processes. But most systems use it as a shutdown warning, so
89 // conservatively assume that the session is ending. If the process
90 // still doesn't quit within a bounded time, most systems will finally
91 // send SIGKILL, which we're unable to install a signal handler for.
92 // TODO(thomasanderson): Try to distinguish if the session is really
93 // ending or not. Maybe there's a systemd or DBus API to query.
94 chrome::SessionEnding();
105 ExitHandler::ExitHandler() {
106 on_session_restored_callback_subscription_ =
107 SessionRestore::RegisterOnSessionRestoredCallback(base::BindRepeating(
108 &ExitHandler::OnSessionRestoreDone, base::Unretained(this)));
111 ExitHandler::~ExitHandler() {
114 void ExitHandler::OnSessionRestoreDone(Profile* profile, int /* num_tabs */) {
115 if (!SessionRestore::IsRestoringSynchronously()) {
116 // At this point the message loop may not be running (meaning we haven't
117 // gotten through browser startup, but are close). Post the task to at which
118 // point the message loop is running.
119 content::GetUIThreadTaskRunner({})->PostTask(
120 FROM_HERE, base::BindOnce(&ExitHandler::Exit));
126 void ExitHandler::Exit() {
127 #if BUILDFLAG(IS_CHROMEOS_ASH)
128 // On ChromeOS, exiting on signal should be always clean.
129 chrome::ExitIgnoreUnloadHandlers();
131 chrome::AttemptExit();
137 // ChromeBrowserMainPartsPosix -------------------------------------------------
139 ChromeBrowserMainPartsPosix::ChromeBrowserMainPartsPosix(
140 bool is_integration_test,
141 StartupData* startup_data)
142 : ChromeBrowserMainParts(is_integration_test, startup_data) {}
144 int ChromeBrowserMainPartsPosix::PreEarlyInitialization() {
145 const int result = ChromeBrowserMainParts::PreEarlyInitialization();
146 if (result != content::RESULT_CODE_NORMAL_EXIT)
149 // We need to accept SIGCHLD, even though our handler is a no-op because
150 // otherwise we cannot wait on children. (According to POSIX 2001.)
151 struct sigaction action;
152 memset(&action, 0, sizeof(action));
153 action.sa_handler = SIGCHLDHandler;
154 CHECK_EQ(0, sigaction(SIGCHLD, &action, nullptr));
156 return content::RESULT_CODE_NORMAL_EXIT;
159 void ChromeBrowserMainPartsPosix::PostCreateMainMessageLoop() {
160 ChromeBrowserMainParts::PostCreateMainMessageLoop();
162 // Exit in response to SIGINT, SIGTERM, etc.
163 InstallShutdownSignalHandlers(
164 base::BindOnce(&ExitHandler::ExitWhenPossibleOnUIThread),
165 content::GetUIThreadTaskRunner({}));
168 void ChromeBrowserMainPartsPosix::ShowMissingLocaleMessageBox() {
169 #if BUILDFLAG(IS_CHROMEOS_ASH)
170 NOTREACHED(); // Should not ever happen on ChromeOS.
171 #elif BUILDFLAG(IS_MAC)
172 // Not called on Mac because we load the locale files differently.
174 #elif defined(USE_AURA)
175 // TODO(port): We may want a views based message dialog here eventually, but
179 #error "Need MessageBox implementation."