blob: d1e7fd5f790ca84627a768c92c1b65301442cd94 [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
dchenge73d8520c2015-12-27 01:19:097#include <utility>
8
michaeln96f887e22015-04-13 23:58:319#include "base/lazy_instance.h"
avie4d7b6f2015-12-26 00:59:1810#include "base/macros.h"
michaeln96f887e22015-04-13 23:58:3111#include "base/memory/scoped_ptr.h"
12#include "base/metrics/histogram_macros.h"
13#include "base/process/process_info.h"
14#include "base/rand_util.h"
15#include "base/synchronization/cancellation_flag.h"
16#include "base/task_runner.h"
17#include "base/tracked_objects.h"
avie4d7b6f2015-12-26 00:59:1818#include "build/build_config.h"
michaeln96f887e22015-04-13 23:58:3119#include "chrome/browser/ui/browser.h"
20#include "chrome/browser/ui/browser_iterator.h"
21#include "chrome/browser/ui/tabs/tab_strip_model.h"
22#include "content/public/browser/browser_thread.h"
23#include "content/public/browser/render_frame_host.h"
24#include "content/public/browser/web_contents.h"
25#include "content/public/browser/web_contents_observer.h"
26
27using content::BrowserThread;
28using content::WebContents;
29using content::WebContentsObserver;
30using StartupCompleteFlag = base::CancellationFlag;
31
32namespace {
33
34struct AfterStartupTask {
35 AfterStartupTask(const tracked_objects::Location& from_here,
36 const scoped_refptr<base::TaskRunner>& task_runner,
37 const base::Closure& task)
38 : from_here(from_here), task_runner(task_runner), task(task) {}
39 ~AfterStartupTask() {}
40
41 const tracked_objects::Location from_here;
42 const scoped_refptr<base::TaskRunner> task_runner;
43 const base::Closure task;
44};
45
46// The flag may be read on any thread, but must only be set on the UI thread.
47base::LazyInstance<StartupCompleteFlag>::Leaky g_startup_complete_flag;
48
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
60void RunTask(scoped_ptr<AfterStartupTask> queued_task) {
61 // We're careful to delete the caller's |task| on the target runner's thread.
62 DCHECK(queued_task->task_runner->RunsTasksOnCurrentThread());
63 queued_task->task.Run();
64}
65
66void ScheduleTask(scoped_ptr<AfterStartupTask> queued_task) {
67 // 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
77void QueueTask(scoped_ptr<AfterStartupTask> queued_task) {
78 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
79 BrowserThread::PostTask(
80 BrowserThread::UI, FROM_HERE,
dchenge73d8520c2015-12-27 01:19:0981 base::Bind(QueueTask, base::Passed(std::move(queued_task))));
michaeln96f887e22015-04-13 23:58:3182 return;
83 }
84
85 // The flag may have been set while the task to invoke this method
86 // on the UI thread was inflight.
87 if (IsBrowserStartupComplete()) {
dchenge73d8520c2015-12-27 01:19:0988 ScheduleTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:3189 return;
90 }
91 g_after_startup_tasks.Get().push_back(queued_task.release());
92}
93
94void SetBrowserStartupIsComplete() {
95 DCHECK_CURRENTLY_ON(BrowserThread::UI);
96#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
97 // CurrentProcessInfo::CreationTime() is not available on all platforms.
98 const base::Time process_creation_time =
99 base::CurrentProcessInfo::CreationTime();
100 if (!process_creation_time.is_null()) {
101 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
102 base::Time::Now() - process_creation_time);
103 }
104#endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
105 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
106 g_after_startup_tasks.Get().size());
107 g_startup_complete_flag.Get().Set();
108 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
109 ScheduleTask(make_scoped_ptr(queued_task));
110 g_after_startup_tasks.Get().clear();
111
112 // The shrink_to_fit() method is not available for all of our build targets.
113 std::deque<AfterStartupTask*>(g_after_startup_tasks.Get())
114 .swap(g_after_startup_tasks.Get());
115}
116
117// Observes the first visible page load and sets the startup complete
118// flag accordingly.
119class StartupObserver : public WebContentsObserver, public base::NonThreadSafe {
120 public:
121 StartupObserver() : weak_factory_(this) {}
122 ~StartupObserver() override { DCHECK(IsBrowserStartupComplete()); }
123
124 void Start();
125
126 private:
127 void OnStartupComplete() {
128 DCHECK(CalledOnValidThread());
129 SetBrowserStartupIsComplete();
130 delete this;
131 }
132
133 void OnFailsafeTimeout() { OnStartupComplete(); }
134
135 // WebContentsObserver overrides
136 void DidFinishLoad(content::RenderFrameHost* render_frame_host,
137 const GURL& validated_url) override {
138 if (!render_frame_host->GetParent())
139 OnStartupComplete();
140 }
141
142 void DidFailLoad(content::RenderFrameHost* render_frame_host,
143 const GURL& validated_url,
144 int error_code,
gsennton6fbb38692015-06-24 19:23:55145 const base::string16& error_description,
146 bool was_ignored_by_handler) override {
michaeln96f887e22015-04-13 23:58:31147 if (!render_frame_host->GetParent())
148 OnStartupComplete();
149 }
150
151 void WebContentsDestroyed() override { OnStartupComplete(); }
152
153 base::WeakPtrFactory<StartupObserver> weak_factory_;
154
155 DISALLOW_COPY_AND_ASSIGN(StartupObserver);
156};
157
158void StartupObserver::Start() {
159 // Signal completion quickly when there is no first page to load.
160 const int kShortDelaySecs = 3;
161 base::TimeDelta delay = base::TimeDelta::FromSeconds(kShortDelaySecs);
162
163#if !defined(OS_ANDROID)
164 WebContents* contents = nullptr;
165 for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
166 contents = (*iter)->tab_strip_model()->GetActiveWebContents();
167 if (contents && contents->GetMainFrame() &&
168 contents->GetMainFrame()->GetVisibilityState() ==
169 blink::WebPageVisibilityStateVisible) {
170 break;
171 }
172 }
173
174 if (contents) {
175 // Give the page time to finish loading.
176 const int kLongerDelayMins = 3;
177 Observe(contents);
178 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
179 }
180#else
michaeln68bf4a8e2015-08-11 01:37:31181 // Startup completion is signaled via AfterStartupTaskUtils.java,
182 // this is just a failsafe timeout.
183 const int kLongerDelayMins = 3;
184 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
michaeln96f887e22015-04-13 23:58:31185#endif // !defined(OS_ANDROID)
186
187 BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
188 base::Bind(&StartupObserver::OnFailsafeTimeout,
189 weak_factory_.GetWeakPtr()),
190 delay);
191}
192
193} // namespace
194
195void AfterStartupTaskUtils::StartMonitoringStartup() {
196 // The observer is self-deleting.
197 (new StartupObserver)->Start();
198}
199
200void AfterStartupTaskUtils::PostTask(
201 const tracked_objects::Location& from_here,
202 const scoped_refptr<base::TaskRunner>& task_runner,
203 const base::Closure& task) {
204 if (IsBrowserStartupComplete()) {
205 task_runner->PostTask(from_here, task);
206 return;
207 }
208
209 scoped_ptr<AfterStartupTask> queued_task(
210 new AfterStartupTask(from_here, task_runner, task));
dchenge73d8520c2015-12-27 01:19:09211 QueueTask(std::move(queued_task));
michaeln96f887e22015-04-13 23:58:31212}
213
wkorman8a21c4f2015-11-18 19:06:11214void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
215 ::SetBrowserStartupIsComplete();
216}
217
michaeln96f887e22015-04-13 23:58:31218void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
219 ::SetBrowserStartupIsComplete();
220}
221
222bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
223 return ::IsBrowserStartupComplete();
224}
225
226void AfterStartupTaskUtils::UnsafeResetForTesting() {
227 DCHECK(g_after_startup_tasks.Get().empty());
228 if (!IsBrowserStartupComplete())
229 return;
230 g_startup_complete_flag.Get().UnsafeResetForTesting();
231 DCHECK(!IsBrowserStartupComplete());
232}