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 virtual 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 virtual void DoneRunOnMainThread() override
{
58 cb_
.Run(has_typed_urls_
);
62 virtual ~HasTypedURLsTask() {}
65 base::Callback
<void(bool)> cb_
;
68 bool HasBookmarks(Profile
* profile
) {
69 BookmarkModel
* bookmarks
= BookmarkModelFactory::GetForProfile(profile
);
70 bool has_bookmarks
= bookmarks
&& bookmarks
->HasBookmarks();
72 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains bookmarks";
76 // Helper functions for Chrome profile signin.
77 class ProfileSigninConfirmationHelper
{
79 ProfileSigninConfirmationHelper(
81 const base::Callback
<void(bool)>& return_result
);
82 void CheckHasHistory(int max_entries
);
83 void CheckHasTypedURLs();
87 ~ProfileSigninConfirmationHelper();
89 void OnHistoryQueryResults(size_t max_entries
,
90 history::QueryResults
* results
);
91 void ReturnResult(bool result
);
93 // Weak pointer to the profile being signed-in.
96 // Used for async tasks.
97 base::CancelableTaskTracker task_tracker_
;
99 // Keep track of how many async requests are pending.
100 int pending_requests_
;
102 // Callback to pass the result back to the caller.
103 const base::Callback
<void(bool)> return_result_
;
105 DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationHelper
);
108 ProfileSigninConfirmationHelper::ProfileSigninConfirmationHelper(
110 const base::Callback
<void(bool)>& return_result
)
112 pending_requests_(0),
113 return_result_(return_result
) {
116 ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() {
117 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
120 void ProfileSigninConfirmationHelper::OnHistoryQueryResults(
122 history::QueryResults
* results
) {
123 history::QueryResults owned_results
;
124 results
->Swap(&owned_results
);
125 bool too_much_history
= owned_results
.size() >= max_entries
;
126 if (too_much_history
) {
127 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains "
128 << owned_results
.size() << " history entries";
130 ReturnResult(too_much_history
);
133 void ProfileSigninConfirmationHelper::CheckHasHistory(int max_entries
) {
134 HistoryService
* service
=
135 HistoryServiceFactory::GetForProfileWithoutCreating(profile_
);
141 history::QueryOptions opts
;
142 opts
.max_count
= max_entries
;
143 service
->QueryHistory(
146 base::Bind(&ProfileSigninConfirmationHelper::OnHistoryQueryResults
,
147 base::Unretained(this),
152 void ProfileSigninConfirmationHelper::CheckHasTypedURLs() {
153 HistoryService
* service
=
154 HistoryServiceFactory::GetForProfileWithoutCreating(profile_
);
160 service
->ScheduleDBTask(
161 scoped_ptr
<history::HistoryDBTask
>(new HasTypedURLsTask(
162 base::Bind(&ProfileSigninConfirmationHelper::ReturnResult
,
163 base::Unretained(this)))),
167 void ProfileSigninConfirmationHelper::ReturnResult(bool result
) {
168 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
169 // Pass |true| into the callback as soon as one of the tasks passes a
170 // result of |true|, otherwise pass the last returned result.
171 if (--pending_requests_
== 0 || result
) {
172 return_result_
.Run(result
);
174 // This leaks at shutdown if the HistoryService is destroyed, but
175 // the process is going to die anyway.
184 SkColor
GetSigninConfirmationPromptBarColor(SkAlpha alpha
) {
185 static const SkColor kBackgroundColor
=
186 ui::NativeTheme::instance()->GetSystemColor(
187 ui::NativeTheme::kColorId_DialogBackground
);
188 return color_utils::BlendTowardOppositeLuminance(kBackgroundColor
, alpha
);
191 bool HasBeenShutdown(Profile
* profile
) {
193 // This check is not useful on iOS: the browser can be shut down without
194 // explicit user action (for example, in response to memory pressure), and
195 // this should be invisible to the user. The desktop assumption that the
196 // profile going through a restart indicates something about user intention
197 // does not hold. We rely on the other profile dirtiness checks.
200 bool has_been_shutdown
= !profile
->IsNewProfile();
201 if (has_been_shutdown
)
202 DVLOG(1) << "ProfileSigninConfirmationHelper: profile is not new";
203 return has_been_shutdown
;
207 bool HasSyncedExtensions(Profile
* profile
) {
208 #if defined(ENABLE_EXTENSIONS)
209 extensions::ExtensionSystem
* system
=
210 extensions::ExtensionSystem::Get(profile
);
211 if (system
&& system
->extension_service()) {
212 const extensions::ExtensionSet
* extensions
=
213 system
->extension_service()->extensions();
214 for (extensions::ExtensionSet::const_iterator iter
= extensions
->begin();
215 iter
!= extensions
->end(); ++iter
) {
216 // The webstore is synced so that it stays put on the new tab
217 // page, but since it's installed by default we don't want to
218 // consider it when determining if the profile is dirty.
219 if (extensions::sync_helper::IsSyncable(iter
->get()) &&
220 (*iter
)->id() != extensions::kWebStoreAppId
&&
221 (*iter
)->id() != extension_misc::kChromeAppId
) {
222 DVLOG(1) << "ProfileSigninConfirmationHelper: "
223 << "profile contains a synced extension: " << (*iter
)->id();
232 void CheckShouldPromptForNewProfile(
234 const base::Callback
<void(bool)>& return_result
) {
235 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
237 if (HasBeenShutdown(profile
) ||
238 HasBookmarks(profile
) ||
239 HasSyncedExtensions(profile
)) {
240 return_result
.Run(true);
243 // Fire asynchronous queries for profile data.
244 ProfileSigninConfirmationHelper
* helper
=
245 new ProfileSigninConfirmationHelper(profile
, return_result
);
246 helper
->CheckHasHistory(kHistoryEntriesBeforeNewProfilePrompt
);
247 helper
->CheckHasTypedURLs();