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.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" 27 28 // TODO(dconnelly): change VLOG to DVLOG (crbug.com/240195) 29 30 namespace { 31 32 const int kHistoryEntriesBeforeNewProfilePrompt = 10; 33 34 // Determines whether a profile has any typed URLs in its history. 35 class HasTypedURLsTask : public history::HistoryDBTask { 36 public: 37 HasTypedURLsTask(const base::Callback<void(bool)>& cb) 38 : has_typed_urls_(false), cb_(cb) { 39 } 40 41 virtual bool RunOnDBThread(history::HistoryBackend* backend, 42 history::HistoryDatabase* db) OVERRIDE { 43 history::URLRows rows; 44 backend->GetAllTypedURLs(&rows); 45 if (!rows.empty()) { 46 VLOG(1) << "ProfileSigninConfirmationHelper: profile contains " 47 << rows.size() << " typed URLs"; 48 has_typed_urls_ = true; 49 } 50 return true; 51 } 52 53 virtual void DoneRunOnMainThread() OVERRIDE { 54 cb_.Run(has_typed_urls_); 55 } 56 57 private: 58 virtual ~HasTypedURLsTask() {} 59 bool has_typed_urls_; 60 base::Callback<void(bool)> cb_; 61 }; 62 63 bool HasBookmarks(Profile* profile) { 64 BookmarkModel* bookmarks = BookmarkModelFactory::GetForProfile(profile); 65 bool has_bookmarks = bookmarks && bookmarks->HasBookmarks(); 66 if (has_bookmarks) 67 VLOG(1) << "ProfileSigninConfirmationHelper: profile contains bookmarks"; 68 return has_bookmarks; 69 } 70 71 // Helper functions for Chrome profile signin. 72 class ProfileSigninConfirmationHelper 73 : public base::RefCounted<ProfileSigninConfirmationHelper> { 74 public: 75 ProfileSigninConfirmationHelper( 76 Profile* profile, 77 const base::Callback<void(bool)>& return_result); 78 void CheckHasHistory(int max_entries); 79 void CheckHasTypedURLs(); 80 void set_pending_requests(int requests); 81 82 private: 83 friend class base::RefCounted<ProfileSigninConfirmationHelper>; 84 85 ~ProfileSigninConfirmationHelper(); 86 87 void OnHistoryQueryResults(size_t max_entries, 88 CancelableRequestProvider::Handle handle, 89 history::QueryResults* results); 90 void ReturnResult(bool result); 91 92 // Weak pointer to the profile being signed-in. 93 Profile* profile_; 94 95 // Used for async tasks. 96 CancelableRequestConsumer request_consumer_; 97 98 // Keep track of how many async requests are pending. 99 int pending_requests_; 100 101 // Indicates whether the result has already been returned to caller. 102 bool result_returned_; 103 104 // Callback to pass the result back to the caller. 105 const base::Callback<void(bool)> return_result_; 106 107 DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationHelper); 108 }; 109 110 ProfileSigninConfirmationHelper::ProfileSigninConfirmationHelper( 111 Profile* profile, 112 const base::Callback<void(bool)>& return_result) 113 : profile_(profile), 114 pending_requests_(0), 115 result_returned_(false), 116 return_result_(return_result) { 117 } 118 119 ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() { 120 } 121 122 void ProfileSigninConfirmationHelper::OnHistoryQueryResults( 123 size_t max_entries, 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"; 132 } 133 ReturnResult(too_much_history); 134 } 135 136 void ProfileSigninConfirmationHelper::CheckHasHistory(int max_entries) { 137 HistoryService* service = 138 HistoryServiceFactory::GetForProfileWithoutCreating(profile_); 139 if (!service) { 140 ReturnResult(false); 141 return; 142 } 143 history::QueryOptions opts; 144 opts.max_count = max_entries; 145 service->QueryHistory( 146 string16(), opts, &request_consumer_, 147 base::Bind(&ProfileSigninConfirmationHelper::OnHistoryQueryResults, 148 this, 149 max_entries)); 150 } 151 152 void ProfileSigninConfirmationHelper::CheckHasTypedURLs() { 153 HistoryService* service = 154 HistoryServiceFactory::GetForProfileWithoutCreating(profile_); 155 if (!service) { 156 ReturnResult(false); 157 return; 158 } 159 service->ScheduleDBTask( 160 new HasTypedURLsTask( 161 base::Bind( 162 &ProfileSigninConfirmationHelper::ReturnResult, 163 this)), 164 &request_consumer_); 165 } 166 167 void ProfileSigninConfirmationHelper::set_pending_requests(int requests) { 168 pending_requests_ = requests; 169 } 170 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); 178 } 179 } 180 181 } // namespace 182 183 namespace ui { 184 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); 190 } 191 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; 197 } 198 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(); 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