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