Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(827)

Unified Diff: chrome/browser/after_startup_task_utils.cc

Issue 949293002: Implement a poor man's PostAfterStartupTask() function. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/after_startup_task_utils.h ('k') | chrome/browser/after_startup_task_utils_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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());
+}
« no previous file with comments | « chrome/browser/after_startup_task_utils.h ('k') | chrome/browser/after_startup_task_utils_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698