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