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 "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" 23 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" 32 #endif 33 34 namespace { 35 36 const int kHistoryEntriesBeforeNewProfilePrompt = 10; 37 38 // Determines whether a profile has any typed URLs in its history. 39 class HasTypedURLsTask : public history::HistoryDBTask { 40 public: 41 explicit HasTypedURLsTask(const base::Callback<void(bool)>& cb) 42 : has_typed_urls_(false), cb_(cb) { 43 } 44 45 virtual bool RunOnDBThread(history::HistoryBackend* backend, 46 history::HistoryDatabase* db) OVERRIDE { 47 history::URLRows rows; 48 backend->GetAllTypedURLs(&rows); 49 if (!rows.empty()) { 50 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains " 51 << rows.size() << " typed URLs"; 52 has_typed_urls_ = true; 53 } 54 return true; 55 } 56 57 virtual void DoneRunOnMainThread() OVERRIDE { 58 cb_.Run(has_typed_urls_); 59 } 60 61 private: 62 virtual ~HasTypedURLsTask() {} 63 64 bool has_typed_urls_; 65 base::Callback<void(bool)> cb_; 66 }; 67 68 bool HasBookmarks(Profile* profile) { 69 BookmarkModel* bookmarks = BookmarkModelFactory::GetForProfile(profile); 70 bool has_bookmarks = bookmarks && bookmarks->HasBookmarks(); 71 if (has_bookmarks) 72 DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains bookmarks"; 73 return has_bookmarks; 74 } 75 76 // Helper functions for Chrome profile signin. 77 class ProfileSigninConfirmationHelper { 78 public: 79 ProfileSigninConfirmationHelper( 80 Profile* profile, 81 const base::Callback<void(bool)>& return_result); 82 void CheckHasHistory(int max_entries); 83 void CheckHasTypedURLs(); 84 85 private: 86 // Deletes itself. 87 ~ProfileSigninConfirmationHelper(); 88 89 void OnHistoryQueryResults(size_t max_entries, 90 history::QueryResults* results); 91 void ReturnResult(bool result); 92 93 // Weak pointer to the profile being signed-in. 94 Profile* profile_; 95 96 // Used for async tasks. 97 base::CancelableTaskTracker task_tracker_; 98 99 // Keep track of how many async requests are pending. 100 int pending_requests_; 101 102 // Callback to pass the result back to the caller. 103 const base::Callback<void(bool)> return_result_; 104 105 DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationHelper); 106 }; 107 108 ProfileSigninConfirmationHelper::ProfileSigninConfirmationHelper( 109 Profile* profile, 110 const base::Callback<void(bool)>& return_result) 111 : profile_(profile), 112 pending_requests_(0), 113 return_result_(return_result) { 114 } 115 116 ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() { 117 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 118 } 119 120 void ProfileSigninConfirmationHelper::OnHistoryQueryResults( 121 size_t max_entries, 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"; 129 } 130 ReturnResult(too_much_history); 131 } 132 133 void ProfileSigninConfirmationHelper::CheckHasHistory(int max_entries) { 134 HistoryService* service = 135 HistoryServiceFactory::GetForProfileWithoutCreating(profile_); 136 pending_requests_++; 137 if (!service) { 138 ReturnResult(false); 139 return; 140 } 141 history::QueryOptions opts; 142 opts.max_count = max_entries; 143 service->QueryHistory( 144 base::string16(), 145 opts, 146 base::Bind(&ProfileSigninConfirmationHelper::OnHistoryQueryResults, 147 base::Unretained(this), 148 max_entries), 149 &task_tracker_); 150 } 151 152 void ProfileSigninConfirmationHelper::CheckHasTypedURLs() { 153 HistoryService* service = 154 HistoryServiceFactory::GetForProfileWithoutCreating(profile_); 155 pending_requests_++; 156 if (!service) { 157 ReturnResult(false); 158 return; 159 } 160 service->ScheduleDBTask( 161 scoped_ptr<history::HistoryDBTask>(new HasTypedURLsTask( 162 base::Bind(&ProfileSigninConfirmationHelper::ReturnResult, 163 base::Unretained(this)))), 164 &task_tracker_); 165 } 166 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); 173 174 // This leaks at shutdown if the HistoryService is destroyed, but 175 // the process is going to die anyway. 176 delete this; 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 #if defined(OS_IOS) 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. 198 return false; 199 #else 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; 204 #endif 205 } 206 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(); 224 return true; 225 } 226 } 227 } 228 #endif 229 return false; 230 } 231 232 void CheckShouldPromptForNewProfile( 233 Profile* profile, 234 const base::Callback<void(bool)>& return_result) { 235 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 236 237 if (HasBeenShutdown(profile) || 238 HasBookmarks(profile) || 239 HasSyncedExtensions(profile)) { 240 return_result.Run(true); 241 return; 242 } 243 // Fire asynchronous queries for profile data. 244 ProfileSigninConfirmationHelper* helper = 245 new ProfileSigninConfirmationHelper(profile, return_result); 246 helper->CheckHasHistory(kHistoryEntriesBeforeNewProfilePrompt); 247 helper->CheckHasTypedURLs(); 248 } 249 250 } // namespace ui 251