| Index: chrome/browser/after_startup_task_utils.cc
|
| diff --git a/chrome/browser/after_startup_task_utils.cc b/chrome/browser/after_startup_task_utils.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a4dc5efbc13e3578528b38e81a59241ef0afb55c
|
| --- /dev/null
|
| +++ b/chrome/browser/after_startup_task_utils.cc
|
| @@ -0,0 +1,223 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/after_startup_task_utils.h"
|
| +
|
| +#include "base/lazy_instance.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/metrics/histogram_macros.h"
|
| +#include "base/process/process_info.h"
|
| +#include "base/rand_util.h"
|
| +#include "base/synchronization/cancellation_flag.h"
|
| +#include "base/task_runner.h"
|
| +#include "base/tracked_objects.h"
|
| +#include "chrome/browser/ui/browser.h"
|
| +#include "chrome/browser/ui/browser_iterator.h"
|
| +#include "chrome/browser/ui/tabs/tab_strip_model.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/render_frame_host.h"
|
| +#include "content/public/browser/web_contents.h"
|
| +#include "content/public/browser/web_contents_observer.h"
|
| +
|
| +using content::BrowserThread;
|
| +using content::WebContents;
|
| +using content::WebContentsObserver;
|
| +using StartupCompleteFlag = base::CancellationFlag;
|
| +
|
| +namespace {
|
| +
|
| +struct AfterStartupTask {
|
| + AfterStartupTask(const tracked_objects::Location& from_here,
|
| + const scoped_refptr<base::TaskRunner>& task_runner,
|
| + const base::Closure& task)
|
| + : from_here(from_here), task_runner(task_runner), task(task) {}
|
| + ~AfterStartupTask() {}
|
| +
|
| + const tracked_objects::Location from_here;
|
| + const scoped_refptr<base::TaskRunner> task_runner;
|
| + const base::Closure task;
|
| +};
|
| +
|
| +// The flag may be read on any thread, but must only be set on the UI thread.
|
| +base::LazyInstance<StartupCompleteFlag>::Leaky g_startup_complete_flag;
|
| +
|
| +// The queue may only be accessed on the UI thread.
|
| +base::LazyInstance<std::deque<AfterStartupTask*>>::Leaky g_after_startup_tasks;
|
| +
|
| +bool IsBrowserStartupComplete() {
|
| + // Be sure to initialize the LazyInstance on the main thread since the flag
|
| + // may only be set on it's initializing thread.
|
| + if (g_startup_complete_flag == nullptr)
|
| + return false;
|
| + return g_startup_complete_flag.Get().IsSet();
|
| +}
|
| +
|
| +void RunTask(scoped_ptr<AfterStartupTask> queued_task) {
|
| + // We're careful to delete the caller's |task| on the target runner's thread.
|
| + DCHECK(queued_task->task_runner->RunsTasksOnCurrentThread());
|
| + queued_task->task.Run();
|
| +}
|
| +
|
| +void ScheduleTask(scoped_ptr<AfterStartupTask> queued_task) {
|
| + // Spread their execution over a brief time.
|
| + const int kMinDelaySec = 0;
|
| + const int kMaxDelaySec = 10;
|
| + scoped_refptr<base::TaskRunner> target_runner = queued_task->task_runner;
|
| + tracked_objects::Location from_here = queued_task->from_here;
|
| + target_runner->PostDelayedTask(
|
| + from_here, base::Bind(&RunTask, base::Passed(queued_task.Pass())),
|
| + base::TimeDelta::FromSeconds(base::RandInt(kMinDelaySec, kMaxDelaySec)));
|
| +}
|
| +
|
| +void QueueTask(scoped_ptr<AfterStartupTask> queued_task) {
|
| + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI, FROM_HERE,
|
| + base::Bind(QueueTask, base::Passed(queued_task.Pass())));
|
| + return;
|
| + }
|
| +
|
| + // The flag may have been set while the task to invoke this method
|
| + // on the UI thread was inflight.
|
| + if (IsBrowserStartupComplete()) {
|
| + ScheduleTask(queued_task.Pass());
|
| + return;
|
| + }
|
| + g_after_startup_tasks.Get().push_back(queued_task.release());
|
| +}
|
| +
|
| +void SetBrowserStartupIsComplete() {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
|
| + // CurrentProcessInfo::CreationTime() is not available on all platforms.
|
| + const base::Time process_creation_time =
|
| + base::CurrentProcessInfo::CreationTime();
|
| + if (!process_creation_time.is_null()) {
|
| + UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
|
| + base::Time::Now() - process_creation_time);
|
| + }
|
| +#endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
|
| + UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
|
| + g_after_startup_tasks.Get().size());
|
| + g_startup_complete_flag.Get().Set();
|
| + for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
|
| + ScheduleTask(make_scoped_ptr(queued_task));
|
| + g_after_startup_tasks.Get().clear();
|
| +
|
| + // The shrink_to_fit() method is not available for all of our build targets.
|
| + std::deque<AfterStartupTask*>(g_after_startup_tasks.Get())
|
| + .swap(g_after_startup_tasks.Get());
|
| +}
|
| +
|
| +// Observes the first visible page load and sets the startup complete
|
| +// flag accordingly.
|
| +class StartupObserver : public WebContentsObserver, public base::NonThreadSafe {
|
| + public:
|
| + StartupObserver() : weak_factory_(this) {}
|
| + ~StartupObserver() override { DCHECK(IsBrowserStartupComplete()); }
|
| +
|
| + void Start();
|
| +
|
| + private:
|
| + void OnStartupComplete() {
|
| + DCHECK(CalledOnValidThread());
|
| + SetBrowserStartupIsComplete();
|
| + delete this;
|
| + }
|
| +
|
| + void OnFailsafeTimeout() { OnStartupComplete(); }
|
| +
|
| + // WebContentsObserver overrides
|
| + void DidFinishLoad(content::RenderFrameHost* render_frame_host,
|
| + const GURL& validated_url) override {
|
| + if (!render_frame_host->GetParent())
|
| + OnStartupComplete();
|
| + }
|
| +
|
| + void DidFailLoad(content::RenderFrameHost* render_frame_host,
|
| + const GURL& validated_url,
|
| + int error_code,
|
| + const base::string16& error_description) override {
|
| + if (!render_frame_host->GetParent())
|
| + OnStartupComplete();
|
| + }
|
| +
|
| + void WebContentsDestroyed() override { OnStartupComplete(); }
|
| +
|
| + base::WeakPtrFactory<StartupObserver> weak_factory_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(StartupObserver);
|
| +};
|
| +
|
| +void StartupObserver::Start() {
|
| + // Signal completion quickly when there is no first page to load.
|
| + const int kShortDelaySecs = 3;
|
| + base::TimeDelta delay = base::TimeDelta::FromSeconds(kShortDelaySecs);
|
| +
|
| +#if !defined(OS_ANDROID)
|
| + WebContents* contents = nullptr;
|
| + for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
|
| + contents = (*iter)->tab_strip_model()->GetActiveWebContents();
|
| + if (contents && contents->GetMainFrame() &&
|
| + contents->GetMainFrame()->GetVisibilityState() ==
|
| + blink::WebPageVisibilityStateVisible) {
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (contents) {
|
| + // Give the page time to finish loading.
|
| + const int kLongerDelayMins = 3;
|
| + Observe(contents);
|
| + delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
|
| + }
|
| +#else
|
| + // TODO(michaeln): We should probably monitor the initial page load here too,
|
| + // but since ChromeBrowserMainExtraPartsMetrics doesn't, not doing that yet.
|
| + const int kAndroidDelaySecs = 10;
|
| + delay = base::TimeDelta::FromSeconds(kAndroidDelaySecs);
|
| +#endif // !defined(OS_ANDROID)
|
| +
|
| + BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
|
| + base::Bind(&StartupObserver::OnFailsafeTimeout,
|
| + weak_factory_.GetWeakPtr()),
|
| + delay);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +void AfterStartupTaskUtils::StartMonitoringStartup() {
|
| + // The observer is self-deleting.
|
| + (new StartupObserver)->Start();
|
| +}
|
| +
|
| +void AfterStartupTaskUtils::PostTask(
|
| + const tracked_objects::Location& from_here,
|
| + const scoped_refptr<base::TaskRunner>& task_runner,
|
| + const base::Closure& task) {
|
| + if (IsBrowserStartupComplete()) {
|
| + task_runner->PostTask(from_here, task);
|
| + return;
|
| + }
|
| +
|
| + scoped_ptr<AfterStartupTask> queued_task(
|
| + new AfterStartupTask(from_here, task_runner, task));
|
| + QueueTask(queued_task.Pass());
|
| +}
|
| +
|
| +void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
|
| + ::SetBrowserStartupIsComplete();
|
| +}
|
| +
|
| +bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
|
| + return ::IsBrowserStartupComplete();
|
| +}
|
| +
|
| +void AfterStartupTaskUtils::UnsafeResetForTesting() {
|
| + DCHECK(g_after_startup_tasks.Get().empty());
|
| + if (!IsBrowserStartupComplete())
|
| + return;
|
| + g_startup_complete_flag.Get().UnsafeResetForTesting();
|
| + DCHECK(!IsBrowserStartupComplete());
|
| +}
|
|
|