blob: 547ef0a41bb536c44795d22592f94b377f44269f [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
dcheng4af48582016-04-19 00:29:357#include <memory>
dchenge73d8520c2015-12-27 01:19:098#include <utility>
9
Brett Wilson275a1372017-09-01 20:27:5410#include "base/containers/circular_deque.h"
michaeln96f887e22015-04-13 23:58:3111#include "base/lazy_instance.h"
avie4d7b6f2015-12-26 00:59:1812#include "base/macros.h"
dcheng4af48582016-04-19 00:29:3513#include "base/memory/ptr_util.h"
michaeln96f887e22015-04-13 23:58:3114#include "base/metrics/histogram_macros.h"
Francois Doray14563752018-10-23 14:15:5715#include "base/process/process.h"
michaeln96f887e22015-04-13 23:58:3116#include "base/rand_util.h"
gab25894fe2017-05-30 03:40:3617#include "base/sequence_checker.h"
Gabriel Charettee926fc12019-12-16 19:00:0218#include "base/sequenced_task_runner.h"
fdorayef191122016-07-25 14:43:1719#include "base/synchronization/atomic_flag.h"
avie4d7b6f2015-12-26 00:59:1820#include "build/build_config.h"
Yuta Hijikata235fc62b2020-12-08 03:48:3221#include "build/chromeos_buildflags.h"
Eric Seckler8652dcd52018-09-20 10:42:2822#include "content/public/browser/browser_task_traits.h"
michaeln96f887e22015-04-13 23:58:3123#include "content/public/browser/browser_thread.h"
Eric Lawrence45eee3a2019-10-14 22:46:3624#include "content/public/browser/navigation_handle.h"
michaeln96f887e22015-04-13 23:58:3125#include "content/public/browser/render_frame_host.h"
26#include "content/public/browser/web_contents.h"
27#include "content/public/browser/web_contents_observer.h"
Collin Baker98457b52019-11-06 21:34:2928#include "content/public/common/page_visibility_state.h"
michaeln96f887e22015-04-13 23:58:3129
Collin Baker8a217552019-05-29 19:47:5130#if !defined(OS_ANDROID)
31#include "chrome/browser/ui/browser.h"
32#include "chrome/browser/ui/browser_list.h"
33#include "chrome/browser/ui/tabs/tab_strip_model.h"
34#endif
35
Yuta Hijikata235fc62b2020-12-08 03:48:3236// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
37// of lacros-chrome is complete.
38#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
Mario Sanchez Prada0a85ddf2018-06-07 20:32:5939#include "ui/views/linux_ui/linux_ui.h"
40#endif
41
michaeln96f887e22015-04-13 23:58:3142using content::BrowserThread;
43using content::WebContents;
44using content::WebContentsObserver;
michaeln96f887e22015-04-13 23:58:3145
46namespace {
47
48struct AfterStartupTask {
Brett Wilsone1a70422017-09-12 05:10:0949 AfterStartupTask(const base::Location& from_here,
Gabriel Charettee926fc12019-12-16 19:00:0250 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
tzik6e427842017-04-05 10:13:2151 base::OnceClosure task)
tzik070c8ffb2017-03-29 05:28:1252 : from_here(from_here), task_runner(task_runner), task(std::move(task)) {}
michaeln96f887e22015-04-13 23:58:3153 ~AfterStartupTask() {}
54
Brett Wilsone1a70422017-09-12 05:10:0955 const base::Location from_here;
Gabriel Charettee926fc12019-12-16 19:00:0256 const scoped_refptr<base::SequencedTaskRunner> task_runner;
tzik6e427842017-04-05 10:13:2157 base::OnceClosure task;
michaeln96f887e22015-04-13 23:58:3158};
59
60// The flag may be read on any thread, but must only be set on the UI thread.
fdorayef191122016-07-25 14:43:1761base::LazyInstance<base::AtomicFlag>::Leaky g_startup_complete_flag;
michaeln96f887e22015-04-13 23:58:3162
63// The queue may only be accessed on the UI thread.
Brett Wilson275a1372017-09-01 20:27:5464base::LazyInstance<base::circular_deque<AfterStartupTask*>>::Leaky
65 g_after_startup_tasks;
michaeln96f887e22015-04-13 23:58:3166
67bool IsBrowserStartupComplete() {
68 // Be sure to initialize the LazyInstance on the main thread since the flag
69 // may only be set on it's initializing thread.
Lukasz Anforowiczd3e19132017-12-06 19:44:2770 if (!g_startup_complete_flag.IsCreated())
michaeln96f887e22015-04-13 23:58:3171 return false;
72 return g_startup_complete_flag.Get().IsSet();
73}
74
dcheng4af48582016-04-19 00:29:3575void RunTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3176 // We're careful to delete the caller's |task| on the target runner's thread.
peary2be588082017-05-17 01:59:4977 DCHECK(queued_task->task_runner->RunsTasksInCurrentSequence());
tzik070c8ffb2017-03-29 05:28:1278 std::move(queued_task->task).Run();
michaeln96f887e22015-04-13 23:58:3179}
80
dcheng4af48582016-04-19 00:29:3581void ScheduleTask(std::unique_ptr<AfterStartupTask> queued_task) {
Gabriel Charettee926fc12019-12-16 19:00:0282 scoped_refptr<base::SequencedTaskRunner> target_runner =
83 queued_task->task_runner;
Brett Wilsone1a70422017-09-12 05:10:0984 base::Location from_here = queued_task->from_here;
Chris Davis (EDGE)75ff92602021-03-29 17:10:1485 target_runner->PostTask(from_here,
86 base::BindOnce(&RunTask, std::move(queued_task)));
michaeln96f887e22015-04-13 23:58:3187}
88
dcheng4af48582016-04-19 00:29:3589void QueueTask(std::unique_ptr<AfterStartupTask> queued_task) {
tzikc6976962017-04-04 17:27:3490 DCHECK(queued_task);
tzik498d42b2017-04-13 07:42:4891
92 // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
93 // for details.
94 CHECK(queued_task->task);
95
michaeln96f887e22015-04-13 23:58:3196 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Eric Seckler0618f402018-10-29 12:08:5297 // Posted with USER_VISIBLE priority to avoid this becoming an after startup
98 // task itself.
Gabriel Charettee7cdc5cd2020-05-27 23:35:0599 content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
100 ->PostTask(FROM_HERE,
Sami Kyostila7d640eb2019-07-31 18:50:26101 base::BindOnce(QueueTask, std::move(queued_task)));
michaeln96f887e22015-04-13 23:58:31102 return;
103 }
104
105 // The flag may have been set while the task to invoke this method
106 // on the UI thread was inflight.
107 if (IsBrowserStartupComplete()) {
dchenge73d8520c2015-12-27 01:19:09108 ScheduleTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31109 return;
110 }
111 g_after_startup_tasks.Get().push_back(queued_task.release());
112}
113
114void SetBrowserStartupIsComplete() {
115 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Sean McAllisterdf807712020-08-13 23:19:09116#if defined(OS_MAC) || defined(OS_WIN) || defined(OS_LINUX) || \
117 defined(OS_CHROMEOS)
Francois Doray14563752018-10-23 14:15:57118 // Process::Current().CreationTime() is not available on all platforms.
michaeln96f887e22015-04-13 23:58:31119 const base::Time process_creation_time =
Francois Doray14563752018-10-23 14:15:57120 base::Process::Current().CreationTime();
michaeln96f887e22015-04-13 23:58:31121 if (!process_creation_time.is_null()) {
122 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
123 base::Time::Now() - process_creation_time);
124 }
Sean McAllisterdf807712020-08-13 23:19:09125#endif // defined(OS_MAC) || defined(OS_WIN) || defined(OS_LINUX) ||
126 // defined(OS_CHROMEOS)
michaeln96f887e22015-04-13 23:58:31127 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
128 g_after_startup_tasks.Get().size());
129 g_startup_complete_flag.Get().Set();
130 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
dcheng4af48582016-04-19 00:29:35131 ScheduleTask(base::WrapUnique(queued_task));
michaeln96f887e22015-04-13 23:58:31132 g_after_startup_tasks.Get().clear();
Lei Zhang3b9950b2018-12-21 22:24:51133 g_after_startup_tasks.Get().shrink_to_fit();
Mario Sanchez Prada0a85ddf2018-06-07 20:32:59134
Yuta Hijikata235fc62b2020-12-08 03:48:32135// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
136// of lacros-chrome is complete.
137#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
Mario Sanchez Prada0a85ddf2018-06-07 20:32:59138 // Make sure we complete the startup notification sequence, or launchers will
139 // get confused by not receiving the expected message from the main process.
140 views::LinuxUI* linux_ui = views::LinuxUI::instance();
141 if (linux_ui)
142 linux_ui->NotifyWindowManagerStartupComplete();
143#endif
michaeln96f887e22015-04-13 23:58:31144}
145
146// Observes the first visible page load and sets the startup complete
147// flag accordingly.
gab25894fe2017-05-30 03:40:36148class StartupObserver : public WebContentsObserver {
michaeln96f887e22015-04-13 23:58:31149 public:
Jeremy Roman495db682019-07-12 16:03:24150 StartupObserver() {}
gab25894fe2017-05-30 03:40:36151 ~StartupObserver() override {
152 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
153 DCHECK(IsBrowserStartupComplete());
154 }
michaeln96f887e22015-04-13 23:58:31155
156 void Start();
157
158 private:
159 void OnStartupComplete() {
gab25894fe2017-05-30 03:40:36160 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
michaeln96f887e22015-04-13 23:58:31161 SetBrowserStartupIsComplete();
162 delete this;
163 }
164
165 void OnFailsafeTimeout() { OnStartupComplete(); }
166
167 // WebContentsObserver overrides
168 void DidFinishLoad(content::RenderFrameHost* render_frame_host,
169 const GURL& validated_url) override {
170 if (!render_frame_host->GetParent())
171 OnStartupComplete();
172 }
173
174 void DidFailLoad(content::RenderFrameHost* render_frame_host,
175 const GURL& validated_url,
Dave Tapuska924ef3c2020-01-22 18:20:59176 int error_code) override {
michaeln96f887e22015-04-13 23:58:31177 if (!render_frame_host->GetParent())
178 OnStartupComplete();
179 }
180
Eric Lawrence45eee3a2019-10-14 22:46:36181 // Starting the browser with a file download url will not result in
182 // DidFinishLoad firing, so watch for this case too. crbug.com/1006954
183 void DidFinishNavigation(
184 content::NavigationHandle* navigation_handle) override {
185 if (navigation_handle->IsInMainFrame() && navigation_handle->IsDownload())
186 OnStartupComplete();
187 }
188
michaeln96f887e22015-04-13 23:58:31189 void WebContentsDestroyed() override { OnStartupComplete(); }
190
gab25894fe2017-05-30 03:40:36191 SEQUENCE_CHECKER(sequence_checker_);
192
Jeremy Roman495db682019-07-12 16:03:24193 base::WeakPtrFactory<StartupObserver> weak_factory_{this};
michaeln96f887e22015-04-13 23:58:31194
195 DISALLOW_COPY_AND_ASSIGN(StartupObserver);
196};
197
198void StartupObserver::Start() {
199 // Signal completion quickly when there is no first page to load.
200 const int kShortDelaySecs = 3;
201 base::TimeDelta delay = base::TimeDelta::FromSeconds(kShortDelaySecs);
202
203#if !defined(OS_ANDROID)
204 WebContents* contents = nullptr;
scottmg8abbff832016-01-28 22:57:37205 for (auto* browser : *BrowserList::GetInstance()) {
206 contents = browser->tab_strip_model()->GetActiveWebContents();
michaeln96f887e22015-04-13 23:58:31207 if (contents && contents->GetMainFrame() &&
208 contents->GetMainFrame()->GetVisibilityState() ==
danakj0018a29a2018-12-01 01:03:43209 content::PageVisibilityState::kVisible) {
michaeln96f887e22015-04-13 23:58:31210 break;
211 }
212 }
213
214 if (contents) {
215 // Give the page time to finish loading.
216 const int kLongerDelayMins = 3;
217 Observe(contents);
218 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
219 }
220#else
michaeln68bf4a8e2015-08-11 01:37:31221 // Startup completion is signaled via AfterStartupTaskUtils.java,
222 // this is just a failsafe timeout.
223 const int kLongerDelayMins = 3;
224 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
michaeln96f887e22015-04-13 23:58:31225#endif // !defined(OS_ANDROID)
226
Gabriel Charettee7cdc5cd2020-05-27 23:35:05227 content::GetUIThreadTaskRunner({})->PostDelayedTask(
228 FROM_HERE,
229 base::BindOnce(&StartupObserver::OnFailsafeTimeout,
230 weak_factory_.GetWeakPtr()),
231 delay);
michaeln96f887e22015-04-13 23:58:31232}
233
234} // namespace
235
236void AfterStartupTaskUtils::StartMonitoringStartup() {
237 // The observer is self-deleting.
238 (new StartupObserver)->Start();
239}
240
241void AfterStartupTaskUtils::PostTask(
Brett Wilsone1a70422017-09-12 05:10:09242 const base::Location& from_here,
Gabriel Charettee926fc12019-12-16 19:00:02243 const scoped_refptr<base::SequencedTaskRunner>& destination_runner,
tzik6e427842017-04-05 10:13:21244 base::OnceClosure task) {
michaeln96f887e22015-04-13 23:58:31245 if (IsBrowserStartupComplete()) {
tzik070c8ffb2017-03-29 05:28:12246 destination_runner->PostTask(from_here, std::move(task));
michaeln96f887e22015-04-13 23:58:31247 return;
248 }
249
dcheng4af48582016-04-19 00:29:35250 std::unique_ptr<AfterStartupTask> queued_task(
tzik070c8ffb2017-03-29 05:28:12251 new AfterStartupTask(from_here, destination_runner, std::move(task)));
dchenge73d8520c2015-12-27 01:19:09252 QueueTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31253}
254
wkorman8a21c4f2015-11-18 19:06:11255void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
256 ::SetBrowserStartupIsComplete();
257}
258
michaeln96f887e22015-04-13 23:58:31259void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
260 ::SetBrowserStartupIsComplete();
261}
262
263bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
264 return ::IsBrowserStartupComplete();
265}
266
267void AfterStartupTaskUtils::UnsafeResetForTesting() {
268 DCHECK(g_after_startup_tasks.Get().empty());
269 if (!IsBrowserStartupComplete())
270 return;
271 g_startup_complete_flag.Get().UnsafeResetForTesting();
272 DCHECK(!IsBrowserStartupComplete());
273}