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