blob: 0912fcdcdeb8d4e4f3cfc0b829a6832e7e684bd8 [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"
Chris Davis5a2d7ce2022-06-24 16:28:2014#include "base/trace_event/trace_event.h"
avie4d7b6f2015-12-26 00:59:1815#include "build/build_config.h"
Yuta Hijikata235fc62b2020-12-08 03:48:3216#include "build/chromeos_buildflags.h"
Chris Davis (EDGE)08c0877d2021-04-28 22:18:4417#include "components/performance_manager/performance_manager_impl.h"
18#include "components/performance_manager/public/graph/graph.h"
19#include "components/performance_manager/public/graph/page_node.h"
Eric Seckler8652dcd52018-09-20 10:42:2820#include "content/public/browser/browser_task_traits.h"
michaeln96f887e22015-04-13 23:58:3121#include "content/public/browser/browser_thread.h"
Collin Baker8a217552019-05-29 19:47:5122
Alexander Alekseev29428722021-11-24 02:08:5223#if BUILDFLAG(IS_CHROMEOS_ASH)
24#include "chrome/browser/ash/login/ui/login_display_host.h"
25#endif
26
Erik Chen25887d7e2021-10-11 20:16:5227#if BUILDFLAG(IS_CHROMEOS_LACROS)
Andrea Orru66066fcd2022-07-21 03:45:5428#include "chromeos/startup/browser_params_proxy.h"
Erik Chen25887d7e2021-10-11 20:16:5229#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
30
michaeln96f887e22015-04-13 23:58:3131using content::BrowserThread;
michaeln96f887e22015-04-13 23:58:3132
33namespace {
34
35struct AfterStartupTask {
Brett Wilsone1a70422017-09-12 05:10:0936 AfterStartupTask(const base::Location& from_here,
Gabriel Charettee926fc12019-12-16 19:00:0237 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
tzik6e427842017-04-05 10:13:2138 base::OnceClosure task)
tzik070c8ffb2017-03-29 05:28:1239 : from_here(from_here), task_runner(task_runner), task(std::move(task)) {}
michaeln96f887e22015-04-13 23:58:3140 ~AfterStartupTask() {}
41
Brett Wilsone1a70422017-09-12 05:10:0942 const base::Location from_here;
Gabriel Charettee926fc12019-12-16 19:00:0243 const scoped_refptr<base::SequencedTaskRunner> task_runner;
tzik6e427842017-04-05 10:13:2144 base::OnceClosure task;
michaeln96f887e22015-04-13 23:58:3145};
46
47// The flag may be read on any thread, but must only be set on the UI thread.
fdorayef191122016-07-25 14:43:1748base::LazyInstance<base::AtomicFlag>::Leaky g_startup_complete_flag;
michaeln96f887e22015-04-13 23:58:3149
50// The queue may only be accessed on the UI thread.
Brett Wilson275a1372017-09-01 20:27:5451base::LazyInstance<base::circular_deque<AfterStartupTask*>>::Leaky
52 g_after_startup_tasks;
michaeln96f887e22015-04-13 23:58:3153
54bool IsBrowserStartupComplete() {
55 // Be sure to initialize the LazyInstance on the main thread since the flag
56 // may only be set on it's initializing thread.
Lukasz Anforowiczd3e19132017-12-06 19:44:2757 if (!g_startup_complete_flag.IsCreated())
michaeln96f887e22015-04-13 23:58:3158 return false;
59 return g_startup_complete_flag.Get().IsSet();
60}
61
dcheng4af48582016-04-19 00:29:3562void RunTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3163 // We're careful to delete the caller's |task| on the target runner's thread.
peary2be588082017-05-17 01:59:4964 DCHECK(queued_task->task_runner->RunsTasksInCurrentSequence());
tzik070c8ffb2017-03-29 05:28:1265 std::move(queued_task->task).Run();
michaeln96f887e22015-04-13 23:58:3166}
67
dcheng4af48582016-04-19 00:29:3568void ScheduleTask(std::unique_ptr<AfterStartupTask> queued_task) {
Gabriel Charettee926fc12019-12-16 19:00:0269 scoped_refptr<base::SequencedTaskRunner> target_runner =
70 queued_task->task_runner;
Brett Wilsone1a70422017-09-12 05:10:0971 base::Location from_here = queued_task->from_here;
Chris Davis (EDGE)75ff92602021-03-29 17:10:1472 target_runner->PostTask(from_here,
73 base::BindOnce(&RunTask, std::move(queued_task)));
michaeln96f887e22015-04-13 23:58:3174}
75
dcheng4af48582016-04-19 00:29:3576void QueueTask(std::unique_ptr<AfterStartupTask> queued_task) {
tzikc6976962017-04-04 17:27:3477 DCHECK(queued_task);
tzik498d42b2017-04-13 07:42:4878
79 // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
80 // for details.
81 CHECK(queued_task->task);
82
michaeln96f887e22015-04-13 23:58:3183 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Eric Seckler0618f402018-10-29 12:08:5284 // Posted with USER_VISIBLE priority to avoid this becoming an after startup
85 // task itself.
Gabriel Charettee7cdc5cd2020-05-27 23:35:0586 content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
87 ->PostTask(FROM_HERE,
Sami Kyostila7d640eb2019-07-31 18:50:2688 base::BindOnce(QueueTask, std::move(queued_task)));
michaeln96f887e22015-04-13 23:58:3189 return;
90 }
91
92 // The flag may have been set while the task to invoke this method
93 // on the UI thread was inflight.
94 if (IsBrowserStartupComplete()) {
dchenge73d8520c2015-12-27 01:19:0995 ScheduleTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:3196 return;
97 }
98 g_after_startup_tasks.Get().push_back(queued_task.release());
99}
100
101void SetBrowserStartupIsComplete() {
102 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44103
104 if (IsBrowserStartupComplete())
105 return;
106
Chris Davis5a2d7ce2022-06-24 16:28:20107 TRACE_EVENT0("startup", "SetBrowserStartupIsComplete");
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44108 g_startup_complete_flag.Get().Set();
Xiaohan Wang55ae2c012022-01-20 21:49:11109#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || \
110 BUILDFLAG(IS_CHROMEOS)
Francois Doray14563752018-10-23 14:15:57111 // Process::Current().CreationTime() is not available on all platforms.
michaeln96f887e22015-04-13 23:58:31112 const base::Time process_creation_time =
Francois Doray14563752018-10-23 14:15:57113 base::Process::Current().CreationTime();
michaeln96f887e22015-04-13 23:58:31114 if (!process_creation_time.is_null()) {
115 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
116 base::Time::Now() - process_creation_time);
117 }
Xiaohan Wang55ae2c012022-01-20 21:49:11118#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) ||
119 // BUILDFLAG(IS_CHROMEOS)
michaeln96f887e22015-04-13 23:58:31120 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
121 g_after_startup_tasks.Get().size());
michaeln96f887e22015-04-13 23:58:31122 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
dcheng4af48582016-04-19 00:29:35123 ScheduleTask(base::WrapUnique(queued_task));
michaeln96f887e22015-04-13 23:58:31124 g_after_startup_tasks.Get().clear();
Lei Zhang3b9950b2018-12-21 22:24:51125 g_after_startup_tasks.Get().shrink_to_fit();
michaeln96f887e22015-04-13 23:58:31126}
127
128// Observes the first visible page load and sets the startup complete
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44129// flag accordingly. Ownership is passed to the Performance Manager
130// after creation.
131class StartupObserver
132 : public performance_manager::GraphOwned,
133 public performance_manager::PageNode::ObserverDefaultImpl {
michaeln96f887e22015-04-13 23:58:31134 public:
Peter Boström53c6c5952021-09-17 09:41:26135 StartupObserver(const StartupObserver&) = delete;
136 StartupObserver& operator=(const StartupObserver&) = delete;
137
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44138 ~StartupObserver() override = default;
michaeln96f887e22015-04-13 23:58:31139
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44140 static void Start();
michaeln96f887e22015-04-13 23:58:31141
142 private:
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44143 StartupObserver() = default;
144
michaeln96f887e22015-04-13 23:58:31145 void OnStartupComplete() {
Joe Masone217851f2022-06-01 22:06:12146 if (!performance_manager::PerformanceManagerImpl::IsAvailable()) {
147 // Already shutting down before startup finished. Do not notify.
148 return;
149 }
150
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44151 // This should only be called once.
152 if (!startup_complete_) {
153 startup_complete_ = true;
154 content::GetUIThreadTaskRunner({})->PostTask(
155 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete));
156 // This will result in delete getting called.
157 TakeFromGraph();
michaeln96f887e22015-04-13 23:58:31158 }
159 }
160
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44161 // GraphOwned overrides
162 void OnPassedToGraph(performance_manager::Graph* graph) override {
163 graph->AddPageNodeObserver(this);
michaeln96f887e22015-04-13 23:58:31164 }
michaeln96f887e22015-04-13 23:58:31165
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44166 void OnTakenFromGraph(performance_manager::Graph* graph) override {
167 graph->RemovePageNodeObserver(this);
168 }
169
170 // PageNodeObserver overrides
171 void OnLoadingStateChanged(
François Doraye90de75a2021-11-15 22:29:07172 const performance_manager::PageNode* page_node,
173 performance_manager::PageNode::LoadingState previous_state) override {
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44174 // Only interested in visible PageNodes
175 if (page_node->IsVisible()) {
176 if (page_node->GetLoadingState() ==
177 performance_manager::PageNode::LoadingState::kLoadedIdle ||
178 page_node->GetLoadingState() ==
179 performance_manager::PageNode::LoadingState::kLoadingTimedOut)
180 OnStartupComplete();
181 }
182 }
183
184 void PassToGraph() {
185 // Pass to the performance manager so we can get notified when
186 // loading completes. Ownership of this object is passed to the
187 // performance manager.
188 DCHECK(performance_manager::PerformanceManagerImpl::IsAvailable());
189 performance_manager::PerformanceManagerImpl::PassToGraph(
190 FROM_HERE, base::WrapUnique(this));
191 }
192
193 void TakeFromGraph() {
194 // Remove this object from the performance manager. This will
195 // cause the object to be deleted.
196 DCHECK(performance_manager::PerformanceManagerImpl::IsAvailable());
197 performance_manager::PerformanceManager::CallOnGraph(
198 FROM_HERE, base::BindOnce(
199 [](performance_manager::GraphOwned* observer,
200 performance_manager::Graph* graph) {
201 graph->TakeFromGraph(observer);
202 },
203 base::Unretained(this)));
204 }
205
206 bool startup_complete_ = false;
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44207};
208
209// static
210void StartupObserver::Start() {
211 // Create the StartupObserver and pass it to the Performance Manager which
212 // will own it going forward.
213 (new StartupObserver)->PassToGraph();
michaeln96f887e22015-04-13 23:58:31214}
215
216} // namespace
217
218void AfterStartupTaskUtils::StartMonitoringStartup() {
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44219 // For Android, startup completion is signaled via
220 // AfterStartupTaskUtils.java. We do not use the StartupObserver.
Xiaohan Wang55ae2c012022-01-20 21:49:11221#if !BUILDFLAG(IS_ANDROID)
Erik Chen25887d7e2021-10-11 20:16:52222#if BUILDFLAG(IS_CHROMEOS_LACROS)
223 // For Lacros, there may not be a Browser created at startup.
Andrea Orru66066fcd2022-07-21 03:45:54224 if (chromeos::BrowserParamsProxy::Get()->InitialBrowserAction() ==
Erik Chen25887d7e2021-10-11 20:16:52225 crosapi::mojom::InitialBrowserAction::kDoNotOpenWindow) {
226 content::GetUIThreadTaskRunner({})->PostTask(
227 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete));
228 return;
229 }
230#endif
231
Alexander Alekseev29428722021-11-24 02:08:52232#if BUILDFLAG(IS_CHROMEOS_ASH)
233 // If we are on a login screen which does not expect WebUI to be loaded,
234 // Browser won't be created at startup.
235 if (ash::LoginDisplayHost::default_host() &&
236 !ash::LoginDisplayHost::default_host()->IsWebUIStarted()) {
237 content::GetUIThreadTaskRunner({})->PostTask(
238 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete));
239 return;
240 }
241#endif
242
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44243 StartupObserver::Start();
Xiaohan Wang55ae2c012022-01-20 21:49:11244#endif // !BUILDFLAG(IS_ANDROID)
Chris Davis (EDGE)08c0877d2021-04-28 22:18:44245
246 // Add failsafe timeout
247 content::GetUIThreadTaskRunner({})->PostDelayedTask(
248 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete),
Peter Kastinge5a38ed2021-10-02 03:06:35249 base::Minutes(3));
michaeln96f887e22015-04-13 23:58:31250}
251
252void AfterStartupTaskUtils::PostTask(
Brett Wilsone1a70422017-09-12 05:10:09253 const base::Location& from_here,
Gabriel Charettee926fc12019-12-16 19:00:02254 const scoped_refptr<base::SequencedTaskRunner>& destination_runner,
tzik6e427842017-04-05 10:13:21255 base::OnceClosure task) {
michaeln96f887e22015-04-13 23:58:31256 if (IsBrowserStartupComplete()) {
tzik070c8ffb2017-03-29 05:28:12257 destination_runner->PostTask(from_here, std::move(task));
michaeln96f887e22015-04-13 23:58:31258 return;
259 }
260
dcheng4af48582016-04-19 00:29:35261 std::unique_ptr<AfterStartupTask> queued_task(
tzik070c8ffb2017-03-29 05:28:12262 new AfterStartupTask(from_here, destination_runner, std::move(task)));
dchenge73d8520c2015-12-27 01:19:09263 QueueTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31264}
265
wkorman8a21c4f2015-11-18 19:06:11266void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
267 ::SetBrowserStartupIsComplete();
268}
269
michaeln96f887e22015-04-13 23:58:31270void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
271 ::SetBrowserStartupIsComplete();
272}
273
274bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
275 return ::IsBrowserStartupComplete();
276}
277
278void AfterStartupTaskUtils::UnsafeResetForTesting() {
279 DCHECK(g_after_startup_tasks.Get().empty());
280 if (!IsBrowserStartupComplete())
281 return;
282 g_startup_complete_flag.Get().UnsafeResetForTesting();
283 DCHECK(!IsBrowserStartupComplete());
284}