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. 4 5 #include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h" 6 7 #include "base/bind.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_factory.h" 12 #include "chrome/browser/common/cancelable_request.h" 13 #include "chrome/browser/extensions/extension_service.h" 14 #include "chrome/browser/history/history_backend.h" 15 #include "chrome/browser/history/history_db_task.h" 16 #include "chrome/browser/history/history_service.h" 17 #include "chrome/browser/history/history_service_factory.h" 18 #include "chrome/browser/history/history_types.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/common/extensions/extension_constants.h" 21 #include "chrome/common/extensions/sync_helper.h" 22 #include "components/bookmarks/browser/bookmark_model.h" 23 #include "extensions/browser/extension_system.h" 24 #include "extensions/common/extension.h" 25 #include "extensions/common/extension_set.h" 26 #include "ui/gfx/color_utils.h" 27 #include "ui/native_theme/native_theme.h" 28 29 namespace { 30 31 const int kHistoryEntriesBeforeNewProfilePrompt = 10; 32 33 // Determines whether a profile has any typed URLs in its history. 34 class HasTypedURLsTask : public history::HistoryDBTask { 35 public: 36 HasTypedURLsTask(const base::Callback<void(bool)>& cb) 37 : has_typed_urls_(false), cb_(cb) { 38 } 39 40 virtual bool RunOnDBThread(history::HistoryBackend* backend, 41 history::HistoryDatabase* db) OVERRIDE { 42 history::URLRows rows; 43 backend->GetAllTypedURLs(&rows); 44 if (!rows.empty()) { 45 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains " 46 << rows.size() << " typed URLs"; 47 has_typed_urls_ = true; 48 } 49 return true; 50 } 51 52 virtual void DoneRunOnMainThread() OVERRIDE { 53 cb_.Run(has_typed_urls_); 54 } 55 56 private: 57 virtual ~HasTypedURLsTask() {} 58 bool has_typed_urls_; 59 base::Callback<void(bool)> cb_; 60 }; 61 62 bool HasBookmarks(Profile* profile) { 63 BookmarkModel* bookmarks = BookmarkModelFactory::GetForProfile(profile); 64 bool has_bookmarks = bookmarks && bookmarks->HasBookmarks(); 65 if (has_bookmarks) 66 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains bookmarks"; 67 return has_bookmarks; 68 } 69 70 // Helper functions for Chrome profile signin. 71 class ProfileSigninConfirmationHelper 72 : public base::RefCounted<ProfileSigninConfirmationHelper> { 73 public: 74 ProfileSigninConfirmationHelper( 75 Profile* profile, 76 const base::Callback<void(bool)>& return_result); 77 void CheckHasHistory(int max_entries); 78 void CheckHasTypedURLs(); 79 void set_pending_requests(int requests); 80 81 private: 82 friend class base::RefCounted<ProfileSigninConfirmationHelper>; 83 84 ~ProfileSigninConfirmationHelper(); 85 86 void OnHistoryQueryResults(size_t max_entries, 87 CancelableRequestProvider::Handle handle, 88 history::QueryResults* results); 89 void ReturnResult(bool result); 90 91 // Weak pointer to the profile being signed-in. 92 Profile* profile_; 93 94 // Used for async tasks. 95 CancelableRequestConsumer request_consumer_; 96 97 // Keep track of how many async requests are pending. 98 int pending_requests_; 99 100 // Indicates whether the result has already been returned to caller. 101 bool result_returned_; 102 103 // Callback to pass the result back to the caller. 104 const base::Callback<void(bool)> return_result_; 105 106 DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationHelper); 107 }; 108 109 ProfileSigninConfirmationHelper::ProfileSigninConfirmationHelper( 110 Profile* profile, 111 const base::Callback<void(bool)>& return_result) 112 : profile_(profile), 113 pending_requests_(0), 114 result_returned_(false), 115 return_result_(return_result) { 116 } 117 118 ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() { 119 } 120 121 void ProfileSigninConfirmationHelper::OnHistoryQueryResults( 122 size_t max_entries, 123 CancelableRequestProvider::Handle handle, 124 history::QueryResults* results) { 125 history::QueryResults owned_results; 126 results->Swap(&owned_results); 127 bool too_much_history = owned_results.size() >= max_entries; 128 if (too_much_history) { 129 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains " 130 << owned_results.size() << " history entries"; 131 } 132 ReturnResult(too_much_history); 133 } 134 135 void ProfileSigninConfirmationHelper::CheckHasHistory(int max_entries) { 136 HistoryService* service = 137 HistoryServiceFactory::GetForProfileWithoutCreating(profile_); 138 if (!service) { 139 ReturnResult(false); 140 return; 141 } 142 history::QueryOptions opts; 143 opts.max_count = max_entries; 144 service->QueryHistory( 145 base::string16(), opts, &request_consumer_, 146 base::Bind(&ProfileSigninConfirmationHelper::OnHistoryQueryResults, 147 this, 148 max_entries)); 149 } 150 151 void ProfileSigninConfirmationHelper::CheckHasTypedURLs() { 152 HistoryService* service = 153 HistoryServiceFactory::GetForProfileWithoutCreating(profile_); 154 if (!service) { 155 ReturnResult(false); 156 return; 157 } 158 service->ScheduleDBTask( 159 new HasTypedURLsTask( 160 base::Bind( 161 &ProfileSigninConfirmationHelper::ReturnResult, 162 this)), 163 &request_consumer_); 164 } 165 166 void ProfileSigninConfirmationHelper::set_pending_requests(int requests) { 167 pending_requests_ = requests; 168 } 169 170 void ProfileSigninConfirmationHelper::ReturnResult(bool result) { 171 // Pass |true| into the callback as soon as one of the tasks passes a 172 // result of |true|, otherwise pass the last returned result. 173 if (!result_returned_ && (--pending_requests_ == 0 || result)) { 174 result_returned_ = true; 175 request_consumer_.CancelAllRequests(); 176 return_result_.Run(result); 177 } 178 } 179 180 } // namespace 181 182 namespace ui { 183 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); 189 } 190 191 bool HasBeenShutdown(Profile* profile) { 192 bool has_been_shutdown = !profile->IsNewProfile(); 193 if (has_been_shutdown) 194 DVLOG(1) << "ProfileSigninConfirmationHelper: profile is not new"; 195 return has_been_shutdown; 196 } 197 198 bool HasSyncedExtensions(Profile* profile) { 199 extensions::ExtensionSystem* system = 200 extensions::ExtensionSystem::Get(profile); 201 if (system && system->extension_service()) { 202 const extensions::ExtensionSet* extensions = 203 system->extension_service()->extensions(); 204 for (extensions::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 DVLOG(1) << "ProfileSigninConfirmationHelper: " 213 << "profile contains a synced extension: " << (*iter)->id(); 214 return true; 215 } 216 } 217 } 218 return false; 219 } 220 221 void CheckShouldPromptForNewProfile( 222 Profile* profile, 223 const base::Callback<void(bool)>& return_result) { 224 if (HasBeenShutdown(profile) || 225 HasBookmarks(profile) || 226 HasSyncedExtensions(profile)) { 227 return_result.Run(true); 228 return; 229 } 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(); 237 } 238 239 } // namespace ui 240