Update V8 to version 4.6.22.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_sync_service.cc
blob5272d5448f0b3f03c5f2b1a98d4af9920b6be5d1
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 "base/basictypes.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/threading/thread_restrictions.h"
10 #include "chrome/browser/extensions/bookmark_app_helper.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_sync_data.h"
13 #include "chrome/browser/extensions/extension_sync_service_factory.h"
14 #include "chrome/browser/extensions/extension_util.h"
15 #include "chrome/browser/extensions/launch_util.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/sync/glue/sync_start_util.h"
18 #include "chrome/common/extensions/extension_constants.h"
19 #include "chrome/common/extensions/sync_helper.h"
20 #include "chrome/common/web_application_info.h"
21 #include "components/sync_driver/sync_prefs.h"
22 #include "extensions/browser/app_sorting.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_util.h"
26 #include "extensions/browser/uninstall_reason.h"
27 #include "extensions/common/extension.h"
28 #include "extensions/common/extension_set.h"
29 #include "extensions/common/image_util.h"
30 #include "sync/api/sync_change.h"
31 #include "sync/api/sync_error_factory.h"
33 using extensions::Extension;
34 using extensions::ExtensionPrefs;
35 using extensions::ExtensionRegistry;
36 using extensions::ExtensionSet;
37 using extensions::ExtensionSyncData;
38 using extensions::SyncBundle;
40 namespace {
42 void OnWebApplicationInfoLoaded(
43 WebApplicationInfo synced_info,
44 base::WeakPtr<ExtensionService> extension_service,
45 const WebApplicationInfo& loaded_info) {
46 DCHECK_EQ(synced_info.app_url, loaded_info.app_url);
48 if (!extension_service)
49 return;
51 // Use the old icons if they exist.
52 synced_info.icons = loaded_info.icons;
53 CreateOrUpdateBookmarkApp(extension_service.get(), &synced_info);
56 // Returns the pref value for "all urls enabled" for the given extension id.
57 ExtensionSyncData::OptionalBoolean GetAllowedOnAllUrlsOptionalBoolean(
58 const std::string& extension_id,
59 content::BrowserContext* context) {
60 bool allowed_on_all_urls =
61 extensions::util::AllowedScriptingOnAllUrls(extension_id, context);
62 // If the extension is not allowed on all urls (which is not the default),
63 // then we have to sync the preference.
64 if (!allowed_on_all_urls)
65 return ExtensionSyncData::BOOLEAN_FALSE;
67 // If the user has explicitly set a value, then we sync it.
68 if (extensions::util::HasSetAllowedScriptingOnAllUrls(extension_id, context))
69 return ExtensionSyncData::BOOLEAN_TRUE;
71 // Otherwise, unset.
72 return ExtensionSyncData::BOOLEAN_UNSET;
75 // Returns true if the sync type of |extension| matches |type|.
76 bool IsCorrectSyncType(const Extension& extension, syncer::ModelType type) {
77 return (type == syncer::EXTENSIONS && extension.is_extension()) ||
78 (type == syncer::APPS && extension.is_app());
81 syncer::SyncDataList ToSyncerSyncDataList(
82 const std::vector<ExtensionSyncData>& data) {
83 syncer::SyncDataList result;
84 result.reserve(data.size());
85 for (const ExtensionSyncData& item : data)
86 result.push_back(item.GetSyncData());
87 return result;
90 } // namespace
92 ExtensionSyncService::ExtensionSyncService(Profile* profile,
93 ExtensionPrefs* extension_prefs,
94 ExtensionService* extension_service)
95 : profile_(profile),
96 extension_prefs_(extension_prefs),
97 extension_service_(extension_service) {
98 SetSyncStartFlare(sync_start_util::GetFlareForSyncableService(
99 profile_->GetPath()));
101 extension_prefs_->app_sorting()->SetExtensionSyncService(this);
104 ExtensionSyncService::~ExtensionSyncService() {}
106 // static
107 ExtensionSyncService* ExtensionSyncService::Get(
108 content::BrowserContext* context) {
109 return ExtensionSyncServiceFactory::GetForBrowserContext(context);
112 void ExtensionSyncService::SyncUninstallExtension(
113 const extensions::Extension& extension) {
114 if (!extensions::util::ShouldSync(&extension, profile_))
115 return;
117 // TODO(tim): If we get here and IsSyncing is false, this will cause
118 // "back from the dead" style bugs, because sync will add-back the extension
119 // that was uninstalled here when MergeDataAndStartSyncing is called.
120 // See crbug.com/256795.
121 syncer::ModelType type =
122 extension.is_app() ? syncer::APPS : syncer::EXTENSIONS;
123 SyncBundle* bundle = GetSyncBundle(type);
124 if (bundle->IsSyncing()) {
125 bundle->PushSyncDeletion(extension.id(),
126 CreateSyncData(extension).GetSyncData());
127 } else if (extension_service_->is_ready() && !flare_.is_null()) {
128 flare_.Run(type); // Tell sync to start ASAP.
132 void ExtensionSyncService::SyncExtensionChangeIfNeeded(
133 const Extension& extension) {
134 if (!extensions::util::ShouldSync(&extension, profile_))
135 return;
137 syncer::ModelType type =
138 extension.is_app() ? syncer::APPS : syncer::EXTENSIONS;
139 SyncBundle* bundle = GetSyncBundle(type);
140 if (bundle->IsSyncing()) {
141 bundle->PushSyncAddOrUpdate(extension.id(),
142 CreateSyncData(extension).GetSyncData());
143 DCHECK(!ExtensionPrefs::Get(profile_)->NeedsSync(extension.id()));
144 } else {
145 ExtensionPrefs::Get(profile_)->SetNeedsSync(extension.id(), true);
146 if (extension_service_->is_ready() && !flare_.is_null())
147 flare_.Run(type); // Tell sync to start ASAP.
151 syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
152 syncer::ModelType type,
153 const syncer::SyncDataList& initial_sync_data,
154 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
155 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
156 CHECK(sync_processor.get());
157 LOG_IF(FATAL, type != syncer::EXTENSIONS && type != syncer::APPS)
158 << "Got " << type << " ModelType";
160 SyncBundle* bundle = GetSyncBundle(type);
161 bundle->StartSyncing(sync_processor.Pass());
163 // Apply the initial sync data, filtering out any items where we have more
164 // recent local changes. Also tell the SyncBundle the extension IDs.
165 for (const syncer::SyncData& sync_data : initial_sync_data) {
166 scoped_ptr<ExtensionSyncData> extension_sync_data(
167 ExtensionSyncData::CreateFromSyncData(sync_data));
168 // If the extension has local state that needs to be synced, ignore this
169 // change (we assume the local state is more recent).
170 if (extension_sync_data &&
171 !ExtensionPrefs::Get(profile_)->NeedsSync(extension_sync_data->id())) {
172 ApplySyncData(*extension_sync_data);
176 // Now push those local changes to sync.
177 // TODO(treib,kalman): We should only have to send out changes for extensions
178 // which have NeedsSync set (i.e. |GetLocalSyncDataList(type, false)|). That
179 // makes some sync_integration_tests fail though - figure out why and fix it!
180 std::vector<ExtensionSyncData> data_list = GetLocalSyncDataList(type, true);
181 bundle->PushSyncDataList(ToSyncerSyncDataList(data_list));
182 for (const ExtensionSyncData& data : data_list)
183 ExtensionPrefs::Get(profile_)->SetNeedsSync(data.id(), false);
185 if (type == syncer::APPS)
186 extension_prefs_->app_sorting()->FixNTPOrdinalCollisions();
188 return syncer::SyncMergeResult(type);
191 void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
192 GetSyncBundle(type)->Reset();
195 syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
196 syncer::ModelType type) const {
197 const SyncBundle* bundle = GetSyncBundle(type);
198 if (!bundle->IsSyncing())
199 return syncer::SyncDataList();
201 std::vector<extensions::ExtensionSyncData> sync_data_list =
202 GetLocalSyncDataList(type, true);
204 // Add pending data (where the local extension is either not installed yet or
205 // outdated).
206 std::vector<ExtensionSyncData> pending_extensions = bundle->GetPendingData();
207 sync_data_list.insert(sync_data_list.begin(),
208 pending_extensions.begin(),
209 pending_extensions.end());
211 return ToSyncerSyncDataList(sync_data_list);
214 syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
215 const tracked_objects::Location& from_here,
216 const syncer::SyncChangeList& change_list) {
217 for (const syncer::SyncChange& sync_change : change_list) {
218 scoped_ptr<ExtensionSyncData> extension_sync_data(
219 ExtensionSyncData::CreateFromSyncChange(sync_change));
220 if (extension_sync_data)
221 ApplySyncData(*extension_sync_data);
224 extension_prefs_->app_sorting()->FixNTPOrdinalCollisions();
226 return syncer::SyncError();
229 ExtensionSyncData ExtensionSyncService::CreateSyncData(
230 const extensions::Extension& extension) const {
231 bool enabled = extension_service_->IsExtensionEnabled(extension.id());
232 int disable_reasons = extension_prefs_->GetDisableReasons(extension.id());
233 bool incognito_enabled = extensions::util::IsIncognitoEnabled(extension.id(),
234 profile_);
235 bool remote_install =
236 extension_prefs_->HasDisableReason(extension.id(),
237 Extension::DISABLE_REMOTE_INSTALL);
238 ExtensionSyncData::OptionalBoolean allowed_on_all_url =
239 GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_);
240 if (extension.is_app()) {
241 return ExtensionSyncData(
242 extension, enabled, disable_reasons, incognito_enabled, remote_install,
243 allowed_on_all_url,
244 extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
245 extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()),
246 extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id()));
248 return ExtensionSyncData(
249 extension, enabled, disable_reasons, incognito_enabled, remote_install,
250 allowed_on_all_url);
253 bool ExtensionSyncService::ApplySyncData(
254 const ExtensionSyncData& extension_sync_data) {
255 syncer::ModelType type = extension_sync_data.is_app() ? syncer::APPS
256 : syncer::EXTENSIONS;
257 SyncBundle* bundle = GetSyncBundle(type);
258 bundle->ApplySyncData(extension_sync_data);
260 const std::string& id = extension_sync_data.id();
262 if (extension_sync_data.is_app()) {
263 if (extension_sync_data.app_launch_ordinal().IsValid() &&
264 extension_sync_data.page_ordinal().IsValid()) {
265 extension_prefs_->app_sorting()->SetAppLaunchOrdinal(
267 extension_sync_data.app_launch_ordinal());
268 extension_prefs_->app_sorting()->SetPageOrdinal(
270 extension_sync_data.page_ordinal());
273 // The corresponding validation of this value during ExtensionSyncData
274 // population is in ExtensionSyncData::ToAppSpecifics.
275 if (extension_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
276 extension_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
277 extensions::SetLaunchType(
278 profile_, id, extension_sync_data.launch_type());
281 if (!extension_sync_data.bookmark_app_url().empty())
282 ApplyBookmarkAppSyncData(extension_sync_data);
285 if (!ApplyExtensionSyncDataHelper(extension_sync_data, type)) {
286 bundle->AddPendingExtension(id, extension_sync_data);
287 extension_service_->CheckForUpdatesSoon();
288 return false;
291 return true;
294 void ExtensionSyncService::ApplyBookmarkAppSyncData(
295 const extensions::ExtensionSyncData& extension_sync_data) {
296 DCHECK(extension_sync_data.is_app());
298 // Process bookmark app sync if necessary.
299 GURL bookmark_app_url(extension_sync_data.bookmark_app_url());
300 if (!bookmark_app_url.is_valid() ||
301 extension_sync_data.uninstalled()) {
302 return;
305 const Extension* extension =
306 extension_service_->GetInstalledExtension(extension_sync_data.id());
308 // Return if there are no bookmark app details that need updating.
309 if (extension &&
310 extension->non_localized_name() == extension_sync_data.name() &&
311 extension->description() ==
312 extension_sync_data.bookmark_app_description()) {
313 return;
316 WebApplicationInfo web_app_info;
317 web_app_info.app_url = bookmark_app_url;
318 web_app_info.title = base::UTF8ToUTF16(extension_sync_data.name());
319 web_app_info.description =
320 base::UTF8ToUTF16(extension_sync_data.bookmark_app_description());
321 if (!extension_sync_data.bookmark_app_icon_color().empty()) {
322 extensions::image_util::ParseCSSColorString(
323 extension_sync_data.bookmark_app_icon_color(),
324 &web_app_info.generated_icon_color);
326 for (const auto& icon : extension_sync_data.linked_icons()) {
327 WebApplicationInfo::IconInfo icon_info;
328 icon_info.url = icon.url;
329 icon_info.width = icon.size;
330 icon_info.height = icon.size;
331 web_app_info.icons.push_back(icon_info);
334 // If the bookmark app already exists, keep the old icons.
335 if (!extension) {
336 CreateOrUpdateBookmarkApp(extension_service_, &web_app_info);
337 } else {
338 GetWebApplicationInfoFromApp(profile_,
339 extension,
340 base::Bind(&OnWebApplicationInfoLoaded,
341 web_app_info,
342 extension_service_->AsWeakPtr()));
346 void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
347 const Extension* ext =
348 extension_service_->GetInstalledExtension(extension_id);
350 if (ext)
351 SyncExtensionChangeIfNeeded(*ext);
354 void ExtensionSyncService::SetSyncStartFlare(
355 const syncer::SyncableService::StartSyncFlare& flare) {
356 flare_ = flare;
359 SyncBundle* ExtensionSyncService::GetSyncBundle(syncer::ModelType type) {
360 return const_cast<SyncBundle*>(
361 const_cast<const ExtensionSyncService&>(*this).GetSyncBundle(type));
364 const SyncBundle* ExtensionSyncService::GetSyncBundle(
365 syncer::ModelType type) const {
366 return (type == syncer::APPS) ? &app_sync_bundle_ : &extension_sync_bundle_;
369 std::vector<ExtensionSyncData> ExtensionSyncService::GetLocalSyncDataList(
370 syncer::ModelType type,
371 bool include_everything) const {
372 // Collect the local state. FillSyncDataList will filter out extensions for
373 // which we have pending data that should be used instead.
374 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
375 std::vector<ExtensionSyncData> data;
376 // TODO(treib, kalman): Should we be including blacklisted/blocked extensions
377 // here? I.e. just calling registry->GeneratedInstalledExtensionsSet()?
378 // It would be more consistent, but the danger is that the black/blocklist
379 // hasn't been updated on all clients by the time sync has kicked in -
380 // so it's safest not to. Take care to add any other extension lists here
381 // in the future if they are added.
382 FillSyncDataList(
383 registry->enabled_extensions(), type, include_everything, &data);
384 FillSyncDataList(
385 registry->disabled_extensions(), type, include_everything, &data);
386 FillSyncDataList(
387 registry->terminated_extensions(), type, include_everything, &data);
388 return data;
391 void ExtensionSyncService::FillSyncDataList(
392 const ExtensionSet& extensions,
393 syncer::ModelType type,
394 bool include_everything,
395 std::vector<ExtensionSyncData>* sync_data_list) const {
396 const SyncBundle* bundle = GetSyncBundle(type);
397 for (const scoped_refptr<const Extension>& extension : extensions) {
398 if (IsCorrectSyncType(*extension, type) &&
399 extensions::util::ShouldSync(extension.get(), profile_) &&
400 !bundle->HasPendingExtensionId(extension->id()) &&
401 (include_everything ||
402 ExtensionPrefs::Get(profile_)->NeedsSync(extension->id()))) {
403 sync_data_list->push_back(CreateSyncData(*extension));
408 bool ExtensionSyncService::ApplyExtensionSyncDataHelper(
409 const ExtensionSyncData& extension_sync_data,
410 syncer::ModelType type) {
411 const std::string& id = extension_sync_data.id();
412 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
413 const Extension* extension = registry->GetInstalledExtension(id);
415 // TODO(bolms): we should really handle this better. The particularly bad
416 // case is where an app becomes an extension or vice versa, and we end up with
417 // a zombie extension that won't go away.
418 if (extension && !IsCorrectSyncType(*extension, type))
419 return true;
421 // Handle uninstalls first.
422 if (extension_sync_data.uninstalled()) {
423 if (!extension_service_->UninstallExtensionHelper(
424 extension_service_, id, extensions::UNINSTALL_REASON_SYNC)) {
425 LOG(WARNING) << "Could not uninstall extension " << id << " for sync";
427 return true;
430 // Extension from sync was uninstalled by the user as external extensions.
431 // Honor user choice and skip installation/enabling.
432 if (ExtensionPrefs::Get(profile_)->IsExternalExtensionUninstalled(id)) {
433 LOG(WARNING) << "Extension with id " << id
434 << " from sync was uninstalled as external extension";
435 return true;
438 int version_compare_result = extension ?
439 extension->version()->CompareTo(extension_sync_data.version()) : 0;
441 // Set user settings.
442 if (extension_sync_data.enabled()) {
443 DCHECK(!extension_sync_data.disable_reasons());
445 // Only grant permissions if the sync data explicitly sets the disable
446 // reasons to Extension::DISABLE_NONE (as opposed to the legacy (<M45) case
447 // where they're not set at all), and if the version from sync matches our
448 // local one. Otherwise we just enable it without granting permissions. If
449 // any permissions are missing, CheckPermissionsIncrease will soon disable
450 // it again.
451 bool grant_permissions =
452 extension_sync_data.supports_disable_reasons() &&
453 extension && (version_compare_result == 0);
454 if (grant_permissions)
455 extension_service_->GrantPermissionsAndEnableExtension(extension);
456 else
457 extension_service_->EnableExtension(id);
458 } else {
459 int disable_reasons = extension_sync_data.disable_reasons();
460 if (extension_sync_data.remote_install()) {
461 if (!(disable_reasons & Extension::DISABLE_REMOTE_INSTALL)) {
462 // In the non-legacy case (>=M45) where disable reasons are synced at
463 // all, DISABLE_REMOTE_INSTALL should be among them already.
464 DCHECK(!extension_sync_data.supports_disable_reasons());
465 disable_reasons |= Extension::DISABLE_REMOTE_INSTALL;
467 } else if (!extension_sync_data.supports_disable_reasons()) {
468 // Legacy case (<M45), from before we synced disable reasons (see
469 // crbug.com/484214).
470 disable_reasons = Extension::DISABLE_UNKNOWN_FROM_SYNC;
473 // In the non-legacy case (>=M45), clear any existing disable reasons first.
474 // Otherwise sync can't remove just some of them.
475 if (extension_sync_data.supports_disable_reasons())
476 ExtensionPrefs::Get(profile_)->ClearDisableReasons(id);
478 extension_service_->DisableExtension(id, disable_reasons);
481 // We need to cache some information here because setting the incognito flag
482 // invalidates the |extension| pointer (it reloads the extension).
483 bool extension_installed = (extension != NULL);
485 // If the target extension has already been installed ephemerally, it can
486 // be promoted to a regular installed extension and downloading from the Web
487 // Store is not necessary.
488 if (extension && extensions::util::IsEphemeralApp(id, profile_))
489 extension_service_->PromoteEphemeralApp(extension, true);
491 // Update the incognito flag.
492 extensions::util::SetIsIncognitoEnabled(
493 id, profile_, extension_sync_data.incognito_enabled());
494 extension = NULL; // No longer safe to use.
496 // Update the all urls flag.
497 if (extension_sync_data.all_urls_enabled() !=
498 ExtensionSyncData::BOOLEAN_UNSET) {
499 bool allowed = extension_sync_data.all_urls_enabled() ==
500 ExtensionSyncData::BOOLEAN_TRUE;
501 extensions::util::SetAllowedScriptingOnAllUrls(id, profile_, allowed);
504 if (extension_installed) {
505 // If the extension is already installed, check if it's outdated.
506 if (version_compare_result < 0) {
507 // Extension is outdated.
508 return false;
510 } else {
511 CHECK(type == syncer::EXTENSIONS || type == syncer::APPS);
512 if (!extension_service_->pending_extension_manager()->AddFromSync(
514 extension_sync_data.update_url(),
515 extensions::sync_helper::IsSyncable,
516 extension_sync_data.remote_install(),
517 extension_sync_data.installed_by_custodian())) {
518 LOG(WARNING) << "Could not add pending extension for " << id;
519 // This means that the extension is already pending installation, with a
520 // non-INTERNAL location. Add to pending_sync_data, even though it will
521 // never be removed (we'll never install a syncable version of the
522 // extension), so that GetAllSyncData() continues to send it.
524 // Track pending extensions so that we can return them in GetAllSyncData().
525 return false;
528 return true;