Home | History | Annotate | Download | only in sync
      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