1 // Copyright (c) 2012 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_data.h"
7 #include "base/logging.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/extensions/app_sync_data.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "components/crx_file/id_util.h"
13 #include "extensions/common/extension.h"
14 #include "extensions/common/manifest_url_handlers.h"
15 #include "sync/api/sync_data.h"
16 #include "sync/protocol/extension_specifics.pb.h"
17 #include "sync/protocol/sync.pb.h"
19 namespace extensions
{
23 std::string
GetExtensionSpecificsLogMessage(
24 const sync_pb::ExtensionSpecifics
& specifics
) {
25 return base::StringPrintf(
26 "id: %s\nversion: %s\nupdate_url: %s\nenabled: %i\ndisable_reasons: %i",
27 specifics
.id().c_str(),
28 specifics
.version().c_str(),
29 specifics
.update_url().c_str(),
31 specifics
.disable_reasons());
34 enum BadSyncDataReason
{
35 // Invalid extension ID.
41 // Invalid update URL.
44 // No ExtensionSpecifics in the EntitySpecifics.
45 NO_EXTENSION_SPECIFICS
,
47 // Enabled extensions can't have disable reasons.
50 // Must be at the end.
51 NUM_BAD_SYNC_DATA_REASONS
54 void RecordBadSyncData(BadSyncDataReason reason
) {
55 UMA_HISTOGRAM_ENUMERATION("Extensions.BadSyncDataReason", reason
,
56 NUM_BAD_SYNC_DATA_REASONS
);
61 ExtensionSyncData::ExtensionSyncData()
62 : uninstalled_(false),
64 supports_disable_reasons_(false),
65 disable_reasons_(Extension::DISABLE_NONE
),
66 incognito_enabled_(false),
67 remote_install_(false),
68 all_urls_enabled_(BOOLEAN_UNSET
),
69 installed_by_custodian_(false) {
72 ExtensionSyncData::ExtensionSyncData(const Extension
& extension
,
75 bool incognito_enabled
,
77 OptionalBoolean all_urls_enabled
)
78 : id_(extension
.id()),
81 supports_disable_reasons_(true),
82 disable_reasons_(disable_reasons
),
83 incognito_enabled_(incognito_enabled
),
84 remote_install_(remote_install
),
85 all_urls_enabled_(all_urls_enabled
),
86 installed_by_custodian_(extension
.was_installed_by_custodian()),
87 version_(extension
.from_bookmark() ? base::Version("0")
88 : *extension
.version()),
89 update_url_(ManifestURL::GetUpdateURL(&extension
)),
90 name_(extension
.non_localized_name()) {
93 ExtensionSyncData::~ExtensionSyncData() {}
96 scoped_ptr
<ExtensionSyncData
> ExtensionSyncData::CreateFromSyncData(
97 const syncer::SyncData
& sync_data
) {
98 scoped_ptr
<ExtensionSyncData
> data(new ExtensionSyncData
);
99 if (data
->PopulateFromSyncData(sync_data
))
101 return scoped_ptr
<ExtensionSyncData
>();
105 scoped_ptr
<ExtensionSyncData
> ExtensionSyncData::CreateFromSyncChange(
106 const syncer::SyncChange
& sync_change
) {
107 scoped_ptr
<ExtensionSyncData
> data(
108 CreateFromSyncData(sync_change
.sync_data()));
110 return scoped_ptr
<ExtensionSyncData
>();
112 data
->set_uninstalled(sync_change
.change_type() ==
113 syncer::SyncChange::ACTION_DELETE
);
117 syncer::SyncData
ExtensionSyncData::GetSyncData() const {
118 sync_pb::EntitySpecifics specifics
;
119 PopulateExtensionSpecifics(specifics
.mutable_extension());
121 return syncer::SyncData::CreateLocalData(id_
, name_
, specifics
);
124 syncer::SyncChange
ExtensionSyncData::GetSyncChange(
125 syncer::SyncChange::SyncChangeType change_type
) const {
126 return syncer::SyncChange(FROM_HERE
, change_type
, GetSyncData());
129 void ExtensionSyncData::PopulateExtensionSpecifics(
130 sync_pb::ExtensionSpecifics
* specifics
) const {
131 DCHECK(crx_file::id_util::IdIsValid(id_
));
132 specifics
->set_id(id_
);
133 specifics
->set_update_url(update_url_
.spec());
134 specifics
->set_version(version_
.GetString());
135 specifics
->set_enabled(enabled_
);
136 if (supports_disable_reasons_
)
137 specifics
->set_disable_reasons(disable_reasons_
);
138 specifics
->set_incognito_enabled(incognito_enabled_
);
139 specifics
->set_remote_install(remote_install_
);
140 if (all_urls_enabled_
!= BOOLEAN_UNSET
)
141 specifics
->set_all_urls_enabled(all_urls_enabled_
== BOOLEAN_TRUE
);
142 specifics
->set_installed_by_custodian(installed_by_custodian_
);
143 specifics
->set_name(name_
);
146 bool ExtensionSyncData::PopulateFromExtensionSpecifics(
147 const sync_pb::ExtensionSpecifics
& specifics
) {
148 if (!crx_file::id_util::IdIsValid(specifics
.id())) {
149 LOG(ERROR
) << "Attempt to sync bad ExtensionSpecifics (bad ID):\n"
150 << GetExtensionSpecificsLogMessage(specifics
);
151 RecordBadSyncData(BAD_EXTENSION_ID
);
155 Version
specifics_version(specifics
.version());
156 if (!specifics_version
.IsValid()) {
157 LOG(ERROR
) << "Attempt to sync bad ExtensionSpecifics (bad version):\n"
158 << GetExtensionSpecificsLogMessage(specifics
);
159 RecordBadSyncData(BAD_VERSION
);
163 // The update URL must be either empty or valid.
164 GURL
specifics_update_url(specifics
.update_url());
165 if (!specifics_update_url
.is_empty() && !specifics_update_url
.is_valid()) {
166 LOG(ERROR
) << "Attempt to sync bad ExtensionSpecifics (bad update URL):\n"
167 << GetExtensionSpecificsLogMessage(specifics
);
168 RecordBadSyncData(BAD_UPDATE_URL
);
172 // Enabled extensions can't have disable reasons. (The proto field may be
173 // unset, in which case it defaults to DISABLE_NONE.)
174 if (specifics
.enabled() &&
175 specifics
.disable_reasons() != Extension::DISABLE_NONE
) {
176 LOG(ERROR
) << "Attempt to sync bad ExtensionSpecifics "
177 << "(enabled extension can't have disable reasons):\n"
178 << GetExtensionSpecificsLogMessage(specifics
);
179 RecordBadSyncData(BAD_DISABLE_REASONS
);
183 id_
= specifics
.id();
184 update_url_
= specifics_update_url
;
185 version_
= specifics_version
;
186 enabled_
= specifics
.enabled();
187 supports_disable_reasons_
= specifics
.has_disable_reasons();
188 disable_reasons_
= specifics
.disable_reasons();
189 incognito_enabled_
= specifics
.incognito_enabled();
190 if (specifics
.has_all_urls_enabled()) {
192 specifics
.all_urls_enabled() ? BOOLEAN_TRUE
: BOOLEAN_FALSE
;
194 // Set this explicitly (even though it's the default) on the offchance
195 // that someone is re-using an ExtensionSyncData object.
196 all_urls_enabled_
= BOOLEAN_UNSET
;
198 remote_install_
= specifics
.remote_install();
199 installed_by_custodian_
= specifics
.installed_by_custodian();
200 name_
= specifics
.name();
204 void ExtensionSyncData::set_uninstalled(bool uninstalled
) {
205 uninstalled_
= uninstalled
;
208 bool ExtensionSyncData::PopulateFromSyncData(
209 const syncer::SyncData
& sync_data
) {
210 const sync_pb::EntitySpecifics
& entity_specifics
= sync_data
.GetSpecifics();
212 if (entity_specifics
.has_extension())
213 return PopulateFromExtensionSpecifics(entity_specifics
.extension());
215 LOG(ERROR
) << "Attempt to sync bad EntitySpecifics: no extension data.";
216 RecordBadSyncData(NO_EXTENSION_SPECIFICS
);
220 } // namespace extensions