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