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