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