1 // Copyright (c) 2012 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/sync/test/integration/apps_helper.h" 6 7 #include "base/logging.h" 8 #include "base/strings/string_number_conversions.h" 9 #include "chrome/browser/chrome_notification_types.h" 10 #include "chrome/browser/profiles/profile.h" 11 #include "chrome/browser/sync/test/integration/status_change_checker.h" 12 #include "chrome/browser/sync/test/integration/sync_app_helper.h" 13 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" 14 #include "chrome/browser/sync/test/integration/sync_extension_helper.h" 15 #include "chrome/browser/sync/test/integration/sync_extension_installer.h" 16 #include "content/public/browser/notification_observer.h" 17 #include "content/public/browser/notification_registrar.h" 18 #include "content/public/browser/notification_service.h" 19 #include "extensions/browser/extension_prefs.h" 20 #include "extensions/browser/extension_prefs_observer.h" 21 #include "extensions/browser/extension_registry.h" 22 #include "extensions/browser/extension_registry_observer.h" 23 #include "extensions/common/manifest.h" 24 25 using sync_datatype_helper::test; 26 27 namespace { 28 29 std::string CreateFakeAppName(int index) { 30 return "fakeapp" + base::IntToString(index); 31 } 32 33 } // namespace 34 35 namespace apps_helper { 36 37 bool HasSameAppsAsVerifier(int index) { 38 return SyncAppHelper::GetInstance()->AppStatesMatch( 39 test()->GetProfile(index), test()->verifier()); 40 } 41 42 bool AllProfilesHaveSameAppsAsVerifier() { 43 for (int i = 0; i < test()->num_clients(); ++i) { 44 if (!HasSameAppsAsVerifier(i)) { 45 DVLOG(1) << "Profile " << i << " doesn't have the same apps as the" 46 " verifier profile."; 47 return false; 48 } 49 } 50 return true; 51 } 52 53 std::string InstallApp(Profile* profile, int index) { 54 return SyncExtensionHelper::GetInstance()->InstallExtension( 55 profile, 56 CreateFakeAppName(index), 57 extensions::Manifest::TYPE_HOSTED_APP); 58 } 59 60 std::string InstallPlatformApp(Profile* profile, int index) { 61 return SyncExtensionHelper::GetInstance()->InstallExtension( 62 profile, 63 CreateFakeAppName(index), 64 extensions::Manifest::TYPE_PLATFORM_APP); 65 } 66 67 std::string InstallAppForAllProfiles(int index) { 68 for (int i = 0; i < test()->num_clients(); ++i) 69 InstallApp(test()->GetProfile(i), index); 70 return InstallApp(test()->verifier(), index); 71 } 72 73 void UninstallApp(Profile* profile, int index) { 74 return SyncExtensionHelper::GetInstance()->UninstallExtension( 75 profile, CreateFakeAppName(index)); 76 } 77 78 void EnableApp(Profile* profile, int index) { 79 return SyncExtensionHelper::GetInstance()->EnableExtension( 80 profile, CreateFakeAppName(index)); 81 } 82 83 void DisableApp(Profile* profile, int index) { 84 return SyncExtensionHelper::GetInstance()->DisableExtension( 85 profile, CreateFakeAppName(index)); 86 } 87 88 void IncognitoEnableApp(Profile* profile, int index) { 89 return SyncExtensionHelper::GetInstance()->IncognitoEnableExtension( 90 profile, CreateFakeAppName(index)); 91 } 92 93 void IncognitoDisableApp(Profile* profile, int index) { 94 return SyncExtensionHelper::GetInstance()->IncognitoDisableExtension( 95 profile, CreateFakeAppName(index)); 96 } 97 98 void InstallAppsPendingForSync(Profile* profile) { 99 SyncExtensionHelper::GetInstance()->InstallExtensionsPendingForSync(profile); 100 } 101 102 syncer::StringOrdinal GetPageOrdinalForApp(Profile* profile, 103 int app_index) { 104 return SyncAppHelper::GetInstance()->GetPageOrdinalForApp( 105 profile, CreateFakeAppName(app_index)); 106 } 107 108 void SetPageOrdinalForApp(Profile* profile, 109 int app_index, 110 const syncer::StringOrdinal& page_ordinal) { 111 SyncAppHelper::GetInstance()->SetPageOrdinalForApp( 112 profile, CreateFakeAppName(app_index), page_ordinal); 113 } 114 115 syncer::StringOrdinal GetAppLaunchOrdinalForApp(Profile* profile, 116 int app_index) { 117 return SyncAppHelper::GetInstance()->GetAppLaunchOrdinalForApp( 118 profile, CreateFakeAppName(app_index)); 119 } 120 121 void SetAppLaunchOrdinalForApp( 122 Profile* profile, 123 int app_index, 124 const syncer::StringOrdinal& app_launch_ordinal) { 125 SyncAppHelper::GetInstance()->SetAppLaunchOrdinalForApp( 126 profile, CreateFakeAppName(app_index), app_launch_ordinal); 127 } 128 129 void CopyNTPOrdinals(Profile* source, Profile* destination, int index) { 130 SetPageOrdinalForApp(destination, index, GetPageOrdinalForApp(source, index)); 131 SetAppLaunchOrdinalForApp( 132 destination, index, GetAppLaunchOrdinalForApp(source, index)); 133 } 134 135 void FixNTPOrdinalCollisions(Profile* profile) { 136 SyncAppHelper::GetInstance()->FixNTPOrdinalCollisions(profile); 137 } 138 139 namespace { 140 141 // A helper class to implement waiting for a set of profiles to have matching 142 // extensions lists. 143 class AppsMatchChecker : public StatusChangeChecker, 144 public extensions::ExtensionRegistryObserver, 145 public extensions::ExtensionPrefsObserver, 146 public content::NotificationObserver { 147 public: 148 explicit AppsMatchChecker(const std::vector<Profile*>& profiles); 149 virtual ~AppsMatchChecker(); 150 151 // StatusChangeChecker implementation. 152 virtual std::string GetDebugMessage() const OVERRIDE; 153 virtual bool IsExitConditionSatisfied() OVERRIDE; 154 155 // extensions::ExtensionRegistryObserver implementation. 156 virtual void OnExtensionLoaded( 157 content::BrowserContext* context, 158 const extensions::Extension* extension) OVERRIDE; 159 virtual void OnExtensionUnloaded( 160 content::BrowserContext* context, 161 const extensions::Extension* extenion, 162 extensions::UnloadedExtensionInfo::Reason reason) OVERRIDE; 163 virtual void OnExtensionInstalled(content::BrowserContext* browser_context, 164 const extensions::Extension* extension, 165 bool is_update) OVERRIDE; 166 virtual void OnExtensionUninstalled( 167 content::BrowserContext* browser_context, 168 const extensions::Extension* extension, 169 extensions::UninstallReason reason) OVERRIDE; 170 171 // extensions::ExtensionPrefsObserver implementation. 172 virtual void OnExtensionDisableReasonsChanged(const std::string& extension_id, 173 int disabled_reasons) OVERRIDE; 174 virtual void OnExtensionRegistered(const std::string& extension_id, 175 const base::Time& install_time, 176 bool is_enabled) OVERRIDE; 177 virtual void OnExtensionPrefsLoaded( 178 const std::string& extension_id, 179 const extensions::ExtensionPrefs* prefs) OVERRIDE; 180 virtual void OnExtensionPrefsDeleted( 181 const std::string& extension_id) OVERRIDE; 182 virtual void OnExtensionStateChanged(const std::string& extension_id, 183 bool state) OVERRIDE; 184 185 // Implementation of content::NotificationObserver. 186 virtual void Observe(int type, 187 const content::NotificationSource& source, 188 const content::NotificationDetails& details) OVERRIDE; 189 190 void Wait(); 191 192 private: 193 std::vector<Profile*> profiles_; 194 bool observing_; 195 196 content::NotificationRegistrar registrar_; 197 198 // This installs apps, too. 199 ScopedVector<SyncedExtensionInstaller> synced_extension_installers_; 200 201 DISALLOW_COPY_AND_ASSIGN(AppsMatchChecker); 202 }; 203 204 AppsMatchChecker::AppsMatchChecker(const std::vector<Profile*>& profiles) 205 : profiles_(profiles), observing_(false) { 206 DCHECK_GE(profiles_.size(), 2U); 207 } 208 209 AppsMatchChecker::~AppsMatchChecker() { 210 if (observing_) { 211 for (std::vector<Profile*>::iterator it = profiles_.begin(); 212 it != profiles_.end(); 213 ++it) { 214 extensions::ExtensionRegistry* registry = 215 extensions::ExtensionRegistry::Get(*it); 216 registry->RemoveObserver(this); 217 extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(*it); 218 prefs->RemoveObserver(this); 219 } 220 } 221 } 222 223 std::string AppsMatchChecker::GetDebugMessage() const { 224 return "Waiting for apps to match"; 225 } 226 227 bool AppsMatchChecker::IsExitConditionSatisfied() { 228 std::vector<Profile*>::iterator it = profiles_.begin(); 229 Profile* profile0 = *it; 230 ++it; 231 for (; it != profiles_.end(); ++it) { 232 if (!SyncAppHelper::GetInstance()->AppStatesMatch(profile0, *it)) { 233 return false; 234 } 235 } 236 return true; 237 } 238 239 void AppsMatchChecker::OnExtensionLoaded( 240 content::BrowserContext* context, 241 const extensions::Extension* extension) { 242 CheckExitCondition(); 243 } 244 245 void AppsMatchChecker::OnExtensionUnloaded( 246 content::BrowserContext* context, 247 const extensions::Extension* extenion, 248 extensions::UnloadedExtensionInfo::Reason reason) { 249 CheckExitCondition(); 250 } 251 252 void AppsMatchChecker::OnExtensionInstalled( 253 content::BrowserContext* browser_context, 254 const extensions::Extension* extension, 255 bool is_update) { 256 CheckExitCondition(); 257 } 258 259 void AppsMatchChecker::OnExtensionUninstalled( 260 content::BrowserContext* browser_context, 261 const extensions::Extension* extension, 262 extensions::UninstallReason reason) { 263 CheckExitCondition(); 264 } 265 266 void AppsMatchChecker::OnExtensionDisableReasonsChanged( 267 const std::string& extension_id, 268 int disabled_reasons) { 269 CheckExitCondition(); 270 } 271 272 void AppsMatchChecker::OnExtensionRegistered(const std::string& extension_id, 273 const base::Time& install_time, 274 bool is_enabled) { 275 CheckExitCondition(); 276 } 277 278 void AppsMatchChecker::OnExtensionPrefsLoaded( 279 const std::string& extension_id, 280 const extensions::ExtensionPrefs* prefs) { 281 CheckExitCondition(); 282 } 283 284 void AppsMatchChecker::OnExtensionPrefsDeleted( 285 const std::string& extension_id) { 286 CheckExitCondition(); 287 } 288 289 void AppsMatchChecker::OnExtensionStateChanged(const std::string& extension_id, 290 bool state) { 291 CheckExitCondition(); 292 } 293 294 void AppsMatchChecker::Observe(int type, 295 const content::NotificationSource& source, 296 const content::NotificationDetails& details) { 297 DCHECK_EQ(chrome::NOTIFICATION_APP_LAUNCHER_REORDERED, type); 298 CheckExitCondition(); 299 } 300 301 void AppsMatchChecker::Wait() { 302 for (std::vector<Profile*>::iterator it = profiles_.begin(); 303 it != profiles_.end(); 304 ++it) { 305 // Begin mocking the installation of synced extensions from the web store. 306 synced_extension_installers_.push_back(new SyncedExtensionInstaller(*it)); 307 308 // Register as an observer of ExtensionsRegistry to receive notifications of 309 // big events, like installs and uninstalls. 310 extensions::ExtensionRegistry* registry = 311 extensions::ExtensionRegistry::Get(*it); 312 registry->AddObserver(this); 313 314 // Register for ExtensionPrefs events, too, so we can get notifications 315 // about 316 // smaller but still syncable events, like launch type changes. 317 extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(*it); 318 prefs->AddObserver(this); 319 } 320 321 registrar_.Add(this, 322 chrome::NOTIFICATION_APP_LAUNCHER_REORDERED, 323 content::NotificationService::AllSources()); 324 325 observing_ = true; 326 327 if (IsExitConditionSatisfied()) { 328 DVLOG(1) << "Apps matched without waiting"; 329 return; 330 } 331 332 DVLOG(1) << "Starting Wait: " << GetDebugMessage(); 333 StartBlockingWait(); 334 } 335 336 } // namespace 337 338 bool AwaitAllProfilesHaveSameAppsAsVerifier() { 339 std::vector<Profile*> profiles; 340 profiles.push_back(test()->verifier()); 341 for (int i = 0; i < test()->num_clients(); ++i) { 342 profiles.push_back(test()->GetProfile(i)); 343 } 344 345 AppsMatchChecker checker(profiles); 346 checker.Wait(); 347 return !checker.TimedOut(); 348 } 349 350 } // namespace apps_helper 351