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_service_factory.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "components/bookmarks/browser/bookmark_model.h"
16 #include "components/history/core/browser/history_backend.h"
17 #include "components/history/core/browser/history_db_task.h"
18 #include "components/history/core/browser/history_service.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/common/extensions/extension_constants.h"
26 #include "chrome/common/extensions/sync_helper.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/common/constants.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/extension_set.h"
33 using bookmarks::BookmarkModel
;
37 const int kHistoryEntriesBeforeNewProfilePrompt
= 10;
39 // Determines whether a profile has any typed URLs in its history.
40 class HasTypedURLsTask
: public history::HistoryDBTask
{
42 explicit HasTypedURLsTask(const base::Callback
<void(bool)>& cb
)
43 : has_typed_urls_(false), cb_(cb
) {
46 bool RunOnDBThread(history::HistoryBackend
* backend
,
47 history::HistoryDatabase
* db
) override
{
48 history::URLRows rows
;
49 backend
->GetAllTypedURLs(&rows
);
51 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains "
52 << rows
.size() << " typed URLs";
53 has_typed_urls_
= true;
58 void DoneRunOnMainThread() override
{ cb_
.Run(has_typed_urls_
); }
61 ~HasTypedURLsTask() override
{}
64 base::Callback
<void(bool)> cb_
;
67 bool HasBookmarks(Profile
* profile
) {
68 BookmarkModel
* bookmarks
= BookmarkModelFactory::GetForProfile(profile
);
69 bool has_bookmarks
= bookmarks
&& bookmarks
->HasBookmarks();
71 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains bookmarks";
75 // Helper functions for Chrome profile signin.
76 class ProfileSigninConfirmationHelper
{
78 ProfileSigninConfirmationHelper(
80 const base::Callback
<void(bool)>& return_result
);
81 void CheckHasHistory(int max_entries
);
82 void CheckHasTypedURLs();
86 ~ProfileSigninConfirmationHelper();
88 void OnHistoryQueryResults(size_t max_entries
,
89 history::QueryResults
* results
);
90 void ReturnResult(bool result
);
92 // Weak pointer to the profile being signed-in.
95 // Used for async tasks.
96 base::CancelableTaskTracker task_tracker_
;
98 // Keep track of how many async requests are pending.
99 int pending_requests_
;
101 // Callback to pass the result back to the caller.
102 const base::Callback
<void(bool)> return_result_
;
104 DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationHelper
);
107 ProfileSigninConfirmationHelper::ProfileSigninConfirmationHelper(
109 const base::Callback
<void(bool)>& return_result
)
111 pending_requests_(0),
112 return_result_(return_result
) {
115 ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() {
116 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
119 void ProfileSigninConfirmationHelper::OnHistoryQueryResults(
121 history::QueryResults
* results
) {
122 history::QueryResults owned_results
;
123 results
->Swap(&owned_results
);
124 bool too_much_history
= owned_results
.size() >= max_entries
;
125 if (too_much_history
) {
126 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains "
127 << owned_results
.size() << " history entries";
129 ReturnResult(too_much_history
);
132 void ProfileSigninConfirmationHelper::CheckHasHistory(int max_entries
) {
133 history::HistoryService
* service
=
134 HistoryServiceFactory::GetForProfileWithoutCreating(profile_
);
140 history::QueryOptions opts
;
141 opts
.max_count
= max_entries
;
142 service
->QueryHistory(
145 base::Bind(&ProfileSigninConfirmationHelper::OnHistoryQueryResults
,
146 base::Unretained(this),
151 void ProfileSigninConfirmationHelper::CheckHasTypedURLs() {
152 history::HistoryService
* service
=
153 HistoryServiceFactory::GetForProfileWithoutCreating(profile_
);
159 service
->ScheduleDBTask(
160 scoped_ptr
<history::HistoryDBTask
>(new HasTypedURLsTask(
161 base::Bind(&ProfileSigninConfirmationHelper::ReturnResult
,
162 base::Unretained(this)))),
166 void ProfileSigninConfirmationHelper::ReturnResult(bool result
) {
167 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
168 // Pass |true| into the callback as soon as one of the tasks passes a
169 // result of |true|, otherwise pass the last returned result.
170 if (--pending_requests_
== 0 || result
) {
171 return_result_
.Run(result
);
173 // This leaks at shutdown if the HistoryService is destroyed, but
174 // the process is going to die anyway.
183 SkColor
GetSigninConfirmationPromptBarColor(SkAlpha alpha
) {
184 static const SkColor kBackgroundColor
=
185 ui::NativeTheme::instance()->GetSystemColor(
186 ui::NativeTheme::kColorId_DialogBackground
);
187 return color_utils::BlendTowardOppositeLuminance(kBackgroundColor
, alpha
);
190 bool HasBeenShutdown(Profile
* profile
) {
192 // This check is not useful on iOS: the browser can be shut down without
193 // explicit user action (for example, in response to memory pressure), and
194 // this should be invisible to the user. The desktop assumption that the
195 // profile going through a restart indicates something about user intention
196 // does not hold. We rely on the other profile dirtiness checks.
199 bool has_been_shutdown
= !profile
->IsNewProfile();
200 if (has_been_shutdown
)
201 DVLOG(1) << "ProfileSigninConfirmationHelper: profile is not new";
202 return has_been_shutdown
;
206 bool HasSyncedExtensions(Profile
* profile
) {
207 #if defined(ENABLE_EXTENSIONS)
208 extensions::ExtensionRegistry
* registry
=
209 extensions::ExtensionRegistry::Get(profile
);
211 for (const scoped_refptr
<const extensions::Extension
>& extension
:
212 registry
->enabled_extensions()) {
213 // The webstore is synced so that it stays put on the new tab
214 // page, but since it's installed by default we don't want to
215 // consider it when determining if the profile is dirty.
216 if (extensions::sync_helper::IsSyncable(extension
.get()) &&
217 !extensions::sync_helper::IsSyncableComponentExtension(
219 DVLOG(1) << "ProfileSigninConfirmationHelper: "
220 << "profile contains a synced extension: " << extension
->id();
229 void CheckShouldPromptForNewProfile(
231 const base::Callback
<void(bool)>& return_result
) {
232 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
234 if (HasBeenShutdown(profile
) ||
235 HasBookmarks(profile
) ||
236 HasSyncedExtensions(profile
)) {
237 return_result
.Run(true);
240 // Fire asynchronous queries for profile data.
241 ProfileSigninConfirmationHelper
* helper
=
242 new ProfileSigninConfirmationHelper(profile
, return_result
);
243 helper
->CheckHasHistory(kHistoryEntriesBeforeNewProfilePrompt
);
244 helper
->CheckHasTypedURLs();