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 "chrome/browser/extensions/bookmark_app_helper.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_sync_data.h"
12 #include "chrome/browser/extensions/extension_sync_service_factory.h"
13 #include "chrome/browser/extensions/extension_util.h"
14 #include "chrome/browser/extensions/launch_util.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/sync/glue/sync_start_util.h"
17 #include "chrome/common/extensions/extension_constants.h"
18 #include "chrome/common/extensions/sync_helper.h"
19 #include "chrome/common/web_application_info.h"
20 #include "extensions/browser/app_sorting.h"
21 #include "extensions/browser/extension_prefs.h"
22 #include "extensions/browser/extension_registry.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/browser/extension_util.h"
25 #include "extensions/browser/uninstall_reason.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/extension_set.h"
28 #include "extensions/common/image_util.h"
29 #include "sync/api/sync_change.h"
30 #include "sync/api/sync_error_factory.h"
32 using extensions::AppSorting
;
33 using extensions::Extension
;
34 using extensions::ExtensionPrefs
;
35 using extensions::ExtensionRegistry
;
36 using extensions::ExtensionSet
;
37 using extensions::ExtensionSyncData
;
38 using extensions::ExtensionSystem
;
39 using extensions::SyncBundle
;
43 void OnWebApplicationInfoLoaded(
44 WebApplicationInfo synced_info
,
45 base::WeakPtr
<ExtensionService
> extension_service
,
46 const WebApplicationInfo
& loaded_info
) {
47 DCHECK_EQ(synced_info
.app_url
, loaded_info
.app_url
);
49 if (!extension_service
)
52 // Use the old icons if they exist.
53 synced_info
.icons
= loaded_info
.icons
;
54 CreateOrUpdateBookmarkApp(extension_service
.get(), &synced_info
);
57 // Returns the pref value for "all urls enabled" for the given extension id.
58 ExtensionSyncData::OptionalBoolean
GetAllowedOnAllUrlsOptionalBoolean(
59 const std::string
& extension_id
,
60 content::BrowserContext
* context
) {
61 bool allowed_on_all_urls
=
62 extensions::util::AllowedScriptingOnAllUrls(extension_id
, context
);
63 // If the extension is not allowed on all urls (which is not the default),
64 // then we have to sync the preference.
65 if (!allowed_on_all_urls
)
66 return ExtensionSyncData::BOOLEAN_FALSE
;
68 // If the user has explicitly set a value, then we sync it.
69 if (extensions::util::HasSetAllowedScriptingOnAllUrls(extension_id
, context
))
70 return ExtensionSyncData::BOOLEAN_TRUE
;
73 return ExtensionSyncData::BOOLEAN_UNSET
;
76 // Returns true if the sync type of |extension| matches |type|.
77 bool IsCorrectSyncType(const Extension
& extension
, syncer::ModelType type
) {
78 return (type
== syncer::EXTENSIONS
&& extension
.is_extension()) ||
79 (type
== syncer::APPS
&& extension
.is_app());
82 syncer::SyncDataList
ToSyncerSyncDataList(
83 const std::vector
<ExtensionSyncData
>& data
) {
84 syncer::SyncDataList result
;
85 result
.reserve(data
.size());
86 for (const ExtensionSyncData
& item
: data
)
87 result
.push_back(item
.GetSyncData());
93 ExtensionSyncService::ExtensionSyncService(Profile
* profile
)
95 registry_observer_(this),
96 prefs_observer_(this),
97 flare_(sync_start_util::GetFlareForSyncableService(profile
->GetPath())) {
98 registry_observer_
.Add(ExtensionRegistry::Get(profile_
));
99 prefs_observer_
.Add(ExtensionPrefs::Get(profile_
));
102 ExtensionSyncService::~ExtensionSyncService() {
106 ExtensionSyncService
* ExtensionSyncService::Get(
107 content::BrowserContext
* context
) {
108 return ExtensionSyncServiceFactory::GetForBrowserContext(context
);
111 void ExtensionSyncService::SyncExtensionChangeIfNeeded(
112 const Extension
& extension
) {
113 if (!extensions::util::ShouldSync(&extension
, profile_
))
116 syncer::ModelType type
=
117 extension
.is_app() ? syncer::APPS
: syncer::EXTENSIONS
;
118 SyncBundle
* bundle
= GetSyncBundle(type
);
119 if (bundle
->IsSyncing()) {
120 bundle
->PushSyncAddOrUpdate(extension
.id(),
121 CreateSyncData(extension
).GetSyncData());
122 DCHECK(!ExtensionPrefs::Get(profile_
)->NeedsSync(extension
.id()));
124 ExtensionPrefs::Get(profile_
)->SetNeedsSync(extension
.id(), true);
125 if (extension_service()->is_ready() && !flare_
.is_null())
126 flare_
.Run(type
); // Tell sync to start ASAP.
130 syncer::SyncMergeResult
ExtensionSyncService::MergeDataAndStartSyncing(
131 syncer::ModelType type
,
132 const syncer::SyncDataList
& initial_sync_data
,
133 scoped_ptr
<syncer::SyncChangeProcessor
> sync_processor
,
134 scoped_ptr
<syncer::SyncErrorFactory
> sync_error_factory
) {
135 CHECK(sync_processor
.get());
136 LOG_IF(FATAL
, type
!= syncer::EXTENSIONS
&& type
!= syncer::APPS
)
137 << "Got " << type
<< " ModelType";
139 SyncBundle
* bundle
= GetSyncBundle(type
);
140 bundle
->StartSyncing(sync_processor
.Pass());
142 // Apply the initial sync data, filtering out any items where we have more
143 // recent local changes. Also tell the SyncBundle the extension IDs.
144 for (const syncer::SyncData
& sync_data
: initial_sync_data
) {
145 scoped_ptr
<ExtensionSyncData
> extension_sync_data(
146 ExtensionSyncData::CreateFromSyncData(sync_data
));
147 // If the extension has local state that needs to be synced, ignore this
148 // change (we assume the local state is more recent).
149 if (extension_sync_data
&&
150 !ExtensionPrefs::Get(profile_
)->NeedsSync(extension_sync_data
->id())) {
151 ApplySyncData(*extension_sync_data
);
155 // Now push those local changes to sync.
156 // TODO(treib,kalman): We should only have to send out changes for extensions
157 // which have NeedsSync set (i.e. |GetLocalSyncDataList(type, false)|). That
158 // makes some sync_integration_tests fail though - figure out why and fix it!
159 std::vector
<ExtensionSyncData
> data_list
= GetLocalSyncDataList(type
, true);
160 bundle
->PushSyncDataList(ToSyncerSyncDataList(data_list
));
161 for (const ExtensionSyncData
& data
: data_list
)
162 ExtensionPrefs::Get(profile_
)->SetNeedsSync(data
.id(), false);
164 if (type
== syncer::APPS
)
165 ExtensionSystem::Get(profile_
)->app_sorting()->FixNTPOrdinalCollisions();
167 return syncer::SyncMergeResult(type
);
170 void ExtensionSyncService::StopSyncing(syncer::ModelType type
) {
171 GetSyncBundle(type
)->Reset();
174 syncer::SyncDataList
ExtensionSyncService::GetAllSyncData(
175 syncer::ModelType type
) const {
176 const SyncBundle
* bundle
= GetSyncBundle(type
);
177 if (!bundle
->IsSyncing())
178 return syncer::SyncDataList();
180 std::vector
<ExtensionSyncData
> sync_data_list
=
181 GetLocalSyncDataList(type
, true);
183 // Add pending data (where the local extension is not installed yet).
184 std::vector
<ExtensionSyncData
> pending_extensions
=
185 bundle
->GetPendingExtensionData();
186 sync_data_list
.insert(sync_data_list
.begin(),
187 pending_extensions
.begin(),
188 pending_extensions
.end());
190 return ToSyncerSyncDataList(sync_data_list
);
193 syncer::SyncError
ExtensionSyncService::ProcessSyncChanges(
194 const tracked_objects::Location
& from_here
,
195 const syncer::SyncChangeList
& change_list
) {
196 for (const syncer::SyncChange
& sync_change
: change_list
) {
197 scoped_ptr
<ExtensionSyncData
> extension_sync_data(
198 ExtensionSyncData::CreateFromSyncChange(sync_change
));
199 if (extension_sync_data
)
200 ApplySyncData(*extension_sync_data
);
203 ExtensionSystem::Get(profile_
)->app_sorting()->FixNTPOrdinalCollisions();
205 return syncer::SyncError();
208 ExtensionSyncData
ExtensionSyncService::CreateSyncData(
209 const Extension
& extension
) const {
210 const ExtensionPrefs
* extension_prefs
= ExtensionPrefs::Get(profile_
);
211 // Query the enabled state from ExtensionPrefs rather than
212 // ExtensionService::IsExtensionEnabled - the latter uses ExtensionRegistry
213 // which might not have been updated yet (ExtensionPrefs are updated first,
214 // and we're listening for changes to these).
215 bool enabled
= !extension_prefs
->IsExtensionDisabled(extension
.id());
217 !extension_prefs
->IsExternalExtensionUninstalled(extension
.id());
218 // Blacklisted extensions are not marked as disabled in ExtensionPrefs.
220 extension_prefs
->GetExtensionBlacklistState(extension
.id()) ==
221 extensions::NOT_BLACKLISTED
;
222 int disable_reasons
= extension_prefs
->GetDisableReasons(extension
.id());
223 bool incognito_enabled
= extensions::util::IsIncognitoEnabled(extension
.id(),
225 bool remote_install
=
226 extension_prefs
->HasDisableReason(extension
.id(),
227 Extension::DISABLE_REMOTE_INSTALL
);
228 ExtensionSyncData::OptionalBoolean allowed_on_all_url
=
229 GetAllowedOnAllUrlsOptionalBoolean(extension
.id(), profile_
);
230 AppSorting
* app_sorting
= ExtensionSystem::Get(profile_
)->app_sorting();
232 ExtensionSyncData result
= extension
.is_app()
234 extension
, enabled
, disable_reasons
, incognito_enabled
,
235 remote_install
, allowed_on_all_url
,
236 app_sorting
->GetAppLaunchOrdinal(extension
.id()),
237 app_sorting
->GetPageOrdinal(extension
.id()),
238 extensions::GetLaunchTypePrefValue(extension_prefs
,
241 extension
, enabled
, disable_reasons
, incognito_enabled
,
242 remote_install
, allowed_on_all_url
);
244 // If there's a pending update, send the new version to sync instead of the
246 auto it
= pending_update_versions_
.find(extension
.id());
247 if (it
!= pending_update_versions_
.end()) {
248 const base::Version
& version
= it
->second
;
249 // If we have a pending version, it should be newer than the installed one.
250 DCHECK_EQ(-1, extension
.version()->CompareTo(version
));
251 result
.set_version(version
);
256 void ExtensionSyncService::ApplySyncData(
257 const ExtensionSyncData
& extension_sync_data
) {
258 syncer::ModelType type
= extension_sync_data
.is_app() ? syncer::APPS
259 : syncer::EXTENSIONS
;
260 const std::string
& id
= extension_sync_data
.id();
261 // Note: |extension| may be null if it hasn't been installed yet.
262 const Extension
* extension
=
263 ExtensionRegistry::Get(profile_
)->GetInstalledExtension(id
);
264 // TODO(bolms): we should really handle this better. The particularly bad
265 // case is where an app becomes an extension or vice versa, and we end up with
266 // a zombie extension that won't go away.
267 // TODO(treib): Is this still true?
268 if (extension
&& !IsCorrectSyncType(*extension
, type
))
271 SyncBundle
* bundle
= GetSyncBundle(type
);
272 // Forward to the bundle. This will just update the list of synced extensions.
273 bundle
->ApplySyncData(extension_sync_data
);
275 // Handle uninstalls first.
276 if (extension_sync_data
.uninstalled()) {
277 if (!ExtensionService::UninstallExtensionHelper(
278 extension_service(), id
, extensions::UNINSTALL_REASON_SYNC
)) {
279 LOG(WARNING
) << "Could not uninstall extension " << id
<< " for sync";
284 // Extension from sync was uninstalled by the user as an external extension.
285 // Honor user choice and skip installation/enabling.
286 // TODO(treib): Should we still apply pref changes?
287 if (ExtensionPrefs::Get(profile_
)->IsExternalExtensionUninstalled(id
)) {
288 LOG(WARNING
) << "Extension with id " << id
289 << " from sync was uninstalled as external extension";
293 int version_compare_result
= extension
?
294 extension
->version()->CompareTo(extension_sync_data
.version()) : 0;
296 // Enable/disable the extension.
297 if (extension_sync_data
.enabled()) {
298 DCHECK(!extension_sync_data
.disable_reasons());
300 // Only grant permissions if the sync data explicitly sets the disable
301 // reasons to Extension::DISABLE_NONE (as opposed to the legacy (<M45) case
302 // where they're not set at all), and if the version from sync matches our
303 // local one. Otherwise we just enable it without granting permissions. If
304 // any permissions are missing, CheckPermissionsIncrease will soon disable
306 bool grant_permissions
=
307 extension_sync_data
.supports_disable_reasons() &&
308 extension
&& (version_compare_result
== 0);
309 if (grant_permissions
)
310 extension_service()->GrantPermissionsAndEnableExtension(extension
);
312 extension_service()->EnableExtension(id
);
314 int disable_reasons
= extension_sync_data
.disable_reasons();
315 if (extension_sync_data
.remote_install()) {
316 if (!(disable_reasons
& Extension::DISABLE_REMOTE_INSTALL
)) {
317 // In the non-legacy case (>=M45) where disable reasons are synced at
318 // all, DISABLE_REMOTE_INSTALL should be among them already.
319 DCHECK(!extension_sync_data
.supports_disable_reasons());
320 disable_reasons
|= Extension::DISABLE_REMOTE_INSTALL
;
322 } else if (!extension_sync_data
.supports_disable_reasons()) {
323 // Legacy case (<M45), from before we synced disable reasons (see
324 // crbug.com/484214).
325 disable_reasons
= Extension::DISABLE_UNKNOWN_FROM_SYNC
;
328 // In the non-legacy case (>=M45), clear any existing disable reasons first.
329 // Otherwise sync can't remove just some of them.
330 if (extension_sync_data
.supports_disable_reasons())
331 ExtensionPrefs::Get(profile_
)->ClearDisableReasons(id
);
333 extension_service()->DisableExtension(id
, disable_reasons
);
336 // If the target extension has already been installed ephemerally, it can
337 // be promoted to a regular installed extension and downloading from the Web
338 // Store is not necessary.
339 if (extension
&& extensions::util::IsEphemeralApp(id
, profile_
))
340 extension_service()->PromoteEphemeralApp(extension
, true);
342 // Cache whether the extension was already installed because setting the
343 // incognito flag invalidates the |extension| pointer (it reloads the
345 bool extension_installed
= (extension
!= nullptr);
347 // Update the incognito flag.
348 extensions::util::SetIsIncognitoEnabled(
349 id
, profile_
, extension_sync_data
.incognito_enabled());
350 extension
= nullptr; // No longer safe to use.
352 // Update the all urls flag.
353 if (extension_sync_data
.all_urls_enabled() !=
354 ExtensionSyncData::BOOLEAN_UNSET
) {
355 bool allowed
= extension_sync_data
.all_urls_enabled() ==
356 ExtensionSyncData::BOOLEAN_TRUE
;
357 extensions::util::SetAllowedScriptingOnAllUrls(id
, profile_
, allowed
);
360 // Set app-specific data.
361 if (extension_sync_data
.is_app()) {
362 if (extension_sync_data
.app_launch_ordinal().IsValid() &&
363 extension_sync_data
.page_ordinal().IsValid()) {
364 AppSorting
* app_sorting
= ExtensionSystem::Get(profile_
)->app_sorting();
365 app_sorting
->SetAppLaunchOrdinal(
367 extension_sync_data
.app_launch_ordinal());
368 app_sorting
->SetPageOrdinal(id
, extension_sync_data
.page_ordinal());
371 // The corresponding validation of this value during ExtensionSyncData
372 // population is in ExtensionSyncData::ToAppSpecifics.
373 if (extension_sync_data
.launch_type() >= extensions::LAUNCH_TYPE_FIRST
&&
374 extension_sync_data
.launch_type() < extensions::NUM_LAUNCH_TYPES
) {
375 extensions::SetLaunchType(
376 profile_
, id
, extension_sync_data
.launch_type());
379 if (!extension_sync_data
.bookmark_app_url().empty())
380 ApplyBookmarkAppSyncData(extension_sync_data
);
383 // Finally, trigger installation/update as required.
384 bool check_for_updates
= false;
385 if (extension_installed
) {
386 // If the extension is installed but outdated, store the new version.
387 if (version_compare_result
< 0) {
388 pending_update_versions_
[id
] = extension_sync_data
.version();
389 check_for_updates
= true;
392 if (!extension_service()->pending_extension_manager()->AddFromSync(
394 extension_sync_data
.update_url(),
395 extensions::sync_helper::IsSyncable
,
396 extension_sync_data
.remote_install(),
397 extension_sync_data
.installed_by_custodian())) {
398 LOG(WARNING
) << "Could not add pending extension for " << id
;
399 // This means that the extension is already pending installation, with a
400 // non-INTERNAL location. Add to pending_sync_data, even though it will
401 // never be removed (we'll never install a syncable version of the
402 // extension), so that GetAllSyncData() continues to send it.
404 // Track pending extensions so that we can return them in GetAllSyncData().
405 bundle
->AddPendingExtensionData(id
, extension_sync_data
);
406 check_for_updates
= true;
409 if (check_for_updates
)
410 extension_service()->CheckForUpdatesSoon();
413 void ExtensionSyncService::ApplyBookmarkAppSyncData(
414 const ExtensionSyncData
& extension_sync_data
) {
415 DCHECK(extension_sync_data
.is_app());
417 // Process bookmark app sync if necessary.
418 GURL
bookmark_app_url(extension_sync_data
.bookmark_app_url());
419 if (!bookmark_app_url
.is_valid() ||
420 extension_sync_data
.uninstalled()) {
424 const Extension
* extension
=
425 extension_service()->GetInstalledExtension(extension_sync_data
.id());
427 // Return if there are no bookmark app details that need updating.
429 extension
->non_localized_name() == extension_sync_data
.name() &&
430 extension
->description() ==
431 extension_sync_data
.bookmark_app_description()) {
435 WebApplicationInfo web_app_info
;
436 web_app_info
.app_url
= bookmark_app_url
;
437 web_app_info
.title
= base::UTF8ToUTF16(extension_sync_data
.name());
438 web_app_info
.description
=
439 base::UTF8ToUTF16(extension_sync_data
.bookmark_app_description());
440 if (!extension_sync_data
.bookmark_app_icon_color().empty()) {
441 extensions::image_util::ParseCSSColorString(
442 extension_sync_data
.bookmark_app_icon_color(),
443 &web_app_info
.generated_icon_color
);
445 for (const auto& icon
: extension_sync_data
.linked_icons()) {
446 WebApplicationInfo::IconInfo icon_info
;
447 icon_info
.url
= icon
.url
;
448 icon_info
.width
= icon
.size
;
449 icon_info
.height
= icon
.size
;
450 web_app_info
.icons
.push_back(icon_info
);
453 // If the bookmark app already exists, keep the old icons.
455 CreateOrUpdateBookmarkApp(extension_service(), &web_app_info
);
457 GetWebApplicationInfoFromApp(profile_
,
459 base::Bind(&OnWebApplicationInfoLoaded
,
461 extension_service()->AsWeakPtr()));
465 void ExtensionSyncService::SetSyncStartFlareForTesting(
466 const syncer::SyncableService::StartSyncFlare
& flare
) {
470 ExtensionService
* ExtensionSyncService::extension_service() const {
471 return ExtensionSystem::Get(profile_
)->extension_service();
474 void ExtensionSyncService::OnExtensionInstalled(
475 content::BrowserContext
* browser_context
,
476 const Extension
* extension
,
478 DCHECK_EQ(profile_
, browser_context
);
479 // Clear pending version if the installed one has caught up.
480 auto it
= pending_update_versions_
.find(extension
->id());
481 if (it
!= pending_update_versions_
.end()) {
482 const base::Version
& pending_version
= it
->second
;
483 if (extension
->version()->CompareTo(pending_version
) >= 0)
484 pending_update_versions_
.erase(it
);
486 SyncExtensionChangeIfNeeded(*extension
);
489 void ExtensionSyncService::OnExtensionUninstalled(
490 content::BrowserContext
* browser_context
,
491 const Extension
* extension
,
492 extensions::UninstallReason reason
) {
493 DCHECK_EQ(profile_
, browser_context
);
494 // Don't bother syncing if the extension will be re-installed momentarily.
495 if (reason
== extensions::UNINSTALL_REASON_REINSTALL
||
496 !extensions::util::ShouldSync(extension
, profile_
))
499 // TODO(tim): If we get here and IsSyncing is false, this will cause
500 // "back from the dead" style bugs, because sync will add-back the extension
501 // that was uninstalled here when MergeDataAndStartSyncing is called.
502 // See crbug.com/256795.
503 // Possible fix: Set NeedsSync here, then in MergeDataAndStartSyncing, if
504 // NeedsSync is set but the extension isn't installed, send a sync deletion.
505 syncer::ModelType type
=
506 extension
->is_app() ? syncer::APPS
: syncer::EXTENSIONS
;
507 SyncBundle
* bundle
= GetSyncBundle(type
);
508 if (bundle
->IsSyncing()) {
509 bundle
->PushSyncDeletion(extension
->id(),
510 CreateSyncData(*extension
).GetSyncData());
511 } else if (extension_service()->is_ready() && !flare_
.is_null()) {
512 flare_
.Run(type
); // Tell sync to start ASAP.
515 pending_update_versions_
.erase(extension
->id());
518 void ExtensionSyncService::OnExtensionStateChanged(
519 const std::string
& extension_id
,
521 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
522 const Extension
* extension
= registry
->GetInstalledExtension(extension_id
);
523 // We can get pref change notifications for extensions that aren't installed
524 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
525 // observation (in OnExtensionInstalled).
527 SyncExtensionChangeIfNeeded(*extension
);
530 void ExtensionSyncService::OnExtensionDisableReasonsChanged(
531 const std::string
& extension_id
,
532 int disabled_reasons
) {
533 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
534 const Extension
* extension
= registry
->GetInstalledExtension(extension_id
);
535 // We can get pref change notifications for extensions that aren't installed
536 // (yet). In that case, we'll pick up the change later via ExtensionRegistry
537 // observation (in OnExtensionInstalled).
539 SyncExtensionChangeIfNeeded(*extension
);
542 SyncBundle
* ExtensionSyncService::GetSyncBundle(syncer::ModelType type
) {
543 return const_cast<SyncBundle
*>(
544 const_cast<const ExtensionSyncService
&>(*this).GetSyncBundle(type
));
547 const SyncBundle
* ExtensionSyncService::GetSyncBundle(
548 syncer::ModelType type
) const {
549 return (type
== syncer::APPS
) ? &app_sync_bundle_
: &extension_sync_bundle_
;
552 std::vector
<ExtensionSyncData
> ExtensionSyncService::GetLocalSyncDataList(
553 syncer::ModelType type
,
554 bool include_everything
) const {
555 // Collect the local state.
556 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
557 std::vector
<ExtensionSyncData
> data
;
558 // TODO(treib, kalman): Should we be including blacklisted/blocked extensions
559 // here? I.e. just calling registry->GeneratedInstalledExtensionsSet()?
560 // It would be more consistent, but the danger is that the black/blocklist
561 // hasn't been updated on all clients by the time sync has kicked in -
562 // so it's safest not to. Take care to add any other extension lists here
563 // in the future if they are added.
565 registry
->enabled_extensions(), type
, include_everything
, &data
);
567 registry
->disabled_extensions(), type
, include_everything
, &data
);
569 registry
->terminated_extensions(), type
, include_everything
, &data
);
573 void ExtensionSyncService::FillSyncDataList(
574 const ExtensionSet
& extensions
,
575 syncer::ModelType type
,
576 bool include_everything
,
577 std::vector
<ExtensionSyncData
>* sync_data_list
) const {
578 for (const scoped_refptr
<const Extension
>& extension
: extensions
) {
579 if (IsCorrectSyncType(*extension
, type
) &&
580 extensions::util::ShouldSync(extension
.get(), profile_
) &&
581 (include_everything
||
582 ExtensionPrefs::Get(profile_
)->NeedsSync(extension
->id()))) {
583 // We should never have pending data for an installed extension.
584 DCHECK(!GetSyncBundle(type
)->HasPendingExtensionData(extension
->id()));
585 sync_data_list
->push_back(CreateSyncData(*extension
));