extension_sync_service.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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/extensions/extension_sync_service.h"
6
7#include <iterator>
8
9#include "base/basictypes.h"
10#include "base/threading/sequenced_worker_pool.h"
11#include "base/threading/thread_restrictions.h"
12#include "chrome/browser/extensions/app_sync_data.h"
13#include "chrome/browser/extensions/extension_service.h"
14#include "chrome/browser/extensions/extension_sync_data.h"
15#include "chrome/browser/extensions/extension_sync_service_factory.h"
16#include "chrome/browser/extensions/extension_util.h"
17#include "chrome/browser/extensions/launch_util.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/browser/sync/glue/sync_start_util.h"
20#include "chrome/common/extensions/sync_helper.h"
21#include "components/sync_driver/sync_prefs.h"
22#include "extensions/browser/app_sorting.h"
23#include "extensions/browser/extension_prefs.h"
24#include "extensions/browser/extension_registry.h"
25#include "extensions/common/extension.h"
26#include "extensions/common/feature_switch.h"
27#include "extensions/common/manifest_constants.h"
28#include "sync/api/sync_change.h"
29#include "sync/api/sync_error_factory.h"
30
31using extensions::Extension;
32using extensions::ExtensionPrefs;
33using extensions::ExtensionRegistry;
34using extensions::FeatureSwitch;
35
36ExtensionSyncService::ExtensionSyncService(Profile* profile,
37                                           ExtensionPrefs* extension_prefs,
38                                           ExtensionService* extension_service)
39    : profile_(profile),
40      extension_prefs_(extension_prefs),
41      extension_service_(extension_service),
42      app_sync_bundle_(this),
43      extension_sync_bundle_(this),
44      pending_app_enables_(make_scoped_ptr(new sync_driver::SyncPrefs(
45                               extension_prefs_->pref_service())),
46                           &app_sync_bundle_,
47                           syncer::APPS),
48      pending_extension_enables_(make_scoped_ptr(new sync_driver::SyncPrefs(
49                                     extension_prefs_->pref_service())),
50                                 &extension_sync_bundle_,
51                                 syncer::EXTENSIONS) {
52  SetSyncStartFlare(sync_start_util::GetFlareForSyncableService(
53      profile_->GetPath()));
54
55  extension_service_->set_extension_sync_service(this);
56  extension_prefs_->app_sorting()->SetExtensionSyncService(this);
57}
58
59ExtensionSyncService::~ExtensionSyncService() {}
60
61// static
62ExtensionSyncService* ExtensionSyncService::Get(Profile* profile) {
63  return ExtensionSyncServiceFactory::GetForProfile(profile);
64}
65
66syncer::SyncChange ExtensionSyncService::PrepareToSyncUninstallExtension(
67    const extensions::Extension* extension, bool extensions_ready) {
68  // Extract the data we need for sync now, but don't actually sync until we've
69  // completed the uninstallation.
70  // TODO(tim): If we get here and IsSyncing is false, this will cause
71  // "back from the dead" style bugs, because sync will add-back the extension
72  // that was uninstalled here when MergeDataAndStartSyncing is called.
73  // See crbug.com/256795.
74  if (extensions::sync_helper::IsSyncableApp(extension)) {
75    if (app_sync_bundle_.IsSyncing())
76      return app_sync_bundle_.CreateSyncChangeToDelete(extension);
77    else if (extensions_ready && !flare_.is_null())
78      flare_.Run(syncer::APPS);  // Tell sync to start ASAP.
79  } else if (extensions::sync_helper::IsSyncableExtension(extension)) {
80    if (extension_sync_bundle_.IsSyncing())
81      return extension_sync_bundle_.CreateSyncChangeToDelete(extension);
82    else if (extensions_ready && !flare_.is_null())
83      flare_.Run(syncer::EXTENSIONS);  // Tell sync to start ASAP.
84  }
85
86  return syncer::SyncChange();
87}
88
89void ExtensionSyncService::ProcessSyncUninstallExtension(
90    const std::string& extension_id,
91    const syncer::SyncChange& sync_change) {
92  if (app_sync_bundle_.HasExtensionId(extension_id) &&
93      sync_change.sync_data().GetDataType() == syncer::APPS) {
94    app_sync_bundle_.ProcessDeletion(extension_id, sync_change);
95  } else if (extension_sync_bundle_.HasExtensionId(extension_id) &&
96             sync_change.sync_data().GetDataType() == syncer::EXTENSIONS) {
97    extension_sync_bundle_.ProcessDeletion(extension_id, sync_change);
98  }
99}
100
101void ExtensionSyncService::SyncEnableExtension(
102    const extensions::Extension& extension) {
103
104  // Syncing may not have started yet, so handle pending enables.
105  if (extensions::sync_helper::IsSyncableApp(&extension))
106    pending_app_enables_.OnExtensionEnabled(extension.id());
107
108  if (extensions::sync_helper::IsSyncableExtension(&extension))
109    pending_extension_enables_.OnExtensionEnabled(extension.id());
110
111  SyncExtensionChangeIfNeeded(extension);
112}
113
114void ExtensionSyncService::SyncDisableExtension(
115    const extensions::Extension& extension) {
116
117  // Syncing may not have started yet, so handle pending enables.
118  if (extensions::sync_helper::IsSyncableApp(&extension))
119    pending_app_enables_.OnExtensionDisabled(extension.id());
120
121  if (extensions::sync_helper::IsSyncableExtension(&extension))
122    pending_extension_enables_.OnExtensionDisabled(extension.id());
123
124  SyncExtensionChangeIfNeeded(extension);
125}
126
127syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
128    syncer::ModelType type,
129    const syncer::SyncDataList& initial_sync_data,
130    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
131    scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
132  CHECK(sync_processor.get());
133  CHECK(sync_error_factory.get());
134
135  switch (type) {
136    case syncer::EXTENSIONS:
137      extension_sync_bundle_.SetupSync(sync_processor.release(),
138                                       sync_error_factory.release(),
139                                       initial_sync_data);
140      pending_extension_enables_.OnSyncStarted(extension_service_);
141      break;
142
143    case syncer::APPS:
144      app_sync_bundle_.SetupSync(sync_processor.release(),
145                                 sync_error_factory.release(),
146                                 initial_sync_data);
147      pending_app_enables_.OnSyncStarted(extension_service_);
148      break;
149
150    default:
151      LOG(FATAL) << "Got " << type << " ModelType";
152  }
153
154  // Process local extensions.
155  // TODO(yoz): Determine whether pending extensions should be considered too.
156  //            See crbug.com/104399.
157  syncer::SyncDataList sync_data_list = GetAllSyncData(type);
158  syncer::SyncChangeList sync_change_list;
159  for (syncer::SyncDataList::const_iterator i = sync_data_list.begin();
160       i != sync_data_list.end();
161       ++i) {
162    switch (type) {
163        case syncer::EXTENSIONS:
164          sync_change_list.push_back(
165              extension_sync_bundle_.CreateSyncChange(*i));
166          break;
167        case syncer::APPS:
168          sync_change_list.push_back(app_sync_bundle_.CreateSyncChange(*i));
169          break;
170      default:
171        LOG(FATAL) << "Got " << type << " ModelType";
172    }
173  }
174
175
176  if (type == syncer::EXTENSIONS) {
177    extension_sync_bundle_.ProcessSyncChangeList(sync_change_list);
178  } else if (type == syncer::APPS) {
179    app_sync_bundle_.ProcessSyncChangeList(sync_change_list);
180  }
181
182  return syncer::SyncMergeResult(type);
183}
184
185void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
186  if (type == syncer::APPS) {
187    app_sync_bundle_.Reset();
188  } else if (type == syncer::EXTENSIONS) {
189    extension_sync_bundle_.Reset();
190  }
191}
192
193syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
194    syncer::ModelType type) const {
195  if (type == syncer::EXTENSIONS)
196    return extension_sync_bundle_.GetAllSyncData();
197  if (type == syncer::APPS)
198    return app_sync_bundle_.GetAllSyncData();
199
200  // We should only get sync data for extensions and apps.
201  NOTREACHED();
202
203  return syncer::SyncDataList();
204}
205
206syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
207    const tracked_objects::Location& from_here,
208    const syncer::SyncChangeList& change_list) {
209  for (syncer::SyncChangeList::const_iterator i = change_list.begin();
210      i != change_list.end();
211      ++i) {
212    syncer::ModelType type = i->sync_data().GetDataType();
213    if (type == syncer::EXTENSIONS) {
214      extension_sync_bundle_.ProcessSyncChange(
215          extensions::ExtensionSyncData(*i));
216    } else if (type == syncer::APPS) {
217      app_sync_bundle_.ProcessSyncChange(extensions::AppSyncData(*i));
218    }
219  }
220
221  extension_prefs_->app_sorting()->FixNTPOrdinalCollisions();
222
223  return syncer::SyncError();
224}
225
226extensions::ExtensionSyncData ExtensionSyncService::GetExtensionSyncData(
227    const Extension& extension) const {
228  return extensions::ExtensionSyncData(
229      extension,
230      extension_service_->IsExtensionEnabled(extension.id()),
231      extensions::util::IsIncognitoEnabled(extension.id(), profile_));
232}
233
234extensions::AppSyncData ExtensionSyncService::GetAppSyncData(
235    const Extension& extension) const {
236  return extensions::AppSyncData(
237      extension,
238      extension_service_->IsExtensionEnabled(extension.id()),
239      extensions::util::IsIncognitoEnabled(extension.id(), profile_),
240      extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
241      extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()),
242      extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id()));
243}
244
245std::vector<extensions::ExtensionSyncData>
246  ExtensionSyncService::GetExtensionSyncDataList() const {
247  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
248  std::vector<extensions::ExtensionSyncData> extension_sync_list;
249  extension_sync_bundle_.GetExtensionSyncDataListHelper(
250      registry->enabled_extensions(), &extension_sync_list);
251  extension_sync_bundle_.GetExtensionSyncDataListHelper(
252      registry->disabled_extensions(), &extension_sync_list);
253  extension_sync_bundle_.GetExtensionSyncDataListHelper(
254      registry->terminated_extensions(), &extension_sync_list);
255
256  std::vector<extensions::ExtensionSyncData> pending_extensions =
257      extension_sync_bundle_.GetPendingData();
258  extension_sync_list.insert(extension_sync_list.begin(),
259                             pending_extensions.begin(),
260                             pending_extensions.end());
261
262  return extension_sync_list;
263}
264
265std::vector<extensions::AppSyncData> ExtensionSyncService::GetAppSyncDataList()
266    const {
267  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
268  std::vector<extensions::AppSyncData> app_sync_list;
269  app_sync_bundle_.GetAppSyncDataListHelper(
270      registry->enabled_extensions(), &app_sync_list);
271  app_sync_bundle_.GetAppSyncDataListHelper(
272      registry->disabled_extensions(), &app_sync_list);
273  app_sync_bundle_.GetAppSyncDataListHelper(
274      registry->terminated_extensions(), &app_sync_list);
275
276  std::vector<extensions::AppSyncData> pending_apps =
277      app_sync_bundle_.GetPendingData();
278  app_sync_list.insert(app_sync_list.begin(),
279                       pending_apps.begin(),
280                       pending_apps.end());
281
282  return app_sync_list;
283}
284
285bool ExtensionSyncService::ProcessExtensionSyncData(
286    const extensions::ExtensionSyncData& extension_sync_data) {
287  if (!ProcessExtensionSyncDataHelper(extension_sync_data,
288                                      syncer::EXTENSIONS)) {
289    extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(),
290                                               extension_sync_data);
291    extension_service_->CheckForUpdatesSoon();
292    return false;
293  }
294
295  return true;
296}
297
298bool ExtensionSyncService::ProcessAppSyncData(
299    const extensions::AppSyncData& app_sync_data) {
300  const std::string& id = app_sync_data.id();
301
302  if (app_sync_data.app_launch_ordinal().IsValid() &&
303      app_sync_data.page_ordinal().IsValid()) {
304    extension_prefs_->app_sorting()->SetAppLaunchOrdinal(
305        id,
306        app_sync_data.app_launch_ordinal());
307    extension_prefs_->app_sorting()->SetPageOrdinal(
308        id,
309        app_sync_data.page_ordinal());
310  }
311
312  // The corresponding validation of this value during AppSyncData population
313  // is in AppSyncData::PopulateAppSpecifics.
314  if (app_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
315      app_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
316    extensions::SetLaunchType(extension_service_, id,
317                              app_sync_data.launch_type());
318  }
319
320  if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(),
321                                      syncer::APPS)) {
322    app_sync_bundle_.AddPendingApp(id, app_sync_data);
323    extension_service_->CheckForUpdatesSoon();
324    return false;
325  }
326
327  return true;
328}
329
330void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
331  const extensions::Extension* ext = extension_service_->GetInstalledExtension(
332      extension_id);
333
334  if (ext)
335    SyncExtensionChangeIfNeeded(*ext);
336}
337
338void ExtensionSyncService::SetSyncStartFlare(
339    const syncer::SyncableService::StartSyncFlare& flare) {
340  flare_ = flare;
341}
342
343bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension,
344                                         syncer::ModelType type) const {
345  if (type == syncer::EXTENSIONS &&
346      extensions::sync_helper::IsSyncableExtension(&extension)) {
347    return true;
348  }
349
350  if (type == syncer::APPS &&
351      extensions::sync_helper::IsSyncableApp(&extension)) {
352    return true;
353  }
354
355  return false;
356}
357
358bool ExtensionSyncService::IsPendingEnable(
359    const std::string& extension_id) const {
360  return pending_app_enables_.Contains(extension_id) ||
361      pending_extension_enables_.Contains(extension_id);
362}
363
364bool ExtensionSyncService::ProcessExtensionSyncDataHelper(
365    const extensions::ExtensionSyncData& extension_sync_data,
366    syncer::ModelType type) {
367  const std::string& id = extension_sync_data.id();
368  const Extension* extension = extension_service_->GetInstalledExtension(id);
369
370  // TODO(bolms): we should really handle this better.  The particularly bad
371  // case is where an app becomes an extension or vice versa, and we end up with
372  // a zombie extension that won't go away.
373  if (extension && !IsCorrectSyncType(*extension, type))
374    return true;
375
376  // Handle uninstalls first.
377  if (extension_sync_data.uninstalled()) {
378    if (!extension_service_->UninstallExtensionHelper(extension_service_, id)) {
379      LOG(WARNING) << "Could not uninstall extension " << id
380                   << " for sync";
381    }
382    return true;
383  }
384
385  // Extension from sync was uninstalled by the user as external extensions.
386  // Honor user choice and skip installation/enabling.
387  if (extensions::ExtensionPrefs::Get(profile_)
388          ->IsExternalExtensionUninstalled(id)) {
389    LOG(WARNING) << "Extension with id " << id
390                 << " from sync was uninstalled as external extension";
391    return true;
392  }
393
394  // Set user settings.
395  // If the extension has been disabled from sync, it may not have
396  // been installed yet, so we don't know if the disable reason was a
397  // permissions increase.  That will be updated once CheckPermissionsIncrease
398  // is called for it.
399  if (extension_sync_data.enabled())
400    extension_service_->EnableExtension(id);
401  else if (!IsPendingEnable(id))
402    extension_service_->DisableExtension(
403        id, Extension::DISABLE_UNKNOWN_FROM_SYNC);
404
405  // We need to cache some version information here because setting the
406  // incognito flag invalidates the |extension| pointer (it reloads the
407  // extension).
408  bool extension_installed = (extension != NULL);
409  int result = extension ?
410      extension->version()->CompareTo(extension_sync_data.version()) : 0;
411  extensions::util::SetIsIncognitoEnabled(
412      id, profile_, extension_sync_data.incognito_enabled());
413  extension = NULL;  // No longer safe to use.
414
415  if (extension_installed) {
416    // If the extension is already installed, check if it's outdated.
417    if (result < 0) {
418      // Extension is outdated.
419      return false;
420    }
421  } else {
422    // TODO(akalin): Replace silent update with a list of enabled
423    // permissions.
424    const bool kInstallSilently = true;
425
426    CHECK(type == syncer::EXTENSIONS || type == syncer::APPS);
427    extensions::PendingExtensionInfo::ShouldAllowInstallPredicate filter =
428        (type == syncer::APPS) ? extensions::sync_helper::IsSyncableApp :
429                                 extensions::sync_helper::IsSyncableExtension;
430
431    if (!extension_service_->pending_extension_manager()->AddFromSync(
432            id,
433            extension_sync_data.update_url(),
434            filter,
435            kInstallSilently)) {
436      LOG(WARNING) << "Could not add pending extension for " << id;
437      // This means that the extension is already pending installation, with a
438      // non-INTERNAL location.  Add to pending_sync_data, even though it will
439      // never be removed (we'll never install a syncable version of the
440      // extension), so that GetAllSyncData() continues to send it.
441    }
442    // Track pending extensions so that we can return them in GetAllSyncData().
443    return false;
444  }
445
446  return true;
447}
448
449void ExtensionSyncService::SyncExtensionChangeIfNeeded(
450    const Extension& extension) {
451  if (extensions::sync_helper::IsSyncableApp(&extension)) {
452    if (app_sync_bundle_.IsSyncing())
453      app_sync_bundle_.SyncChangeIfNeeded(extension);
454    else if (extension_service_->is_ready() && !flare_.is_null())
455      flare_.Run(syncer::APPS);
456  } else if (extensions::sync_helper::IsSyncableExtension(&extension)) {
457    if (extension_sync_bundle_.IsSyncing())
458      extension_sync_bundle_.SyncChangeIfNeeded(extension);
459    else if (extension_service_->is_ready() && !flare_.is_null())
460      flare_.Run(syncer::EXTENSIONS);
461  }
462}
463