NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / extensions / extension_sync_service.cc
blob833a1ce4bac9d0b15ef932582e8a976d6d320170
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"
7 #include <iterator>
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)
40 : profile_(profile),
41 extension_prefs_(extension_prefs),
42 extension_service_(extension_service),
43 app_sync_bundle_(this),
44 extension_sync_bundle_(this),
45 pending_app_enables_(
46 make_scoped_ptr(new browser_sync::SyncPrefs(
47 extension_prefs_->pref_service())),
48 &app_sync_bundle_,
49 syncer::APPS),
50 pending_extension_enables_(
51 make_scoped_ptr(new browser_sync::SyncPrefs(
52 extension_prefs_->pref_service())),
53 &extension_sync_bundle_,
54 syncer::EXTENSIONS) {
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() {}
64 // static
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());
138 switch (type) {
139 case syncer::EXTENSIONS:
140 extension_sync_bundle_.SetupSync(sync_processor.release(),
141 sync_error_factory.release(),
142 initial_sync_data);
143 pending_extension_enables_.OnSyncStarted(extension_service_);
144 break;
146 case syncer::APPS:
147 app_sync_bundle_.SetupSync(sync_processor.release(),
148 sync_error_factory.release(),
149 initial_sync_data);
150 pending_app_enables_.OnSyncStarted(extension_service_);
151 break;
153 default:
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();
164 ++i) {
165 switch (type) {
166 case syncer::EXTENSIONS:
167 sync_change_list.push_back(
168 extension_sync_bundle_.CreateSyncChange(*i));
169 break;
170 case syncer::APPS:
171 sync_change_list.push_back(app_sync_bundle_.CreateSyncChange(*i));
172 break;
173 default:
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.
204 NOTREACHED();
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();
214 ++i) {
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(
232 extension,
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(
240 extension,
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()
269 const {
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(),
283 pending_apps.end());
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();
295 return false;
298 return true;
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(),
324 syncer::APPS)) {
325 app_sync_bundle_.AddPendingApp(id, app_sync_data);
326 extension_service_->CheckForUpdatesSoon();
327 return false;
330 return true;
333 void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
334 const extensions::Extension* ext = extension_service_->GetInstalledExtension(
335 extension_id);
337 if (ext)
338 SyncExtensionChangeIfNeeded(*ext);
341 void ExtensionSyncService::SetSyncStartFlare(
342 const syncer::SyncableService::StartSyncFlare& flare) {
343 flare_ = flare;
346 bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension,
347 syncer::ModelType type) const {
348 if (type == syncer::EXTENSIONS &&
349 extensions::sync_helper::IsSyncableExtension(&extension)) {
350 return true;
353 if (type == syncer::APPS &&
354 extensions::sync_helper::IsSyncableApp(&extension)) {
355 return true;
358 return false;
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))
377 return true;
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
383 << " for sync";
385 return true;
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";
393 return true;
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
400 // is called for it.
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
409 // extension).
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.
419 if (result < 0) {
420 // Extension is outdated.
421 return false;
423 } else {
424 // TODO(akalin): Replace silent update with a list of enabled
425 // permissions.
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(),
436 filter,
437 kInstallSilently)) {
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().
445 return false;
448 return true;
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);