blob: aa7ce571d747d93372c45f5579dffb9a6484a339 [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"
15#include "base/process/process_info.h"
16#include "base/rand_util.h"
gab25894fe2017-05-30 03:40:3617#include "base/sequence_checker.h"
fdorayef191122016-07-25 14:43:1718#include "base/synchronization/atomic_flag.h"
michaeln96f887e22015-04-13 23:58:3119#include "base/task_runner.h"
20#include "base/tracked_objects.h"
avie4d7b6f2015-12-26 00:59:1821#include "build/build_config.h"
michaeln96f887e22015-04-13 23:58:3122#include "chrome/browser/ui/browser.h"
scottmg8abbff832016-01-28 22:57:3723#include "chrome/browser/ui/browser_list.h"
michaeln96f887e22015-04-13 23:58:3124#include "chrome/browser/ui/tabs/tab_strip_model.h"
25#include "content/public/browser/browser_thread.h"
26#include "content/public/browser/render_frame_host.h"
27#include "content/public/browser/web_contents.h"
28#include "content/public/browser/web_contents_observer.h"
29
30using content::BrowserThread;
31using content::WebContents;
32using content::WebContentsObserver;
michaeln96f887e22015-04-13 23:58:3133
34namespace {
35
36struct AfterStartupTask {
Brett Wilsone1a70422017-09-12 05:10:0937 AfterStartupTask(const base::Location& from_here,
michaeln96f887e22015-04-13 23:58:3138 const scoped_refptr<base::TaskRunner>& task_runner,
tzik6e427842017-04-05 10:13:2139 base::OnceClosure task)
tzik070c8ffb2017-03-29 05:28:1240 : from_here(from_here), task_runner(task_runner), task(std::move(task)) {}
michaeln96f887e22015-04-13 23:58:3141 ~AfterStartupTask() {}
42
Brett Wilsone1a70422017-09-12 05:10:0943 const base::Location from_here;
michaeln96f887e22015-04-13 23:58:3144 const scoped_refptr<base::TaskRunner> task_runner;
tzik6e427842017-04-05 10:13:2145 base::OnceClosure task;
michaeln96f887e22015-04-13 23:58:3146};
47
48// The flag may be read on any thread, but must only be set on the UI thread.
fdorayef191122016-07-25 14:43:1749base::LazyInstance<base::AtomicFlag>::Leaky g_startup_complete_flag;
michaeln96f887e22015-04-13 23:58:3150
51// The queue may only be accessed on the UI thread.
Brett Wilson275a1372017-09-01 20:27:5452base::LazyInstance<base::circular_deque<AfterStartupTask*>>::Leaky
53 g_after_startup_tasks;
michaeln96f887e22015-04-13 23:58:3154
55bool IsBrowserStartupComplete() {
56 // Be sure to initialize the LazyInstance on the main thread since the flag
57 // may only be set on it's initializing thread.
58 if (g_startup_complete_flag == nullptr)
59 return false;
60 return g_startup_complete_flag.Get().IsSet();
61}
62
dcheng4af48582016-04-19 00:29:3563void RunTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3164 // We're careful to delete the caller's |task| on the target runner's thread.
peary2be588082017-05-17 01:59:4965 DCHECK(queued_task->task_runner->RunsTasksInCurrentSequence());
tzik070c8ffb2017-03-29 05:28:1266 std::move(queued_task->task).Run();
michaeln96f887e22015-04-13 23:58:3167}
68
dcheng4af48582016-04-19 00:29:3569void ScheduleTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3170 // Spread their execution over a brief time.
71 const int kMinDelaySec = 0;
72 const int kMaxDelaySec = 10;
73 scoped_refptr<base::TaskRunner> target_runner = queued_task->task_runner;
Brett Wilsone1a70422017-09-12 05:10:0974 base::Location from_here = queued_task->from_here;
michaeln96f887e22015-04-13 23:58:3175 target_runner->PostDelayedTask(
tzik29ea5c72017-04-20 02:16:5176 from_here, base::BindOnce(&RunTask, base::Passed(std::move(queued_task))),
michaeln96f887e22015-04-13 23:58:3177 base::TimeDelta::FromSeconds(base::RandInt(kMinDelaySec, kMaxDelaySec)));
78}
79
dcheng4af48582016-04-19 00:29:3580void QueueTask(std::unique_ptr<AfterStartupTask> queued_task) {
tzikc6976962017-04-04 17:27:3481 DCHECK(queued_task);
tzik498d42b2017-04-13 07:42:4882
83 // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
84 // for details.
85 CHECK(queued_task->task);
86
michaeln96f887e22015-04-13 23:58:3187 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
88 BrowserThread::PostTask(
89 BrowserThread::UI, FROM_HERE,
tzik29ea5c72017-04-20 02:16:5190 base::BindOnce(QueueTask, base::Passed(std::move(queued_task))));
michaeln96f887e22015-04-13 23:58:3191 return;
92 }
93
94 // The flag may have been set while the task to invoke this method
95 // on the UI thread was inflight.
96 if (IsBrowserStartupComplete()) {
dchenge73d8520c2015-12-27 01:19:0997 ScheduleTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:3198 return;
99 }
100 g_after_startup_tasks.Get().push_back(queued_task.release());
101}
102
103void SetBrowserStartupIsComplete() {
104 DCHECK_CURRENTLY_ON(BrowserThread::UI);
105#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
106 // CurrentProcessInfo::CreationTime() is not available on all platforms.
107 const base::Time process_creation_time =
108 base::CurrentProcessInfo::CreationTime();
109 if (!process_creation_time.is_null()) {
110 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
111 base::Time::Now() - process_creation_time);
112 }
113#endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
114 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
115 g_after_startup_tasks.Get().size());
116 g_startup_complete_flag.Get().Set();
117 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
dcheng4af48582016-04-19 00:29:35118 ScheduleTask(base::WrapUnique(queued_task));
michaeln96f887e22015-04-13 23:58:31119 g_after_startup_tasks.Get().clear();
120
121 // The shrink_to_fit() method is not available for all of our build targets.
Brett Wilson275a1372017-09-01 20:27:54122 base::circular_deque<AfterStartupTask*>(g_after_startup_tasks.Get())
michaeln96f887e22015-04-13 23:58:31123 .swap(g_after_startup_tasks.Get());
124}
125
126// Observes the first visible page load and sets the startup complete
127// flag accordingly.
gab25894fe2017-05-30 03:40:36128class StartupObserver : public WebContentsObserver {
michaeln96f887e22015-04-13 23:58:31129 public:
130 StartupObserver() : weak_factory_(this) {}
gab25894fe2017-05-30 03:40:36131 ~StartupObserver() override {
132 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
133 DCHECK(IsBrowserStartupComplete());
134 }
michaeln96f887e22015-04-13 23:58:31135
136 void Start();
137
138 private:
139 void OnStartupComplete() {
gab25894fe2017-05-30 03:40:36140 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
michaeln96f887e22015-04-13 23:58:31141 SetBrowserStartupIsComplete();
142 delete this;
143 }
144
145 void OnFailsafeTimeout() { OnStartupComplete(); }
146
147 // WebContentsObserver overrides
148 void DidFinishLoad(content::RenderFrameHost* render_frame_host,
149 const GURL& validated_url) override {
150 if (!render_frame_host->GetParent())
151 OnStartupComplete();
152 }
153
154 void DidFailLoad(content::RenderFrameHost* render_frame_host,
155 const GURL& validated_url,
156 int error_code,
Yutaka Hirano16d3c5d2017-07-31 06:17:30157 const base::string16& error_description) override {
michaeln96f887e22015-04-13 23:58:31158 if (!render_frame_host->GetParent())
159 OnStartupComplete();
160 }
161
162 void WebContentsDestroyed() override { OnStartupComplete(); }
163
gab25894fe2017-05-30 03:40:36164 SEQUENCE_CHECKER(sequence_checker_);
165
michaeln96f887e22015-04-13 23:58:31166 base::WeakPtrFactory<StartupObserver> weak_factory_;
167
168 DISALLOW_COPY_AND_ASSIGN(StartupObserver);
169};
170
171void StartupObserver::Start() {
172 // Signal completion quickly when there is no first page to load.
173 const int kShortDelaySecs = 3;
174 base::TimeDelta delay = base::TimeDelta::FromSeconds(kShortDelaySecs);
175
176#if !defined(OS_ANDROID)
177 WebContents* contents = nullptr;
scottmg8abbff832016-01-28 22:57:37178 for (auto* browser : *BrowserList::GetInstance()) {
179 contents = browser->tab_strip_model()->GetActiveWebContents();
michaeln96f887e22015-04-13 23:58:31180 if (contents && contents->GetMainFrame() &&
181 contents->GetMainFrame()->GetVisibilityState() ==
Blink Reformat1c4d759e2017-04-09 16:34:54182 blink::kWebPageVisibilityStateVisible) {
michaeln96f887e22015-04-13 23:58:31183 break;
184 }
185 }
186
187 if (contents) {
188 // Give the page time to finish loading.
189 const int kLongerDelayMins = 3;
190 Observe(contents);
191 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
192 }
193#else
michaeln68bf4a8e2015-08-11 01:37:31194 // Startup completion is signaled via AfterStartupTaskUtils.java,
195 // this is just a failsafe timeout.
196 const int kLongerDelayMins = 3;
197 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
michaeln96f887e22015-04-13 23:58:31198#endif // !defined(OS_ANDROID)
199
tzik29ea5c72017-04-20 02:16:51200 BrowserThread::PostDelayedTask(
201 BrowserThread::UI, FROM_HERE,
202 base::BindOnce(&StartupObserver::OnFailsafeTimeout,
203 weak_factory_.GetWeakPtr()),
204 delay);
michaeln96f887e22015-04-13 23:58:31205}
206
207} // namespace
208
gab27e6d33f2016-08-11 13:15:33209AfterStartupTaskUtils::Runner::Runner(
210 scoped_refptr<base::TaskRunner> destination_runner)
211 : destination_runner_(std::move(destination_runner)) {
212 DCHECK(destination_runner_);
213}
214
215AfterStartupTaskUtils::Runner::~Runner() = default;
216
217bool AfterStartupTaskUtils::Runner::PostDelayedTask(
Brett Wilsone1a70422017-09-12 05:10:09218 const base::Location& from_here,
tzik6e427842017-04-05 10:13:21219 base::OnceClosure task,
gab27e6d33f2016-08-11 13:15:33220 base::TimeDelta delay) {
221 DCHECK(delay.is_zero());
tzik070c8ffb2017-03-29 05:28:12222 AfterStartupTaskUtils::PostTask(from_here, destination_runner_,
223 std::move(task));
gab27e6d33f2016-08-11 13:15:33224 return true;
225}
226
peary23322df62017-05-09 03:55:48227bool AfterStartupTaskUtils::Runner::RunsTasksInCurrentSequence() const {
228 return destination_runner_->RunsTasksInCurrentSequence();
gab27e6d33f2016-08-11 13:15:33229}
230
michaeln96f887e22015-04-13 23:58:31231void AfterStartupTaskUtils::StartMonitoringStartup() {
232 // The observer is self-deleting.
233 (new StartupObserver)->Start();
234}
235
236void AfterStartupTaskUtils::PostTask(
Brett Wilsone1a70422017-09-12 05:10:09237 const base::Location& from_here,
gab27e6d33f2016-08-11 13:15:33238 const scoped_refptr<base::TaskRunner>& destination_runner,
tzik6e427842017-04-05 10:13:21239 base::OnceClosure task) {
michaeln96f887e22015-04-13 23:58:31240 if (IsBrowserStartupComplete()) {
tzik070c8ffb2017-03-29 05:28:12241 destination_runner->PostTask(from_here, std::move(task));
michaeln96f887e22015-04-13 23:58:31242 return;
243 }
244
dcheng4af48582016-04-19 00:29:35245 std::unique_ptr<AfterStartupTask> queued_task(
tzik070c8ffb2017-03-29 05:28:12246 new AfterStartupTask(from_here, destination_runner, std::move(task)));
dchenge73d8520c2015-12-27 01:19:09247 QueueTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31248}
249
wkorman8a21c4f2015-11-18 19:06:11250void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
251 ::SetBrowserStartupIsComplete();
252}
253
michaeln96f887e22015-04-13 23:58:31254void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
255 ::SetBrowserStartupIsComplete();
256}
257
258bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
259 return ::IsBrowserStartupComplete();
260}
261
262void AfterStartupTaskUtils::UnsafeResetForTesting() {
263 DCHECK(g_after_startup_tasks.Get().empty());
264 if (!IsBrowserStartupComplete())
265 return;
266 g_startup_complete_flag.Get().UnsafeResetForTesting();
267 DCHECK(!IsBrowserStartupComplete());
268}