1// Copyright 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/ui/sync/profile_signin_confirmation_helper.h" 6 7#include "base/basictypes.h" 8#include "base/bind.h" 9#include "base/bind_helpers.h" 10#include "base/callback.h" 11#include "base/command_line.h" 12#include "base/compiler_specific.h" 13#include "base/memory/scoped_ptr.h" 14#include "base/message_loop/message_loop.h" 15#include "base/prefs/pref_notifier_impl.h" 16#include "base/prefs/pref_service.h" 17#include "base/prefs/testing_pref_service.h" 18#include "base/run_loop.h" 19#include "base/strings/string16.h" 20#include "base/strings/string_util.h" 21#include "base/strings/utf_string_conversions.h" 22#include "chrome/browser/bookmarks/bookmark_model_factory.h" 23#include "chrome/browser/extensions/extension_service.h" 24#include "chrome/browser/extensions/test_extension_system.h" 25#include "chrome/browser/history/history_service.h" 26#include "chrome/browser/history/history_service_factory.h" 27#include "chrome/browser/prefs/browser_prefs.h" 28#include "chrome/common/extensions/extension_constants.h" 29#include "chrome/test/base/testing_pref_service_syncable.h" 30#include "chrome/test/base/testing_profile.h" 31#include "components/bookmarks/browser/bookmark_model.h" 32#include "components/bookmarks/test/bookmark_test_helpers.h" 33#include "components/pref_registry/pref_registry_syncable.h" 34#include "content/public/test/test_browser_thread_bundle.h" 35#include "content/public/test/test_utils.h" 36#include "extensions/browser/extension_prefs.h" 37#include "extensions/common/extension.h" 38#include "extensions/common/manifest_constants.h" 39#include "extensions/common/permissions/permission_set.h" 40#include "testing/gmock/include/gmock/gmock.h" 41#include "testing/gtest/include/gtest/gtest.h" 42 43#if defined(OS_CHROMEOS) 44#include "chrome/browser/chromeos/login/users/user_manager.h" 45#include "chrome/browser/chromeos/settings/cros_settings.h" 46#include "chrome/browser/chromeos/settings/device_settings_service.h" 47#endif 48 49namespace { 50 51template<typename T> 52void GetValueAndQuit(T* result, const base::Closure& quit, T actual) { 53 *result = actual; 54 quit.Run(); 55} 56 57template<typename T> 58T GetCallbackResult( 59 const base::Callback<void(const base::Callback<void(T)>&)>& callback) { 60 T result = false; 61 base::RunLoop loop; 62 callback.Run(base::Bind(&GetValueAndQuit<T>, &result, loop.QuitClosure())); 63 loop.Run(); 64 return result; 65} 66 67// A pref store that can have its read_error property changed for testing. 68class TestingPrefStoreWithCustomReadError : public TestingPrefStore { 69 public: 70 TestingPrefStoreWithCustomReadError() 71 : read_error_(PersistentPrefStore::PREF_READ_ERROR_NO_FILE) { 72 // By default the profile is "new" (NO_FILE means that the profile 73 // wasn't found on disk, so it was created). 74 } 75 virtual PrefReadError GetReadError() const OVERRIDE { 76 return read_error_; 77 } 78 virtual bool IsInitializationComplete() const OVERRIDE { 79 return true; 80 } 81 void set_read_error(PrefReadError read_error) { 82 read_error_ = read_error; 83 } 84 private: 85 virtual ~TestingPrefStoreWithCustomReadError() {} 86 PrefReadError read_error_; 87}; 88 89#if defined(OS_WIN) 90const base::FilePath::CharType kExtensionFilePath[] = 91 FILE_PATH_LITERAL("c:\\foo"); 92#elif defined(OS_POSIX) 93const base::FilePath::CharType kExtensionFilePath[] = 94 FILE_PATH_LITERAL("/oo"); 95#endif 96 97static scoped_refptr<extensions::Extension> CreateExtension( 98 const std::string& name, 99 const std::string& id) { 100 base::DictionaryValue manifest; 101 manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0"); 102 manifest.SetString(extensions::manifest_keys::kName, name); 103 std::string error; 104 scoped_refptr<extensions::Extension> extension = 105 extensions::Extension::Create( 106 base::FilePath(kExtensionFilePath).AppendASCII(name), 107 extensions::Manifest::INTERNAL, 108 manifest, 109 extensions::Extension::NO_FLAGS, 110 id, 111 &error); 112 return extension; 113} 114 115} // namespace 116 117class ProfileSigninConfirmationHelperTest : public testing::Test { 118 public: 119 ProfileSigninConfirmationHelperTest() 120 : user_prefs_(NULL), 121 model_(NULL) { 122 } 123 124 virtual void SetUp() OVERRIDE { 125 // Create the profile. 126 TestingProfile::Builder builder; 127 user_prefs_ = new TestingPrefStoreWithCustomReadError; 128 TestingPrefServiceSyncable* pref_service = new TestingPrefServiceSyncable( 129 new TestingPrefStore(), 130 user_prefs_, 131 new TestingPrefStore(), 132 new user_prefs::PrefRegistrySyncable(), 133 new PrefNotifierImpl()); 134 chrome::RegisterUserProfilePrefs(pref_service->registry()); 135 builder.SetPrefService(make_scoped_ptr<PrefServiceSyncable>(pref_service)); 136 profile_ = builder.Build(); 137 138 // Initialize the services we check. 139 profile_->CreateBookmarkModel(true); 140 model_ = BookmarkModelFactory::GetForProfile(profile_.get()); 141 test::WaitForBookmarkModelToLoad(model_); 142 ASSERT_TRUE(profile_->CreateHistoryService(true, false)); 143 extensions::TestExtensionSystem* system = 144 static_cast<extensions::TestExtensionSystem*>( 145 extensions::ExtensionSystem::Get(profile_.get())); 146 CommandLine command_line(CommandLine::NO_PROGRAM); 147 system->CreateExtensionService(&command_line, 148 base::FilePath(kExtensionFilePath), 149 false); 150 } 151 152 virtual void TearDown() OVERRIDE { 153 // TestExtensionSystem uses DeleteSoon, so we need to delete the profile 154 // and then run the message queue to clean up. 155 profile_.reset(); 156 base::RunLoop().RunUntilIdle(); 157 } 158 159 protected: 160 content::TestBrowserThreadBundle thread_bundle_; 161 scoped_ptr<TestingProfile> profile_; 162 TestingPrefStoreWithCustomReadError* user_prefs_; 163 BookmarkModel* model_; 164 165#if defined OS_CHROMEOS 166 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 167 chromeos::ScopedTestCrosSettings test_cros_settings_; 168 chromeos::ScopedTestUserManager test_user_manager_; 169#endif 170}; 171 172TEST_F(ProfileSigninConfirmationHelperTest, DoNotPromptForNewProfile) { 173 // Profile is new and there's no profile data. 174 EXPECT_FALSE( 175 GetCallbackResult( 176 base::Bind( 177 &ui::CheckShouldPromptForNewProfile, 178 profile_.get()))); 179} 180 181TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Bookmarks) { 182 ASSERT_TRUE(model_); 183 184 // Profile is new but has bookmarks. 185 model_->AddURL(model_->bookmark_bar_node(), 0, 186 base::string16(base::ASCIIToUTF16("foo")), 187 GURL("http://foo.com")); 188 EXPECT_TRUE( 189 GetCallbackResult( 190 base::Bind( 191 &ui::CheckShouldPromptForNewProfile, 192 profile_.get()))); 193} 194 195TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Extensions) { 196 ExtensionService* extensions = 197 extensions::ExtensionSystem::Get(profile_.get())->extension_service(); 198 ASSERT_TRUE(extensions); 199 200 // Profile is new but has synced extensions. 201 202 // (The web store doesn't count.) 203 scoped_refptr<extensions::Extension> webstore = 204 CreateExtension("web store", extension_misc::kWebStoreAppId); 205 extensions::ExtensionPrefs::Get(profile_.get())->AddGrantedPermissions( 206 webstore->id(), make_scoped_refptr(new extensions::PermissionSet).get()); 207 extensions->AddExtension(webstore.get()); 208 EXPECT_FALSE(GetCallbackResult( 209 base::Bind(&ui::CheckShouldPromptForNewProfile, profile_.get()))); 210 211 scoped_refptr<extensions::Extension> extension = 212 CreateExtension("foo", std::string()); 213 extensions::ExtensionPrefs::Get(profile_.get())->AddGrantedPermissions( 214 extension->id(), make_scoped_refptr(new extensions::PermissionSet).get()); 215 extensions->AddExtension(extension.get()); 216 EXPECT_TRUE(GetCallbackResult( 217 base::Bind(&ui::CheckShouldPromptForNewProfile, profile_.get()))); 218} 219 220TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_History) { 221 HistoryService* history = HistoryServiceFactory::GetForProfile( 222 profile_.get(), 223 Profile::EXPLICIT_ACCESS); 224 ASSERT_TRUE(history); 225 226 // Profile is new but has more than $(kHistoryEntriesBeforeNewProfilePrompt) 227 // history items. 228 char buf[18]; 229 for (int i = 0; i < 10; i++) { 230 base::snprintf(buf, arraysize(buf), "http://foo.com/%d", i); 231 history->AddPage( 232 GURL(std::string(buf)), base::Time::Now(), NULL, 1, 233 GURL(), history::RedirectList(), content::PAGE_TRANSITION_LINK, 234 history::SOURCE_BROWSED, false); 235 } 236 EXPECT_TRUE( 237 GetCallbackResult( 238 base::Bind( 239 &ui::CheckShouldPromptForNewProfile, 240 profile_.get()))); 241} 242 243TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_TypedURLs) { 244 HistoryService* history = HistoryServiceFactory::GetForProfile( 245 profile_.get(), 246 Profile::EXPLICIT_ACCESS); 247 ASSERT_TRUE(history); 248 249 // Profile is new but has a typed URL. 250 history->AddPage( 251 GURL("http://example.com"), base::Time::Now(), NULL, 1, 252 GURL(), history::RedirectList(), content::PAGE_TRANSITION_TYPED, 253 history::SOURCE_BROWSED, false); 254 EXPECT_TRUE( 255 GetCallbackResult( 256 base::Bind( 257 &ui::CheckShouldPromptForNewProfile, 258 profile_.get()))); 259} 260 261TEST_F(ProfileSigninConfirmationHelperTest, PromptForNewProfile_Restarted) { 262 // Browser has been shut down since profile was created. 263 user_prefs_->set_read_error(PersistentPrefStore::PREF_READ_ERROR_NONE); 264 EXPECT_TRUE( 265 GetCallbackResult( 266 base::Bind( 267 &ui::CheckShouldPromptForNewProfile, 268 profile_.get()))); 269} 270