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 "chrome/browser/bookmarks/bookmark_model.h"
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
13 #include "chrome/browser/common/cancelable_request.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/extension_system.h"
16 #include "chrome/browser/history/history_backend.h"
17 #include "chrome/browser/history/history_db_task.h"
18 #include "chrome/browser/history/history_service.h"
19 #include "chrome/browser/history/history_service_factory.h"
20 #include "chrome/browser/history/history_types.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/extensions/extension.h"
23 #include "chrome/common/extensions/extension_set.h"
24 #include "chrome/common/extensions/sync_helper.h"
25 #include "ui/gfx/color_utils.h"
26 #include "ui/native_theme/native_theme.h"
28 // TODO(dconnelly): change VLOG to DVLOG (crbug.com/240195)
32 const int kHistoryEntriesBeforeNewProfilePrompt
= 10;
34 // Determines whether a profile has any typed URLs in its history.
35 class HasTypedURLsTask
: public history::HistoryDBTask
{
37 HasTypedURLsTask(const base::Callback
<void(bool)>& cb
)
38 : has_typed_urls_(false), cb_(cb
) {
41 virtual bool RunOnDBThread(history::HistoryBackend
* backend
,
42 history::HistoryDatabase
* db
) OVERRIDE
{
43 history::URLRows rows
;
44 backend
->GetAllTypedURLs(&rows
);
46 VLOG(1) << "ProfileSigninConfirmationHelper: profile contains "
47 << rows
.size() << " typed URLs";
48 has_typed_urls_
= true;
53 virtual void DoneRunOnMainThread() OVERRIDE
{
54 cb_
.Run(has_typed_urls_
);
58 virtual ~HasTypedURLsTask() {}
60 base::Callback
<void(bool)> cb_
;
63 bool HasBookmarks(Profile
* profile
) {
64 BookmarkModel
* bookmarks
= BookmarkModelFactory::GetForProfile(profile
);
65 bool has_bookmarks
= bookmarks
&& bookmarks
->HasBookmarks();
67 VLOG(1) << "ProfileSigninConfirmationHelper: profile contains bookmarks";
71 // Helper functions for Chrome profile signin.
72 class ProfileSigninConfirmationHelper
73 : public base::RefCounted
<ProfileSigninConfirmationHelper
> {
75 ProfileSigninConfirmationHelper(
77 const base::Callback
<void(bool)>& return_result
);
78 void CheckHasHistory(int max_entries
);
79 void CheckHasTypedURLs();
80 void set_pending_requests(int requests
);
83 friend class base::RefCounted
<ProfileSigninConfirmationHelper
>;
85 ~ProfileSigninConfirmationHelper();
87 void OnHistoryQueryResults(size_t max_entries
,
88 CancelableRequestProvider::Handle handle
,
89 history::QueryResults
* results
);
90 void ReturnResult(bool result
);
92 // Weak pointer to the profile being signed-in.
95 // Used for async tasks.
96 CancelableRequestConsumer request_consumer_
;
98 // Keep track of how many async requests are pending.
99 int pending_requests_
;
101 // Indicates whether the result has already been returned to caller.
102 bool result_returned_
;
104 // Callback to pass the result back to the caller.
105 const base::Callback
<void(bool)> return_result_
;
107 DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationHelper
);
110 ProfileSigninConfirmationHelper::ProfileSigninConfirmationHelper(
112 const base::Callback
<void(bool)>& return_result
)
114 pending_requests_(0),
115 result_returned_(false),
116 return_result_(return_result
) {
119 ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() {
122 void ProfileSigninConfirmationHelper::OnHistoryQueryResults(
124 CancelableRequestProvider::Handle handle
,
125 history::QueryResults
* results
) {
126 history::QueryResults owned_results
;
127 results
->Swap(&owned_results
);
128 bool too_much_history
= owned_results
.size() >= max_entries
;
129 if (too_much_history
) {
130 VLOG(1) << "ProfileSigninConfirmationHelper: profile contains "
131 << owned_results
.size() << " history entries";
133 ReturnResult(too_much_history
);
136 void ProfileSigninConfirmationHelper::CheckHasHistory(int max_entries
) {
137 HistoryService
* service
=
138 HistoryServiceFactory::GetForProfileWithoutCreating(profile_
);
143 history::QueryOptions opts
;
144 opts
.max_count
= max_entries
;
145 service
->QueryHistory(
146 string16(), opts
, &request_consumer_
,
147 base::Bind(&ProfileSigninConfirmationHelper::OnHistoryQueryResults
,
152 void ProfileSigninConfirmationHelper::CheckHasTypedURLs() {
153 HistoryService
* service
=
154 HistoryServiceFactory::GetForProfileWithoutCreating(profile_
);
159 service
->ScheduleDBTask(
160 new HasTypedURLsTask(
162 &ProfileSigninConfirmationHelper::ReturnResult
,
167 void ProfileSigninConfirmationHelper::set_pending_requests(int requests
) {
168 pending_requests_
= requests
;
171 void ProfileSigninConfirmationHelper::ReturnResult(bool result
) {
172 // Pass |true| into the callback as soon as one of the tasks passes a
173 // result of |true|, otherwise pass the last returned result.
174 if (!result_returned_
&& (--pending_requests_
== 0 || result
)) {
175 result_returned_
= true;
176 request_consumer_
.CancelAllRequests();
177 return_result_
.Run(result
);
185 SkColor
GetSigninConfirmationPromptBarColor(SkAlpha alpha
) {
186 static const SkColor kBackgroundColor
=
187 ui::NativeTheme::instance()->GetSystemColor(
188 ui::NativeTheme::kColorId_DialogBackground
);
189 return color_utils::BlendTowardOppositeLuminance(kBackgroundColor
, alpha
);
192 bool HasBeenShutdown(Profile
* profile
) {
193 bool has_been_shutdown
= !profile
->IsNewProfile();
194 if (has_been_shutdown
)
195 VLOG(1) << "ProfileSigninConfirmationHelper: profile is not new";
196 return has_been_shutdown
;
199 bool HasSyncedExtensions(Profile
* profile
) {
200 extensions::ExtensionSystem
* system
=
201 extensions::ExtensionSystem::Get(profile
);
202 if (system
&& system
->extension_service()) {
203 const ExtensionSet
* extensions
= system
->extension_service()->extensions();
204 for (ExtensionSet::const_iterator iter
= extensions
->begin();
205 iter
!= extensions
->end(); ++iter
) {
206 // The webstore is synced so that it stays put on the new tab
207 // page, but since it's installed by default we don't want to
208 // consider it when determining if the profile is dirty.
209 if (extensions::sync_helper::IsSyncable(iter
->get()) &&
210 (*iter
)->id() != extension_misc::kWebStoreAppId
&&
211 (*iter
)->id() != extension_misc::kChromeAppId
) {
212 VLOG(1) << "ProfileSigninConfirmationHelper: "
213 << "profile contains a synced extension: " << (*iter
)->id();
221 void CheckShouldPromptForNewProfile(
223 const base::Callback
<void(bool)>& return_result
) {
224 if (HasBeenShutdown(profile
) ||
225 HasBookmarks(profile
) ||
226 HasSyncedExtensions(profile
)) {
227 return_result
.Run(true);
230 // Fire asynchronous queries for profile data.
231 scoped_refptr
<ProfileSigninConfirmationHelper
> helper
=
232 new ProfileSigninConfirmationHelper(profile
, return_result
);
233 const int requests
= 2;
234 helper
->set_pending_requests(requests
);
235 helper
->CheckHasHistory(kHistoryEntriesBeforeNewProfilePrompt
);
236 helper
->CheckHasTypedURLs();