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/profile_resetter.h"
6
7#include <string>
8
9#include "base/prefs/pref_service.h"
10#include "base/prefs/scoped_user_pref_update.h"
11#include "base/synchronization/cancellation_flag.h"
12#include "chrome/browser/browsing_data/browsing_data_helper.h"
13#include "chrome/browser/content_settings/host_content_settings_map.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/google/google_url_tracker_factory.h"
16#include "chrome/browser/profile_resetter/brandcoded_default_settings.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/search_engines/template_url_service_factory.h"
19#include "chrome/browser/ui/browser.h"
20#include "chrome/browser/ui/browser_iterator.h"
21#include "chrome/browser/ui/tabs/tab_strip_model.h"
22#include "chrome/common/pref_names.h"
23#include "chrome/installer/util/browser_distribution.h"
24#include "components/google/core/browser/google_pref_names.h"
25#include "components/google/core/browser/google_url_tracker.h"
26#include "components/search_engines/search_engines_pref_names.h"
27#include "components/search_engines/template_url_prepopulate_data.h"
28#include "components/search_engines/template_url_service.h"
29#include "content/public/browser/browser_thread.h"
30#include "extensions/browser/extension_system.h"
31#include "extensions/browser/management_policy.h"
32
33#if defined(OS_WIN)
34#include "base/base_paths.h"
35#include "base/path_service.h"
36#include "chrome/installer/util/shell_util.h"
37
38namespace {
39
40void ResetShortcutsOnFileThread() {
41  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
42  // Get full path of chrome.
43  base::FilePath chrome_exe;
44  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
45    return;
46  BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
47      BrowserDistribution::CHROME_BROWSER);
48  for (int location = ShellUtil::SHORTCUT_LOCATION_FIRST;
49       location < ShellUtil::NUM_SHORTCUT_LOCATIONS; ++location) {
50    ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
51        static_cast<ShellUtil::ShortcutLocation>(location),
52        dist,
53        ShellUtil::CURRENT_USER,
54        chrome_exe,
55        true,
56        NULL,
57        NULL);
58  }
59}
60
61}  // namespace
62#endif  // defined(OS_WIN)
63
64ProfileResetter::ProfileResetter(Profile* profile)
65    : profile_(profile),
66      template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_)),
67      pending_reset_flags_(0),
68      cookies_remover_(NULL),
69      weak_ptr_factory_(this) {
70  DCHECK(CalledOnValidThread());
71  DCHECK(profile_);
72}
73
74ProfileResetter::~ProfileResetter() {
75  if (cookies_remover_)
76    cookies_remover_->RemoveObserver(this);
77}
78
79void ProfileResetter::Reset(
80    ProfileResetter::ResettableFlags resettable_flags,
81    scoped_ptr<BrandcodedDefaultSettings> master_settings,
82    bool accepted_send_feedback,
83    const base::Closure& callback) {
84  DCHECK(CalledOnValidThread());
85  DCHECK(master_settings);
86
87  // We should never be called with unknown flags.
88  CHECK_EQ(static_cast<ResettableFlags>(0), resettable_flags & ~ALL);
89
90  // We should never be called when a previous reset has not finished.
91  CHECK_EQ(static_cast<ResettableFlags>(0), pending_reset_flags_);
92
93  if (!resettable_flags) {
94    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
95                                     callback);
96    return;
97  }
98
99  master_settings_.swap(master_settings);
100  callback_ = callback;
101
102  // These flags are set to false by the individual reset functions.
103  pending_reset_flags_ = resettable_flags;
104
105  struct {
106    Resettable flag;
107    void (ProfileResetter::*method)();
108  } flagToMethod[] = {
109    {DEFAULT_SEARCH_ENGINE, &ProfileResetter::ResetDefaultSearchEngine},
110    {HOMEPAGE, &ProfileResetter::ResetHomepage},
111    {CONTENT_SETTINGS, &ProfileResetter::ResetContentSettings},
112    {COOKIES_AND_SITE_DATA, &ProfileResetter::ResetCookiesAndSiteData},
113    {EXTENSIONS, &ProfileResetter::ResetExtensions},
114    {STARTUP_PAGES, &ProfileResetter::ResetStartupPages},
115    {PINNED_TABS, &ProfileResetter::ResetPinnedTabs},
116    {SHORTCUTS, &ProfileResetter::ResetShortcuts},
117  };
118
119  ResettableFlags reset_triggered_for_flags = 0;
120  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(flagToMethod); ++i) {
121    if (resettable_flags & flagToMethod[i].flag) {
122      reset_triggered_for_flags |= flagToMethod[i].flag;
123      (this->*flagToMethod[i].method)();
124    }
125  }
126
127  DCHECK_EQ(resettable_flags, reset_triggered_for_flags);
128}
129
130bool ProfileResetter::IsActive() const {
131  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
132  return pending_reset_flags_ != 0;
133}
134
135void ProfileResetter::MarkAsDone(Resettable resettable) {
136  DCHECK(CalledOnValidThread());
137
138  // Check that we are never called twice or unexpectedly.
139  CHECK(pending_reset_flags_ & resettable);
140
141  pending_reset_flags_ &= ~resettable;
142
143  if (!pending_reset_flags_) {
144    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
145                                     callback_);
146    callback_.Reset();
147    master_settings_.reset();
148    template_url_service_sub_.reset();
149  }
150}
151
152void ProfileResetter::ResetDefaultSearchEngine() {
153  DCHECK(CalledOnValidThread());
154  DCHECK(template_url_service_);
155  // If TemplateURLServiceFactory is ready we can clean it right now.
156  // Otherwise, load it and continue from ProfileResetter::Observe.
157  if (template_url_service_->loaded()) {
158    PrefService* prefs = profile_->GetPrefs();
159    DCHECK(prefs);
160    TemplateURLPrepopulateData::ClearPrepopulatedEnginesInPrefs(
161        profile_->GetPrefs());
162    scoped_ptr<base::ListValue> search_engines(
163        master_settings_->GetSearchProviderOverrides());
164    if (search_engines) {
165      // This Chrome distribution channel provides a custom search engine. We
166      // must reset to it.
167      ListPrefUpdate update(prefs, prefs::kSearchProviderOverrides);
168      update->Swap(search_engines.get());
169    }
170
171    template_url_service_->RepairPrepopulatedSearchEngines();
172
173    // Reset Google search URL.
174    prefs->ClearPref(prefs::kLastPromptedGoogleURL);
175    const TemplateURL* default_search_provider =
176        template_url_service_->GetDefaultSearchProvider();
177    if (default_search_provider &&
178        default_search_provider->HasGoogleBaseURLs(
179            template_url_service_->search_terms_data())) {
180      GoogleURLTracker* tracker =
181          GoogleURLTrackerFactory::GetForProfile(profile_);
182      if (tracker)
183        tracker->RequestServerCheck(true);
184    }
185
186    MarkAsDone(DEFAULT_SEARCH_ENGINE);
187  } else {
188    template_url_service_sub_ =
189        template_url_service_->RegisterOnLoadedCallback(
190            base::Bind(&ProfileResetter::OnTemplateURLServiceLoaded,
191                       weak_ptr_factory_.GetWeakPtr()));
192    template_url_service_->Load();
193  }
194}
195
196void ProfileResetter::ResetHomepage() {
197  DCHECK(CalledOnValidThread());
198  PrefService* prefs = profile_->GetPrefs();
199  DCHECK(prefs);
200  std::string homepage;
201  bool homepage_is_ntp, show_home_button;
202
203  if (master_settings_->GetHomepage(&homepage))
204    prefs->SetString(prefs::kHomePage, homepage);
205
206  if (master_settings_->GetHomepageIsNewTab(&homepage_is_ntp))
207    prefs->SetBoolean(prefs::kHomePageIsNewTabPage, homepage_is_ntp);
208  else
209    prefs->ClearPref(prefs::kHomePageIsNewTabPage);
210
211  if (master_settings_->GetShowHomeButton(&show_home_button))
212    prefs->SetBoolean(prefs::kShowHomeButton, show_home_button);
213  else
214    prefs->ClearPref(prefs::kShowHomeButton);
215  MarkAsDone(HOMEPAGE);
216}
217
218void ProfileResetter::ResetContentSettings() {
219  DCHECK(CalledOnValidThread());
220  PrefService* prefs = profile_->GetPrefs();
221  HostContentSettingsMap* map = profile_->GetHostContentSettingsMap();
222
223  for (int type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
224    map->ClearSettingsForOneType(static_cast<ContentSettingsType>(type));
225    if (HostContentSettingsMap::IsSettingAllowedForType(
226            prefs,
227            CONTENT_SETTING_DEFAULT,
228            static_cast<ContentSettingsType>(type)))
229      map->SetDefaultContentSetting(static_cast<ContentSettingsType>(type),
230                                    CONTENT_SETTING_DEFAULT);
231  }
232  MarkAsDone(CONTENT_SETTINGS);
233}
234
235void ProfileResetter::ResetCookiesAndSiteData() {
236  DCHECK(CalledOnValidThread());
237  DCHECK(!cookies_remover_);
238
239  cookies_remover_ = BrowsingDataRemover::CreateForUnboundedRange(profile_);
240  cookies_remover_->AddObserver(this);
241  int remove_mask = BrowsingDataRemover::REMOVE_SITE_DATA |
242                    BrowsingDataRemover::REMOVE_CACHE;
243  PrefService* prefs = profile_->GetPrefs();
244  DCHECK(prefs);
245  // Don't try to clear LSO data if it's not supported.
246  if (!prefs->GetBoolean(prefs::kClearPluginLSODataEnabled))
247    remove_mask &= ~BrowsingDataRemover::REMOVE_PLUGIN_DATA;
248  cookies_remover_->Remove(remove_mask, BrowsingDataHelper::UNPROTECTED_WEB);
249}
250
251void ProfileResetter::ResetExtensions() {
252  DCHECK(CalledOnValidThread());
253
254  std::vector<std::string> brandcode_extensions;
255  master_settings_->GetExtensions(&brandcode_extensions);
256
257  ExtensionService* extension_service =
258      extensions::ExtensionSystem::Get(profile_)->extension_service();
259  DCHECK(extension_service);
260  extension_service->DisableUserExtensions(brandcode_extensions);
261
262  MarkAsDone(EXTENSIONS);
263}
264
265void ProfileResetter::ResetStartupPages() {
266  DCHECK(CalledOnValidThread());
267  PrefService* prefs = profile_->GetPrefs();
268  DCHECK(prefs);
269  scoped_ptr<base::ListValue> url_list(
270      master_settings_->GetUrlsToRestoreOnStartup());
271  if (url_list)
272    ListPrefUpdate(prefs, prefs::kURLsToRestoreOnStartup)->Swap(url_list.get());
273
274  int restore_on_startup;
275  if (master_settings_->GetRestoreOnStartup(&restore_on_startup))
276    prefs->SetInteger(prefs::kRestoreOnStartup, restore_on_startup);
277  else
278    prefs->ClearPref(prefs::kRestoreOnStartup);
279
280  prefs->SetBoolean(prefs::kRestoreOnStartupMigrated, true);
281  MarkAsDone(STARTUP_PAGES);
282}
283
284void ProfileResetter::ResetPinnedTabs() {
285  // Unpin all the tabs.
286  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
287    if (it->is_type_tabbed() && it->profile() == profile_) {
288      TabStripModel* tab_model = it->tab_strip_model();
289      // Here we assume that indexof(any mini tab) < indexof(any normal tab).
290      // If we unpin the tab, it can be moved to the right. Thus traversing in
291      // reverse direction is correct.
292      for (int i = tab_model->count() - 1; i >= 0; --i) {
293        if (tab_model->IsTabPinned(i) && !tab_model->IsAppTab(i))
294          tab_model->SetTabPinned(i, false);
295      }
296    }
297  }
298  MarkAsDone(PINNED_TABS);
299}
300
301void ProfileResetter::ResetShortcuts() {
302#if defined(OS_WIN)
303  content::BrowserThread::PostTaskAndReply(
304      content::BrowserThread::FILE,
305      FROM_HERE,
306      base::Bind(&ResetShortcutsOnFileThread),
307      base::Bind(&ProfileResetter::MarkAsDone,
308                 weak_ptr_factory_.GetWeakPtr(),
309                 SHORTCUTS));
310#else
311  MarkAsDone(SHORTCUTS);
312#endif
313}
314
315void ProfileResetter::OnTemplateURLServiceLoaded() {
316  // TemplateURLService has loaded. If we need to clean search engines, it's
317  // time to go on.
318  DCHECK(CalledOnValidThread());
319  template_url_service_sub_.reset();
320  if (pending_reset_flags_ & DEFAULT_SEARCH_ENGINE)
321    ResetDefaultSearchEngine();
322}
323
324void ProfileResetter::OnBrowsingDataRemoverDone() {
325  cookies_remover_ = NULL;
326  MarkAsDone(COOKIES_AND_SITE_DATA);
327}
328
329std::vector<ShortcutCommand> GetChromeLaunchShortcuts(
330    const scoped_refptr<SharedCancellationFlag>& cancel) {
331  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
332#if defined(OS_WIN)
333  // Get full path of chrome.
334  base::FilePath chrome_exe;
335  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
336    return std::vector<ShortcutCommand>();
337  BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
338      BrowserDistribution::CHROME_BROWSER);
339  std::vector<ShortcutCommand> shortcuts;
340  for (int location = ShellUtil::SHORTCUT_LOCATION_FIRST;
341       location < ShellUtil::NUM_SHORTCUT_LOCATIONS; ++location) {
342    if (cancel && cancel->data.IsSet())
343      break;
344    ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
345        static_cast<ShellUtil::ShortcutLocation>(location),
346        dist,
347        ShellUtil::CURRENT_USER,
348        chrome_exe,
349        false,
350        cancel,
351        &shortcuts);
352  }
353  return shortcuts;
354#else
355  return std::vector<ShortcutCommand>();
356#endif
357}
358