| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h" |
| |
| #include "base/bind.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/strings/string16.h" |
| #include "base/task/cancelable_task_tracker.h" |
| #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| #include "chrome/browser/common/cancelable_request.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/history/history_backend.h" |
| #include "chrome/browser/history/history_db_task.h" |
| #include "chrome/browser/history/history_service.h" |
| #include "chrome/browser/history/history_service_factory.h" |
| #include "chrome/browser/history/history_types.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/extensions/sync_helper.h" |
| #include "components/bookmarks/browser/bookmark_model.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_set.h" |
| #include "ui/gfx/color_utils.h" |
| #include "ui/native_theme/native_theme.h" |
| |
| namespace { |
| |
| const int kHistoryEntriesBeforeNewProfilePrompt = 10; |
| |
| // Determines whether a profile has any typed URLs in its history. |
| class HasTypedURLsTask : public history::HistoryDBTask { |
| public: |
| HasTypedURLsTask(const base::Callback<void(bool)>& cb) |
| : has_typed_urls_(false), cb_(cb) { |
| } |
| |
| virtual bool RunOnDBThread(history::HistoryBackend* backend, |
| history::HistoryDatabase* db) OVERRIDE { |
| history::URLRows rows; |
| backend->GetAllTypedURLs(&rows); |
| if (!rows.empty()) { |
| DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains " |
| << rows.size() << " typed URLs"; |
| has_typed_urls_ = true; |
| } |
| return true; |
| } |
| |
| virtual void DoneRunOnMainThread() OVERRIDE { |
| cb_.Run(has_typed_urls_); |
| } |
| |
| private: |
| virtual ~HasTypedURLsTask() {} |
| |
| bool has_typed_urls_; |
| base::Callback<void(bool)> cb_; |
| }; |
| |
| bool HasBookmarks(Profile* profile) { |
| BookmarkModel* bookmarks = BookmarkModelFactory::GetForProfile(profile); |
| bool has_bookmarks = bookmarks && bookmarks->HasBookmarks(); |
| if (has_bookmarks) |
| DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains bookmarks"; |
| return has_bookmarks; |
| } |
| |
| // Helper functions for Chrome profile signin. |
| class ProfileSigninConfirmationHelper { |
| public: |
| ProfileSigninConfirmationHelper( |
| Profile* profile, |
| const base::Callback<void(bool)>& return_result); |
| void CheckHasHistory(int max_entries); |
| void CheckHasTypedURLs(); |
| |
| private: |
| // Deletes itself. |
| ~ProfileSigninConfirmationHelper(); |
| |
| void OnHistoryQueryResults(size_t max_entries, |
| history::QueryResults* results); |
| void ReturnResult(bool result); |
| |
| // Weak pointer to the profile being signed-in. |
| Profile* profile_; |
| |
| // Used for async tasks. |
| base::CancelableTaskTracker task_tracker_; |
| |
| // Keep track of how many async requests are pending. |
| int pending_requests_; |
| |
| // Callback to pass the result back to the caller. |
| const base::Callback<void(bool)> return_result_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationHelper); |
| }; |
| |
| ProfileSigninConfirmationHelper::ProfileSigninConfirmationHelper( |
| Profile* profile, |
| const base::Callback<void(bool)>& return_result) |
| : profile_(profile), |
| pending_requests_(0), |
| return_result_(return_result) { |
| } |
| |
| ProfileSigninConfirmationHelper::~ProfileSigninConfirmationHelper() { |
| DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| } |
| |
| void ProfileSigninConfirmationHelper::OnHistoryQueryResults( |
| size_t max_entries, |
| history::QueryResults* results) { |
| history::QueryResults owned_results; |
| results->Swap(&owned_results); |
| bool too_much_history = owned_results.size() >= max_entries; |
| if (too_much_history) { |
| DVLOG(1) << "ProfileSigninConfirmationHelper: profile contains " |
| << owned_results.size() << " history entries"; |
| } |
| ReturnResult(too_much_history); |
| } |
| |
| void ProfileSigninConfirmationHelper::CheckHasHistory(int max_entries) { |
| HistoryService* service = |
| HistoryServiceFactory::GetForProfileWithoutCreating(profile_); |
| pending_requests_++; |
| if (!service) { |
| ReturnResult(false); |
| return; |
| } |
| history::QueryOptions opts; |
| opts.max_count = max_entries; |
| service->QueryHistory( |
| base::string16(), |
| opts, |
| base::Bind(&ProfileSigninConfirmationHelper::OnHistoryQueryResults, |
| base::Unretained(this), |
| max_entries), |
| &task_tracker_); |
| } |
| |
| void ProfileSigninConfirmationHelper::CheckHasTypedURLs() { |
| HistoryService* service = |
| HistoryServiceFactory::GetForProfileWithoutCreating(profile_); |
| pending_requests_++; |
| if (!service) { |
| ReturnResult(false); |
| return; |
| } |
| service->ScheduleDBTask( |
| new HasTypedURLsTask( |
| base::Bind(&ProfileSigninConfirmationHelper::ReturnResult, |
| base::Unretained(this))), |
| &task_tracker_); |
| } |
| |
| void ProfileSigninConfirmationHelper::ReturnResult(bool result) { |
| DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| // Pass |true| into the callback as soon as one of the tasks passes a |
| // result of |true|, otherwise pass the last returned result. |
| if (--pending_requests_ == 0 || result) { |
| return_result_.Run(result); |
| |
| // This leaks at shutdown if the HistoryService is destroyed, but |
| // the process is going to die anyway. |
| delete this; |
| } |
| } |
| |
| } // namespace |
| |
| namespace ui { |
| |
| SkColor GetSigninConfirmationPromptBarColor(SkAlpha alpha) { |
| static const SkColor kBackgroundColor = |
| ui::NativeTheme::instance()->GetSystemColor( |
| ui::NativeTheme::kColorId_DialogBackground); |
| return color_utils::BlendTowardOppositeLuminance(kBackgroundColor, alpha); |
| } |
| |
| bool HasBeenShutdown(Profile* profile) { |
| bool has_been_shutdown = !profile->IsNewProfile(); |
| if (has_been_shutdown) |
| DVLOG(1) << "ProfileSigninConfirmationHelper: profile is not new"; |
| return has_been_shutdown; |
| } |
| |
| bool HasSyncedExtensions(Profile* profile) { |
| extensions::ExtensionSystem* system = |
| extensions::ExtensionSystem::Get(profile); |
| if (system && system->extension_service()) { |
| const extensions::ExtensionSet* extensions = |
| system->extension_service()->extensions(); |
| for (extensions::ExtensionSet::const_iterator iter = extensions->begin(); |
| iter != extensions->end(); ++iter) { |
| // The webstore is synced so that it stays put on the new tab |
| // page, but since it's installed by default we don't want to |
| // consider it when determining if the profile is dirty. |
| if (extensions::sync_helper::IsSyncable(iter->get()) && |
| (*iter)->id() != extension_misc::kWebStoreAppId && |
| (*iter)->id() != extension_misc::kChromeAppId) { |
| DVLOG(1) << "ProfileSigninConfirmationHelper: " |
| << "profile contains a synced extension: " << (*iter)->id(); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| void CheckShouldPromptForNewProfile( |
| Profile* profile, |
| const base::Callback<void(bool)>& return_result) { |
| DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| |
| if (HasBeenShutdown(profile) || |
| HasBookmarks(profile) || |
| HasSyncedExtensions(profile)) { |
| return_result.Run(true); |
| return; |
| } |
| // Fire asynchronous queries for profile data. |
| ProfileSigninConfirmationHelper* helper = |
| new ProfileSigninConfirmationHelper(profile, return_result); |
| helper->CheckHasHistory(kHistoryEntriesBeforeNewProfilePrompt); |
| helper->CheckHasTypedURLs(); |
| } |
| |
| } // namespace ui |