blob: 8816c2dcc0a368ab0848d5a965e84f31e1b0dce1 [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
Lei Zhangf713c642019-01-11 19:19:3467bool g_schedule_tasks_with_delay = true;
68
michaeln96f887e22015-04-13 23:58:3169bool IsBrowserStartupComplete() {
70 // Be sure to initialize the LazyInstance on the main thread since the flag
71 // may only be set on it's initializing thread.
Lukasz Anforowiczd3e19132017-12-06 19:44:2772 if (!g_startup_complete_flag.IsCreated())
michaeln96f887e22015-04-13 23:58:3173 return false;
74 return g_startup_complete_flag.Get().IsSet();
75}
76
dcheng4af48582016-04-19 00:29:3577void RunTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3178 // We're careful to delete the caller's |task| on the target runner's thread.
peary2be588082017-05-17 01:59:4979 DCHECK(queued_task->task_runner->RunsTasksInCurrentSequence());
tzik070c8ffb2017-03-29 05:28:1280 std::move(queued_task->task).Run();
michaeln96f887e22015-04-13 23:58:3181}
82
dcheng4af48582016-04-19 00:29:3583void ScheduleTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3184 // Spread their execution over a brief time.
Lei Zhangf713c642019-01-11 19:19:3485 constexpr int kMinDelaySec = 0;
86 constexpr int kMaxDelaySec = 10;
Gabriel Charettee926fc12019-12-16 19:00:0287 scoped_refptr<base::SequencedTaskRunner> target_runner =
88 queued_task->task_runner;
Brett Wilsone1a70422017-09-12 05:10:0989 base::Location from_here = queued_task->from_here;
Lei Zhangf713c642019-01-11 19:19:3490 int delay_in_seconds = g_schedule_tasks_with_delay
91 ? base::RandInt(kMinDelaySec, kMaxDelaySec)
92 : 0;
michaeln96f887e22015-04-13 23:58:3193 target_runner->PostDelayedTask(
tzik6d3cd7562018-02-21 12:07:2294 from_here, base::BindOnce(&RunTask, std::move(queued_task)),
Lei Zhangf713c642019-01-11 19:19:3495 base::TimeDelta::FromSeconds(delay_in_seconds));
michaeln96f887e22015-04-13 23:58:3196}
97
dcheng4af48582016-04-19 00:29:3598void QueueTask(std::unique_ptr<AfterStartupTask> queued_task) {
tzikc6976962017-04-04 17:27:3499 DCHECK(queued_task);
tzik498d42b2017-04-13 07:42:48100
101 // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
102 // for details.
103 CHECK(queued_task->task);
104
michaeln96f887e22015-04-13 23:58:31105 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Eric Seckler0618f402018-10-29 12:08:52106 // Posted with USER_VISIBLE priority to avoid this becoming an after startup
107 // task itself.
Gabriel Charettee7cdc5cd2020-05-27 23:35:05108 content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
109 ->PostTask(FROM_HERE,
Sami Kyostila7d640eb2019-07-31 18:50:26110 base::BindOnce(QueueTask, std::move(queued_task)));
michaeln96f887e22015-04-13 23:58:31111 return;
112 }
113
114 // The flag may have been set while the task to invoke this method
115 // on the UI thread was inflight.
116 if (IsBrowserStartupComplete()) {
dchenge73d8520c2015-12-27 01:19:09117 ScheduleTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31118 return;
119 }
120 g_after_startup_tasks.Get().push_back(queued_task.release());
121}
122
123void SetBrowserStartupIsComplete() {
124 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Sean McAllisterdf807712020-08-13 23:19:09125#if defined(OS_MAC) || defined(OS_WIN) || defined(OS_LINUX) || \
126 defined(OS_CHROMEOS)
Francois Doray14563752018-10-23 14:15:57127 // Process::Current().CreationTime() is not available on all platforms.
michaeln96f887e22015-04-13 23:58:31128 const base::Time process_creation_time =
Francois Doray14563752018-10-23 14:15:57129 base::Process::Current().CreationTime();
michaeln96f887e22015-04-13 23:58:31130 if (!process_creation_time.is_null()) {
131 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
132 base::Time::Now() - process_creation_time);
133 }
Sean McAllisterdf807712020-08-13 23:19:09134#endif // defined(OS_MAC) || defined(OS_WIN) || defined(OS_LINUX) ||
135 // defined(OS_CHROMEOS)
michaeln96f887e22015-04-13 23:58:31136 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
137 g_after_startup_tasks.Get().size());
138 g_startup_complete_flag.Get().Set();
139 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
dcheng4af48582016-04-19 00:29:35140 ScheduleTask(base::WrapUnique(queued_task));
michaeln96f887e22015-04-13 23:58:31141 g_after_startup_tasks.Get().clear();
Lei Zhang3b9950b2018-12-21 22:24:51142 g_after_startup_tasks.Get().shrink_to_fit();
Mario Sanchez Prada0a85ddf2018-06-07 20:32:59143
Yuta Hijikata235fc62b2020-12-08 03:48:32144// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
145// of lacros-chrome is complete.
146#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
Mario Sanchez Prada0a85ddf2018-06-07 20:32:59147 // Make sure we complete the startup notification sequence, or launchers will
148 // get confused by not receiving the expected message from the main process.
149 views::LinuxUI* linux_ui = views::LinuxUI::instance();
150 if (linux_ui)
151 linux_ui->NotifyWindowManagerStartupComplete();
152#endif
michaeln96f887e22015-04-13 23:58:31153}
154
155// Observes the first visible page load and sets the startup complete
156// flag accordingly.
gab25894fe2017-05-30 03:40:36157class StartupObserver : public WebContentsObserver {
michaeln96f887e22015-04-13 23:58:31158 public:
Jeremy Roman495db682019-07-12 16:03:24159 StartupObserver() {}
gab25894fe2017-05-30 03:40:36160 ~StartupObserver() override {
161 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
162 DCHECK(IsBrowserStartupComplete());
163 }
michaeln96f887e22015-04-13 23:58:31164
165 void Start();
166
167 private:
168 void OnStartupComplete() {
gab25894fe2017-05-30 03:40:36169 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
michaeln96f887e22015-04-13 23:58:31170 SetBrowserStartupIsComplete();
171 delete this;
172 }
173
174 void OnFailsafeTimeout() { OnStartupComplete(); }
175
176 // WebContentsObserver overrides
177 void DidFinishLoad(content::RenderFrameHost* render_frame_host,
178 const GURL& validated_url) override {
179 if (!render_frame_host->GetParent())
180 OnStartupComplete();
181 }
182
183 void DidFailLoad(content::RenderFrameHost* render_frame_host,
184 const GURL& validated_url,
Dave Tapuska924ef3c2020-01-22 18:20:59185 int error_code) override {
michaeln96f887e22015-04-13 23:58:31186 if (!render_frame_host->GetParent())
187 OnStartupComplete();
188 }
189
Eric Lawrence45eee3a2019-10-14 22:46:36190 // Starting the browser with a file download url will not result in
191 // DidFinishLoad firing, so watch for this case too. crbug.com/1006954
192 void DidFinishNavigation(
193 content::NavigationHandle* navigation_handle) override {
194 if (navigation_handle->IsInMainFrame() && navigation_handle->IsDownload())
195 OnStartupComplete();
196 }
197
michaeln96f887e22015-04-13 23:58:31198 void WebContentsDestroyed() override { OnStartupComplete(); }
199
gab25894fe2017-05-30 03:40:36200 SEQUENCE_CHECKER(sequence_checker_);
201
Jeremy Roman495db682019-07-12 16:03:24202 base::WeakPtrFactory<StartupObserver> weak_factory_{this};
michaeln96f887e22015-04-13 23:58:31203
204 DISALLOW_COPY_AND_ASSIGN(StartupObserver);
205};
206
207void StartupObserver::Start() {
208 // Signal completion quickly when there is no first page to load.
209 const int kShortDelaySecs = 3;
210 base::TimeDelta delay = base::TimeDelta::FromSeconds(kShortDelaySecs);
211
212#if !defined(OS_ANDROID)
213 WebContents* contents = nullptr;
scottmg8abbff832016-01-28 22:57:37214 for (auto* browser : *BrowserList::GetInstance()) {
215 contents = browser->tab_strip_model()->GetActiveWebContents();
michaeln96f887e22015-04-13 23:58:31216 if (contents && contents->GetMainFrame() &&
217 contents->GetMainFrame()->GetVisibilityState() ==
danakj0018a29a2018-12-01 01:03:43218 content::PageVisibilityState::kVisible) {
michaeln96f887e22015-04-13 23:58:31219 break;
220 }
221 }
222
223 if (contents) {
224 // Give the page time to finish loading.
225 const int kLongerDelayMins = 3;
226 Observe(contents);
227 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
228 }
229#else
michaeln68bf4a8e2015-08-11 01:37:31230 // Startup completion is signaled via AfterStartupTaskUtils.java,
231 // this is just a failsafe timeout.
232 const int kLongerDelayMins = 3;
233 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
michaeln96f887e22015-04-13 23:58:31234#endif // !defined(OS_ANDROID)
235
Gabriel Charettee7cdc5cd2020-05-27 23:35:05236 content::GetUIThreadTaskRunner({})->PostDelayedTask(
237 FROM_HERE,
238 base::BindOnce(&StartupObserver::OnFailsafeTimeout,
239 weak_factory_.GetWeakPtr()),
240 delay);
michaeln96f887e22015-04-13 23:58:31241}
242
243} // namespace
244
245void AfterStartupTaskUtils::StartMonitoringStartup() {
246 // The observer is self-deleting.
247 (new StartupObserver)->Start();
248}
249
250void AfterStartupTaskUtils::PostTask(
Brett Wilsone1a70422017-09-12 05:10:09251 const base::Location& from_here,
Gabriel Charettee926fc12019-12-16 19:00:02252 const scoped_refptr<base::SequencedTaskRunner>& destination_runner,
tzik6e427842017-04-05 10:13:21253 base::OnceClosure task) {
michaeln96f887e22015-04-13 23:58:31254 if (IsBrowserStartupComplete()) {
tzik070c8ffb2017-03-29 05:28:12255 destination_runner->PostTask(from_here, std::move(task));
michaeln96f887e22015-04-13 23:58:31256 return;
257 }
258
dcheng4af48582016-04-19 00:29:35259 std::unique_ptr<AfterStartupTask> queued_task(
tzik070c8ffb2017-03-29 05:28:12260 new AfterStartupTask(from_here, destination_runner, std::move(task)));
dchenge73d8520c2015-12-27 01:19:09261 QueueTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31262}
263
wkorman8a21c4f2015-11-18 19:06:11264void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
265 ::SetBrowserStartupIsComplete();
266}
267
michaeln96f887e22015-04-13 23:58:31268void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
269 ::SetBrowserStartupIsComplete();
270}
271
272bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
273 return ::IsBrowserStartupComplete();
274}
275
276void AfterStartupTaskUtils::UnsafeResetForTesting() {
277 DCHECK(g_after_startup_tasks.Get().empty());
278 if (!IsBrowserStartupComplete())
279 return;
280 g_startup_complete_flag.Get().UnsafeResetForTesting();
281 DCHECK(!IsBrowserStartupComplete());
282}
Lei Zhangf713c642019-01-11 19:19:34283
284// static
285void AfterStartupTaskUtils::DisableScheduleTaskDelayForTesting() {
286 g_schedule_tasks_with_delay = false;
287}