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 34namespace { 35 36const int kHistoryEntriesBeforeNewProfilePrompt = 10; 37 38// Determines whether a profile has any typed URLs in its history. 39class 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 68bool 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. 77class 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 108ProfileSigninConfirmationHelper::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 116ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() { 117 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 118} 119 120void 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 133void 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 152void 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 167void 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 182namespace ui { 183 184SkColor 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 191bool 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 207bool 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 232void 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