blob: 9243e19daddc643b2bcb56bc9bd8cbf64f68a743 [file] [log] [blame]
michaeln96f887e22015-04-13 23:58:311// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/after_startup_task_utils.h"
6
Brett Wilson275a1372017-09-01 20:27:547#include "base/containers/circular_deque.h"
michaeln96f887e22015-04-13 23:58:318#include "base/lazy_instance.h"
dcheng4af48582016-04-19 00:29:359#include "base/memory/ptr_util.h"
michaeln96f887e22015-04-13 23:58:3110#include "base/metrics/histogram_macros.h"
Francois Doray14563752018-10-23 14:15:5711#include "base/process/process.h"
fdorayef191122016-07-25 14:43:1712#include "base/synchronization/atomic_flag.h"
Patrick Monette643cdf62021-10-15 19:13:4213#include "base/task/sequenced_task_runner.h"
avie4d7b6f2015-12-26 00:59:1814#include "build/build_config.h"
Yuta Hijikata235fc62b2020-12-08 03:48:3215#include "build/chromeos_buildflags.h"
Chris Davis (EDGE)08c0877d2021-04-28 22:18:4416#include "components/performance_manager/performance_manager_impl.h"
17#include "components/performance_manager/public/graph/graph.h"
18#include "components/performance_manager/public/graph/page_node.h"
Eric Seckler8652dcd52018-09-20 10:42:2819#include "content/public/browser/browser_task_traits.h"
michaeln96f887e22015-04-13 23:58:3120#include "content/public/browser/browser_thread.h"
Collin Baker8a217552019-05-29 19:47:5121
Alexander Alekseev29428722021-11-24 02:08:5222#if BUILDFLAG(IS_CHROMEOS_ASH)
23#include "chrome/browser/ash/login/ui/login_display_host.h"
24#endif
25
Yuta Hijikata235fc62b2020-12-08 03:48:3226// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
27// of lacros-chrome is complete.
28#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
Mario Sanchez Prada0a85ddf2018-06-07 20:32:5929#include "ui/views/linux_ui/linux_ui.h"
30#endif
31
Erik Chen25887d7e2021-10-11 20:16:5232#if BUILDFLAG(IS_CHROMEOS_LACROS)
33#include "chromeos/lacros/lacros_service.h"
34#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
35
michaeln96f887e22015-04-13 23:58:3136using content::BrowserThread;
michaeln96f887e22015-04-13 23:58:3137
38namespace {
39
40struct AfterStartupTask {
Brett Wilsone1a70422017-09-12 05:10:0941 AfterStartupTask(const base::Location& from_here,
Gabriel Charettee926fc12019-12-16 19:00:0242 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
tzik6e427842017-04-05 10:13:2143 base::OnceClosure task)
tzik070c8ffb2017-03-29 05:28:1244 : from_here(from_here), task_runner(task_runner), task(std::move(task)) {}
michaeln96f887e22015-04-13 23:58:3145 ~AfterStartupTask() {}
46
Brett Wilsone1a70422017-09-12 05:10:0947 const base::Location from_here;
Gabriel Charettee926fc12019-12-16 19:00:0248 const scoped_refptr<base::SequencedTaskRunner> task_runner;
tzik6e427842017-04-05 10:13:2149 base::OnceClosure task;
michaeln96f887e22015-04-13 23:58:3150};
51
52// The flag may be read on any thread, but must only be set on the UI thread.
fdorayef191122016-07-25 14:43:1753base::LazyInstance<base::AtomicFlag>::Leaky g_startup_complete_flag;
michaeln96f887e22015-04-13 23:58:3154
55// The queue may only be accessed on the UI thread.
Brett Wilson275a1372017-09-01 20:27:5456base::LazyInstance<base::circular_deque<AfterStartupTask*>>::Leaky
57 g_after_startup_tasks;
michaeln96f887e22015-04-13 23:58:3158
59bool IsBrowserStartupComplete() {
60 // Be sure to initialize the LazyInstance on the main thread since the flag
61 // may only be set on it's initializing thread.
Lukasz Anforowiczd3e19132017-12-06 19:44:2762 if (!g_startup_complete_flag.IsCreated())
michaeln96f887e22015-04-13 23:58:3163 return false;
64 return g_startup_complete_flag.Get().IsSet();
65}
66
dcheng4af48582016-04-19 00:29:3567void RunTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3168 // We're careful to delete the caller's |task| on the target runner's thread.
peary2be588082017-05-17 01:59:4969 DCHECK(queued_task->task_runner->RunsTasksInCurrentSequence());
tzik070c8ffb2017-03-29 05:28:1270 std::move(queued_task->task).Run();
michaeln96f887e22015-04-13 23:58:3171}
72
dcheng4af48582016-04-19 00:29:3573void ScheduleTask(std::unique_ptr<AfterStartupTask> queued_task) {
Gabriel Charettee926fc12019-12-16 19:00:0274 scoped_refptr<base::SequencedTaskRunner> target_runner =
75 queued_task->task_runner;
Brett Wilsone1a70422017-09-12 05:10:0976 base::Location from_here = queued_task->from_here;
Chris Davis (EDGE)75ff92602021-03-29 17:10:1477 target_runner->PostTask(from_here,
78 base::BindOnce(&RunTask, std::move(queued_task)));
michaeln96f887e22015-04-13 23:58:3179}
80
dcheng4af48582016-04-19 00:29:3581void QueueTask(std::unique_ptr<AfterStartupTask> queued_task) {
tzikc6976962017-04-04 17:27:3482 DCHECK(queued_task);
tzik498d42b2017-04-13 07:42:4883
84 // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
85 // for details.
86 CHECK(queued_task->task);
87
michaeln96f887e22015-04-13 23:58:3188 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Eric Seckler0618f402018-10-29 12:08:5289 // Posted with USER_VISIBLE priority to avoid this becoming an after startup
90 // task itself.
Gabriel Charettee7cdc5cd2020-05-27 23:35:0591 content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
92 ->PostTask(FROM_HERE,
Sami Kyostila7d640eb2019-07-31 18:50:2693 base::BindOnce(QueueTask, std::move(queued_task)));
michaeln96f887e22015-04-13 23:58:3194 return;
95 }
96
97 // The flag may have been set while the task to invoke this method
98 // on the UI thread was inflight.
99 if (IsBrowserStartupComplete()) {
dchenge73d8520c2015-12-27 01:19:09100 ScheduleTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31101 return;
102 }
103 g_after_startup_tasks.Get().push_back(queued_task.release());
104}
105
106void SetBrowserStartupIsComplete() {
107 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44108
109 if (IsBrowserStartupComplete())
110 return;
111
112 g_startup_complete_flag.Get().Set();
Sean McAllisterdf807712020-08-13 23:19:09113#if defined(OS_MAC) || defined(OS_WIN) || defined(OS_LINUX) || \
114 defined(OS_CHROMEOS)
Francois Doray14563752018-10-23 14:15:57115 // Process::Current().CreationTime() is not available on all platforms.
michaeln96f887e22015-04-13 23:58:31116 const base::Time process_creation_time =
Francois Doray14563752018-10-23 14:15:57117 base::Process::Current().CreationTime();
michaeln96f887e22015-04-13 23:58:31118 if (!process_creation_time.is_null()) {
119 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
120 base::Time::Now() - process_creation_time);
121 }
Sean McAllisterdf807712020-08-13 23:19:09122#endif // defined(OS_MAC) || defined(OS_WIN) || defined(OS_LINUX) ||
123 // defined(OS_CHROMEOS)
michaeln96f887e22015-04-13 23:58:31124 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
125 g_after_startup_tasks.Get().size());
michaeln96f887e22015-04-13 23:58:31126 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
dcheng4af48582016-04-19 00:29:35127 ScheduleTask(base::WrapUnique(queued_task));
michaeln96f887e22015-04-13 23:58:31128 g_after_startup_tasks.Get().clear();
Lei Zhang3b9950b2018-12-21 22:24:51129 g_after_startup_tasks.Get().shrink_to_fit();
Mario Sanchez Prada0a85ddf2018-06-07 20:32:59130
Yuta Hijikata235fc62b2020-12-08 03:48:32131// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
132// of lacros-chrome is complete.
133#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
Mario Sanchez Prada0a85ddf2018-06-07 20:32:59134 // Make sure we complete the startup notification sequence, or launchers will
135 // get confused by not receiving the expected message from the main process.
136 views::LinuxUI* linux_ui = views::LinuxUI::instance();
137 if (linux_ui)
138 linux_ui->NotifyWindowManagerStartupComplete();
139#endif
michaeln96f887e22015-04-13 23:58:31140}
141
142// Observes the first visible page load and sets the startup complete
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44143// flag accordingly. Ownership is passed to the Performance Manager
144// after creation.
145class StartupObserver
146 : public performance_manager::GraphOwned,
147 public performance_manager::PageNode::ObserverDefaultImpl {
michaeln96f887e22015-04-13 23:58:31148 public:
Peter Boström53c6c5952021-09-17 09:41:26149 StartupObserver(const StartupObserver&) = delete;
150 StartupObserver& operator=(const StartupObserver&) = delete;
151
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44152 ~StartupObserver() override = default;
michaeln96f887e22015-04-13 23:58:31153
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44154 static void Start();
michaeln96f887e22015-04-13 23:58:31155
156 private:
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44157 StartupObserver() = default;
158
michaeln96f887e22015-04-13 23:58:31159 void OnStartupComplete() {
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44160 // This should only be called once.
161 if (!startup_complete_) {
162 startup_complete_ = true;
163 content::GetUIThreadTaskRunner({})->PostTask(
164 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete));
165 // This will result in delete getting called.
166 TakeFromGraph();
michaeln96f887e22015-04-13 23:58:31167 }
168 }
169
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44170 // GraphOwned overrides
171 void OnPassedToGraph(performance_manager::Graph* graph) override {
172 graph->AddPageNodeObserver(this);
michaeln96f887e22015-04-13 23:58:31173 }
michaeln96f887e22015-04-13 23:58:31174
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44175 void OnTakenFromGraph(performance_manager::Graph* graph) override {
176 graph->RemovePageNodeObserver(this);
177 }
178
179 // PageNodeObserver overrides
180 void OnLoadingStateChanged(
François Doraye90de75a2021-11-15 22:29:07181 const performance_manager::PageNode* page_node,
182 performance_manager::PageNode::LoadingState previous_state) override {
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44183 // Only interested in visible PageNodes
184 if (page_node->IsVisible()) {
185 if (page_node->GetLoadingState() ==
186 performance_manager::PageNode::LoadingState::kLoadedIdle ||
187 page_node->GetLoadingState() ==
188 performance_manager::PageNode::LoadingState::kLoadingTimedOut)
189 OnStartupComplete();
190 }
191 }
192
193 void PassToGraph() {
194 // Pass to the performance manager so we can get notified when
195 // loading completes. Ownership of this object is passed to the
196 // performance manager.
197 DCHECK(performance_manager::PerformanceManagerImpl::IsAvailable());
198 performance_manager::PerformanceManagerImpl::PassToGraph(
199 FROM_HERE, base::WrapUnique(this));
200 }
201
202 void TakeFromGraph() {
203 // Remove this object from the performance manager. This will
204 // cause the object to be deleted.
205 DCHECK(performance_manager::PerformanceManagerImpl::IsAvailable());
206 performance_manager::PerformanceManager::CallOnGraph(
207 FROM_HERE, base::BindOnce(
208 [](performance_manager::GraphOwned* observer,
209 performance_manager::Graph* graph) {
210 graph->TakeFromGraph(observer);
211 },
212 base::Unretained(this)));
213 }
214
215 bool startup_complete_ = false;
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44216};
217
218// static
219void StartupObserver::Start() {
220 // Create the StartupObserver and pass it to the Performance Manager which
221 // will own it going forward.
222 (new StartupObserver)->PassToGraph();
michaeln96f887e22015-04-13 23:58:31223}
224
225} // namespace
226
227void AfterStartupTaskUtils::StartMonitoringStartup() {
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44228 // For Android, startup completion is signaled via
229 // AfterStartupTaskUtils.java. We do not use the StartupObserver.
230#if !defined(OS_ANDROID)
Erik Chen25887d7e2021-10-11 20:16:52231#if BUILDFLAG(IS_CHROMEOS_LACROS)
232 // For Lacros, there may not be a Browser created at startup.
233 if (chromeos::LacrosService::Get()->init_params()->initial_browser_action ==
234 crosapi::mojom::InitialBrowserAction::kDoNotOpenWindow) {
235 content::GetUIThreadTaskRunner({})->PostTask(
236 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete));
237 return;
238 }
239#endif
240
Alexander Alekseev29428722021-11-24 02:08:52241#if BUILDFLAG(IS_CHROMEOS_ASH)
242 // If we are on a login screen which does not expect WebUI to be loaded,
243 // Browser won't be created at startup.
244 if (ash::LoginDisplayHost::default_host() &&
245 !ash::LoginDisplayHost::default_host()->IsWebUIStarted()) {
246 content::GetUIThreadTaskRunner({})->PostTask(
247 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete));
248 return;
249 }
250#endif
251
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44252 StartupObserver::Start();
253#endif // !defined(OS_ANDROID)
254
255 // Add failsafe timeout
256 content::GetUIThreadTaskRunner({})->PostDelayedTask(
257 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete),
Peter Kastinge5a38ed2021-10-02 03:06:35258 base::Minutes(3));
michaeln96f887e22015-04-13 23:58:31259}
260
261void AfterStartupTaskUtils::PostTask(
Brett Wilsone1a70422017-09-12 05:10:09262 const base::Location& from_here,
Gabriel Charettee926fc12019-12-16 19:00:02263 const scoped_refptr<base::SequencedTaskRunner>& destination_runner,
tzik6e427842017-04-05 10:13:21264 base::OnceClosure task) {
michaeln96f887e22015-04-13 23:58:31265 if (IsBrowserStartupComplete()) {
tzik070c8ffb2017-03-29 05:28:12266 destination_runner->PostTask(from_here, std::move(task));
michaeln96f887e22015-04-13 23:58:31267 return;
268 }
269
dcheng4af48582016-04-19 00:29:35270 std::unique_ptr<AfterStartupTask> queued_task(
tzik070c8ffb2017-03-29 05:28:12271 new AfterStartupTask(from_here, destination_runner, std::move(task)));
dchenge73d8520c2015-12-27 01:19:09272 QueueTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31273}
274
wkorman8a21c4f2015-11-18 19:06:11275void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
276 ::SetBrowserStartupIsComplete();
277}
278
michaeln96f887e22015-04-13 23:58:31279void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
280 ::SetBrowserStartupIsComplete();
281}
282
283bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
284 return ::IsBrowserStartupComplete();
285}
286
287void AfterStartupTaskUtils::UnsafeResetForTesting() {
288 DCHECK(g_after_startup_tasks.Get().empty());
289 if (!IsBrowserStartupComplete())
290 return;
291 g_startup_complete_flag.Get().UnsafeResetForTesting();
292 DCHECK(!IsBrowserStartupComplete());
293}