1 // Copyright 2015 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/after_startup_task_utils.h"
7 #include "base/containers/circular_deque.h"
8 #include "base/lazy_instance.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/process/process.h"
12 #include "base/synchronization/atomic_flag.h"
13 #include "base/task/sequenced_task_runner.h"
14 #include "base/trace_event/trace_event.h"
15 #include "build/build_config.h"
16 #include "build/chromeos_buildflags.h"
17 #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"
20 #include "content/public/browser/browser_task_traits.h"
21 #include "content/public/browser/browser_thread.h"
23 #if BUILDFLAG(IS_CHROMEOS_ASH)
24 #include "chrome/browser/ash/login/ui/login_display_host.h"
27 #if BUILDFLAG(IS_CHROMEOS_LACROS)
28 #include "chromeos/startup/browser_params_proxy.h"
29 #endif // BUILDFLAG(IS_CHROMEOS_LACROS)
31 using content::BrowserThread;
35 struct AfterStartupTask {
36 AfterStartupTask(const base::Location& from_here,
37 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
38 base::OnceClosure task)
39 : from_here(from_here), task_runner(task_runner), task(std::move(task)) {}
40 ~AfterStartupTask() {}
42 const base::Location from_here;
43 const scoped_refptr<base::SequencedTaskRunner> task_runner;
44 base::OnceClosure task;
47 // The flag may be read on any thread, but must only be set on the UI thread.
48 base::LazyInstance<base::AtomicFlag>::Leaky g_startup_complete_flag;
50 // The queue may only be accessed on the UI thread.
51 base::LazyInstance<base::circular_deque<AfterStartupTask*>>::Leaky
52 g_after_startup_tasks;
54 bool 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.
57 if (!g_startup_complete_flag.IsCreated())
59 return g_startup_complete_flag.Get().IsSet();
62 void RunTask(std::unique_ptr<AfterStartupTask> queued_task) {
63 // We're careful to delete the caller's |task| on the target runner's thread.
64 DCHECK(queued_task->task_runner->RunsTasksInCurrentSequence());
65 std::move(queued_task->task).Run();
68 void ScheduleTask(std::unique_ptr<AfterStartupTask> queued_task) {
69 scoped_refptr<base::SequencedTaskRunner> target_runner =
70 queued_task->task_runner;
71 base::Location from_here = queued_task->from_here;
72 target_runner->PostTask(from_here,
73 base::BindOnce(&RunTask, std::move(queued_task)));
76 void QueueTask(std::unique_ptr<AfterStartupTask> queued_task) {
79 // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
81 CHECK(queued_task->task);
83 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
84 // Posted with USER_VISIBLE priority to avoid this becoming an after startup
86 content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
88 base::BindOnce(QueueTask, std::move(queued_task)));
92 // The flag may have been set while the task to invoke this method
93 // on the UI thread was inflight.
94 if (IsBrowserStartupComplete()) {
95 ScheduleTask(std::move(queued_task));
98 g_after_startup_tasks.Get().push_back(queued_task.release());
101 void SetBrowserStartupIsComplete() {
102 DCHECK_CURRENTLY_ON(BrowserThread::UI);
104 if (IsBrowserStartupComplete())
107 TRACE_EVENT0("startup", "SetBrowserStartupIsComplete");
108 g_startup_complete_flag.Get().Set();
109 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || \
110 BUILDFLAG(IS_CHROMEOS)
111 // Process::Current().CreationTime() is not available on all platforms.
112 const base::Time process_creation_time =
113 base::Process::Current().CreationTime();
114 if (!process_creation_time.is_null()) {
115 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
116 base::Time::Now() - process_creation_time);
118 #endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) ||
119 // BUILDFLAG(IS_CHROMEOS)
120 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
121 g_after_startup_tasks.Get().size());
122 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
123 ScheduleTask(base::WrapUnique(queued_task));
124 g_after_startup_tasks.Get().clear();
125 g_after_startup_tasks.Get().shrink_to_fit();
128 // Observes the first visible page load and sets the startup complete
129 // flag accordingly. Ownership is passed to the Performance Manager
131 class StartupObserver
132 : public performance_manager::GraphOwned,
133 public performance_manager::PageNode::ObserverDefaultImpl {
135 StartupObserver(const StartupObserver&) = delete;
136 StartupObserver& operator=(const StartupObserver&) = delete;
138 ~StartupObserver() override = default;
143 StartupObserver() = default;
145 void OnStartupComplete() {
146 if (!performance_manager::PerformanceManagerImpl::IsAvailable()) {
147 // Already shutting down before startup finished. Do not notify.
151 // 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.
161 // GraphOwned overrides
162 void OnPassedToGraph(performance_manager::Graph* graph) override {
163 graph->AddPageNodeObserver(this);
166 void OnTakenFromGraph(performance_manager::Graph* graph) override {
167 graph->RemovePageNodeObserver(this);
170 // PageNodeObserver overrides
171 void OnLoadingStateChanged(
172 const performance_manager::PageNode* page_node,
173 performance_manager::PageNode::LoadingState previous_state) override {
174 // 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)
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));
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);
203 base::Unretained(this)));
206 bool startup_complete_ = false;
210 void StartupObserver::Start() {
211 // Create the StartupObserver and pass it to the Performance Manager which
212 // will own it going forward.
213 (new StartupObserver)->PassToGraph();
218 void AfterStartupTaskUtils::StartMonitoringStartup() {
219 // For Android, startup completion is signaled via
220 // AfterStartupTaskUtils.java. We do not use the StartupObserver.
221 #if !BUILDFLAG(IS_ANDROID)
222 #if BUILDFLAG(IS_CHROMEOS_LACROS)
223 // For Lacros, there may not be a Browser created at startup.
224 if (chromeos::BrowserParamsProxy::Get()->InitialBrowserAction() ==
225 crosapi::mojom::InitialBrowserAction::kDoNotOpenWindow) {
226 content::GetUIThreadTaskRunner({})->PostTask(
227 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete));
232 #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));
243 StartupObserver::Start();
244 #endif // !BUILDFLAG(IS_ANDROID)
246 // Add failsafe timeout
247 content::GetUIThreadTaskRunner({})->PostDelayedTask(
248 FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete),
252 void AfterStartupTaskUtils::PostTask(
253 const base::Location& from_here,
254 const scoped_refptr<base::SequencedTaskRunner>& destination_runner,
255 base::OnceClosure task) {
256 if (IsBrowserStartupComplete()) {
257 destination_runner->PostTask(from_here, std::move(task));
261 std::unique_ptr<AfterStartupTask> queued_task(
262 new AfterStartupTask(from_here, destination_runner, std::move(task)));
263 QueueTask(std::move(queued_task));
266 void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
267 ::SetBrowserStartupIsComplete();
270 void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
271 ::SetBrowserStartupIsComplete();
274 bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
275 return ::IsBrowserStartupComplete();
278 void AfterStartupTaskUtils::UnsafeResetForTesting() {
279 DCHECK(g_after_startup_tasks.Get().empty());
280 if (!IsBrowserStartupComplete())
282 g_startup_complete_flag.Get().UnsafeResetForTesting();
283 DCHECK(!IsBrowserStartupComplete());