Home | History | Annotate | Download | only in profile_resetter
      1 // Copyright (c) 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/profile_resetter/resettable_settings_snapshot.h"
      6 
      7 #include "base/json/json_writer.h"
      8 #include "base/prefs/pref_service.h"
      9 #include "base/strings/string_util.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "base/synchronization/cancellation_flag.h"
     12 #include "base/values.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/search_engines/template_url_service_factory.h"
     16 #include "chrome/common/chrome_content_client.h"
     17 #include "chrome/common/chrome_version_info.h"
     18 #include "chrome/common/pref_names.h"
     19 #include "chrome/grit/generated_resources.h"
     20 #include "chrome/grit/google_chrome_strings.h"
     21 #include "components/feedback/feedback_data.h"
     22 #include "components/feedback/feedback_util.h"
     23 #include "components/search_engines/template_url_service.h"
     24 #include "content/public/browser/browser_thread.h"
     25 #include "extensions/browser/extension_registry.h"
     26 #include "ui/base/l10n/l10n_util.h"
     27 
     28 using feedback::FeedbackData;
     29 
     30 namespace {
     31 
     32 // Feedback bucket labels.
     33 const char kProfileResetPromptBucket[] = "SamplingOfSettingsResetPrompt";
     34 const char kProfileResetWebUIBucket[] = "ProfileResetReport";
     35 
     36 // Dictionary keys for feedback report.
     37 const char kDefaultSearchEnginePath[] = "default_search_engine";
     38 const char kEnabledExtensions[] = "enabled_extensions";
     39 const char kHomepageIsNewTabPage[] = "homepage_is_ntp";
     40 const char kHomepagePath[] = "homepage";
     41 const char kShortcuts[] = "shortcuts";
     42 const char kShowHomeButton[] = "show_home_button";
     43 const char kStartupTypePath[] = "startup_type";
     44 const char kStartupURLPath[] = "startup_urls";
     45 
     46 template <class StringType>
     47 void AddPair(base::ListValue* list,
     48              const base::string16& key,
     49              const StringType& value) {
     50   base::DictionaryValue* results = new base::DictionaryValue();
     51   results->SetString("key", key);
     52   results->SetString("value", value);
     53   list->Append(results);
     54 }
     55 
     56 }  // namespace
     57 
     58 ResettableSettingsSnapshot::ResettableSettingsSnapshot(
     59     Profile* profile)
     60     : startup_(SessionStartupPref::GetStartupPref(profile)),
     61       shortcuts_determined_(false),
     62       weak_ptr_factory_(this) {
     63   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     64   // URLs are always stored sorted.
     65   std::sort(startup_.urls.begin(), startup_.urls.end());
     66 
     67   PrefService* prefs = profile->GetPrefs();
     68   DCHECK(prefs);
     69   homepage_ = prefs->GetString(prefs::kHomePage);
     70   homepage_is_ntp_ = prefs->GetBoolean(prefs::kHomePageIsNewTabPage);
     71   show_home_button_ = prefs->GetBoolean(prefs::kShowHomeButton);
     72 
     73   TemplateURLService* service =
     74       TemplateURLServiceFactory::GetForProfile(profile);
     75   DCHECK(service);
     76   TemplateURL* dse = service->GetDefaultSearchProvider();
     77   if (dse)
     78     dse_url_ = dse->url();
     79 
     80   const extensions::ExtensionSet& enabled_ext =
     81       extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
     82   enabled_extensions_.reserve(enabled_ext.size());
     83 
     84   for (extensions::ExtensionSet::const_iterator it = enabled_ext.begin();
     85        it != enabled_ext.end(); ++it)
     86     enabled_extensions_.push_back(std::make_pair((*it)->id(), (*it)->name()));
     87 
     88   // ExtensionSet is sorted but it seems to be an implementation detail.
     89   std::sort(enabled_extensions_.begin(), enabled_extensions_.end());
     90 }
     91 
     92 ResettableSettingsSnapshot::~ResettableSettingsSnapshot() {
     93   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     94   if (cancellation_flag_.get())
     95     cancellation_flag_->data.Set();
     96 }
     97 
     98 void ResettableSettingsSnapshot::Subtract(
     99     const ResettableSettingsSnapshot& snapshot) {
    100   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    101   ExtensionList extensions = base::STLSetDifference<ExtensionList>(
    102       enabled_extensions_, snapshot.enabled_extensions_);
    103   enabled_extensions_.swap(extensions);
    104 }
    105 
    106 int ResettableSettingsSnapshot::FindDifferentFields(
    107     const ResettableSettingsSnapshot& snapshot) const {
    108   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    109   int bit_mask = 0;
    110 
    111   if (startup_.type != snapshot.startup_.type ||
    112       startup_.urls != snapshot.startup_.urls)
    113     bit_mask |= STARTUP_MODE;
    114 
    115   if (homepage_is_ntp_ != snapshot.homepage_is_ntp_ ||
    116       homepage_ != snapshot.homepage_ ||
    117       show_home_button_ != snapshot.show_home_button_)
    118     bit_mask |= HOMEPAGE;
    119 
    120   if (dse_url_ != snapshot.dse_url_)
    121     bit_mask |= DSE_URL;
    122 
    123   if (enabled_extensions_ != snapshot.enabled_extensions_)
    124     bit_mask |= EXTENSIONS;
    125 
    126   if (shortcuts_ != snapshot.shortcuts_)
    127     bit_mask |= SHORTCUTS;
    128 
    129   COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31,
    130                  add_new_field_here);
    131 
    132   return bit_mask;
    133 }
    134 
    135 void ResettableSettingsSnapshot::RequestShortcuts(
    136     const base::Closure& callback) {
    137   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    138   DCHECK(!cancellation_flag_.get() && !shortcuts_determined());
    139 
    140   cancellation_flag_ = new SharedCancellationFlag;
    141   content::BrowserThread::PostTaskAndReplyWithResult(
    142       content::BrowserThread::FILE,
    143       FROM_HERE,
    144       base::Bind(&GetChromeLaunchShortcuts, cancellation_flag_),
    145       base::Bind(&ResettableSettingsSnapshot::SetShortcutsAndReport,
    146                  weak_ptr_factory_.GetWeakPtr(),
    147                  callback));
    148 }
    149 
    150 void ResettableSettingsSnapshot::SetShortcutsAndReport(
    151     const base::Closure& callback,
    152     const std::vector<ShortcutCommand>& shortcuts) {
    153   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    154   shortcuts_ = shortcuts;
    155   shortcuts_determined_ = true;
    156   cancellation_flag_ = NULL;
    157 
    158   if (!callback.is_null())
    159     callback.Run();
    160 }
    161 
    162 std::string SerializeSettingsReport(const ResettableSettingsSnapshot& snapshot,
    163                                     int field_mask) {
    164   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    165   base::DictionaryValue dict;
    166 
    167   if (field_mask & ResettableSettingsSnapshot::STARTUP_MODE) {
    168     base::ListValue* list = new base::ListValue;
    169     const std::vector<GURL>& urls = snapshot.startup_urls();
    170     for (std::vector<GURL>::const_iterator i = urls.begin();
    171          i != urls.end(); ++i)
    172       list->AppendString(i->spec());
    173     dict.Set(kStartupURLPath, list);
    174     dict.SetInteger(kStartupTypePath, snapshot.startup_type());
    175   }
    176 
    177   if (field_mask & ResettableSettingsSnapshot::HOMEPAGE) {
    178     dict.SetString(kHomepagePath, snapshot.homepage());
    179     dict.SetBoolean(kHomepageIsNewTabPage, snapshot.homepage_is_ntp());
    180     dict.SetBoolean(kShowHomeButton, snapshot.show_home_button());
    181   }
    182 
    183   if (field_mask & ResettableSettingsSnapshot::DSE_URL)
    184     dict.SetString(kDefaultSearchEnginePath, snapshot.dse_url());
    185 
    186   if (field_mask & ResettableSettingsSnapshot::EXTENSIONS) {
    187     base::ListValue* list = new base::ListValue;
    188     const ResettableSettingsSnapshot::ExtensionList& extensions =
    189         snapshot.enabled_extensions();
    190     for (ResettableSettingsSnapshot::ExtensionList::const_iterator i =
    191          extensions.begin(); i != extensions.end(); ++i) {
    192       // Replace "\"" to simplify server-side analysis.
    193       std::string ext_name;
    194       base::ReplaceChars(i->second, "\"", "\'", &ext_name);
    195       list->AppendString(i->first + ";" + ext_name);
    196     }
    197     dict.Set(kEnabledExtensions, list);
    198   }
    199 
    200   if (field_mask & ResettableSettingsSnapshot::SHORTCUTS) {
    201     base::ListValue* list = new base::ListValue;
    202     const std::vector<ShortcutCommand>& shortcuts = snapshot.shortcuts();
    203     for (std::vector<ShortcutCommand>::const_iterator i = shortcuts.begin();
    204          i != shortcuts.end(); ++i) {
    205       base::string16 arguments;
    206       // Replace "\"" to simplify server-side analysis.
    207       base::ReplaceChars(i->second, base::ASCIIToUTF16("\""),
    208                          base::ASCIIToUTF16("\'"), &arguments);
    209       list->AppendString(arguments);
    210     }
    211     dict.Set(kShortcuts, list);
    212   }
    213 
    214   COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31,
    215                  serialize_new_field_here);
    216 
    217   std::string json;
    218   base::JSONWriter::Write(&dict, &json);
    219   return json;
    220 }
    221 
    222 void SendSettingsFeedback(const std::string& report,
    223                           Profile* profile,
    224                           SnapshotCaller caller) {
    225   scoped_refptr<FeedbackData> feedback_data = new FeedbackData();
    226   std::string bucket;
    227   switch (caller) {
    228     case PROFILE_RESET_WEBUI:
    229       bucket = kProfileResetWebUIBucket;
    230       break;
    231     case PROFILE_RESET_PROMPT:
    232       bucket = kProfileResetPromptBucket;
    233       break;
    234   }
    235   feedback_data->set_category_tag(bucket);
    236   feedback_data->set_description(report);
    237 
    238   feedback_data->set_image(make_scoped_ptr(new std::string));
    239   feedback_data->set_context(profile);
    240 
    241   feedback_data->set_page_url("");
    242   feedback_data->set_user_email("");
    243 
    244   feedback_util::SendReport(feedback_data);
    245 }
    246 
    247 scoped_ptr<base::ListValue> GetReadableFeedbackForSnapshot(
    248     Profile* profile,
    249     const ResettableSettingsSnapshot& snapshot) {
    250   DCHECK(profile);
    251   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    252   scoped_ptr<base::ListValue> list(new base::ListValue);
    253   AddPair(list.get(),
    254           l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE),
    255           g_browser_process->GetApplicationLocale());
    256   AddPair(list.get(),
    257           l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT),
    258           GetUserAgent());
    259   chrome::VersionInfo version_info;
    260   std::string version = version_info.Version();
    261   version += chrome::VersionInfo::GetVersionStringModifier();
    262   AddPair(list.get(),
    263           l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
    264           version);
    265 
    266   // Add snapshot data.
    267   const std::vector<GURL>& urls = snapshot.startup_urls();
    268   std::string startup_urls;
    269   for (std::vector<GURL>::const_iterator i = urls.begin();
    270        i != urls.end(); ++i) {
    271     if (!startup_urls.empty())
    272       startup_urls += ' ';
    273     startup_urls += i->host();
    274   }
    275   if (!startup_urls.empty()) {
    276     AddPair(list.get(),
    277             l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS),
    278             startup_urls);
    279   }
    280 
    281   base::string16 startup_type;
    282   switch (snapshot.startup_type()) {
    283     case SessionStartupPref::DEFAULT:
    284       startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB);
    285       break;
    286     case SessionStartupPref::LAST:
    287       startup_type = l10n_util::GetStringUTF16(
    288           IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION);
    289       break;
    290     case SessionStartupPref::URLS:
    291       startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES);
    292       break;
    293     default:
    294       break;
    295   }
    296   AddPair(list.get(),
    297           l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE),
    298           startup_type);
    299 
    300   if (!snapshot.homepage().empty()) {
    301     AddPair(list.get(),
    302             l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE),
    303             snapshot.homepage());
    304   }
    305 
    306   int is_ntp_message_id = snapshot.homepage_is_ntp() ?
    307       IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE :
    308       IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE;
    309   AddPair(list.get(),
    310           l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP),
    311           l10n_util::GetStringUTF16(is_ntp_message_id));
    312 
    313   int show_home_button_id = snapshot.show_home_button() ?
    314       IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON_TRUE :
    315       IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON_FALSE;
    316   AddPair(
    317       list.get(),
    318       l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON),
    319       l10n_util::GetStringUTF16(show_home_button_id));
    320 
    321   TemplateURLService* service =
    322       TemplateURLServiceFactory::GetForProfile(profile);
    323   DCHECK(service);
    324   TemplateURL* dse = service->GetDefaultSearchProvider();
    325   if (dse) {
    326     AddPair(list.get(),
    327             l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE),
    328             dse->GenerateSearchURL(service->search_terms_data()).host());
    329   }
    330 
    331   if (snapshot.shortcuts_determined()) {
    332     base::string16 shortcut_targets;
    333     const std::vector<ShortcutCommand>& shortcuts = snapshot.shortcuts();
    334     for (std::vector<ShortcutCommand>::const_iterator i =
    335          shortcuts.begin(); i != shortcuts.end(); ++i) {
    336       if (!shortcut_targets.empty())
    337         shortcut_targets += base::ASCIIToUTF16("\n");
    338       shortcut_targets += base::ASCIIToUTF16("chrome.exe ");
    339       shortcut_targets += i->second;
    340     }
    341     if (!shortcut_targets.empty()) {
    342       AddPair(list.get(),
    343               l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS),
    344               shortcut_targets);
    345     }
    346   } else {
    347     AddPair(list.get(),
    348             l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS),
    349             l10n_util::GetStringUTF16(
    350                 IDS_RESET_PROFILE_SETTINGS_PROCESSING_SHORTCUTS));
    351   }
    352 
    353   const ResettableSettingsSnapshot::ExtensionList& extensions =
    354       snapshot.enabled_extensions();
    355   std::string extension_names;
    356   for (ResettableSettingsSnapshot::ExtensionList::const_iterator i =
    357        extensions.begin(); i != extensions.end(); ++i) {
    358     if (!extension_names.empty())
    359       extension_names += '\n';
    360     extension_names += i->second;
    361   }
    362   if (!extension_names.empty()) {
    363     AddPair(list.get(),
    364             l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS),
    365             extension_names);
    366   }
    367   return list.Pass();
    368 }
    369