• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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/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 "content/public/browser/browser_thread.h"
24 #include "extensions/browser/app_sorting.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/feature_switch.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 
ExtensionSyncService(Profile * profile,ExtensionPrefs * extension_prefs,ExtensionService * extension_service)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_->app_sorting()->SetExtensionSyncService(this);
58 }
59 
~ExtensionSyncService()60 ExtensionSyncService::~ExtensionSyncService() {}
61 
62 // static
Get(Profile * profile)63 ExtensionSyncService* ExtensionSyncService::Get(Profile* profile) {
64   return ExtensionSyncServiceFactory::GetForProfile(profile);
65 }
66 
PrepareToSyncUninstallExtension(const extensions::Extension * extension,bool extensions_ready)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 
ProcessSyncUninstallExtension(const std::string & extension_id,const syncer::SyncChange & sync_change)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 
SyncEnableExtension(const extensions::Extension & extension)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 
SyncDisableExtension(const extensions::Extension & extension)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 
MergeDataAndStartSyncing(syncer::ModelType type,const syncer::SyncDataList & initial_sync_data,scoped_ptr<syncer::SyncChangeProcessor> sync_processor,scoped_ptr<syncer::SyncErrorFactory> sync_error_factory)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 
StopSyncing(syncer::ModelType 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();
191   }
192 }
193 
GetAllSyncData(syncer::ModelType type) const194 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 
ProcessSyncChanges(const tracked_objects::Location & from_here,const syncer::SyncChangeList & change_list)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_->app_sorting()->FixNTPOrdinalCollisions();
223 
224   return syncer::SyncError();
225 }
226 
GetExtensionSyncData(const Extension & extension) const227 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 
GetAppSyncData(const Extension & extension) const235 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_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
242       extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()));
243 }
244 
245 std::vector<extensions::ExtensionSyncData>
GetExtensionSyncDataList() const246   ExtensionSyncService::GetExtensionSyncDataList() const {
247   std::vector<extensions::ExtensionSyncData> extension_sync_list;
248   extension_sync_bundle_.GetExtensionSyncDataListHelper(
249       extension_service_->extensions(), &extension_sync_list);
250   extension_sync_bundle_.GetExtensionSyncDataListHelper(
251       extension_service_->disabled_extensions(), &extension_sync_list);
252   extension_sync_bundle_.GetExtensionSyncDataListHelper(
253       extension_service_->terminated_extensions(), &extension_sync_list);
254 
255   std::vector<extensions::ExtensionSyncData> pending_extensions =
256       extension_sync_bundle_.GetPendingData();
257   extension_sync_list.insert(extension_sync_list.begin(),
258                              pending_extensions.begin(),
259                              pending_extensions.end());
260 
261   return extension_sync_list;
262 }
263 
GetAppSyncDataList() const264 std::vector<extensions::AppSyncData> ExtensionSyncService::GetAppSyncDataList()
265     const {
266   std::vector<extensions::AppSyncData> app_sync_list;
267   app_sync_bundle_.GetAppSyncDataListHelper(
268       extension_service_->extensions(), &app_sync_list);
269   app_sync_bundle_.GetAppSyncDataListHelper(
270       extension_service_->disabled_extensions(), &app_sync_list);
271   app_sync_bundle_.GetAppSyncDataListHelper(
272       extension_service_->terminated_extensions(), &app_sync_list);
273 
274   std::vector<extensions::AppSyncData> pending_apps =
275       app_sync_bundle_.GetPendingData();
276   app_sync_list.insert(app_sync_list.begin(),
277                        pending_apps.begin(),
278                        pending_apps.end());
279 
280   return app_sync_list;
281 }
282 
ProcessExtensionSyncData(const extensions::ExtensionSyncData & extension_sync_data)283 bool ExtensionSyncService::ProcessExtensionSyncData(
284     const extensions::ExtensionSyncData& extension_sync_data) {
285   if (!ProcessExtensionSyncDataHelper(extension_sync_data,
286                                       syncer::EXTENSIONS)) {
287     extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(),
288                                                extension_sync_data);
289     extension_service_->CheckForUpdatesSoon();
290     return false;
291   }
292 
293   return true;
294 }
295 
ProcessAppSyncData(const extensions::AppSyncData & app_sync_data)296 bool ExtensionSyncService::ProcessAppSyncData(
297     const extensions::AppSyncData& app_sync_data) {
298   const std::string& id = app_sync_data.id();
299 
300   if (app_sync_data.app_launch_ordinal().IsValid() &&
301       app_sync_data.page_ordinal().IsValid()) {
302     extension_prefs_->app_sorting()->SetAppLaunchOrdinal(
303         id,
304         app_sync_data.app_launch_ordinal());
305     extension_prefs_->app_sorting()->SetPageOrdinal(
306         id,
307         app_sync_data.page_ordinal());
308   }
309 
310   if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(),
311                                       syncer::APPS)) {
312     app_sync_bundle_.AddPendingApp(id, app_sync_data);
313     extension_service_->CheckForUpdatesSoon();
314     return false;
315   }
316 
317   return true;
318 }
319 
SyncOrderingChange(const std::string & extension_id)320 void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
321   const extensions::Extension* ext = extension_service_->GetInstalledExtension(
322       extension_id);
323 
324   if (ext)
325     SyncExtensionChangeIfNeeded(*ext);
326 }
327 
SetSyncStartFlare(const syncer::SyncableService::StartSyncFlare & flare)328 void ExtensionSyncService::SetSyncStartFlare(
329     const syncer::SyncableService::StartSyncFlare& flare) {
330   flare_ = flare;
331 }
332 
IsCorrectSyncType(const Extension & extension,syncer::ModelType type) const333 bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension,
334                                          syncer::ModelType type) const {
335   if (type == syncer::EXTENSIONS &&
336       extensions::sync_helper::IsSyncableExtension(&extension)) {
337     return true;
338   }
339 
340   if (type == syncer::APPS &&
341       extensions::sync_helper::IsSyncableApp(&extension)) {
342     return true;
343   }
344 
345   return false;
346 }
347 
IsPendingEnable(const std::string & extension_id) const348 bool ExtensionSyncService::IsPendingEnable(
349     const std::string& extension_id) const {
350   return pending_app_enables_.Contains(extension_id) ||
351       pending_extension_enables_.Contains(extension_id);
352 }
353 
ProcessExtensionSyncDataHelper(const extensions::ExtensionSyncData & extension_sync_data,syncer::ModelType type)354 bool ExtensionSyncService::ProcessExtensionSyncDataHelper(
355     const extensions::ExtensionSyncData& extension_sync_data,
356     syncer::ModelType type) {
357   const std::string& id = extension_sync_data.id();
358   const Extension* extension = extension_service_->GetInstalledExtension(id);
359 
360   // TODO(bolms): we should really handle this better.  The particularly bad
361   // case is where an app becomes an extension or vice versa, and we end up with
362   // a zombie extension that won't go away.
363   if (extension && !IsCorrectSyncType(*extension, type))
364     return true;
365 
366   // Handle uninstalls first.
367   if (extension_sync_data.uninstalled()) {
368     if (!extension_service_->UninstallExtensionHelper(extension_service_, id)) {
369       LOG(WARNING) << "Could not uninstall extension " << id
370                    << " for sync";
371     }
372     return true;
373   }
374 
375   // Extension from sync was uninstalled by the user as external extensions.
376   // Honor user choice and skip installation/enabling.
377   if (extension_service_->IsExternalExtensionUninstalled(id)) {
378     LOG(WARNING) << "Extension with id " << id
379                  << " from sync was uninstalled as external extension";
380     return true;
381   }
382 
383   // Set user settings.
384   // If the extension has been disabled from sync, it may not have
385   // been installed yet, so we don't know if the disable reason was a
386   // permissions increase.  That will be updated once CheckPermissionsIncrease
387   // is called for it.
388   if (extension_sync_data.enabled())
389     extension_service_->EnableExtension(id);
390   else if (!IsPendingEnable(id))
391     extension_service_->DisableExtension(
392         id, Extension::DISABLE_UNKNOWN_FROM_SYNC);
393 
394   // We need to cache some version information here because setting the
395   // incognito flag invalidates the |extension| pointer (it reloads the
396   // extension).
397   bool extension_installed = (extension != NULL);
398   int result = extension ?
399       extension->version()->CompareTo(extension_sync_data.version()) : 0;
400   extension_util::SetIsIncognitoEnabled(
401       id, extension_service_, extension_sync_data.incognito_enabled());
402   extension = NULL;  // No longer safe to use.
403 
404   if (extension_installed) {
405     // If the extension is already installed, check if it's outdated.
406     if (result < 0) {
407       // Extension is outdated.
408       return false;
409     }
410   } else {
411     // TODO(akalin): Replace silent update with a list of enabled
412     // permissions.
413     const bool kInstallSilently = true;
414 
415     CHECK(type == syncer::EXTENSIONS || type == syncer::APPS);
416     extensions::PendingExtensionInfo::ShouldAllowInstallPredicate filter =
417         (type == syncer::APPS) ? extensions::sync_helper::IsSyncableApp :
418                                  extensions::sync_helper::IsSyncableExtension;
419 
420     if (!extension_service_->pending_extension_manager()->AddFromSync(
421             id,
422             extension_sync_data.update_url(),
423             filter,
424             kInstallSilently)) {
425       LOG(WARNING) << "Could not add pending extension for " << id;
426       // This means that the extension is already pending installation, with a
427       // non-INTERNAL location.  Add to pending_sync_data, even though it will
428       // never be removed (we'll never install a syncable version of the
429       // extension), so that GetAllSyncData() continues to send it.
430     }
431     // Track pending extensions so that we can return them in GetAllSyncData().
432     return false;
433   }
434 
435   return true;
436 }
437 
SyncExtensionChangeIfNeeded(const Extension & extension)438 void ExtensionSyncService::SyncExtensionChangeIfNeeded(
439     const Extension& extension) {
440   if (extensions::sync_helper::IsSyncableApp(&extension)) {
441     if (app_sync_bundle_.IsSyncing())
442       app_sync_bundle_.SyncChangeIfNeeded(extension);
443     else if (extension_service_->is_ready() && !flare_.is_null())
444       flare_.Run(syncer::APPS);
445   } else if (extensions::sync_helper::IsSyncableExtension(&extension)) {
446     if (extension_sync_bundle_.IsSyncing())
447       extension_sync_bundle_.SyncChangeIfNeeded(extension);
448     else if (extension_service_->is_ready() && !flare_.is_null())
449       flare_.Run(syncer::EXTENSIONS);
450   }
451 }
452