- add sources.
[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/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"
30
31 using extensions::Extension;
32 using extensions::ExtensionPrefs;
33 using extensions::FeatureSwitch;
34
35 ExtensionSyncService::ExtensionSyncService(Profile* profile,
36                                            ExtensionPrefs* extension_prefs,
37                                            ExtensionService* extension_service)
38     : profile_(profile),
39       extension_prefs_(extension_prefs),
40       extension_service_(extension_service),
41       app_sync_bundle_(this),
42       extension_sync_bundle_(this),
43       pending_app_enables_(
44           make_scoped_ptr(new browser_sync::SyncPrefs(
45               extension_prefs_->pref_service())),
46           &app_sync_bundle_,
47           syncer::APPS),
48       pending_extension_enables_(
49           make_scoped_ptr(new browser_sync::SyncPrefs(
50               extension_prefs_->pref_service())),
51           &extension_sync_bundle_,
52           syncer::EXTENSIONS) {
53   SetSyncStartFlare(sync_start_util::GetFlareForSyncableService(
54       profile_->GetPath()));
55
56   extension_service_->set_extension_sync_service(this);
57   extension_prefs_->extension_sorting()->SetExtensionSyncService(this);
58 }
59
60 ExtensionSyncService::~ExtensionSyncService() {}
61
62 // static
63 ExtensionSyncService* ExtensionSyncService::Get(Profile* profile) {
64   return ExtensionSyncServiceFactory::GetForProfile(profile);
65 }
66
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.
85   }
86
87   return syncer::SyncChange();
88 }
89
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);
99   }
100 }
101
102 void ExtensionSyncService::SyncEnableExtension(
103     const extensions::Extension& extension) {
104
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());
108
109   if (extensions::sync_helper::IsSyncableExtension(&extension))
110     pending_extension_enables_.OnExtensionEnabled(extension.id());
111
112   SyncExtensionChangeIfNeeded(extension);
113 }
114
115 void ExtensionSyncService::SyncDisableExtension(
116     const extensions::Extension& extension) {
117
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());
121
122   if (extensions::sync_helper::IsSyncableExtension(&extension))
123     pending_extension_enables_.OnExtensionDisabled(extension.id());
124
125   SyncExtensionChangeIfNeeded(extension);
126 }
127
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());
135
136   switch (type) {
137     case syncer::EXTENSIONS:
138       extension_sync_bundle_.SetupSync(sync_processor.release(),
139                                        sync_error_factory.release(),
140                                        initial_sync_data);
141       pending_extension_enables_.OnSyncStarted(extension_service_);
142       break;
143
144     case syncer::APPS:
145       app_sync_bundle_.SetupSync(sync_processor.release(),
146                                  sync_error_factory.release(),
147                                  initial_sync_data);
148       pending_app_enables_.OnSyncStarted(extension_service_);
149       break;
150
151     default:
152       LOG(FATAL) << "Got " << type << " ModelType";
153   }
154
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();
162        ++i) {
163     switch (type) {
164         case syncer::EXTENSIONS:
165           sync_change_list.push_back(
166               extension_sync_bundle_.CreateSyncChange(*i));
167           break;
168         case syncer::APPS:
169           sync_change_list.push_back(app_sync_bundle_.CreateSyncChange(*i));
170           break;
171       default:
172         LOG(FATAL) << "Got " << type << " ModelType";
173     }
174   }
175
176
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);
181   }
182
183   return syncer::SyncMergeResult(type);
184 }
185
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();
191   }
192 }
193
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();
200
201   // We should only get sync data for extensions and apps.
202   NOTREACHED();
203
204   return syncer::SyncDataList();
205 }
206
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();
212       ++i) {
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));
219     }
220   }
221
222   extension_prefs_->extension_sorting()->FixNTPOrdinalCollisions();
223
224   return syncer::SyncError();
225 }
226
227 extensions::ExtensionSyncData ExtensionSyncService::GetExtensionSyncData(
228     const Extension& extension) const {
229   return extensions::ExtensionSyncData(
230       extension,
231       extension_service_->IsExtensionEnabled(extension.id()),
232       extension_util::IsIncognitoEnabled(extension.id(), extension_service_));
233 }
234
235 extensions::AppSyncData ExtensionSyncService::GetAppSyncData(
236     const Extension& extension) const {
237   return extensions::AppSyncData(
238       extension,
239       extension_service_->IsExtensionEnabled(extension.id()),
240       extension_util::IsIncognitoEnabled(extension.id(), extension_service_),
241       extension_prefs_->extension_sorting()->GetAppLaunchOrdinal(
242           extension.id()),
243       extension_prefs_->extension_sorting()->GetPageOrdinal(extension.id()));
244 }
245
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);
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
265 std::vector<extensions::AppSyncData> ExtensionSyncService::GetAppSyncDataList()
266     const {
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);
274
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(),
279                        pending_apps.end());
280
281   return app_sync_list;
282 }
283
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();
291     return false;
292   }
293
294   return true;
295 }
296
297 bool ExtensionSyncService::ProcessAppSyncData(
298     const extensions::AppSyncData& app_sync_data) {
299   const std::string& id = app_sync_data.id();
300
301   if (app_sync_data.app_launch_ordinal().IsValid() &&
302       app_sync_data.page_ordinal().IsValid()) {
303     extension_prefs_->extension_sorting()->SetAppLaunchOrdinal(
304         id,
305         app_sync_data.app_launch_ordinal());
306     extension_prefs_->extension_sorting()->SetPageOrdinal(
307         id,
308         app_sync_data.page_ordinal());
309   }
310
311   if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(),
312                                       syncer::APPS)) {
313     app_sync_bundle_.AddPendingApp(id, app_sync_data);
314     extension_service_->CheckForUpdatesSoon();
315     return false;
316   }
317
318   return true;
319 }
320
321 void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
322   const extensions::Extension* ext = extension_service_->GetInstalledExtension(
323       extension_id);
324
325   if (ext)
326     SyncExtensionChangeIfNeeded(*ext);
327 }
328
329 void ExtensionSyncService::SetSyncStartFlare(
330     const syncer::SyncableService::StartSyncFlare& flare) {
331   flare_ = flare;
332 }
333
334 bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension,
335                                          syncer::ModelType type) const {
336   if (type == syncer::EXTENSIONS &&
337       extensions::sync_helper::IsSyncableExtension(&extension)) {
338     return true;
339   }
340
341   if (type == syncer::APPS &&
342       extensions::sync_helper::IsSyncableApp(&extension)) {
343     return true;
344   }
345
346   return false;
347 }
348
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);
353 }
354
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);
360
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))
365     return true;
366
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
371                    << " for sync";
372     }
373     return true;
374   }
375
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";
381     return true;
382   }
383
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
388   // is called for it.
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);
394
395   // We need to cache some version information here because setting the
396   // incognito flag invalidates the |extension| pointer (it reloads the
397   // extension).
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.
404
405   if (extension_installed) {
406     // If the extension is already installed, check if it's outdated.
407     if (result < 0) {
408       // Extension is outdated.
409       return false;
410     }
411   } else {
412     // TODO(akalin): Replace silent update with a list of enabled
413     // permissions.
414     const bool kInstallSilently = true;
415
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;
420
421     if (!extension_service_->pending_extension_manager()->AddFromSync(
422             id,
423             extension_sync_data.update_url(),
424             filter,
425             kInstallSilently)) {
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.
431     }
432     // Track pending extensions so that we can return them in GetAllSyncData().
433     return false;
434   }
435
436   return true;
437 }
438
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);
451   }
452 }