blob: 5b1513c6e3387d123a132d2a8f354151771ad561 [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
michaeln96f887e22015-04-13 23:58:3110#include "base/lazy_instance.h"
avie4d7b6f2015-12-26 00:59:1811#include "base/macros.h"
dcheng4af48582016-04-19 00:29:3512#include "base/memory/ptr_util.h"
michaeln96f887e22015-04-13 23:58:3113#include "base/metrics/histogram_macros.h"
14#include "base/process/process_info.h"
15#include "base/rand_util.h"
fdorayef191122016-07-25 14:43:1716#include "base/synchronization/atomic_flag.h"
michaeln96f887e22015-04-13 23:58:3117#include "base/task_runner.h"
18#include "base/tracked_objects.h"
avie4d7b6f2015-12-26 00:59:1819#include "build/build_config.h"
michaeln96f887e22015-04-13 23:58:3120#include "chrome/browser/ui/browser.h"
scottmg8abbff832016-01-28 22:57:3721#include "chrome/browser/ui/browser_list.h"
michaeln96f887e22015-04-13 23:58:3122#include "chrome/browser/ui/tabs/tab_strip_model.h"
23#include "content/public/browser/browser_thread.h"
24#include "content/public/browser/render_frame_host.h"
25#include "content/public/browser/web_contents.h"
26#include "content/public/browser/web_contents_observer.h"
27
28using content::BrowserThread;
29using content::WebContents;
30using content::WebContentsObserver;
michaeln96f887e22015-04-13 23:58:3131
32namespace {
33
34struct AfterStartupTask {
35 AfterStartupTask(const tracked_objects::Location& from_here,
36 const scoped_refptr<base::TaskRunner>& task_runner,
tzik6e427842017-04-05 10:13:2137 base::OnceClosure task)
tzik070c8ffb2017-03-29 05:28:1238 : from_here(from_here), task_runner(task_runner), task(std::move(task)) {}
michaeln96f887e22015-04-13 23:58:3139 ~AfterStartupTask() {}
40
41 const tracked_objects::Location from_here;
42 const scoped_refptr<base::TaskRunner> task_runner;
tzik6e427842017-04-05 10:13:2143 base::OnceClosure task;
michaeln96f887e22015-04-13 23:58:3144};
45
46// The flag may be read on any thread, but must only be set on the UI thread.
fdorayef191122016-07-25 14:43:1747base::LazyInstance<base::AtomicFlag>::Leaky g_startup_complete_flag;
michaeln96f887e22015-04-13 23:58:3148
49// The queue may only be accessed on the UI thread.
50base::LazyInstance<std::deque<AfterStartupTask*>>::Leaky g_after_startup_tasks;
51
52bool IsBrowserStartupComplete() {
53 // Be sure to initialize the LazyInstance on the main thread since the flag
54 // may only be set on it's initializing thread.
55 if (g_startup_complete_flag == nullptr)
56 return false;
57 return g_startup_complete_flag.Get().IsSet();
58}
59
dcheng4af48582016-04-19 00:29:3560void RunTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3161 // We're careful to delete the caller's |task| on the target runner's thread.
62 DCHECK(queued_task->task_runner->RunsTasksOnCurrentThread());
tzik070c8ffb2017-03-29 05:28:1263 std::move(queued_task->task).Run();
michaeln96f887e22015-04-13 23:58:3164}
65
dcheng4af48582016-04-19 00:29:3566void ScheduleTask(std::unique_ptr<AfterStartupTask> queued_task) {
michaeln96f887e22015-04-13 23:58:3167 // Spread their execution over a brief time.
68 const int kMinDelaySec = 0;
69 const int kMaxDelaySec = 10;
70 scoped_refptr<base::TaskRunner> target_runner = queued_task->task_runner;
71 tracked_objects::Location from_here = queued_task->from_here;
72 target_runner->PostDelayedTask(
dchenge73d8520c2015-12-27 01:19:0973 from_here, base::Bind(&RunTask, base::Passed(std::move(queued_task))),
michaeln96f887e22015-04-13 23:58:3174 base::TimeDelta::FromSeconds(base::RandInt(kMinDelaySec, kMaxDelaySec)));
75}
76
dcheng4af48582016-04-19 00:29:3577void QueueTask(std::unique_ptr<AfterStartupTask> queued_task) {
tzikc6976962017-04-04 17:27:3478 DCHECK(queued_task);
79 DCHECK(queued_task->task);
michaeln96f887e22015-04-13 23:58:3180 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
81 BrowserThread::PostTask(
82 BrowserThread::UI, FROM_HERE,
dchenge73d8520c2015-12-27 01:19:0983 base::Bind(QueueTask, base::Passed(std::move(queued_task))));
michaeln96f887e22015-04-13 23:58:3184 return;
85 }
86
87 // The flag may have been set while the task to invoke this method
88 // on the UI thread was inflight.
89 if (IsBrowserStartupComplete()) {
dchenge73d8520c2015-12-27 01:19:0990 ScheduleTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:3191 return;
92 }
93 g_after_startup_tasks.Get().push_back(queued_task.release());
94}
95
96void SetBrowserStartupIsComplete() {
97 DCHECK_CURRENTLY_ON(BrowserThread::UI);
98#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
99 // CurrentProcessInfo::CreationTime() is not available on all platforms.
100 const base::Time process_creation_time =
101 base::CurrentProcessInfo::CreationTime();
102 if (!process_creation_time.is_null()) {
103 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
104 base::Time::Now() - process_creation_time);
105 }
106#endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
107 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
108 g_after_startup_tasks.Get().size());
109 g_startup_complete_flag.Get().Set();
110 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
dcheng4af48582016-04-19 00:29:35111 ScheduleTask(base::WrapUnique(queued_task));
michaeln96f887e22015-04-13 23:58:31112 g_after_startup_tasks.Get().clear();
113
114 // The shrink_to_fit() method is not available for all of our build targets.
115 std::deque<AfterStartupTask*>(g_after_startup_tasks.Get())
116 .swap(g_after_startup_tasks.Get());
117}
118
119// Observes the first visible page load and sets the startup complete
120// flag accordingly.
121class StartupObserver : public WebContentsObserver, public base::NonThreadSafe {
122 public:
123 StartupObserver() : weak_factory_(this) {}
124 ~StartupObserver() override { DCHECK(IsBrowserStartupComplete()); }
125
126 void Start();
127
128 private:
129 void OnStartupComplete() {
130 DCHECK(CalledOnValidThread());
131 SetBrowserStartupIsComplete();
132 delete this;
133 }
134
135 void OnFailsafeTimeout() { OnStartupComplete(); }
136
137 // WebContentsObserver overrides
138 void DidFinishLoad(content::RenderFrameHost* render_frame_host,
139 const GURL& validated_url) override {
140 if (!render_frame_host->GetParent())
141 OnStartupComplete();
142 }
143
144 void DidFailLoad(content::RenderFrameHost* render_frame_host,
145 const GURL& validated_url,
146 int error_code,
gsennton6fbb38692015-06-24 19:23:55147 const base::string16& error_description,
148 bool was_ignored_by_handler) override {
michaeln96f887e22015-04-13 23:58:31149 if (!render_frame_host->GetParent())
150 OnStartupComplete();
151 }
152
153 void WebContentsDestroyed() override { OnStartupComplete(); }
154
155 base::WeakPtrFactory<StartupObserver> weak_factory_;
156
157 DISALLOW_COPY_AND_ASSIGN(StartupObserver);
158};
159
160void StartupObserver::Start() {
161 // Signal completion quickly when there is no first page to load.
162 const int kShortDelaySecs = 3;
163 base::TimeDelta delay = base::TimeDelta::FromSeconds(kShortDelaySecs);
164
165#if !defined(OS_ANDROID)
166 WebContents* contents = nullptr;
scottmg8abbff832016-01-28 22:57:37167 for (auto* browser : *BrowserList::GetInstance()) {
168 contents = browser->tab_strip_model()->GetActiveWebContents();
michaeln96f887e22015-04-13 23:58:31169 if (contents && contents->GetMainFrame() &&
170 contents->GetMainFrame()->GetVisibilityState() ==
171 blink::WebPageVisibilityStateVisible) {
172 break;
173 }
174 }
175
176 if (contents) {
177 // Give the page time to finish loading.
178 const int kLongerDelayMins = 3;
179 Observe(contents);
180 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
181 }
182#else
michaeln68bf4a8e2015-08-11 01:37:31183 // Startup completion is signaled via AfterStartupTaskUtils.java,
184 // this is just a failsafe timeout.
185 const int kLongerDelayMins = 3;
186 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
michaeln96f887e22015-04-13 23:58:31187#endif // !defined(OS_ANDROID)
188
189 BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
190 base::Bind(&StartupObserver::OnFailsafeTimeout,
191 weak_factory_.GetWeakPtr()),
192 delay);
193}
194
195} // namespace
196
gab27e6d33f2016-08-11 13:15:33197AfterStartupTaskUtils::Runner::Runner(
198 scoped_refptr<base::TaskRunner> destination_runner)
199 : destination_runner_(std::move(destination_runner)) {
200 DCHECK(destination_runner_);
201}
202
203AfterStartupTaskUtils::Runner::~Runner() = default;
204
205bool AfterStartupTaskUtils::Runner::PostDelayedTask(
206 const tracked_objects::Location& from_here,
tzik6e427842017-04-05 10:13:21207 base::OnceClosure task,
gab27e6d33f2016-08-11 13:15:33208 base::TimeDelta delay) {
209 DCHECK(delay.is_zero());
tzik070c8ffb2017-03-29 05:28:12210 AfterStartupTaskUtils::PostTask(from_here, destination_runner_,
211 std::move(task));
gab27e6d33f2016-08-11 13:15:33212 return true;
213}
214
215bool AfterStartupTaskUtils::Runner::RunsTasksOnCurrentThread() const {
216 return destination_runner_->RunsTasksOnCurrentThread();
217}
218
michaeln96f887e22015-04-13 23:58:31219void AfterStartupTaskUtils::StartMonitoringStartup() {
220 // The observer is self-deleting.
221 (new StartupObserver)->Start();
222}
223
224void AfterStartupTaskUtils::PostTask(
225 const tracked_objects::Location& from_here,
gab27e6d33f2016-08-11 13:15:33226 const scoped_refptr<base::TaskRunner>& destination_runner,
tzik6e427842017-04-05 10:13:21227 base::OnceClosure task) {
michaeln96f887e22015-04-13 23:58:31228 if (IsBrowserStartupComplete()) {
tzik070c8ffb2017-03-29 05:28:12229 destination_runner->PostTask(from_here, std::move(task));
michaeln96f887e22015-04-13 23:58:31230 return;
231 }
232
dcheng4af48582016-04-19 00:29:35233 std::unique_ptr<AfterStartupTask> queued_task(
tzik070c8ffb2017-03-29 05:28:12234 new AfterStartupTask(from_here, destination_runner, std::move(task)));
dchenge73d8520c2015-12-27 01:19:09235 QueueTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31236}
237
wkorman8a21c4f2015-11-18 19:06:11238void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
239 ::SetBrowserStartupIsComplete();
240}
241
michaeln96f887e22015-04-13 23:58:31242void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
243 ::SetBrowserStartupIsComplete();
244}
245
246bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
247 return ::IsBrowserStartupComplete();
248}
249
250void AfterStartupTaskUtils::UnsafeResetForTesting() {
251 DCHECK(g_after_startup_tasks.Get().empty());
252 if (!IsBrowserStartupComplete())
253 return;
254 g_startup_complete_flag.Get().UnsafeResetForTesting();
255 DCHECK(!IsBrowserStartupComplete());
256}