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
25using sync_datatype_helper::test;
26
27namespace {
28
29std::string CreateFakeAppName(int index) {
30  return "fakeapp" + base::IntToString(index);
31}
32
33}  // namespace
34
35namespace apps_helper {
36
37bool HasSameAppsAsVerifier(int index) {
38  return SyncAppHelper::GetInstance()->AppStatesMatch(
39      test()->GetProfile(index), test()->verifier());
40}
41
42bool 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
53std::string InstallApp(Profile* profile, int index) {
54  return SyncExtensionHelper::GetInstance()->InstallExtension(
55      profile,
56      CreateFakeAppName(index),
57      extensions::Manifest::TYPE_HOSTED_APP);
58}
59
60std::string InstallPlatformApp(Profile* profile, int index) {
61  return SyncExtensionHelper::GetInstance()->InstallExtension(
62      profile,
63      CreateFakeAppName(index),
64      extensions::Manifest::TYPE_PLATFORM_APP);
65}
66
67std::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
73void UninstallApp(Profile* profile, int index) {
74  return SyncExtensionHelper::GetInstance()->UninstallExtension(
75      profile, CreateFakeAppName(index));
76}
77
78void EnableApp(Profile* profile, int index) {
79  return SyncExtensionHelper::GetInstance()->EnableExtension(
80      profile, CreateFakeAppName(index));
81}
82
83void DisableApp(Profile* profile, int index) {
84  return SyncExtensionHelper::GetInstance()->DisableExtension(
85      profile, CreateFakeAppName(index));
86}
87
88void IncognitoEnableApp(Profile* profile, int index) {
89  return SyncExtensionHelper::GetInstance()->IncognitoEnableExtension(
90      profile, CreateFakeAppName(index));
91}
92
93void IncognitoDisableApp(Profile* profile, int index) {
94  return SyncExtensionHelper::GetInstance()->IncognitoDisableExtension(
95      profile, CreateFakeAppName(index));
96}
97
98void InstallAppsPendingForSync(Profile* profile) {
99  SyncExtensionHelper::GetInstance()->InstallExtensionsPendingForSync(profile);
100}
101
102syncer::StringOrdinal GetPageOrdinalForApp(Profile* profile,
103                                           int app_index) {
104  return SyncAppHelper::GetInstance()->GetPageOrdinalForApp(
105      profile, CreateFakeAppName(app_index));
106}
107
108void 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
115syncer::StringOrdinal GetAppLaunchOrdinalForApp(Profile* profile,
116                                                int app_index) {
117  return SyncAppHelper::GetInstance()->GetAppLaunchOrdinalForApp(
118      profile, CreateFakeAppName(app_index));
119}
120
121void 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
129void CopyNTPOrdinals(Profile* source, Profile* destination, int index) {
130  SetPageOrdinalForApp(destination, index, GetPageOrdinalForApp(source, index));
131  SetAppLaunchOrdinalForApp(
132      destination, index, GetAppLaunchOrdinalForApp(source, index));
133}
134
135void FixNTPOrdinalCollisions(Profile* profile) {
136  SyncAppHelper::GetInstance()->FixNTPOrdinalCollisions(profile);
137}
138
139namespace {
140
141// A helper class to implement waiting for a set of profiles to have matching
142// extensions lists.
143class 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
204AppsMatchChecker::AppsMatchChecker(const std::vector<Profile*>& profiles)
205    : profiles_(profiles), observing_(false) {
206  DCHECK_GE(profiles_.size(), 2U);
207}
208
209AppsMatchChecker::~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
223std::string AppsMatchChecker::GetDebugMessage() const {
224  return "Waiting for apps to match";
225}
226
227bool 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
239void AppsMatchChecker::OnExtensionLoaded(
240    content::BrowserContext* context,
241    const extensions::Extension* extension) {
242  CheckExitCondition();
243}
244
245void AppsMatchChecker::OnExtensionUnloaded(
246    content::BrowserContext* context,
247    const extensions::Extension* extenion,
248    extensions::UnloadedExtensionInfo::Reason reason) {
249  CheckExitCondition();
250}
251
252void AppsMatchChecker::OnExtensionInstalled(
253    content::BrowserContext* browser_context,
254    const extensions::Extension* extension,
255    bool is_update) {
256  CheckExitCondition();
257}
258
259void AppsMatchChecker::OnExtensionUninstalled(
260    content::BrowserContext* browser_context,
261    const extensions::Extension* extension,
262    extensions::UninstallReason reason) {
263  CheckExitCondition();
264}
265
266void AppsMatchChecker::OnExtensionDisableReasonsChanged(
267    const std::string& extension_id,
268    int disabled_reasons) {
269  CheckExitCondition();
270}
271
272void AppsMatchChecker::OnExtensionRegistered(const std::string& extension_id,
273                                             const base::Time& install_time,
274                                             bool is_enabled) {
275  CheckExitCondition();
276}
277
278void AppsMatchChecker::OnExtensionPrefsLoaded(
279    const std::string& extension_id,
280    const extensions::ExtensionPrefs* prefs) {
281  CheckExitCondition();
282}
283
284void AppsMatchChecker::OnExtensionPrefsDeleted(
285    const std::string& extension_id) {
286  CheckExitCondition();
287}
288
289void AppsMatchChecker::OnExtensionStateChanged(const std::string& extension_id,
290                                               bool state) {
291  CheckExitCondition();
292}
293
294void 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
301void 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
338bool 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