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.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