profile_resetter_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 "base/json/json_string_value_serializer.h"
8#include "base/prefs/pref_service.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/test/scoped_path_override.h"
11#include "chrome/browser/content_settings/host_content_settings_map.h"
12#include "chrome/browser/extensions/extension_service.h"
13#include "chrome/browser/extensions/extension_service_test_base.h"
14#include "chrome/browser/extensions/tab_helper.h"
15#include "chrome/browser/prefs/session_startup_pref.h"
16#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
17#include "chrome/browser/profile_resetter/profile_resetter_test_base.h"
18#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
19#include "chrome/browser/search_engines/template_url_service_factory.h"
20#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
21#include "chrome/browser/themes/theme_service.h"
22#include "chrome/browser/themes/theme_service_factory.h"
23#include "chrome/browser/ui/tabs/tab_strip_model.h"
24#include "chrome/browser/webdata/web_data_service_factory.h"
25#include "chrome/common/pref_names.h"
26#include "chrome/test/base/browser_with_test_window_test.h"
27#include "components/google/core/browser/google_pref_names.h"
28#include "components/search_engines/template_url_service.h"
29#include "components/search_engines/template_url_service_client.h"
30#include "content/public/browser/web_contents.h"
31#include "content/public/test/test_browser_thread.h"
32#include "extensions/common/extension.h"
33#include "extensions/common/manifest_constants.h"
34#include "net/http/http_response_headers.h"
35#include "net/http/http_status_code.h"
36#include "net/url_request/test_url_fetcher_factory.h"
37#include "net/url_request/url_request_status.h"
38#include "url/gurl.h"
39
40#if defined(OS_WIN)
41#include "base/files/file_util.h"
42#include "base/path_service.h"
43#include "base/process/process_handle.h"
44#include "base/rand_util.h"
45#include "base/strings/string_number_conversions.h"
46#include "base/win/scoped_com_initializer.h"
47#include "base/win/shortcut.h"
48#endif
49
50
51namespace {
52
53const char kDistributionConfig[] = "{"
54    " \"homepage\" : \"http://www.foo.com\","
55    " \"homepage_is_newtabpage\" : false,"
56    " \"browser\" : {"
57    "   \"show_home_button\" : true"
58    "  },"
59    " \"session\" : {"
60    "   \"restore_on_startup\" : 4,"
61    "   \"startup_urls\" : [\"http://goo.gl\", \"http://foo.de\"]"
62    "  },"
63    " \"search_provider_overrides\" : ["
64    "    {"
65    "      \"name\" : \"first\","
66    "      \"keyword\" : \"firstkey\","
67    "      \"search_url\" : \"http://www.foo.com/s?q={searchTerms}\","
68    "      \"favicon_url\" : \"http://www.foo.com/favicon.ico\","
69    "      \"suggest_url\" : \"http://www.foo.com/s?q={searchTerms}\","
70    "      \"encoding\" : \"UTF-8\","
71    "      \"id\" : 1001"
72    "    }"
73    "  ],"
74    " \"extensions\" : {"
75    "   \"settings\" : {"
76    "     \"placeholder_for_id\": {"
77    "      }"
78    "    }"
79    "  }"
80    "}";
81
82const char kXmlConfig[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
83    "<response protocol=\"3.0\" server=\"prod\">"
84      "<app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" status=\"ok\">"
85        "<data index=\"skipfirstrunui-importsearch-defaultbrowser\" "
86          "name=\"install\" status=\"ok\">"
87          "placeholder_for_data"
88        "</data>"
89      "</app>"
90    "</response>";
91
92using extensions::Extension;
93using extensions::Manifest;
94
95
96// ProfileResetterTest --------------------------------------------------------
97
98// ProfileResetterTest sets up the extension, WebData and TemplateURL services.
99class ProfileResetterTest : public extensions::ExtensionServiceTestBase,
100                            public ProfileResetterTestBase {
101 public:
102  ProfileResetterTest();
103  virtual ~ProfileResetterTest();
104
105 protected:
106  virtual void SetUp() OVERRIDE;
107
108  TestingProfile* profile() { return profile_.get(); }
109
110  static KeyedService* CreateTemplateURLService(
111      content::BrowserContext* context);
112
113 private:
114#if defined(OS_WIN)
115  base::ScopedPathOverride user_desktop_override_;
116  base::ScopedPathOverride app_dir_override_;
117  base::ScopedPathOverride start_menu_override_;
118  base::ScopedPathOverride taskbar_pins_override_;
119  base::win::ScopedCOMInitializer com_init_;
120#endif
121};
122
123ProfileResetterTest::ProfileResetterTest()
124#if defined(OS_WIN)
125    : user_desktop_override_(base::DIR_USER_DESKTOP),
126      app_dir_override_(base::DIR_APP_DATA),
127      start_menu_override_(base::DIR_START_MENU),
128      taskbar_pins_override_(base::DIR_TASKBAR_PINS)
129#endif
130{}
131
132ProfileResetterTest::~ProfileResetterTest() {
133}
134
135void ProfileResetterTest::SetUp() {
136  extensions::ExtensionServiceTestBase::SetUp();
137  InitializeEmptyExtensionService();
138
139  profile()->CreateWebDataService();
140  TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
141      profile(),
142      &ProfileResetterTest::CreateTemplateURLService);
143  resetter_.reset(new ProfileResetter(profile()));
144}
145
146// static
147KeyedService* ProfileResetterTest::CreateTemplateURLService(
148    content::BrowserContext* context) {
149  Profile* profile = static_cast<Profile*>(context);
150  return new TemplateURLService(
151      profile->GetPrefs(),
152      scoped_ptr<SearchTermsData>(new UIThreadSearchTermsData(profile)),
153      WebDataServiceFactory::GetKeywordWebDataForProfile(
154          profile, Profile::EXPLICIT_ACCESS),
155      scoped_ptr<TemplateURLServiceClient>(), NULL, NULL, base::Closure());
156}
157
158
159// PinnedTabsResetTest --------------------------------------------------------
160
161class PinnedTabsResetTest : public BrowserWithTestWindowTest,
162                            public ProfileResetterTestBase {
163 protected:
164  virtual void SetUp() OVERRIDE;
165
166  content::WebContents* CreateWebContents();
167};
168
169void PinnedTabsResetTest::SetUp() {
170  BrowserWithTestWindowTest::SetUp();
171  resetter_.reset(new ProfileResetter(profile()));
172}
173
174content::WebContents* PinnedTabsResetTest::CreateWebContents() {
175  return content::WebContents::Create(
176      content::WebContents::CreateParams(profile()));
177}
178
179
180// ConfigParserTest -----------------------------------------------------------
181
182// URLFetcher delegate that simply records the upload data.
183struct URLFetcherRequestListener : net::URLFetcherDelegate {
184  URLFetcherRequestListener();
185  virtual ~URLFetcherRequestListener();
186
187  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
188
189  std::string upload_data;
190  net::URLFetcherDelegate* real_delegate;
191};
192
193URLFetcherRequestListener::URLFetcherRequestListener()
194    : real_delegate(NULL) {
195}
196
197URLFetcherRequestListener::~URLFetcherRequestListener() {
198}
199
200void URLFetcherRequestListener::OnURLFetchComplete(
201    const net::URLFetcher* source) {
202  const net::TestURLFetcher* test_fetcher =
203      static_cast<const net::TestURLFetcher*>(source);
204  upload_data = test_fetcher->upload_data();
205  DCHECK(real_delegate);
206  real_delegate->OnURLFetchComplete(source);
207}
208
209class ConfigParserTest : public testing::Test {
210 protected:
211  ConfigParserTest();
212  virtual ~ConfigParserTest();
213
214  scoped_ptr<BrandcodeConfigFetcher> WaitForRequest(const GURL& url);
215
216  net::FakeURLFetcherFactory& factory() { return factory_; }
217
218 private:
219  scoped_ptr<net::FakeURLFetcher> CreateFakeURLFetcher(
220      const GURL& url,
221      net::URLFetcherDelegate* fetcher_delegate,
222      const std::string& response_data,
223      net::HttpStatusCode response_code,
224      net::URLRequestStatus::Status status);
225
226  MOCK_METHOD0(Callback, void(void));
227
228  base::MessageLoopForIO loop_;
229  content::TestBrowserThread ui_thread_;
230  content::TestBrowserThread io_thread_;
231  URLFetcherRequestListener request_listener_;
232  net::FakeURLFetcherFactory factory_;
233};
234
235ConfigParserTest::ConfigParserTest()
236    : ui_thread_(content::BrowserThread::UI, &loop_),
237      io_thread_(content::BrowserThread::IO, &loop_),
238      factory_(NULL, base::Bind(&ConfigParserTest::CreateFakeURLFetcher,
239                                base::Unretained(this))) {
240}
241
242ConfigParserTest::~ConfigParserTest() {}
243
244scoped_ptr<BrandcodeConfigFetcher> ConfigParserTest::WaitForRequest(
245    const GURL& url) {
246  EXPECT_CALL(*this, Callback());
247  scoped_ptr<BrandcodeConfigFetcher> fetcher(
248      new BrandcodeConfigFetcher(base::Bind(&ConfigParserTest::Callback,
249                                            base::Unretained(this)),
250                                 url,
251                                 "ABCD"));
252  base::MessageLoop::current()->RunUntilIdle();
253  EXPECT_FALSE(fetcher->IsActive());
254  // Look for the brand code in the request.
255  EXPECT_NE(std::string::npos, request_listener_.upload_data.find("ABCD"));
256  return fetcher.Pass();
257}
258
259scoped_ptr<net::FakeURLFetcher> ConfigParserTest::CreateFakeURLFetcher(
260    const GURL& url,
261    net::URLFetcherDelegate* fetcher_delegate,
262    const std::string& response_data,
263    net::HttpStatusCode response_code,
264    net::URLRequestStatus::Status status) {
265  request_listener_.real_delegate = fetcher_delegate;
266  scoped_ptr<net::FakeURLFetcher> fetcher(
267      new net::FakeURLFetcher(
268          url, &request_listener_, response_data, response_code, status));
269  scoped_refptr<net::HttpResponseHeaders> download_headers =
270      new net::HttpResponseHeaders("");
271  download_headers->AddHeader("Content-Type: text/xml");
272  fetcher->set_response_headers(download_headers);
273  return fetcher.Pass();
274}
275
276// A helper class to create/delete/check a Chrome desktop shortcut on Windows.
277class ShortcutHandler {
278 public:
279  ShortcutHandler();
280  ~ShortcutHandler();
281
282  static bool IsSupported();
283  ShortcutCommand CreateWithArguments(const base::string16& name,
284                                      const base::string16& args);
285  void CheckShortcutHasArguments(const base::string16& desired_args) const;
286  void Delete();
287
288 private:
289#if defined(OS_WIN)
290  base::FilePath shortcut_path_;
291#endif
292  DISALLOW_COPY_AND_ASSIGN(ShortcutHandler);
293};
294
295#if defined(OS_WIN)
296ShortcutHandler::ShortcutHandler() {
297}
298
299ShortcutHandler::~ShortcutHandler() {
300  if (!shortcut_path_.empty())
301    Delete();
302}
303
304// static
305bool ShortcutHandler::IsSupported() {
306  return true;
307}
308
309ShortcutCommand ShortcutHandler::CreateWithArguments(
310    const base::string16& name,
311    const base::string16& args) {
312  EXPECT_TRUE(shortcut_path_.empty());
313  base::FilePath path_to_create;
314  EXPECT_TRUE(PathService::Get(base::DIR_USER_DESKTOP, &path_to_create));
315  path_to_create = path_to_create.Append(name);
316  EXPECT_FALSE(base::PathExists(path_to_create)) << path_to_create.value();
317
318  base::FilePath path_exe;
319  EXPECT_TRUE(PathService::Get(base::FILE_EXE, &path_exe));
320  base::win::ShortcutProperties shortcut_properties;
321  shortcut_properties.set_target(path_exe);
322  shortcut_properties.set_arguments(args);
323  EXPECT_TRUE(base::win::CreateOrUpdateShortcutLink(
324      path_to_create, shortcut_properties,
325      base::win::SHORTCUT_CREATE_ALWAYS)) << path_to_create.value();
326  shortcut_path_ = path_to_create;
327  return ShortcutCommand(shortcut_path_, args);
328}
329
330void ShortcutHandler::CheckShortcutHasArguments(
331    const base::string16& desired_args) const {
332  EXPECT_FALSE(shortcut_path_.empty());
333  base::string16 args;
334  EXPECT_TRUE(base::win::ResolveShortcut(shortcut_path_, NULL, &args));
335  EXPECT_EQ(desired_args, args);
336}
337
338void ShortcutHandler::Delete() {
339  EXPECT_FALSE(shortcut_path_.empty());
340  EXPECT_TRUE(base::DeleteFile(shortcut_path_, false));
341  shortcut_path_.clear();
342}
343#else
344ShortcutHandler::ShortcutHandler() {}
345
346ShortcutHandler::~ShortcutHandler() {}
347
348// static
349bool ShortcutHandler::IsSupported() {
350  return false;
351}
352
353ShortcutCommand ShortcutHandler::CreateWithArguments(
354    const base::string16& name,
355    const base::string16& args) {
356  return ShortcutCommand();
357}
358
359void ShortcutHandler::CheckShortcutHasArguments(
360    const base::string16& desired_args) const {
361}
362
363void ShortcutHandler::Delete() {
364}
365#endif  // defined(OS_WIN)
366
367
368// helper functions -----------------------------------------------------------
369
370scoped_refptr<Extension> CreateExtension(const base::string16& name,
371                                         const base::FilePath& path,
372                                         Manifest::Location location,
373                                         extensions::Manifest::Type type,
374                                         bool installed_by_default) {
375  base::DictionaryValue manifest;
376  manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
377  manifest.SetString(extensions::manifest_keys::kName, name);
378  switch (type) {
379    case extensions::Manifest::TYPE_THEME:
380      manifest.Set(extensions::manifest_keys::kTheme,
381                   new base::DictionaryValue);
382      break;
383    case extensions::Manifest::TYPE_HOSTED_APP:
384      manifest.SetString(extensions::manifest_keys::kLaunchWebURL,
385                         "http://www.google.com");
386      manifest.SetString(extensions::manifest_keys::kUpdateURL,
387                         "http://clients2.google.com/service/update2/crx");
388      break;
389    case extensions::Manifest::TYPE_EXTENSION:
390      // do nothing
391      break;
392    default:
393      NOTREACHED();
394  }
395  manifest.SetString(extensions::manifest_keys::kOmniboxKeyword, name);
396  std::string error;
397  scoped_refptr<Extension> extension = Extension::Create(
398      path,
399      location,
400      manifest,
401      installed_by_default ? Extension::WAS_INSTALLED_BY_DEFAULT
402                           : Extension::NO_FLAGS,
403      &error);
404  EXPECT_TRUE(extension.get() != NULL) << error;
405  return extension;
406}
407
408void ReplaceString(std::string* str,
409                   const std::string& placeholder,
410                   const std::string& substitution) {
411  ASSERT_NE(static_cast<std::string*>(NULL), str);
412  size_t placeholder_pos = str->find(placeholder);
413  ASSERT_NE(std::string::npos, placeholder_pos);
414  str->replace(placeholder_pos, placeholder.size(), substitution);
415}
416
417
418/********************* Tests *********************/
419
420TEST_F(ProfileResetterTest, ResetNothing) {
421  // The callback should be called even if there is nothing to reset.
422  ResetAndWait(0);
423}
424
425TEST_F(ProfileResetterTest, ResetDefaultSearchEngineNonOrganic) {
426  PrefService* prefs = profile()->GetPrefs();
427  DCHECK(prefs);
428  prefs->SetString(prefs::kLastPromptedGoogleURL, "http://www.foo.com/");
429
430  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE, kDistributionConfig);
431
432  TemplateURLService* model =
433      TemplateURLServiceFactory::GetForProfile(profile());
434  TemplateURL* default_engine = model->GetDefaultSearchProvider();
435  ASSERT_NE(static_cast<TemplateURL*>(NULL), default_engine);
436  EXPECT_EQ(base::ASCIIToUTF16("first"), default_engine->short_name());
437  EXPECT_EQ(base::ASCIIToUTF16("firstkey"), default_engine->keyword());
438  EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", default_engine->url());
439
440  EXPECT_EQ("", prefs->GetString(prefs::kLastPromptedGoogleURL));
441}
442
443TEST_F(ProfileResetterTest, ResetDefaultSearchEnginePartially) {
444  // Search engine's logic is tested by
445  // TemplateURLServiceTest.RepairPrepopulatedSearchEngines.
446  PrefService* prefs = profile()->GetPrefs();
447  DCHECK(prefs);
448  prefs->SetString(prefs::kLastPromptedGoogleURL, "http://www.foo.com/");
449
450  // Make sure TemplateURLService has loaded.
451  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);
452
453  TemplateURLService* model =
454      TemplateURLServiceFactory::GetForProfile(profile());
455  TemplateURLService::TemplateURLVector urls = model->GetTemplateURLs();
456
457  // The second call should produce no effect.
458  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE);
459
460  EXPECT_EQ(urls, model->GetTemplateURLs());
461  EXPECT_EQ(std::string(), prefs->GetString(prefs::kLastPromptedGoogleURL));
462}
463
464TEST_F(ProfileResetterTest, ResetHomepageNonOrganic) {
465  PrefService* prefs = profile()->GetPrefs();
466  DCHECK(prefs);
467  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, true);
468  prefs->SetString(prefs::kHomePage, "http://google.com");
469  prefs->SetBoolean(prefs::kShowHomeButton, false);
470
471  ResetAndWait(ProfileResetter::HOMEPAGE, kDistributionConfig);
472
473  EXPECT_FALSE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
474  EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage));
475  EXPECT_TRUE(prefs->GetBoolean(prefs::kShowHomeButton));
476}
477
478TEST_F(ProfileResetterTest, ResetHomepagePartially) {
479  PrefService* prefs = profile()->GetPrefs();
480  DCHECK(prefs);
481  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false);
482  prefs->SetString(prefs::kHomePage, "http://www.foo.com");
483  prefs->SetBoolean(prefs::kShowHomeButton, true);
484
485  ResetAndWait(ProfileResetter::HOMEPAGE);
486
487  EXPECT_TRUE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage));
488  EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage));
489  EXPECT_FALSE(prefs->GetBoolean(prefs::kShowHomeButton));
490}
491
492TEST_F(ProfileResetterTest, ResetContentSettings) {
493  HostContentSettingsMap* host_content_settings_map =
494      profile()->GetHostContentSettingsMap();
495  ContentSettingsPattern pattern =
496      ContentSettingsPattern::FromString("[*.]example.org");
497  std::map<ContentSettingsType, ContentSetting> default_settings;
498
499  for (int type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
500    if (type == CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE ||
501        type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT ||
502        type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) {
503      // These types are excluded because one can't call
504      // GetDefaultContentSetting() for them.
505    } else {
506      ContentSettingsType content_type = static_cast<ContentSettingsType>(type);
507      ContentSetting default_setting =
508          host_content_settings_map->GetDefaultContentSetting(content_type,
509                                                              NULL);
510      default_settings[content_type] = default_setting;
511      ContentSetting wildcard_setting =
512          default_setting == CONTENT_SETTING_BLOCK ? CONTENT_SETTING_ALLOW
513                                                   : CONTENT_SETTING_BLOCK;
514      ContentSetting site_setting =
515          default_setting == CONTENT_SETTING_ALLOW ? CONTENT_SETTING_ALLOW
516                                                   : CONTENT_SETTING_BLOCK;
517      if (HostContentSettingsMap::IsSettingAllowedForType(
518              profile()->GetPrefs(),
519              wildcard_setting,
520              content_type)) {
521        host_content_settings_map->SetDefaultContentSetting(
522            content_type,
523            wildcard_setting);
524      }
525      if (!HostContentSettingsMap::ContentTypeHasCompoundValue(content_type) &&
526          HostContentSettingsMap::IsSettingAllowedForType(
527              profile()->GetPrefs(),
528              site_setting,
529              content_type)) {
530        host_content_settings_map->SetContentSetting(
531            pattern,
532            ContentSettingsPattern::Wildcard(),
533            content_type,
534            std::string(),
535            site_setting);
536        ContentSettingsForOneType host_settings;
537        host_content_settings_map->GetSettingsForOneType(
538            content_type, std::string(), &host_settings);
539        EXPECT_EQ(2U, host_settings.size());
540      }
541    }
542  }
543
544  ResetAndWait(ProfileResetter::CONTENT_SETTINGS);
545
546  for (int type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
547    ContentSettingsType content_type = static_cast<ContentSettingsType>(type);
548    if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type) ||
549        type == CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE ||
550        content_type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT ||
551        content_type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS)
552      continue;
553    ContentSetting default_setting =
554        host_content_settings_map->GetDefaultContentSetting(content_type,
555                                                              NULL);
556    EXPECT_TRUE(default_settings.count(content_type));
557    EXPECT_EQ(default_settings[content_type], default_setting);
558    if (!HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) {
559      ContentSetting site_setting =
560          host_content_settings_map->GetContentSetting(
561              GURL("example.org"),
562              GURL(),
563              content_type,
564              std::string());
565      EXPECT_EQ(default_setting, site_setting);
566    }
567
568    ContentSettingsForOneType host_settings;
569    host_content_settings_map->GetSettingsForOneType(
570        content_type, std::string(), &host_settings);
571    EXPECT_EQ(1U, host_settings.size());
572  }
573}
574
575TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) {
576  service_->Init();
577
578  base::ScopedTempDir temp_dir;
579  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
580
581  scoped_refptr<Extension> theme =
582      CreateExtension(base::ASCIIToUTF16("example1"),
583                      temp_dir.path(),
584                      Manifest::INVALID_LOCATION,
585                      extensions::Manifest::TYPE_THEME,
586                      false);
587  service_->FinishInstallationForTest(theme.get());
588  // Let ThemeService finish creating the theme pack.
589  base::MessageLoop::current()->RunUntilIdle();
590
591  ThemeService* theme_service =
592      ThemeServiceFactory::GetForProfile(profile());
593  EXPECT_FALSE(theme_service->UsingDefaultTheme());
594
595  scoped_refptr<Extension> ext2 = CreateExtension(
596      base::ASCIIToUTF16("example2"),
597      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
598      Manifest::INVALID_LOCATION,
599      extensions::Manifest::TYPE_EXTENSION,
600      false);
601  service_->AddExtension(ext2.get());
602  // Component extensions and policy-managed extensions shouldn't be disabled.
603  scoped_refptr<Extension> ext3 = CreateExtension(
604      base::ASCIIToUTF16("example3"),
605      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
606      Manifest::COMPONENT,
607      extensions::Manifest::TYPE_EXTENSION,
608      false);
609  service_->AddExtension(ext3.get());
610  scoped_refptr<Extension> ext4 =
611      CreateExtension(base::ASCIIToUTF16("example4"),
612                      base::FilePath(FILE_PATH_LITERAL("//nonexistent3")),
613                      Manifest::EXTERNAL_POLICY_DOWNLOAD,
614                      extensions::Manifest::TYPE_EXTENSION,
615                      false);
616  service_->AddExtension(ext4.get());
617  scoped_refptr<Extension> ext5 = CreateExtension(
618      base::ASCIIToUTF16("example5"),
619      base::FilePath(FILE_PATH_LITERAL("//nonexistent4")),
620      Manifest::EXTERNAL_COMPONENT,
621      extensions::Manifest::TYPE_EXTENSION,
622      false);
623  service_->AddExtension(ext5.get());
624  scoped_refptr<Extension> ext6 = CreateExtension(
625      base::ASCIIToUTF16("example6"),
626      base::FilePath(FILE_PATH_LITERAL("//nonexistent5")),
627      Manifest::EXTERNAL_POLICY,
628      extensions::Manifest::TYPE_EXTENSION,
629      false);
630  service_->AddExtension(ext6.get());
631  EXPECT_EQ(6u, service_->extensions()->size());
632
633  ResetAndWait(ProfileResetter::EXTENSIONS);
634  EXPECT_EQ(4u, service_->extensions()->size());
635  EXPECT_FALSE(service_->extensions()->Contains(theme->id()));
636  EXPECT_FALSE(service_->extensions()->Contains(ext2->id()));
637  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
638  EXPECT_TRUE(service_->extensions()->Contains(ext4->id()));
639  EXPECT_TRUE(service_->extensions()->Contains(ext5->id()));
640  EXPECT_TRUE(service_->extensions()->Contains(ext6->id()));
641  EXPECT_TRUE(theme_service->UsingDefaultTheme());
642}
643
644TEST_F(ProfileResetterTest, ResetExtensionsByDisablingNonOrganic) {
645  scoped_refptr<Extension> ext2 = CreateExtension(
646      base::ASCIIToUTF16("example2"),
647      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
648      Manifest::INVALID_LOCATION,
649      extensions::Manifest::TYPE_EXTENSION,
650      false);
651  service_->AddExtension(ext2.get());
652  // Components and external policy extensions shouldn't be deleted.
653  scoped_refptr<Extension> ext3 = CreateExtension(
654      base::ASCIIToUTF16("example3"),
655      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
656      Manifest::INVALID_LOCATION,
657      extensions::Manifest::TYPE_EXTENSION,
658      false);
659  service_->AddExtension(ext3.get());
660  EXPECT_EQ(2u, service_->extensions()->size());
661
662  std::string master_prefs(kDistributionConfig);
663  ReplaceString(&master_prefs, "placeholder_for_id", ext3->id());
664
665  ResetAndWait(ProfileResetter::EXTENSIONS, master_prefs);
666
667  EXPECT_EQ(1u, service_->extensions()->size());
668  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
669}
670
671TEST_F(ProfileResetterTest, ResetExtensionsAndDefaultApps) {
672  service_->Init();
673
674  base::ScopedTempDir temp_dir;
675  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
676
677  scoped_refptr<Extension> ext1 =
678      CreateExtension(base::ASCIIToUTF16("example1"),
679                      temp_dir.path(),
680                      Manifest::INVALID_LOCATION,
681                      extensions::Manifest::TYPE_THEME,
682                      false);
683  service_->FinishInstallationForTest(ext1.get());
684  // Let ThemeService finish creating the theme pack.
685  base::MessageLoop::current()->RunUntilIdle();
686
687  ThemeService* theme_service =
688      ThemeServiceFactory::GetForProfile(profile());
689  EXPECT_FALSE(theme_service->UsingDefaultTheme());
690
691  scoped_refptr<Extension> ext2 =
692      CreateExtension(base::ASCIIToUTF16("example2"),
693                      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
694                      Manifest::INVALID_LOCATION,
695                      extensions::Manifest::TYPE_EXTENSION,
696                      false);
697  service_->AddExtension(ext2.get());
698
699  scoped_refptr<Extension> ext3 =
700      CreateExtension(base::ASCIIToUTF16("example2"),
701                      base::FilePath(FILE_PATH_LITERAL("//nonexistent3")),
702                      Manifest::INVALID_LOCATION,
703                      extensions::Manifest::TYPE_HOSTED_APP,
704                      true);
705  service_->AddExtension(ext3.get());
706  EXPECT_EQ(3u, service_->extensions()->size());
707
708  ResetAndWait(ProfileResetter::EXTENSIONS);
709
710  EXPECT_EQ(1u, service_->extensions()->size());
711  EXPECT_FALSE(service_->extensions()->Contains(ext1->id()));
712  EXPECT_FALSE(service_->extensions()->Contains(ext2->id()));
713  EXPECT_TRUE(service_->extensions()->Contains(ext3->id()));
714  EXPECT_TRUE(theme_service->UsingDefaultTheme());
715}
716
717TEST_F(ProfileResetterTest, ResetStartPageNonOrganic) {
718  PrefService* prefs = profile()->GetPrefs();
719  DCHECK(prefs);
720
721  SessionStartupPref startup_pref(SessionStartupPref::LAST);
722  SessionStartupPref::SetStartupPref(prefs, startup_pref);
723
724  ResetAndWait(ProfileResetter::STARTUP_PAGES, kDistributionConfig);
725
726  startup_pref = SessionStartupPref::GetStartupPref(prefs);
727  EXPECT_EQ(SessionStartupPref::URLS, startup_pref.type);
728  const GURL urls[] = {GURL("http://goo.gl"), GURL("http://foo.de")};
729  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)), startup_pref.urls);
730}
731
732
733TEST_F(ProfileResetterTest, ResetStartPagePartially) {
734  PrefService* prefs = profile()->GetPrefs();
735  DCHECK(prefs);
736
737  const GURL urls[] = {GURL("http://foo"), GURL("http://bar")};
738  SessionStartupPref startup_pref(SessionStartupPref::URLS);
739  startup_pref.urls.assign(urls, urls + arraysize(urls));
740  SessionStartupPref::SetStartupPref(prefs, startup_pref);
741
742  ResetAndWait(ProfileResetter::STARTUP_PAGES, std::string());
743
744  startup_pref = SessionStartupPref::GetStartupPref(prefs);
745  EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(), startup_pref.type);
746  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)), startup_pref.urls);
747}
748
749TEST_F(PinnedTabsResetTest, ResetPinnedTabs) {
750  scoped_refptr<Extension> extension_app = CreateExtension(
751      base::ASCIIToUTF16("hello!"),
752      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
753      Manifest::INVALID_LOCATION,
754      extensions::Manifest::TYPE_HOSTED_APP,
755      false);
756  scoped_ptr<content::WebContents> contents1(CreateWebContents());
757  extensions::TabHelper::CreateForWebContents(contents1.get());
758  extensions::TabHelper::FromWebContents(contents1.get())->
759      SetExtensionApp(extension_app.get());
760  scoped_ptr<content::WebContents> contents2(CreateWebContents());
761  scoped_ptr<content::WebContents> contents3(CreateWebContents());
762  scoped_ptr<content::WebContents> contents4(CreateWebContents());
763  TabStripModel* tab_strip_model = browser()->tab_strip_model();
764
765  tab_strip_model->AppendWebContents(contents4.get(), true);
766  tab_strip_model->AppendWebContents(contents3.get(), true);
767  tab_strip_model->AppendWebContents(contents2.get(), true);
768  tab_strip_model->SetTabPinned(2, true);
769  tab_strip_model->AppendWebContents(contents1.get(), true);
770  tab_strip_model->SetTabPinned(3, true);
771
772  EXPECT_EQ(contents2, tab_strip_model->GetWebContentsAt(0));
773  EXPECT_EQ(contents1, tab_strip_model->GetWebContentsAt(1));
774  EXPECT_EQ(contents3, tab_strip_model->GetWebContentsAt(2));
775  EXPECT_EQ(contents4, tab_strip_model->GetWebContentsAt(3));
776  EXPECT_EQ(3, tab_strip_model->IndexOfFirstNonMiniTab());
777
778  ResetAndWait(ProfileResetter::PINNED_TABS);
779
780  EXPECT_EQ(contents1, tab_strip_model->GetWebContentsAt(0));
781  EXPECT_EQ(contents2, tab_strip_model->GetWebContentsAt(1));
782  EXPECT_EQ(contents3, tab_strip_model->GetWebContentsAt(2));
783  EXPECT_EQ(contents4, tab_strip_model->GetWebContentsAt(3));
784  EXPECT_EQ(1, tab_strip_model->IndexOfFirstNonMiniTab());
785}
786
787TEST_F(ProfileResetterTest, ResetShortcuts) {
788  ShortcutHandler shortcut;
789  ShortcutCommand command_line = shortcut.CreateWithArguments(
790      base::ASCIIToUTF16("chrome.lnk"),
791      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
792  shortcut.CheckShortcutHasArguments(base::ASCIIToUTF16(
793      "--profile-directory=Default foo.com"));
794
795  ResetAndWait(ProfileResetter::SHORTCUTS);
796
797  shortcut.CheckShortcutHasArguments(base::ASCIIToUTF16(
798      "--profile-directory=Default"));
799}
800
801TEST_F(ProfileResetterTest, ResetFewFlags) {
802  // mock_object_ is a StrictMock, so we verify that it is called only once.
803  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
804               ProfileResetter::HOMEPAGE |
805               ProfileResetter::CONTENT_SETTINGS);
806}
807
808// Tries to load unavailable config file.
809TEST_F(ConfigParserTest, NoConnectivity) {
810  const GURL url("http://test");
811  factory().SetFakeResponse(url, "", net::HTTP_INTERNAL_SERVER_ERROR,
812                            net::URLRequestStatus::FAILED);
813
814  scoped_ptr<BrandcodeConfigFetcher> fetcher = WaitForRequest(GURL(url));
815  EXPECT_FALSE(fetcher->GetSettings());
816}
817
818// Tries to load available config file.
819TEST_F(ConfigParserTest, ParseConfig) {
820  const GURL url("http://test");
821  std::string xml_config(kXmlConfig);
822  ReplaceString(&xml_config, "placeholder_for_data", kDistributionConfig);
823  ReplaceString(&xml_config,
824                "placeholder_for_id",
825                "abbaabbaabbaabbaabbaabbaabbaabba");
826  factory().SetFakeResponse(url, xml_config, net::HTTP_OK,
827                            net::URLRequestStatus::SUCCESS);
828
829  scoped_ptr<BrandcodeConfigFetcher> fetcher = WaitForRequest(GURL(url));
830  scoped_ptr<BrandcodedDefaultSettings> settings = fetcher->GetSettings();
831  ASSERT_TRUE(settings);
832
833  std::vector<std::string> extension_ids;
834  EXPECT_TRUE(settings->GetExtensions(&extension_ids));
835  EXPECT_EQ(1u, extension_ids.size());
836  EXPECT_EQ("abbaabbaabbaabbaabbaabbaabbaabba", extension_ids[0]);
837
838  std::string homepage;
839  EXPECT_TRUE(settings->GetHomepage(&homepage));
840  EXPECT_EQ("http://www.foo.com", homepage);
841
842  scoped_ptr<base::ListValue> startup_list(
843      settings->GetUrlsToRestoreOnStartup());
844  EXPECT_TRUE(startup_list);
845  std::vector<std::string> startup_pages;
846  for (base::ListValue::iterator i = startup_list->begin();
847       i != startup_list->end(); ++i) {
848    std::string url;
849    EXPECT_TRUE((*i)->GetAsString(&url));
850    startup_pages.push_back(url);
851  }
852  ASSERT_EQ(2u, startup_pages.size());
853  EXPECT_EQ("http://goo.gl", startup_pages[0]);
854  EXPECT_EQ("http://foo.de", startup_pages[1]);
855}
856
857TEST_F(ProfileResetterTest, CheckSnapshots) {
858  ResettableSettingsSnapshot empty_snap(profile());
859  EXPECT_EQ(0, empty_snap.FindDifferentFields(empty_snap));
860
861  scoped_refptr<Extension> ext = CreateExtension(
862      base::ASCIIToUTF16("example"),
863      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
864      Manifest::INVALID_LOCATION,
865      extensions::Manifest::TYPE_EXTENSION,
866      false);
867  ASSERT_TRUE(ext.get());
868  service_->AddExtension(ext.get());
869
870  std::string master_prefs(kDistributionConfig);
871  std::string ext_id = ext->id();
872  ReplaceString(&master_prefs, "placeholder_for_id", ext_id);
873
874  // Reset to non organic defaults.
875  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
876               ProfileResetter::HOMEPAGE |
877               ProfileResetter::STARTUP_PAGES,
878               master_prefs);
879  ShortcutHandler shortcut_hijacked;
880  ShortcutCommand command_line = shortcut_hijacked.CreateWithArguments(
881      base::ASCIIToUTF16("chrome1.lnk"),
882      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
883  shortcut_hijacked.CheckShortcutHasArguments(
884      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
885  ShortcutHandler shortcut_ok;
886  shortcut_ok.CreateWithArguments(
887      base::ASCIIToUTF16("chrome2.lnk"),
888      base::ASCIIToUTF16("--profile-directory=Default1"));
889
890  ResettableSettingsSnapshot nonorganic_snap(profile());
891  nonorganic_snap.RequestShortcuts(base::Closure());
892  // Let it enumerate shortcuts on the FILE thread.
893  base::MessageLoop::current()->RunUntilIdle();
894  int diff_fields = ResettableSettingsSnapshot::ALL_FIELDS;
895  if (!ShortcutHandler::IsSupported())
896    diff_fields &= ~ResettableSettingsSnapshot::SHORTCUTS;
897  EXPECT_EQ(diff_fields,
898            empty_snap.FindDifferentFields(nonorganic_snap));
899  empty_snap.Subtract(nonorganic_snap);
900  EXPECT_TRUE(empty_snap.startup_urls().empty());
901  EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(),
902            empty_snap.startup_type());
903  EXPECT_TRUE(empty_snap.homepage().empty());
904  EXPECT_TRUE(empty_snap.homepage_is_ntp());
905  EXPECT_FALSE(empty_snap.show_home_button());
906  EXPECT_NE(std::string::npos, empty_snap.dse_url().find("{google:baseURL}"));
907  EXPECT_EQ(ResettableSettingsSnapshot::ExtensionList(),
908            empty_snap.enabled_extensions());
909  EXPECT_EQ(std::vector<ShortcutCommand>(), empty_snap.shortcuts());
910
911  // Reset to organic defaults.
912  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
913               ProfileResetter::HOMEPAGE |
914               ProfileResetter::STARTUP_PAGES |
915               ProfileResetter::EXTENSIONS |
916               ProfileResetter::SHORTCUTS);
917
918  ResettableSettingsSnapshot organic_snap(profile());
919  organic_snap.RequestShortcuts(base::Closure());
920  // Let it enumerate shortcuts on the FILE thread.
921  base::MessageLoop::current()->RunUntilIdle();
922  EXPECT_EQ(diff_fields, nonorganic_snap.FindDifferentFields(organic_snap));
923  nonorganic_snap.Subtract(organic_snap);
924  const GURL urls[] = {GURL("http://foo.de"), GURL("http://goo.gl")};
925  EXPECT_EQ(std::vector<GURL>(urls, urls + arraysize(urls)),
926            nonorganic_snap.startup_urls());
927  EXPECT_EQ(SessionStartupPref::URLS, nonorganic_snap.startup_type());
928  EXPECT_EQ("http://www.foo.com", nonorganic_snap.homepage());
929  EXPECT_FALSE(nonorganic_snap.homepage_is_ntp());
930  EXPECT_TRUE(nonorganic_snap.show_home_button());
931  EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", nonorganic_snap.dse_url());
932  EXPECT_EQ(ResettableSettingsSnapshot::ExtensionList(
933      1, std::make_pair(ext_id, "example")),
934      nonorganic_snap.enabled_extensions());
935  if (ShortcutHandler::IsSupported()) {
936    std::vector<ShortcutCommand> shortcuts = nonorganic_snap.shortcuts();
937    ASSERT_EQ(1u, shortcuts.size());
938    EXPECT_EQ(command_line.first.value(), shortcuts[0].first.value());
939    EXPECT_EQ(command_line.second, shortcuts[0].second);
940  }
941}
942
943TEST_F(ProfileResetterTest, FeedbackSerializationTest) {
944  // Reset to non organic defaults.
945  ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE |
946               ProfileResetter::HOMEPAGE |
947               ProfileResetter::STARTUP_PAGES,
948               kDistributionConfig);
949
950  scoped_refptr<Extension> ext = CreateExtension(
951      base::ASCIIToUTF16("example"),
952      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
953      Manifest::INVALID_LOCATION,
954      extensions::Manifest::TYPE_EXTENSION,
955      false);
956  ASSERT_TRUE(ext.get());
957  service_->AddExtension(ext.get());
958
959  ShortcutHandler shortcut;
960  ShortcutCommand command_line = shortcut.CreateWithArguments(
961      base::ASCIIToUTF16("chrome.lnk"),
962      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
963
964  ResettableSettingsSnapshot nonorganic_snap(profile());
965  nonorganic_snap.RequestShortcuts(base::Closure());
966  // Let it enumerate shortcuts on the FILE thread.
967  base::MessageLoop::current()->RunUntilIdle();
968
969  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31,
970                 expand_this_test);
971  for (int field_mask = 0; field_mask <= ResettableSettingsSnapshot::ALL_FIELDS;
972       ++field_mask) {
973    std::string report = SerializeSettingsReport(nonorganic_snap, field_mask);
974    JSONStringValueSerializer json(report);
975    std::string error;
976    scoped_ptr<base::Value> root(json.Deserialize(NULL, &error));
977    ASSERT_TRUE(root) << error;
978    ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY)) << error;
979
980    base::DictionaryValue* dict =
981        static_cast<base::DictionaryValue*>(root.get());
982
983    base::ListValue* startup_urls = NULL;
984    int startup_type = 0;
985    std::string homepage;
986    bool homepage_is_ntp = true;
987    bool show_home_button = true;
988    std::string default_search_engine;
989    base::ListValue* extensions = NULL;
990    base::ListValue* shortcuts = NULL;
991
992    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE),
993              dict->GetList("startup_urls", &startup_urls));
994    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE),
995              dict->GetInteger("startup_type", &startup_type));
996    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
997              dict->GetString("homepage", &homepage));
998    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
999              dict->GetBoolean("homepage_is_ntp", &homepage_is_ntp));
1000    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE),
1001              dict->GetBoolean("show_home_button", &show_home_button));
1002    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::DSE_URL),
1003              dict->GetString("default_search_engine", &default_search_engine));
1004    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::EXTENSIONS),
1005              dict->GetList("enabled_extensions", &extensions));
1006    EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::SHORTCUTS),
1007              dict->GetList("shortcuts", &shortcuts));
1008  }
1009}
1010
1011struct FeedbackCapture {
1012  void SetFeedback(Profile* profile,
1013                   const ResettableSettingsSnapshot& snapshot) {
1014    list_ = GetReadableFeedbackForSnapshot(profile, snapshot).Pass();
1015    OnUpdatedList();
1016  }
1017
1018  void Fail() {
1019    ADD_FAILURE() << "This method shouldn't be called.";
1020  }
1021
1022  MOCK_METHOD0(OnUpdatedList, void(void));
1023
1024  scoped_ptr<base::ListValue> list_;
1025};
1026
1027// Make sure GetReadableFeedback handles non-ascii letters.
1028TEST_F(ProfileResetterTest, GetReadableFeedback) {
1029  scoped_refptr<Extension> ext = CreateExtension(
1030      base::WideToUTF16(L"Tiësto"),
1031      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
1032      Manifest::INVALID_LOCATION,
1033      extensions::Manifest::TYPE_EXTENSION,
1034      false);
1035  ASSERT_TRUE(ext.get());
1036  service_->AddExtension(ext.get());
1037
1038  PrefService* prefs = profile()->GetPrefs();
1039  DCHECK(prefs);
1040  // The URL is "http://россия.рф".
1041  std::wstring url(L"http://"
1042    L"\u0440\u043e\u0441\u0441\u0438\u044f.\u0440\u0444");
1043  prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false);
1044  prefs->SetString(prefs::kHomePage, base::WideToUTF8(url));
1045
1046  SessionStartupPref startup_pref(SessionStartupPref::URLS);
1047  startup_pref.urls.push_back(GURL(base::WideToUTF8(url)));
1048  SessionStartupPref::SetStartupPref(prefs, startup_pref);
1049
1050  ShortcutHandler shortcut;
1051  ShortcutCommand command_line = shortcut.CreateWithArguments(
1052      base::ASCIIToUTF16("chrome.lnk"),
1053      base::ASCIIToUTF16("--profile-directory=Default foo.com"));
1054
1055  FeedbackCapture capture;
1056  EXPECT_CALL(capture, OnUpdatedList());
1057  ResettableSettingsSnapshot snapshot(profile());
1058  snapshot.RequestShortcuts(base::Bind(&FeedbackCapture::SetFeedback,
1059                                       base::Unretained(&capture),
1060                                       profile(),
1061                                       base::ConstRef(snapshot)));
1062  // Let it enumerate shortcuts on the FILE thread.
1063  base::MessageLoop::current()->RunUntilIdle();
1064  ::testing::Mock::VerifyAndClearExpectations(&capture);
1065  // The homepage and the startup page are in punycode. They are unreadable.
1066  // Trying to find the extension name.
1067  scoped_ptr<base::ListValue> list = capture.list_.Pass();
1068  ASSERT_TRUE(list);
1069  bool checked_extensions = false;
1070  bool checked_shortcuts = false;
1071  for (size_t i = 0; i < list->GetSize(); ++i) {
1072    base::DictionaryValue* dict = NULL;
1073    ASSERT_TRUE(list->GetDictionary(i, &dict));
1074    std::string value;
1075    ASSERT_TRUE(dict->GetString("key", &value));
1076    if (value == "Extensions") {
1077      base::string16 extensions;
1078      EXPECT_TRUE(dict->GetString("value", &extensions));
1079      EXPECT_EQ(base::WideToUTF16(L"Tiësto"), extensions);
1080      checked_extensions = true;
1081    } else if (value == "Shortcut targets") {
1082      base::string16 targets;
1083      EXPECT_TRUE(dict->GetString("value", &targets));
1084      EXPECT_NE(base::string16::npos,
1085                targets.find(base::ASCIIToUTF16("foo.com"))) << targets;
1086      checked_shortcuts = true;
1087    }
1088  }
1089  EXPECT_TRUE(checked_extensions);
1090  EXPECT_EQ(ShortcutHandler::IsSupported(), checked_shortcuts);
1091}
1092
1093TEST_F(ProfileResetterTest, DestroySnapshotFast) {
1094  FeedbackCapture capture;
1095  scoped_ptr<ResettableSettingsSnapshot> deleted_snapshot(
1096      new ResettableSettingsSnapshot(profile()));
1097  deleted_snapshot->RequestShortcuts(base::Bind(&FeedbackCapture::Fail,
1098                                                base::Unretained(&capture)));
1099  deleted_snapshot.reset();
1100  // Running remaining tasks shouldn't trigger the callback to be called as
1101  // |deleted_snapshot| was deleted before it could run.
1102  base::MessageLoop::current()->RunUntilIdle();
1103}
1104
1105}  // namespace
1106