1 // 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.
5 #include "chrome/browser/after_startup_task_utils.h"
7 #include "base/lazy_instance.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/process/process_info.h"
11 #include "base/rand_util.h"
12 #include "base/synchronization/cancellation_flag.h"
13 #include "base/task_runner.h"
14 #include "base/tracked_objects.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_iterator.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_contents_observer.h"
23 using content::BrowserThread
;
24 using content::WebContents
;
25 using content::WebContentsObserver
;
26 using StartupCompleteFlag
= base::CancellationFlag
;
30 struct AfterStartupTask
{
31 AfterStartupTask(const tracked_objects::Location
& from_here
,
32 const scoped_refptr
<base::TaskRunner
>& task_runner
,
33 const base::Closure
& task
)
34 : from_here(from_here
), task_runner(task_runner
), task(task
) {}
35 ~AfterStartupTask() {}
37 const tracked_objects::Location from_here
;
38 const scoped_refptr
<base::TaskRunner
> task_runner
;
39 const base::Closure task
;
42 // The flag may be read on any thread, but must only be set on the UI thread.
43 base::LazyInstance
<StartupCompleteFlag
>::Leaky g_startup_complete_flag
;
45 // The queue may only be accessed on the UI thread.
46 base::LazyInstance
<std::deque
<AfterStartupTask
*>>::Leaky g_after_startup_tasks
;
48 bool IsBrowserStartupComplete() {
49 // Be sure to initialize the LazyInstance on the main thread since the flag
50 // may only be set on it's initializing thread.
51 if (g_startup_complete_flag
== nullptr)
53 return g_startup_complete_flag
.Get().IsSet();
56 void RunTask(scoped_ptr
<AfterStartupTask
> queued_task
) {
57 // We're careful to delete the caller's |task| on the target runner's thread.
58 DCHECK(queued_task
->task_runner
->RunsTasksOnCurrentThread());
59 queued_task
->task
.Run();
62 void ScheduleTask(scoped_ptr
<AfterStartupTask
> queued_task
) {
63 // Spread their execution over a brief time.
64 const int kMinDelaySec
= 0;
65 const int kMaxDelaySec
= 10;
66 scoped_refptr
<base::TaskRunner
> target_runner
= queued_task
->task_runner
;
67 tracked_objects::Location from_here
= queued_task
->from_here
;
68 target_runner
->PostDelayedTask(
69 from_here
, base::Bind(&RunTask
, base::Passed(queued_task
.Pass())),
70 base::TimeDelta::FromSeconds(base::RandInt(kMinDelaySec
, kMaxDelaySec
)));
73 void QueueTask(scoped_ptr
<AfterStartupTask
> queued_task
) {
74 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
75 BrowserThread::PostTask(
76 BrowserThread::UI
, FROM_HERE
,
77 base::Bind(QueueTask
, base::Passed(queued_task
.Pass())));
81 // The flag may have been set while the task to invoke this method
82 // on the UI thread was inflight.
83 if (IsBrowserStartupComplete()) {
84 ScheduleTask(queued_task
.Pass());
87 g_after_startup_tasks
.Get().push_back(queued_task
.release());
90 void SetBrowserStartupIsComplete() {
91 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
92 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
93 // CurrentProcessInfo::CreationTime() is not available on all platforms.
94 const base::Time process_creation_time
=
95 base::CurrentProcessInfo::CreationTime();
96 if (!process_creation_time
.is_null()) {
97 UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
98 base::Time::Now() - process_creation_time
);
100 #endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
101 UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
102 g_after_startup_tasks
.Get().size());
103 g_startup_complete_flag
.Get().Set();
104 for (AfterStartupTask
* queued_task
: g_after_startup_tasks
.Get())
105 ScheduleTask(make_scoped_ptr(queued_task
));
106 g_after_startup_tasks
.Get().clear();
108 // The shrink_to_fit() method is not available for all of our build targets.
109 std::deque
<AfterStartupTask
*>(g_after_startup_tasks
.Get())
110 .swap(g_after_startup_tasks
.Get());
113 // Observes the first visible page load and sets the startup complete
115 class StartupObserver
: public WebContentsObserver
, public base::NonThreadSafe
{
117 StartupObserver() : weak_factory_(this) {}
118 ~StartupObserver() override
{ DCHECK(IsBrowserStartupComplete()); }
123 void OnStartupComplete() {
124 DCHECK(CalledOnValidThread());
125 SetBrowserStartupIsComplete();
129 void OnFailsafeTimeout() { OnStartupComplete(); }
131 // WebContentsObserver overrides
132 void DidFinishLoad(content::RenderFrameHost
* render_frame_host
,
133 const GURL
& validated_url
) override
{
134 if (!render_frame_host
->GetParent())
138 void DidFailLoad(content::RenderFrameHost
* render_frame_host
,
139 const GURL
& validated_url
,
141 const base::string16
& error_description
) override
{
142 if (!render_frame_host
->GetParent())
146 void WebContentsDestroyed() override
{ OnStartupComplete(); }
148 base::WeakPtrFactory
<StartupObserver
> weak_factory_
;
150 DISALLOW_COPY_AND_ASSIGN(StartupObserver
);
153 void StartupObserver::Start() {
154 // Signal completion quickly when there is no first page to load.
155 const int kShortDelaySecs
= 3;
156 base::TimeDelta delay
= base::TimeDelta::FromSeconds(kShortDelaySecs
);
158 #if !defined(OS_ANDROID)
159 WebContents
* contents
= nullptr;
160 for (chrome::BrowserIterator iter
; !iter
.done(); iter
.Next()) {
161 contents
= (*iter
)->tab_strip_model()->GetActiveWebContents();
162 if (contents
&& contents
->GetMainFrame() &&
163 contents
->GetMainFrame()->GetVisibilityState() ==
164 blink::WebPageVisibilityStateVisible
) {
170 // Give the page time to finish loading.
171 const int kLongerDelayMins
= 3;
173 delay
= base::TimeDelta::FromMinutes(kLongerDelayMins
);
176 // TODO(michaeln): We should probably monitor the initial page load here too,
177 // but since ChromeBrowserMainExtraPartsMetrics doesn't, not doing that yet.
178 const int kAndroidDelaySecs
= 10;
179 delay
= base::TimeDelta::FromSeconds(kAndroidDelaySecs
);
180 #endif // !defined(OS_ANDROID)
182 BrowserThread::PostDelayedTask(BrowserThread::UI
, FROM_HERE
,
183 base::Bind(&StartupObserver::OnFailsafeTimeout
,
184 weak_factory_
.GetWeakPtr()),
190 void AfterStartupTaskUtils::StartMonitoringStartup() {
191 // The observer is self-deleting.
192 (new StartupObserver
)->Start();
195 void AfterStartupTaskUtils::PostTask(
196 const tracked_objects::Location
& from_here
,
197 const scoped_refptr
<base::TaskRunner
>& task_runner
,
198 const base::Closure
& task
) {
199 if (IsBrowserStartupComplete()) {
200 task_runner
->PostTask(from_here
, task
);
204 scoped_ptr
<AfterStartupTask
> queued_task(
205 new AfterStartupTask(from_here
, task_runner
, task
));
206 QueueTask(queued_task
.Pass());
209 void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
210 ::SetBrowserStartupIsComplete();
213 bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
214 return ::IsBrowserStartupComplete();
217 void AfterStartupTaskUtils::UnsafeResetForTesting() {
218 DCHECK(g_after_startup_tasks
.Get().empty());
219 if (!IsBrowserStartupComplete())
221 g_startup_complete_flag
.Get().UnsafeResetForTesting();
222 DCHECK(!IsBrowserStartupComplete());