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.
5 #include "chrome/browser/extensions/extension_sync_service.h"
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_error_ui.h"
14 #include "chrome/browser/extensions/extension_prefs.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_sorting.h"
17 #include "chrome/browser/extensions/extension_sync_data.h"
18 #include "chrome/browser/extensions/extension_sync_service_factory.h"
19 #include "chrome/browser/extensions/extension_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sync/glue/sync_start_util.h"
22 #include "chrome/browser/sync/sync_prefs.h"
23 #include "chrome/common/extensions/extension.h"
24 #include "chrome/common/extensions/feature_switch.h"
25 #include "chrome/common/extensions/sync_helper.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "extensions/common/manifest_constants.h"
28 #include "sync/api/sync_change.h"
29 #include "sync/api/sync_error_factory.h"
31 using extensions::Extension;
32 using extensions::ExtensionPrefs;
33 using extensions::FeatureSwitch;
35 ExtensionSyncService::ExtensionSyncService(Profile* profile,
36 ExtensionPrefs* extension_prefs,
37 ExtensionService* extension_service)
39 extension_prefs_(extension_prefs),
40 extension_service_(extension_service),
41 app_sync_bundle_(this),
42 extension_sync_bundle_(this),
44 make_scoped_ptr(new browser_sync::SyncPrefs(
45 extension_prefs_->pref_service())),
48 pending_extension_enables_(
49 make_scoped_ptr(new browser_sync::SyncPrefs(
50 extension_prefs_->pref_service())),
51 &extension_sync_bundle_,
53 SetSyncStartFlare(sync_start_util::GetFlareForSyncableService(
54 profile_->GetPath()));
56 extension_service_->set_extension_sync_service(this);
57 extension_prefs_->extension_sorting()->SetExtensionSyncService(this);
60 ExtensionSyncService::~ExtensionSyncService() {}
63 ExtensionSyncService* ExtensionSyncService::Get(Profile* profile) {
64 return ExtensionSyncServiceFactory::GetForProfile(profile);
67 syncer::SyncChange ExtensionSyncService::PrepareToSyncUninstallExtension(
68 const extensions::Extension* extension, bool extensions_ready) {
69 // Extract the data we need for sync now, but don't actually sync until we've
70 // completed the uninstallation.
71 // TODO(tim): If we get here and IsSyncing is false, this will cause
72 // "back from the dead" style bugs, because sync will add-back the extension
73 // that was uninstalled here when MergeDataAndStartSyncing is called.
74 // See crbug.com/256795.
75 if (extensions::sync_helper::IsSyncableApp(extension)) {
76 if (app_sync_bundle_.IsSyncing())
77 return app_sync_bundle_.CreateSyncChangeToDelete(extension);
78 else if (extensions_ready && !flare_.is_null())
79 flare_.Run(syncer::APPS); // Tell sync to start ASAP.
80 } else if (extensions::sync_helper::IsSyncableExtension(extension)) {
81 if (extension_sync_bundle_.IsSyncing())
82 return extension_sync_bundle_.CreateSyncChangeToDelete(extension);
83 else if (extensions_ready && !flare_.is_null())
84 flare_.Run(syncer::EXTENSIONS); // Tell sync to start ASAP.
87 return syncer::SyncChange();
90 void ExtensionSyncService::ProcessSyncUninstallExtension(
91 const std::string& extension_id,
92 const syncer::SyncChange& sync_change) {
93 if (app_sync_bundle_.HasExtensionId(extension_id) &&
94 sync_change.sync_data().GetDataType() == syncer::APPS) {
95 app_sync_bundle_.ProcessDeletion(extension_id, sync_change);
96 } else if (extension_sync_bundle_.HasExtensionId(extension_id) &&
97 sync_change.sync_data().GetDataType() == syncer::EXTENSIONS) {
98 extension_sync_bundle_.ProcessDeletion(extension_id, sync_change);
102 void ExtensionSyncService::SyncEnableExtension(
103 const extensions::Extension& extension) {
105 // Syncing may not have started yet, so handle pending enables.
106 if (extensions::sync_helper::IsSyncableApp(&extension))
107 pending_app_enables_.OnExtensionEnabled(extension.id());
109 if (extensions::sync_helper::IsSyncableExtension(&extension))
110 pending_extension_enables_.OnExtensionEnabled(extension.id());
112 SyncExtensionChangeIfNeeded(extension);
115 void ExtensionSyncService::SyncDisableExtension(
116 const extensions::Extension& extension) {
118 // Syncing may not have started yet, so handle pending enables.
119 if (extensions::sync_helper::IsSyncableApp(&extension))
120 pending_app_enables_.OnExtensionDisabled(extension.id());
122 if (extensions::sync_helper::IsSyncableExtension(&extension))
123 pending_extension_enables_.OnExtensionDisabled(extension.id());
125 SyncExtensionChangeIfNeeded(extension);
128 syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
129 syncer::ModelType type,
130 const syncer::SyncDataList& initial_sync_data,
131 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
132 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
133 CHECK(sync_processor.get());
134 CHECK(sync_error_factory.get());
137 case syncer::EXTENSIONS:
138 extension_sync_bundle_.SetupSync(sync_processor.release(),
139 sync_error_factory.release(),
141 pending_extension_enables_.OnSyncStarted(extension_service_);
145 app_sync_bundle_.SetupSync(sync_processor.release(),
146 sync_error_factory.release(),
148 pending_app_enables_.OnSyncStarted(extension_service_);
152 LOG(FATAL) << "Got " << type << " ModelType";
155 // Process local extensions.
156 // TODO(yoz): Determine whether pending extensions should be considered too.
157 // See crbug.com/104399.
158 syncer::SyncDataList sync_data_list = GetAllSyncData(type);
159 syncer::SyncChangeList sync_change_list;
160 for (syncer::SyncDataList::const_iterator i = sync_data_list.begin();
161 i != sync_data_list.end();
164 case syncer::EXTENSIONS:
165 sync_change_list.push_back(
166 extension_sync_bundle_.CreateSyncChange(*i));
169 sync_change_list.push_back(app_sync_bundle_.CreateSyncChange(*i));
172 LOG(FATAL) << "Got " << type << " ModelType";
177 if (type == syncer::EXTENSIONS) {
178 extension_sync_bundle_.ProcessSyncChangeList(sync_change_list);
179 } else if (type == syncer::APPS) {
180 app_sync_bundle_.ProcessSyncChangeList(sync_change_list);
183 return syncer::SyncMergeResult(type);
186 void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
187 if (type == syncer::APPS) {
188 app_sync_bundle_.Reset();
189 } else if (type == syncer::EXTENSIONS) {
190 extension_sync_bundle_.Reset();
194 syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
195 syncer::ModelType type) const {
196 if (type == syncer::EXTENSIONS)
197 return extension_sync_bundle_.GetAllSyncData();
198 if (type == syncer::APPS)
199 return app_sync_bundle_.GetAllSyncData();
201 // We should only get sync data for extensions and apps.
204 return syncer::SyncDataList();
207 syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
208 const tracked_objects::Location& from_here,
209 const syncer::SyncChangeList& change_list) {
210 for (syncer::SyncChangeList::const_iterator i = change_list.begin();
211 i != change_list.end();
213 syncer::ModelType type = i->sync_data().GetDataType();
214 if (type == syncer::EXTENSIONS) {
215 extension_sync_bundle_.ProcessSyncChange(
216 extensions::ExtensionSyncData(*i));
217 } else if (type == syncer::APPS) {
218 app_sync_bundle_.ProcessSyncChange(extensions::AppSyncData(*i));
222 extension_prefs_->extension_sorting()->FixNTPOrdinalCollisions();
224 return syncer::SyncError();
227 extensions::ExtensionSyncData ExtensionSyncService::GetExtensionSyncData(
228 const Extension& extension) const {
229 return extensions::ExtensionSyncData(
231 extension_service_->IsExtensionEnabled(extension.id()),
232 extension_util::IsIncognitoEnabled(extension.id(), extension_service_));
235 extensions::AppSyncData ExtensionSyncService::GetAppSyncData(
236 const Extension& extension) const {
237 return extensions::AppSyncData(
239 extension_service_->IsExtensionEnabled(extension.id()),
240 extension_util::IsIncognitoEnabled(extension.id(), extension_service_),
241 extension_prefs_->extension_sorting()->GetAppLaunchOrdinal(
243 extension_prefs_->extension_sorting()->GetPageOrdinal(extension.id()));
246 std::vector<extensions::ExtensionSyncData>
247 ExtensionSyncService::GetExtensionSyncDataList() const {
248 std::vector<extensions::ExtensionSyncData> extension_sync_list;
249 extension_sync_bundle_.GetExtensionSyncDataListHelper(
250 extension_service_->extensions(), &extension_sync_list);
251 extension_sync_bundle_.GetExtensionSyncDataListHelper(
252 extension_service_->disabled_extensions(), &extension_sync_list);
253 extension_sync_bundle_.GetExtensionSyncDataListHelper(
254 extension_service_->terminated_extensions(), &extension_sync_list);
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());
262 return extension_sync_list;
265 std::vector<extensions::AppSyncData> ExtensionSyncService::GetAppSyncDataList()
267 std::vector<extensions::AppSyncData> app_sync_list;
268 app_sync_bundle_.GetAppSyncDataListHelper(
269 extension_service_->extensions(), &app_sync_list);
270 app_sync_bundle_.GetAppSyncDataListHelper(
271 extension_service_->disabled_extensions(), &app_sync_list);
272 app_sync_bundle_.GetAppSyncDataListHelper(
273 extension_service_->terminated_extensions(), &app_sync_list);
275 std::vector<extensions::AppSyncData> pending_apps =
276 app_sync_bundle_.GetPendingData();
277 app_sync_list.insert(app_sync_list.begin(),
278 pending_apps.begin(),
281 return app_sync_list;
284 bool ExtensionSyncService::ProcessExtensionSyncData(
285 const extensions::ExtensionSyncData& extension_sync_data) {
286 if (!ProcessExtensionSyncDataHelper(extension_sync_data,
287 syncer::EXTENSIONS)) {
288 extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(),
289 extension_sync_data);
290 extension_service_->CheckForUpdatesSoon();
297 bool ExtensionSyncService::ProcessAppSyncData(
298 const extensions::AppSyncData& app_sync_data) {
299 const std::string& id = app_sync_data.id();
301 if (app_sync_data.app_launch_ordinal().IsValid() &&
302 app_sync_data.page_ordinal().IsValid()) {
303 extension_prefs_->extension_sorting()->SetAppLaunchOrdinal(
305 app_sync_data.app_launch_ordinal());
306 extension_prefs_->extension_sorting()->SetPageOrdinal(
308 app_sync_data.page_ordinal());
311 if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(),
313 app_sync_bundle_.AddPendingApp(id, app_sync_data);
314 extension_service_->CheckForUpdatesSoon();
321 void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
322 const extensions::Extension* ext = extension_service_->GetInstalledExtension(
326 SyncExtensionChangeIfNeeded(*ext);
329 void ExtensionSyncService::SetSyncStartFlare(
330 const syncer::SyncableService::StartSyncFlare& flare) {
334 bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension,
335 syncer::ModelType type) const {
336 if (type == syncer::EXTENSIONS &&
337 extensions::sync_helper::IsSyncableExtension(&extension)) {
341 if (type == syncer::APPS &&
342 extensions::sync_helper::IsSyncableApp(&extension)) {
349 bool ExtensionSyncService::IsPendingEnable(
350 const std::string& extension_id) const {
351 return pending_app_enables_.Contains(extension_id) ||
352 pending_extension_enables_.Contains(extension_id);
355 bool ExtensionSyncService::ProcessExtensionSyncDataHelper(
356 const extensions::ExtensionSyncData& extension_sync_data,
357 syncer::ModelType type) {
358 const std::string& id = extension_sync_data.id();
359 const Extension* extension = extension_service_->GetInstalledExtension(id);
361 // TODO(bolms): we should really handle this better. The particularly bad
362 // case is where an app becomes an extension or vice versa, and we end up with
363 // a zombie extension that won't go away.
364 if (extension && !IsCorrectSyncType(*extension, type))
367 // Handle uninstalls first.
368 if (extension_sync_data.uninstalled()) {
369 if (!extension_service_->UninstallExtensionHelper(extension_service_, id)) {
370 LOG(WARNING) << "Could not uninstall extension " << id
376 // Extension from sync was uninstalled by the user as external extensions.
377 // Honor user choice and skip installation/enabling.
378 if (extension_service_->IsExternalExtensionUninstalled(id)) {
379 LOG(WARNING) << "Extension with id " << id
380 << " from sync was uninstalled as external extension";
384 // Set user settings.
385 // If the extension has been disabled from sync, it may not have
386 // been installed yet, so we don't know if the disable reason was a
387 // permissions increase. That will be updated once CheckPermissionsIncrease
389 if (extension_sync_data.enabled())
390 extension_service_->EnableExtension(id);
391 else if (!IsPendingEnable(id))
392 extension_service_->DisableExtension(
393 id, Extension::DISABLE_UNKNOWN_FROM_SYNC);
395 // We need to cache some version information here because setting the
396 // incognito flag invalidates the |extension| pointer (it reloads the
398 bool extension_installed = (extension != NULL);
399 int result = extension ?
400 extension->version()->CompareTo(extension_sync_data.version()) : 0;
401 extension_util::SetIsIncognitoEnabled(
402 id, extension_service_, extension_sync_data.incognito_enabled());
403 extension = NULL; // No longer safe to use.
405 if (extension_installed) {
406 // If the extension is already installed, check if it's outdated.
408 // Extension is outdated.
412 // TODO(akalin): Replace silent update with a list of enabled
414 const bool kInstallSilently = true;
416 CHECK(type == syncer::EXTENSIONS || type == syncer::APPS);
417 extensions::PendingExtensionInfo::ShouldAllowInstallPredicate filter =
418 (type == syncer::APPS) ? extensions::sync_helper::IsSyncableApp :
419 extensions::sync_helper::IsSyncableExtension;
421 if (!extension_service_->pending_extension_manager()->AddFromSync(
423 extension_sync_data.update_url(),
426 LOG(WARNING) << "Could not add pending extension for " << id;
427 // This means that the extension is already pending installation, with a
428 // non-INTERNAL location. Add to pending_sync_data, even though it will
429 // never be removed (we'll never install a syncable version of the
430 // extension), so that GetAllSyncData() continues to send it.
432 // Track pending extensions so that we can return them in GetAllSyncData().
439 void ExtensionSyncService::SyncExtensionChangeIfNeeded(
440 const Extension& extension) {
441 if (extensions::sync_helper::IsSyncableApp(&extension)) {
442 if (app_sync_bundle_.IsSyncing())
443 app_sync_bundle_.SyncChangeIfNeeded(extension);
444 else if (extension_service_->is_ready() && !flare_.is_null())
445 flare_.Run(syncer::APPS);
446 } else if (extensions::sync_helper::IsSyncableExtension(&extension)) {
447 if (extension_sync_bundle_.IsSyncing())
448 extension_sync_bundle_.SyncChangeIfNeeded(extension);
449 else if (extension_service_->is_ready() && !flare_.is_null())
450 flare_.Run(syncer::EXTENSIONS);