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_service.h"
15 #include "chrome/browser/extensions/extension_sync_data.h"
16 #include "chrome/browser/extensions/extension_sync_service_factory.h"
17 #include "chrome/browser/extensions/extension_util.h"
18 #include "chrome/browser/extensions/launch_util.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/sync/glue/sync_start_util.h"
21 #include "chrome/browser/sync/sync_prefs.h"
22 #include "chrome/common/extensions/sync_helper.h"
23 #include "extensions/browser/app_sorting.h"
24 #include "extensions/browser/extension_prefs.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/feature_switch.h"
28 #include "extensions/common/manifest_constants.h"
29 #include "sync/api/sync_change.h"
30 #include "sync/api/sync_error_factory.h"
32 using extensions::Extension
;
33 using extensions::ExtensionPrefs
;
34 using extensions::ExtensionRegistry
;
35 using extensions::FeatureSwitch
;
37 ExtensionSyncService::ExtensionSyncService(Profile
* profile
,
38 ExtensionPrefs
* extension_prefs
,
39 ExtensionService
* extension_service
)
41 extension_prefs_(extension_prefs
),
42 extension_service_(extension_service
),
43 app_sync_bundle_(this),
44 extension_sync_bundle_(this),
46 make_scoped_ptr(new browser_sync::SyncPrefs(
47 extension_prefs_
->pref_service())),
50 pending_extension_enables_(
51 make_scoped_ptr(new browser_sync::SyncPrefs(
52 extension_prefs_
->pref_service())),
53 &extension_sync_bundle_
,
55 SetSyncStartFlare(sync_start_util::GetFlareForSyncableService(
56 profile_
->GetPath()));
58 extension_service_
->set_extension_sync_service(this);
59 extension_prefs_
->app_sorting()->SetExtensionSyncService(this);
62 ExtensionSyncService::~ExtensionSyncService() {}
65 ExtensionSyncService
* ExtensionSyncService::Get(Profile
* profile
) {
66 return ExtensionSyncServiceFactory::GetForProfile(profile
);
69 syncer::SyncChange
ExtensionSyncService::PrepareToSyncUninstallExtension(
70 const extensions::Extension
* extension
, bool extensions_ready
) {
71 // Extract the data we need for sync now, but don't actually sync until we've
72 // completed the uninstallation.
73 // TODO(tim): If we get here and IsSyncing is false, this will cause
74 // "back from the dead" style bugs, because sync will add-back the extension
75 // that was uninstalled here when MergeDataAndStartSyncing is called.
76 // See crbug.com/256795.
77 if (extensions::sync_helper::IsSyncableApp(extension
)) {
78 if (app_sync_bundle_
.IsSyncing())
79 return app_sync_bundle_
.CreateSyncChangeToDelete(extension
);
80 else if (extensions_ready
&& !flare_
.is_null())
81 flare_
.Run(syncer::APPS
); // Tell sync to start ASAP.
82 } else if (extensions::sync_helper::IsSyncableExtension(extension
)) {
83 if (extension_sync_bundle_
.IsSyncing())
84 return extension_sync_bundle_
.CreateSyncChangeToDelete(extension
);
85 else if (extensions_ready
&& !flare_
.is_null())
86 flare_
.Run(syncer::EXTENSIONS
); // Tell sync to start ASAP.
89 return syncer::SyncChange();
92 void ExtensionSyncService::ProcessSyncUninstallExtension(
93 const std::string
& extension_id
,
94 const syncer::SyncChange
& sync_change
) {
95 if (app_sync_bundle_
.HasExtensionId(extension_id
) &&
96 sync_change
.sync_data().GetDataType() == syncer::APPS
) {
97 app_sync_bundle_
.ProcessDeletion(extension_id
, sync_change
);
98 } else if (extension_sync_bundle_
.HasExtensionId(extension_id
) &&
99 sync_change
.sync_data().GetDataType() == syncer::EXTENSIONS
) {
100 extension_sync_bundle_
.ProcessDeletion(extension_id
, sync_change
);
104 void ExtensionSyncService::SyncEnableExtension(
105 const extensions::Extension
& extension
) {
107 // Syncing may not have started yet, so handle pending enables.
108 if (extensions::sync_helper::IsSyncableApp(&extension
))
109 pending_app_enables_
.OnExtensionEnabled(extension
.id());
111 if (extensions::sync_helper::IsSyncableExtension(&extension
))
112 pending_extension_enables_
.OnExtensionEnabled(extension
.id());
114 SyncExtensionChangeIfNeeded(extension
);
117 void ExtensionSyncService::SyncDisableExtension(
118 const extensions::Extension
& extension
) {
120 // Syncing may not have started yet, so handle pending enables.
121 if (extensions::sync_helper::IsSyncableApp(&extension
))
122 pending_app_enables_
.OnExtensionDisabled(extension
.id());
124 if (extensions::sync_helper::IsSyncableExtension(&extension
))
125 pending_extension_enables_
.OnExtensionDisabled(extension
.id());
127 SyncExtensionChangeIfNeeded(extension
);
130 syncer::SyncMergeResult
ExtensionSyncService::MergeDataAndStartSyncing(
131 syncer::ModelType type
,
132 const syncer::SyncDataList
& initial_sync_data
,
133 scoped_ptr
<syncer::SyncChangeProcessor
> sync_processor
,
134 scoped_ptr
<syncer::SyncErrorFactory
> sync_error_factory
) {
135 CHECK(sync_processor
.get());
136 CHECK(sync_error_factory
.get());
139 case syncer::EXTENSIONS
:
140 extension_sync_bundle_
.SetupSync(sync_processor
.release(),
141 sync_error_factory
.release(),
143 pending_extension_enables_
.OnSyncStarted(extension_service_
);
147 app_sync_bundle_
.SetupSync(sync_processor
.release(),
148 sync_error_factory
.release(),
150 pending_app_enables_
.OnSyncStarted(extension_service_
);
154 LOG(FATAL
) << "Got " << type
<< " ModelType";
157 // Process local extensions.
158 // TODO(yoz): Determine whether pending extensions should be considered too.
159 // See crbug.com/104399.
160 syncer::SyncDataList sync_data_list
= GetAllSyncData(type
);
161 syncer::SyncChangeList sync_change_list
;
162 for (syncer::SyncDataList::const_iterator i
= sync_data_list
.begin();
163 i
!= sync_data_list
.end();
166 case syncer::EXTENSIONS
:
167 sync_change_list
.push_back(
168 extension_sync_bundle_
.CreateSyncChange(*i
));
171 sync_change_list
.push_back(app_sync_bundle_
.CreateSyncChange(*i
));
174 LOG(FATAL
) << "Got " << type
<< " ModelType";
179 if (type
== syncer::EXTENSIONS
) {
180 extension_sync_bundle_
.ProcessSyncChangeList(sync_change_list
);
181 } else if (type
== syncer::APPS
) {
182 app_sync_bundle_
.ProcessSyncChangeList(sync_change_list
);
185 return syncer::SyncMergeResult(type
);
188 void ExtensionSyncService::StopSyncing(syncer::ModelType type
) {
189 if (type
== syncer::APPS
) {
190 app_sync_bundle_
.Reset();
191 } else if (type
== syncer::EXTENSIONS
) {
192 extension_sync_bundle_
.Reset();
196 syncer::SyncDataList
ExtensionSyncService::GetAllSyncData(
197 syncer::ModelType type
) const {
198 if (type
== syncer::EXTENSIONS
)
199 return extension_sync_bundle_
.GetAllSyncData();
200 if (type
== syncer::APPS
)
201 return app_sync_bundle_
.GetAllSyncData();
203 // We should only get sync data for extensions and apps.
206 return syncer::SyncDataList();
209 syncer::SyncError
ExtensionSyncService::ProcessSyncChanges(
210 const tracked_objects::Location
& from_here
,
211 const syncer::SyncChangeList
& change_list
) {
212 for (syncer::SyncChangeList::const_iterator i
= change_list
.begin();
213 i
!= change_list
.end();
215 syncer::ModelType type
= i
->sync_data().GetDataType();
216 if (type
== syncer::EXTENSIONS
) {
217 extension_sync_bundle_
.ProcessSyncChange(
218 extensions::ExtensionSyncData(*i
));
219 } else if (type
== syncer::APPS
) {
220 app_sync_bundle_
.ProcessSyncChange(extensions::AppSyncData(*i
));
224 extension_prefs_
->app_sorting()->FixNTPOrdinalCollisions();
226 return syncer::SyncError();
229 extensions::ExtensionSyncData
ExtensionSyncService::GetExtensionSyncData(
230 const Extension
& extension
) const {
231 return extensions::ExtensionSyncData(
233 extension_service_
->IsExtensionEnabled(extension
.id()),
234 extensions::util::IsIncognitoEnabled(extension
.id(), profile_
));
237 extensions::AppSyncData
ExtensionSyncService::GetAppSyncData(
238 const Extension
& extension
) const {
239 return extensions::AppSyncData(
241 extension_service_
->IsExtensionEnabled(extension
.id()),
242 extensions::util::IsIncognitoEnabled(extension
.id(), profile_
),
243 extension_prefs_
->app_sorting()->GetAppLaunchOrdinal(extension
.id()),
244 extension_prefs_
->app_sorting()->GetPageOrdinal(extension
.id()),
245 extensions::GetLaunchTypePrefValue(extension_prefs_
, extension
.id()));
248 std::vector
<extensions::ExtensionSyncData
>
249 ExtensionSyncService::GetExtensionSyncDataList() const {
250 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
251 std::vector
<extensions::ExtensionSyncData
> extension_sync_list
;
252 extension_sync_bundle_
.GetExtensionSyncDataListHelper(
253 registry
->enabled_extensions(), &extension_sync_list
);
254 extension_sync_bundle_
.GetExtensionSyncDataListHelper(
255 registry
->disabled_extensions(), &extension_sync_list
);
256 extension_sync_bundle_
.GetExtensionSyncDataListHelper(
257 registry
->terminated_extensions(), &extension_sync_list
);
259 std::vector
<extensions::ExtensionSyncData
> pending_extensions
=
260 extension_sync_bundle_
.GetPendingData();
261 extension_sync_list
.insert(extension_sync_list
.begin(),
262 pending_extensions
.begin(),
263 pending_extensions
.end());
265 return extension_sync_list
;
268 std::vector
<extensions::AppSyncData
> ExtensionSyncService::GetAppSyncDataList()
270 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
271 std::vector
<extensions::AppSyncData
> app_sync_list
;
272 app_sync_bundle_
.GetAppSyncDataListHelper(
273 registry
->enabled_extensions(), &app_sync_list
);
274 app_sync_bundle_
.GetAppSyncDataListHelper(
275 registry
->disabled_extensions(), &app_sync_list
);
276 app_sync_bundle_
.GetAppSyncDataListHelper(
277 registry
->terminated_extensions(), &app_sync_list
);
279 std::vector
<extensions::AppSyncData
> pending_apps
=
280 app_sync_bundle_
.GetPendingData();
281 app_sync_list
.insert(app_sync_list
.begin(),
282 pending_apps
.begin(),
285 return app_sync_list
;
288 bool ExtensionSyncService::ProcessExtensionSyncData(
289 const extensions::ExtensionSyncData
& extension_sync_data
) {
290 if (!ProcessExtensionSyncDataHelper(extension_sync_data
,
291 syncer::EXTENSIONS
)) {
292 extension_sync_bundle_
.AddPendingExtension(extension_sync_data
.id(),
293 extension_sync_data
);
294 extension_service_
->CheckForUpdatesSoon();
301 bool ExtensionSyncService::ProcessAppSyncData(
302 const extensions::AppSyncData
& app_sync_data
) {
303 const std::string
& id
= app_sync_data
.id();
305 if (app_sync_data
.app_launch_ordinal().IsValid() &&
306 app_sync_data
.page_ordinal().IsValid()) {
307 extension_prefs_
->app_sorting()->SetAppLaunchOrdinal(
309 app_sync_data
.app_launch_ordinal());
310 extension_prefs_
->app_sorting()->SetPageOrdinal(
312 app_sync_data
.page_ordinal());
315 // The corresponding validation of this value during AppSyncData population
316 // is in AppSyncData::PopulateAppSpecifics.
317 if (app_sync_data
.launch_type() >= extensions::LAUNCH_TYPE_FIRST
&&
318 app_sync_data
.launch_type() < extensions::NUM_LAUNCH_TYPES
) {
319 extensions::SetLaunchType(extension_service_
, id
,
320 app_sync_data
.launch_type());
323 if (!ProcessExtensionSyncDataHelper(app_sync_data
.extension_sync_data(),
325 app_sync_bundle_
.AddPendingApp(id
, app_sync_data
);
326 extension_service_
->CheckForUpdatesSoon();
333 void ExtensionSyncService::SyncOrderingChange(const std::string
& extension_id
) {
334 const extensions::Extension
* ext
= extension_service_
->GetInstalledExtension(
338 SyncExtensionChangeIfNeeded(*ext
);
341 void ExtensionSyncService::SetSyncStartFlare(
342 const syncer::SyncableService::StartSyncFlare
& flare
) {
346 bool ExtensionSyncService::IsCorrectSyncType(const Extension
& extension
,
347 syncer::ModelType type
) const {
348 if (type
== syncer::EXTENSIONS
&&
349 extensions::sync_helper::IsSyncableExtension(&extension
)) {
353 if (type
== syncer::APPS
&&
354 extensions::sync_helper::IsSyncableApp(&extension
)) {
361 bool ExtensionSyncService::IsPendingEnable(
362 const std::string
& extension_id
) const {
363 return pending_app_enables_
.Contains(extension_id
) ||
364 pending_extension_enables_
.Contains(extension_id
);
367 bool ExtensionSyncService::ProcessExtensionSyncDataHelper(
368 const extensions::ExtensionSyncData
& extension_sync_data
,
369 syncer::ModelType type
) {
370 const std::string
& id
= extension_sync_data
.id();
371 const Extension
* extension
= extension_service_
->GetInstalledExtension(id
);
373 // TODO(bolms): we should really handle this better. The particularly bad
374 // case is where an app becomes an extension or vice versa, and we end up with
375 // a zombie extension that won't go away.
376 if (extension
&& !IsCorrectSyncType(*extension
, type
))
379 // Handle uninstalls first.
380 if (extension_sync_data
.uninstalled()) {
381 if (!extension_service_
->UninstallExtensionHelper(extension_service_
, id
)) {
382 LOG(WARNING
) << "Could not uninstall extension " << id
388 // Extension from sync was uninstalled by the user as external extensions.
389 // Honor user choice and skip installation/enabling.
390 if (extension_service_
->IsExternalExtensionUninstalled(id
)) {
391 LOG(WARNING
) << "Extension with id " << id
392 << " from sync was uninstalled as external extension";
396 // Set user settings.
397 // If the extension has been disabled from sync, it may not have
398 // been installed yet, so we don't know if the disable reason was a
399 // permissions increase. That will be updated once CheckPermissionsIncrease
401 if (extension_sync_data
.enabled())
402 extension_service_
->EnableExtension(id
);
403 else if (!IsPendingEnable(id
))
404 extension_service_
->DisableExtension(
405 id
, Extension::DISABLE_UNKNOWN_FROM_SYNC
);
407 // We need to cache some version information here because setting the
408 // incognito flag invalidates the |extension| pointer (it reloads the
410 bool extension_installed
= (extension
!= NULL
);
411 int result
= extension
?
412 extension
->version()->CompareTo(extension_sync_data
.version()) : 0;
413 extensions::util::SetIsIncognitoEnabled(
414 id
, profile_
, extension_sync_data
.incognito_enabled());
415 extension
= NULL
; // No longer safe to use.
417 if (extension_installed
) {
418 // If the extension is already installed, check if it's outdated.
420 // Extension is outdated.
424 // TODO(akalin): Replace silent update with a list of enabled
426 const bool kInstallSilently
= true;
428 CHECK(type
== syncer::EXTENSIONS
|| type
== syncer::APPS
);
429 extensions::PendingExtensionInfo::ShouldAllowInstallPredicate filter
=
430 (type
== syncer::APPS
) ? extensions::sync_helper::IsSyncableApp
:
431 extensions::sync_helper::IsSyncableExtension
;
433 if (!extension_service_
->pending_extension_manager()->AddFromSync(
435 extension_sync_data
.update_url(),
438 LOG(WARNING
) << "Could not add pending extension for " << id
;
439 // This means that the extension is already pending installation, with a
440 // non-INTERNAL location. Add to pending_sync_data, even though it will
441 // never be removed (we'll never install a syncable version of the
442 // extension), so that GetAllSyncData() continues to send it.
444 // Track pending extensions so that we can return them in GetAllSyncData().
451 void ExtensionSyncService::SyncExtensionChangeIfNeeded(
452 const Extension
& extension
) {
453 if (extensions::sync_helper::IsSyncableApp(&extension
)) {
454 if (app_sync_bundle_
.IsSyncing())
455 app_sync_bundle_
.SyncChangeIfNeeded(extension
);
456 else if (extension_service_
->is_ready() && !flare_
.is_null())
457 flare_
.Run(syncer::APPS
);
458 } else if (extensions::sync_helper::IsSyncableExtension(&extension
)) {
459 if (extension_sync_bundle_
.IsSyncing())
460 extension_sync_bundle_
.SyncChangeIfNeeded(extension
);
461 else if (extension_service_
->is_ready() && !flare_
.is_null())
462 flare_
.Run(syncer::EXTENSIONS
);