1 // Copyright 2013 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/ui/sync/profile_signin_confirmation_helper.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/string16.h"
11 #include "base/task/cancelable_task_tracker.h"
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
13 #include "chrome/browser/history/history_backend.h"
14 #include "chrome/browser/history/history_db_task.h"
15 #include "chrome/browser/history/history_service.h"
16 #include "chrome/browser/history/history_service_factory.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "components/bookmarks/browser/bookmark_model.h"
19 #include "components/history/core/browser/history_types.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "ui/gfx/color_utils.h"
22 #include "ui/native_theme/native_theme.h"
24 #if defined(ENABLE_EXTENSIONS)
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/common/extensions/extension_constants.h"
27 #include "chrome/common/extensions/sync_helper.h"
28 #include "extensions/browser/extension_system.h"
29 #include "extensions/common/constants.h"
30 #include "extensions/common/extension.h"
31 #include "extensions/common/extension_set.h"
36 const int kHistoryEntriesBeforeNewProfilePrompt = 10;
38 // Determines whether a profile has any typed URLs in its history.
39 class HasTypedURLsTask : public history::HistoryDBTask {
41 explicit HasTypedURLsTask(const base::Callback<void(bool)>& cb)
42 : has_typed_urls_(false), cb_(cb) {
45 bool RunOnDBThread(history::HistoryBackend* backend,
46 history::HistoryDatabase* db) override {
47 history::URLRows rows;
48 backend->GetAllTypedURLs(&rows);
50 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains "
51 << rows.size() << " typed URLs";
52 has_typed_urls_ = true;
57 void DoneRunOnMainThread() override { cb_.Run(has_typed_urls_); }
60 ~HasTypedURLsTask() override {}
63 base::Callback<void(bool)> cb_;
66 bool HasBookmarks(Profile* profile) {
67 BookmarkModel* bookmarks = BookmarkModelFactory::GetForProfile(profile);
68 bool has_bookmarks = bookmarks && bookmarks->HasBookmarks();
70 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains bookmarks";
74 // Helper functions for Chrome profile signin.
75 class ProfileSigninConfirmationHelper {
77 ProfileSigninConfirmationHelper(
79 const base::Callback<void(bool)>& return_result);
80 void CheckHasHistory(int max_entries);
81 void CheckHasTypedURLs();
85 ~ProfileSigninConfirmationHelper();
87 void OnHistoryQueryResults(size_t max_entries,
88 history::QueryResults* results);
89 void ReturnResult(bool result);
91 // Weak pointer to the profile being signed-in.
94 // Used for async tasks.
95 base::CancelableTaskTracker task_tracker_;
97 // Keep track of how many async requests are pending.
98 int pending_requests_;
100 // Callback to pass the result back to the caller.
101 const base::Callback<void(bool)> return_result_;
103 DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationHelper);
106 ProfileSigninConfirmationHelper::ProfileSigninConfirmationHelper(
108 const base::Callback<void(bool)>& return_result)
110 pending_requests_(0),
111 return_result_(return_result) {
114 ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() {
115 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
118 void ProfileSigninConfirmationHelper::OnHistoryQueryResults(
120 history::QueryResults* results) {
121 history::QueryResults owned_results;
122 results->Swap(&owned_results);
123 bool too_much_history = owned_results.size() >= max_entries;
124 if (too_much_history) {
125 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains "
126 << owned_results.size() << " history entries";
128 ReturnResult(too_much_history);
131 void ProfileSigninConfirmationHelper::CheckHasHistory(int max_entries) {
132 HistoryService* service =
133 HistoryServiceFactory::GetForProfileWithoutCreating(profile_);
139 history::QueryOptions opts;
140 opts.max_count = max_entries;
141 service->QueryHistory(
144 base::Bind(&ProfileSigninConfirmationHelper::OnHistoryQueryResults,
145 base::Unretained(this),
150 void ProfileSigninConfirmationHelper::CheckHasTypedURLs() {
151 HistoryService* service =
152 HistoryServiceFactory::GetForProfileWithoutCreating(profile_);
158 service->ScheduleDBTask(
159 scoped_ptr<history::HistoryDBTask>(new HasTypedURLsTask(
160 base::Bind(&ProfileSigninConfirmationHelper::ReturnResult,
161 base::Unretained(this)))),
165 void ProfileSigninConfirmationHelper::ReturnResult(bool result) {
166 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
167 // Pass |true| into the callback as soon as one of the tasks passes a
168 // result of |true|, otherwise pass the last returned result.
169 if (--pending_requests_ == 0 || result) {
170 return_result_.Run(result);
172 // This leaks at shutdown if the HistoryService is destroyed, but
173 // the process is going to die anyway.
182 SkColor GetSigninConfirmationPromptBarColor(SkAlpha alpha) {
183 static const SkColor kBackgroundColor =
184 ui::NativeTheme::instance()->GetSystemColor(
185 ui::NativeTheme::kColorId_DialogBackground);
186 return color_utils::BlendTowardOppositeLuminance(kBackgroundColor, alpha);
189 bool HasBeenShutdown(Profile* profile) {
191 // This check is not useful on iOS: the browser can be shut down without
192 // explicit user action (for example, in response to memory pressure), and
193 // this should be invisible to the user. The desktop assumption that the
194 // profile going through a restart indicates something about user intention
195 // does not hold. We rely on the other profile dirtiness checks.
198 bool has_been_shutdown = !profile->IsNewProfile();
199 if (has_been_shutdown)
200 DVLOG(1) << "ProfileSigninConfirmationHelper: profile is not new";
201 return has_been_shutdown;
205 bool HasSyncedExtensions(Profile* profile) {
206 #if defined(ENABLE_EXTENSIONS)
207 extensions::ExtensionSystem* system =
208 extensions::ExtensionSystem::Get(profile);
209 if (system && system->extension_service()) {
210 const extensions::ExtensionSet* extensions =
211 system->extension_service()->extensions();
212 for (extensions::ExtensionSet::const_iterator iter = extensions->begin();
213 iter != extensions->end(); ++iter) {
214 // The webstore is synced so that it stays put on the new tab
215 // page, but since it's installed by default we don't want to
216 // consider it when determining if the profile is dirty.
217 if (extensions::sync_helper::IsSyncable(iter->get()) &&
218 (*iter)->id() != extensions::kWebStoreAppId &&
219 (*iter)->id() != extension_misc::kChromeAppId) {
220 DVLOG(1) << "ProfileSigninConfirmationHelper: "
221 << "profile contains a synced extension: " << (*iter)->id();
230 void CheckShouldPromptForNewProfile(
232 const base::Callback<void(bool)>& return_result) {
233 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
235 if (HasBeenShutdown(profile) ||
236 HasBookmarks(profile) ||
237 HasSyncedExtensions(profile)) {
238 return_result.Run(true);
241 // Fire asynchronous queries for profile data.
242 ProfileSigninConfirmationHelper* helper =
243 new ProfileSigninConfirmationHelper(profile, return_result);
244 helper->CheckHasHistory(kHistoryEntriesBeforeNewProfilePrompt);
245 helper->CheckHasTypedURLs();