Prevent chrome://net-internals/#export from flickering
[chromium-blink-merge.git] / chrome / browser / extensions / extension_sync_service.cc
blobda191a6efbf38df8198d6fd35c52ca87d9e1866a
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/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/image_util.h"
36 #include "extensions/common/manifest_constants.h"
37 #include "extensions/common/manifest_handlers/icons_handler.h"
38 #include "sync/api/sync_change.h"
39 #include "sync/api/sync_error_factory.h"
40 #include "ui/gfx/image/image_family.h"
42 using extensions::Extension;
43 using extensions::ExtensionPrefs;
44 using extensions::ExtensionRegistry;
45 using extensions::ExtensionSyncData;
46 using extensions::FeatureSwitch;
48 namespace {
50 void OnWebApplicationInfoLoaded(
51 WebApplicationInfo synced_info,
52 base::WeakPtr<ExtensionService> extension_service,
53 const WebApplicationInfo& loaded_info) {
54 DCHECK_EQ(synced_info.app_url, loaded_info.app_url);
56 if (!extension_service)
57 return;
59 // Use the old icons if they exist.
60 synced_info.icons = loaded_info.icons;
61 CreateOrUpdateBookmarkApp(extension_service.get(), &synced_info);
64 // Returns the pref value for "all urls enabled" for the given extension id.
65 ExtensionSyncData::OptionalBoolean GetAllowedOnAllUrlsOptionalBoolean(
66 const std::string& extension_id,
67 content::BrowserContext* context) {
68 bool allowed_on_all_urls =
69 extensions::util::AllowedScriptingOnAllUrls(extension_id, context);
70 // If the extension is not allowed on all urls (which is not the default),
71 // then we have to sync the preference.
72 if (!allowed_on_all_urls)
73 return ExtensionSyncData::BOOLEAN_FALSE;
75 // If the user has explicitly set a value, then we sync it.
76 if (extensions::util::HasSetAllowedScriptingOnAllUrls(extension_id, context))
77 return ExtensionSyncData::BOOLEAN_TRUE;
79 // Otherwise, unset.
80 return ExtensionSyncData::BOOLEAN_UNSET;
83 } // namespace
85 ExtensionSyncService::ExtensionSyncService(Profile* profile,
86 ExtensionPrefs* extension_prefs,
87 ExtensionService* extension_service)
88 : profile_(profile),
89 extension_prefs_(extension_prefs),
90 extension_service_(extension_service),
91 app_sync_bundle_(this),
92 extension_sync_bundle_(this),
93 pending_app_enables_(make_scoped_ptr(new sync_driver::SyncPrefs(
94 extension_prefs_->pref_service())),
95 &app_sync_bundle_,
96 syncer::APPS),
97 pending_extension_enables_(make_scoped_ptr(new sync_driver::SyncPrefs(
98 extension_prefs_->pref_service())),
99 &extension_sync_bundle_,
100 syncer::EXTENSIONS) {
101 SetSyncStartFlare(sync_start_util::GetFlareForSyncableService(
102 profile_->GetPath()));
104 extension_service_->set_extension_sync_service(this);
105 extension_prefs_->app_sorting()->SetExtensionSyncService(this);
108 ExtensionSyncService::~ExtensionSyncService() {}
110 // static
111 ExtensionSyncService* ExtensionSyncService::Get(
112 content::BrowserContext* context) {
113 return ExtensionSyncServiceFactory::GetForBrowserContext(context);
116 syncer::SyncChange ExtensionSyncService::PrepareToSyncUninstallExtension(
117 const extensions::Extension* extension, bool extensions_ready) {
118 // Extract the data we need for sync now, but don't actually sync until we've
119 // completed the uninstallation.
120 // TODO(tim): If we get here and IsSyncing is false, this will cause
121 // "back from the dead" style bugs, because sync will add-back the extension
122 // that was uninstalled here when MergeDataAndStartSyncing is called.
123 // See crbug.com/256795.
124 if (extensions::util::ShouldSyncApp(extension, profile_)) {
125 if (app_sync_bundle_.IsSyncing())
126 return app_sync_bundle_.CreateSyncChangeToDelete(extension);
127 else if (extensions_ready && !flare_.is_null())
128 flare_.Run(syncer::APPS); // Tell sync to start ASAP.
129 } else if (extensions::sync_helper::IsSyncableExtension(extension)) {
130 if (extension_sync_bundle_.IsSyncing())
131 return extension_sync_bundle_.CreateSyncChangeToDelete(extension);
132 else if (extensions_ready && !flare_.is_null())
133 flare_.Run(syncer::EXTENSIONS); // Tell sync to start ASAP.
136 return syncer::SyncChange();
139 void ExtensionSyncService::ProcessSyncUninstallExtension(
140 const std::string& extension_id,
141 const syncer::SyncChange& sync_change) {
142 if (app_sync_bundle_.HasExtensionId(extension_id) &&
143 sync_change.sync_data().GetDataType() == syncer::APPS) {
144 app_sync_bundle_.ProcessDeletion(extension_id, sync_change);
145 } else if (extension_sync_bundle_.HasExtensionId(extension_id) &&
146 sync_change.sync_data().GetDataType() == syncer::EXTENSIONS) {
147 extension_sync_bundle_.ProcessDeletion(extension_id, sync_change);
151 void ExtensionSyncService::SyncEnableExtension(
152 const extensions::Extension& extension) {
154 // Syncing may not have started yet, so handle pending enables.
155 if (extensions::util::ShouldSyncApp(&extension, profile_))
156 pending_app_enables_.OnExtensionEnabled(extension.id());
158 if (extensions::util::ShouldSyncExtension(&extension, profile_))
159 pending_extension_enables_.OnExtensionEnabled(extension.id());
161 SyncExtensionChangeIfNeeded(extension);
164 void ExtensionSyncService::SyncDisableExtension(
165 const extensions::Extension& extension) {
167 // Syncing may not have started yet, so handle pending enables.
168 if (extensions::util::ShouldSyncApp(&extension, profile_))
169 pending_app_enables_.OnExtensionDisabled(extension.id());
171 if (extensions::util::ShouldSyncExtension(&extension, profile_))
172 pending_extension_enables_.OnExtensionDisabled(extension.id());
174 SyncExtensionChangeIfNeeded(extension);
177 syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
178 syncer::ModelType type,
179 const syncer::SyncDataList& initial_sync_data,
180 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
181 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
182 CHECK(sync_processor.get());
183 CHECK(sync_error_factory.get());
185 switch (type) {
186 case syncer::EXTENSIONS:
187 extension_sync_bundle_.SetupSync(sync_processor.release(),
188 sync_error_factory.release(),
189 initial_sync_data);
190 pending_extension_enables_.OnSyncStarted(extension_service_);
191 break;
193 case syncer::APPS:
194 app_sync_bundle_.SetupSync(sync_processor.release(),
195 sync_error_factory.release(),
196 initial_sync_data);
197 pending_app_enables_.OnSyncStarted(extension_service_);
198 break;
200 default:
201 LOG(FATAL) << "Got " << type << " ModelType";
204 // Process local extensions.
205 // TODO(yoz): Determine whether pending extensions should be considered too.
206 // See crbug.com/104399.
207 syncer::SyncDataList sync_data_list = GetAllSyncData(type);
208 syncer::SyncChangeList sync_change_list;
209 for (syncer::SyncDataList::const_iterator i = sync_data_list.begin();
210 i != sync_data_list.end();
211 ++i) {
212 switch (type) {
213 case syncer::EXTENSIONS:
214 sync_change_list.push_back(
215 extension_sync_bundle_.CreateSyncChange(*i));
216 break;
217 case syncer::APPS:
218 sync_change_list.push_back(app_sync_bundle_.CreateSyncChange(*i));
219 break;
220 default:
221 LOG(FATAL) << "Got " << type << " ModelType";
226 if (type == syncer::EXTENSIONS) {
227 extension_sync_bundle_.ProcessSyncChangeList(sync_change_list);
228 } else if (type == syncer::APPS) {
229 app_sync_bundle_.ProcessSyncChangeList(sync_change_list);
232 return syncer::SyncMergeResult(type);
235 void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
236 if (type == syncer::APPS) {
237 app_sync_bundle_.Reset();
238 } else if (type == syncer::EXTENSIONS) {
239 extension_sync_bundle_.Reset();
243 syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
244 syncer::ModelType type) const {
245 if (type == syncer::EXTENSIONS)
246 return extension_sync_bundle_.GetAllSyncData();
247 if (type == syncer::APPS)
248 return app_sync_bundle_.GetAllSyncData();
250 // We should only get sync data for extensions and apps.
251 NOTREACHED();
253 return syncer::SyncDataList();
256 syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
257 const tracked_objects::Location& from_here,
258 const syncer::SyncChangeList& change_list) {
259 for (syncer::SyncChangeList::const_iterator i = change_list.begin();
260 i != change_list.end();
261 ++i) {
262 syncer::ModelType type = i->sync_data().GetDataType();
263 if (type == syncer::EXTENSIONS)
264 extension_sync_bundle_.ProcessSyncChange(ExtensionSyncData(*i));
265 else if (type == syncer::APPS)
266 app_sync_bundle_.ProcessSyncChange(extensions::AppSyncData(*i));
269 extension_prefs_->app_sorting()->FixNTPOrdinalCollisions();
271 return syncer::SyncError();
274 ExtensionSyncData ExtensionSyncService::GetExtensionSyncData(
275 const Extension& extension) const {
276 return ExtensionSyncData(
277 extension,
278 extension_service_->IsExtensionEnabled(extension.id()),
279 extensions::util::IsIncognitoEnabled(extension.id(), profile_),
280 extension_prefs_->HasDisableReason(extension.id(),
281 Extension::DISABLE_REMOTE_INSTALL),
282 GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_));
285 extensions::AppSyncData ExtensionSyncService::GetAppSyncData(
286 const Extension& extension) const {
287 return extensions::AppSyncData(
288 extension,
289 extension_service_->IsExtensionEnabled(extension.id()),
290 extensions::util::IsIncognitoEnabled(extension.id(), profile_),
291 extension_prefs_->HasDisableReason(extension.id(),
292 Extension::DISABLE_REMOTE_INSTALL),
293 GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_),
294 extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
295 extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()),
296 extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id()));
299 std::vector<ExtensionSyncData>
300 ExtensionSyncService::GetExtensionSyncDataList() const {
301 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
302 std::vector<ExtensionSyncData> extension_sync_list;
303 extension_sync_bundle_.GetExtensionSyncDataListHelper(
304 registry->enabled_extensions(), &extension_sync_list);
305 extension_sync_bundle_.GetExtensionSyncDataListHelper(
306 registry->disabled_extensions(), &extension_sync_list);
307 extension_sync_bundle_.GetExtensionSyncDataListHelper(
308 registry->terminated_extensions(), &extension_sync_list);
310 std::vector<ExtensionSyncData> pending_extensions =
311 extension_sync_bundle_.GetPendingData();
312 extension_sync_list.insert(extension_sync_list.begin(),
313 pending_extensions.begin(),
314 pending_extensions.end());
316 return extension_sync_list;
319 std::vector<extensions::AppSyncData> ExtensionSyncService::GetAppSyncDataList()
320 const {
321 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
322 std::vector<extensions::AppSyncData> app_sync_list;
323 app_sync_bundle_.GetAppSyncDataListHelper(
324 registry->enabled_extensions(), &app_sync_list);
325 app_sync_bundle_.GetAppSyncDataListHelper(
326 registry->disabled_extensions(), &app_sync_list);
327 app_sync_bundle_.GetAppSyncDataListHelper(
328 registry->terminated_extensions(), &app_sync_list);
330 std::vector<extensions::AppSyncData> pending_apps =
331 app_sync_bundle_.GetPendingData();
332 app_sync_list.insert(app_sync_list.begin(),
333 pending_apps.begin(),
334 pending_apps.end());
336 return app_sync_list;
339 bool ExtensionSyncService::ProcessExtensionSyncData(
340 const ExtensionSyncData& extension_sync_data) {
341 if (!ProcessExtensionSyncDataHelper(extension_sync_data,
342 syncer::EXTENSIONS)) {
343 extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(),
344 extension_sync_data);
345 extension_service_->CheckForUpdatesSoon();
346 return false;
349 return true;
352 bool ExtensionSyncService::ProcessAppSyncData(
353 const extensions::AppSyncData& app_sync_data) {
354 const std::string& id = app_sync_data.id();
356 if (app_sync_data.app_launch_ordinal().IsValid() &&
357 app_sync_data.page_ordinal().IsValid()) {
358 extension_prefs_->app_sorting()->SetAppLaunchOrdinal(
360 app_sync_data.app_launch_ordinal());
361 extension_prefs_->app_sorting()->SetPageOrdinal(
363 app_sync_data.page_ordinal());
366 // The corresponding validation of this value during AppSyncData population
367 // is in AppSyncData::PopulateAppSpecifics.
368 if (app_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
369 app_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
370 extensions::SetLaunchType(profile_, id, app_sync_data.launch_type());
373 if (!app_sync_data.bookmark_app_url().empty())
374 ProcessBookmarkAppSyncData(app_sync_data);
376 if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(),
377 syncer::APPS)) {
378 app_sync_bundle_.AddPendingApp(id, app_sync_data);
379 extension_service_->CheckForUpdatesSoon();
380 return false;
383 return true;
386 void ExtensionSyncService::ProcessBookmarkAppSyncData(
387 const extensions::AppSyncData& app_sync_data) {
388 // Process bookmark app sync if necessary.
389 GURL bookmark_app_url(app_sync_data.bookmark_app_url());
390 if (!bookmark_app_url.is_valid() ||
391 app_sync_data.extension_sync_data().uninstalled()) {
392 return;
395 const extensions::Extension* extension =
396 extension_service_->GetInstalledExtension(
397 app_sync_data.extension_sync_data().id());
399 // Return if there are no bookmark app details that need updating.
400 if (extension && extension->non_localized_name() ==
401 app_sync_data.extension_sync_data().name() &&
402 extension->description() == app_sync_data.bookmark_app_description()) {
403 return;
406 WebApplicationInfo web_app_info;
407 web_app_info.app_url = bookmark_app_url;
408 web_app_info.title =
409 base::UTF8ToUTF16(app_sync_data.extension_sync_data().name());
410 web_app_info.description =
411 base::UTF8ToUTF16(app_sync_data.bookmark_app_description());
412 if (!app_sync_data.bookmark_app_icon_color().empty()) {
413 extensions::image_util::ParseCSSColorString(
414 app_sync_data.bookmark_app_icon_color(),
415 &web_app_info.generated_icon_color);
418 // If the bookmark app already exists, keep the old icons.
419 if (!extension) {
420 CreateOrUpdateBookmarkApp(extension_service_, &web_app_info);
421 } else {
422 app_sync_data.extension_sync_data().name();
423 GetWebApplicationInfoFromApp(profile_,
424 extension,
425 base::Bind(&OnWebApplicationInfoLoaded,
426 web_app_info,
427 extension_service_->AsWeakPtr()));
431 void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
432 const extensions::Extension* ext = extension_service_->GetInstalledExtension(
433 extension_id);
435 if (ext)
436 SyncExtensionChangeIfNeeded(*ext);
439 void ExtensionSyncService::SetSyncStartFlare(
440 const syncer::SyncableService::StartSyncFlare& flare) {
441 flare_ = flare;
444 bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension,
445 syncer::ModelType type) const {
446 if (type == syncer::EXTENSIONS &&
447 extensions::sync_helper::IsSyncableExtension(&extension)) {
448 return true;
451 if (type == syncer::APPS &&
452 extensions::sync_helper::IsSyncableApp(&extension)) {
453 return true;
456 return false;
459 bool ExtensionSyncService::IsPendingEnable(
460 const std::string& extension_id) const {
461 return pending_app_enables_.Contains(extension_id) ||
462 pending_extension_enables_.Contains(extension_id);
465 bool ExtensionSyncService::ProcessExtensionSyncDataHelper(
466 const ExtensionSyncData& extension_sync_data,
467 syncer::ModelType type) {
468 const std::string& id = extension_sync_data.id();
469 const Extension* extension = extension_service_->GetInstalledExtension(id);
471 // TODO(bolms): we should really handle this better. The particularly bad
472 // case is where an app becomes an extension or vice versa, and we end up with
473 // a zombie extension that won't go away.
474 if (extension && !IsCorrectSyncType(*extension, type))
475 return true;
477 // Handle uninstalls first.
478 if (extension_sync_data.uninstalled()) {
479 if (!extension_service_->UninstallExtensionHelper(
480 extension_service_, id, extensions::UNINSTALL_REASON_SYNC)) {
481 LOG(WARNING) << "Could not uninstall extension " << id
482 << " for sync";
484 return true;
487 // Extension from sync was uninstalled by the user as external extensions.
488 // Honor user choice and skip installation/enabling.
489 if (extensions::ExtensionPrefs::Get(profile_)
490 ->IsExternalExtensionUninstalled(id)) {
491 LOG(WARNING) << "Extension with id " << id
492 << " from sync was uninstalled as external extension";
493 return true;
496 // Set user settings.
497 // If the extension has been disabled from sync, it may not have
498 // been installed yet, so we don't know if the disable reason was a
499 // permissions increase. That will be updated once CheckPermissionsIncrease
500 // is called for it.
501 // However if the extension is marked as a remote install in sync, we know
502 // what the disable reason is, so set it to that directly. Note that when
503 // CheckPermissionsIncrease runs, it might still add permissions increase
504 // as a disable reason for the extension.
505 if (extension_sync_data.enabled()) {
506 extension_service_->EnableExtension(id);
507 } else if (!IsPendingEnable(id)) {
508 if (extension_sync_data.remote_install()) {
509 extension_service_->DisableExtension(id,
510 Extension::DISABLE_REMOTE_INSTALL);
511 } else {
512 extension_service_->DisableExtension(
513 id, Extension::DISABLE_UNKNOWN_FROM_SYNC);
517 // We need to cache some version information here because setting the
518 // incognito flag invalidates the |extension| pointer (it reloads the
519 // extension).
520 bool extension_installed = (extension != NULL);
521 int version_compare_result = extension ?
522 extension->version()->CompareTo(extension_sync_data.version()) : 0;
524 // If the target extension has already been installed ephemerally, it can
525 // be promoted to a regular installed extension and downloading from the Web
526 // Store is not necessary.
527 if (extension && extensions::util::IsEphemeralApp(id, profile_))
528 extension_service_->PromoteEphemeralApp(extension, true);
530 // Update the incognito flag.
531 extensions::util::SetIsIncognitoEnabled(
532 id, profile_, extension_sync_data.incognito_enabled());
533 extension = NULL; // No longer safe to use.
535 // Update the all urls flag.
536 if (extension_sync_data.all_urls_enabled() !=
537 ExtensionSyncData::BOOLEAN_UNSET) {
538 bool allowed = extension_sync_data.all_urls_enabled() ==
539 ExtensionSyncData::BOOLEAN_TRUE;
540 extensions::util::SetAllowedScriptingOnAllUrls(id, profile_, allowed);
543 if (extension_installed) {
544 // If the extension is already installed, check if it's outdated.
545 if (version_compare_result < 0) {
546 // Extension is outdated.
547 return false;
549 } else {
550 CHECK(type == syncer::EXTENSIONS || type == syncer::APPS);
551 extensions::PendingExtensionInfo::ShouldAllowInstallPredicate filter =
552 (type == syncer::APPS) ? extensions::sync_helper::IsSyncableApp :
553 extensions::sync_helper::IsSyncableExtension;
555 if (!extension_service_->pending_extension_manager()->AddFromSync(
557 extension_sync_data.update_url(),
558 filter,
559 extension_sync_data.remote_install(),
560 extension_sync_data.installed_by_custodian())) {
561 LOG(WARNING) << "Could not add pending extension for " << id;
562 // This means that the extension is already pending installation, with a
563 // non-INTERNAL location. Add to pending_sync_data, even though it will
564 // never be removed (we'll never install a syncable version of the
565 // extension), so that GetAllSyncData() continues to send it.
567 // Track pending extensions so that we can return them in GetAllSyncData().
568 return false;
571 return true;
574 void ExtensionSyncService::SyncExtensionChangeIfNeeded(
575 const Extension& extension) {
576 if (extensions::util::ShouldSyncApp(&extension, profile_)) {
577 if (app_sync_bundle_.IsSyncing())
578 app_sync_bundle_.SyncChangeIfNeeded(extension);
579 else if (extension_service_->is_ready() && !flare_.is_null())
580 flare_.Run(syncer::APPS);
581 } else if (extensions::util::ShouldSyncExtension(&extension, profile_)) {
582 if (extension_sync_bundle_.IsSyncing())
583 extension_sync_bundle_.SyncChangeIfNeeded(extension);
584 else if (extension_service_->is_ready() && !flare_.is_null())
585 flare_.Run(syncer::EXTENSIONS);