Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / extensions / extension_sync_data.cc
blob6404e8b61ca690b75f3cea42094aaf779b885019
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/extension_service.h"
11 #include "chrome/common/extensions/manifest_handlers/app_icon_color_info.h"
12 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
13 #include "chrome/common/extensions/manifest_handlers/linked_app_icons.h"
14 #include "components/crx_file/id_util.h"
15 #include "extensions/common/extension.h"
16 #include "extensions/common/manifest_url_handlers.h"
17 #include "sync/api/sync_data.h"
18 #include "sync/protocol/app_specifics.pb.h"
19 #include "sync/protocol/extension_specifics.pb.h"
20 #include "sync/protocol/sync.pb.h"
22 using syncer::StringOrdinal;
24 namespace extensions {
26 namespace {
28 std::string GetExtensionSpecificsLogMessage(
29 const sync_pb::ExtensionSpecifics& specifics) {
30 return base::StringPrintf(
31 "id: %s\nversion: %s\nupdate_url: %s\nenabled: %i\ndisable_reasons: %i",
32 specifics.id().c_str(),
33 specifics.version().c_str(),
34 specifics.update_url().c_str(),
35 specifics.enabled(),
36 specifics.disable_reasons());
39 enum BadSyncDataReason {
40 // Invalid extension ID.
41 BAD_EXTENSION_ID,
43 // Invalid version.
44 BAD_VERSION,
46 // Invalid update URL.
47 BAD_UPDATE_URL,
49 // No ExtensionSpecifics in the EntitySpecifics.
50 NO_EXTENSION_SPECIFICS,
52 // Enabled extensions can't have disable reasons.
53 BAD_DISABLE_REASONS,
55 // Must be at the end.
56 NUM_BAD_SYNC_DATA_REASONS
59 void RecordBadSyncData(BadSyncDataReason reason) {
60 UMA_HISTOGRAM_ENUMERATION("Extensions.BadSyncDataReason", reason,
61 NUM_BAD_SYNC_DATA_REASONS);
64 } // namespace
66 ExtensionSyncData::LinkedAppIconInfo::LinkedAppIconInfo() {
69 ExtensionSyncData::LinkedAppIconInfo::~LinkedAppIconInfo() {
72 ExtensionSyncData::ExtensionSyncData()
73 : is_app_(false),
74 uninstalled_(false),
75 enabled_(false),
76 supports_disable_reasons_(false),
77 disable_reasons_(Extension::DISABLE_NONE),
78 incognito_enabled_(false),
79 remote_install_(false),
80 all_urls_enabled_(BOOLEAN_UNSET),
81 installed_by_custodian_(false),
82 launch_type_(LAUNCH_TYPE_INVALID) {
85 ExtensionSyncData::ExtensionSyncData(const Extension& extension,
86 bool enabled,
87 int disable_reasons,
88 bool incognito_enabled,
89 bool remote_install,
90 OptionalBoolean all_urls_enabled)
91 : ExtensionSyncData(extension, enabled, disable_reasons, incognito_enabled,
92 remote_install, all_urls_enabled, StringOrdinal(),
93 StringOrdinal(), LAUNCH_TYPE_INVALID) {
96 ExtensionSyncData::ExtensionSyncData(const Extension& extension,
97 bool enabled,
98 int disable_reasons,
99 bool incognito_enabled,
100 bool remote_install,
101 OptionalBoolean all_urls_enabled,
102 const StringOrdinal& app_launch_ordinal,
103 const StringOrdinal& page_ordinal,
104 extensions::LaunchType launch_type)
105 : is_app_(extension.is_app()),
106 id_(extension.id()),
107 uninstalled_(false),
108 enabled_(enabled),
109 supports_disable_reasons_(true),
110 disable_reasons_(disable_reasons),
111 incognito_enabled_(incognito_enabled),
112 remote_install_(remote_install),
113 all_urls_enabled_(all_urls_enabled),
114 installed_by_custodian_(extension.was_installed_by_custodian()),
115 version_(extension.from_bookmark() ? base::Version("0")
116 : *extension.version()),
117 update_url_(ManifestURL::GetUpdateURL(&extension)),
118 name_(extension.non_localized_name()),
119 app_launch_ordinal_(app_launch_ordinal),
120 page_ordinal_(page_ordinal),
121 launch_type_(launch_type) {
122 if (is_app_ && extension.from_bookmark()) {
123 bookmark_app_description_ = extension.description();
124 bookmark_app_url_ = AppLaunchInfo::GetLaunchWebURL(&extension).spec();
125 bookmark_app_icon_color_ = AppIconColorInfo::GetIconColorString(&extension);
126 extensions::LinkedAppIcons icons =
127 LinkedAppIcons::GetLinkedAppIcons(&extension);
128 for (const auto& icon : icons.icons) {
129 LinkedAppIconInfo linked_icon;
130 linked_icon.url = icon.url;
131 linked_icon.size = icon.size;
132 linked_icons_.push_back(linked_icon);
137 ExtensionSyncData::~ExtensionSyncData() {}
139 // static
140 scoped_ptr<ExtensionSyncData> ExtensionSyncData::CreateFromSyncData(
141 const syncer::SyncData& sync_data) {
142 scoped_ptr<ExtensionSyncData> data(new ExtensionSyncData);
143 if (data->PopulateFromSyncData(sync_data))
144 return data.Pass();
145 return nullptr;
148 // static
149 scoped_ptr<ExtensionSyncData> ExtensionSyncData::CreateFromSyncChange(
150 const syncer::SyncChange& sync_change) {
151 scoped_ptr<ExtensionSyncData> data(
152 CreateFromSyncData(sync_change.sync_data()));
153 if (!data.get())
154 return nullptr;
156 data->set_uninstalled(sync_change.change_type() ==
157 syncer::SyncChange::ACTION_DELETE);
158 return data.Pass();
161 syncer::SyncData ExtensionSyncData::GetSyncData() const {
162 sync_pb::EntitySpecifics specifics;
163 if (is_app_)
164 ToAppSpecifics(specifics.mutable_app());
165 else
166 ToExtensionSpecifics(specifics.mutable_extension());
168 return syncer::SyncData::CreateLocalData(id_, name_, specifics);
171 syncer::SyncChange ExtensionSyncData::GetSyncChange(
172 syncer::SyncChange::SyncChangeType change_type) const {
173 return syncer::SyncChange(FROM_HERE, change_type, GetSyncData());
176 void ExtensionSyncData::ToExtensionSpecifics(
177 sync_pb::ExtensionSpecifics* specifics) const {
178 DCHECK(crx_file::id_util::IdIsValid(id_));
179 specifics->set_id(id_);
180 specifics->set_update_url(update_url_.spec());
181 specifics->set_version(version_.GetString());
182 specifics->set_enabled(enabled_);
183 if (supports_disable_reasons_)
184 specifics->set_disable_reasons(disable_reasons_);
185 specifics->set_incognito_enabled(incognito_enabled_);
186 specifics->set_remote_install(remote_install_);
187 if (all_urls_enabled_ != BOOLEAN_UNSET)
188 specifics->set_all_urls_enabled(all_urls_enabled_ == BOOLEAN_TRUE);
189 specifics->set_installed_by_custodian(installed_by_custodian_);
190 specifics->set_name(name_);
193 void ExtensionSyncData::ToAppSpecifics(sync_pb::AppSpecifics* specifics) const {
194 DCHECK(specifics);
195 // Only sync the ordinal values and launch type if they are valid.
196 if (app_launch_ordinal_.IsValid())
197 specifics->set_app_launch_ordinal(app_launch_ordinal_.ToInternalValue());
198 if (page_ordinal_.IsValid())
199 specifics->set_page_ordinal(page_ordinal_.ToInternalValue());
201 sync_pb::AppSpecifics::LaunchType sync_launch_type =
202 static_cast<sync_pb::AppSpecifics::LaunchType>(launch_type_);
204 // The corresponding validation of this value during processing of an
205 // ExtensionSyncData is in ExtensionSyncService::ApplySyncData.
206 if (launch_type_ >= LAUNCH_TYPE_FIRST && launch_type_ < NUM_LAUNCH_TYPES &&
207 sync_pb::AppSpecifics_LaunchType_IsValid(sync_launch_type)) {
208 specifics->set_launch_type(sync_launch_type);
211 if (!bookmark_app_url_.empty())
212 specifics->set_bookmark_app_url(bookmark_app_url_);
214 if (!bookmark_app_description_.empty())
215 specifics->set_bookmark_app_description(bookmark_app_description_);
217 if (!bookmark_app_icon_color_.empty())
218 specifics->set_bookmark_app_icon_color(bookmark_app_icon_color_);
220 for (const auto& linked_icon : linked_icons_) {
221 sync_pb::LinkedAppIconInfo* linked_app_icon_info =
222 specifics->add_linked_app_icons();
223 linked_app_icon_info->set_url(linked_icon.url.spec());
224 linked_app_icon_info->set_size(linked_icon.size);
227 ToExtensionSpecifics(specifics->mutable_extension());
230 bool ExtensionSyncData::PopulateFromExtensionSpecifics(
231 const sync_pb::ExtensionSpecifics& specifics) {
232 if (!crx_file::id_util::IdIsValid(specifics.id())) {
233 LOG(ERROR) << "Attempt to sync bad ExtensionSpecifics (bad ID):\n"
234 << GetExtensionSpecificsLogMessage(specifics);
235 RecordBadSyncData(BAD_EXTENSION_ID);
236 return false;
239 Version specifics_version(specifics.version());
240 if (!specifics_version.IsValid()) {
241 LOG(ERROR) << "Attempt to sync bad ExtensionSpecifics (bad version):\n"
242 << GetExtensionSpecificsLogMessage(specifics);
243 RecordBadSyncData(BAD_VERSION);
244 return false;
247 // The update URL must be either empty or valid.
248 GURL specifics_update_url(specifics.update_url());
249 if (!specifics_update_url.is_empty() && !specifics_update_url.is_valid()) {
250 LOG(ERROR) << "Attempt to sync bad ExtensionSpecifics (bad update URL):\n"
251 << GetExtensionSpecificsLogMessage(specifics);
252 RecordBadSyncData(BAD_UPDATE_URL);
253 return false;
256 // Enabled extensions can't have disable reasons. (The proto field may be
257 // unset, in which case it defaults to DISABLE_NONE.)
258 if (specifics.enabled() &&
259 specifics.disable_reasons() != Extension::DISABLE_NONE) {
260 LOG(ERROR) << "Attempt to sync bad ExtensionSpecifics "
261 << "(enabled extension can't have disable reasons):\n"
262 << GetExtensionSpecificsLogMessage(specifics);
263 RecordBadSyncData(BAD_DISABLE_REASONS);
264 return false;
267 id_ = specifics.id();
268 update_url_ = specifics_update_url;
269 version_ = specifics_version;
270 enabled_ = specifics.enabled();
271 supports_disable_reasons_ = specifics.has_disable_reasons();
272 disable_reasons_ = specifics.disable_reasons();
273 incognito_enabled_ = specifics.incognito_enabled();
274 if (specifics.has_all_urls_enabled()) {
275 all_urls_enabled_ =
276 specifics.all_urls_enabled() ? BOOLEAN_TRUE : BOOLEAN_FALSE;
277 } else {
278 // Set this explicitly (even though it's the default) on the offchance
279 // that someone is re-using an ExtensionSyncData object.
280 all_urls_enabled_ = BOOLEAN_UNSET;
282 remote_install_ = specifics.remote_install();
283 installed_by_custodian_ = specifics.installed_by_custodian();
284 name_ = specifics.name();
285 return true;
288 bool ExtensionSyncData::PopulateFromAppSpecifics(
289 const sync_pb::AppSpecifics& specifics) {
290 if (!PopulateFromExtensionSpecifics(specifics.extension()))
291 return false;
293 is_app_ = true;
295 app_launch_ordinal_ = syncer::StringOrdinal(specifics.app_launch_ordinal());
296 page_ordinal_ = syncer::StringOrdinal(specifics.page_ordinal());
298 launch_type_ = specifics.has_launch_type()
299 ? static_cast<extensions::LaunchType>(specifics.launch_type())
300 : LAUNCH_TYPE_INVALID;
302 bookmark_app_url_ = specifics.bookmark_app_url();
303 bookmark_app_description_ = specifics.bookmark_app_description();
304 bookmark_app_icon_color_ = specifics.bookmark_app_icon_color();
306 for (int i = 0; i < specifics.linked_app_icons_size(); ++i) {
307 const sync_pb::LinkedAppIconInfo& linked_app_icon_info =
308 specifics.linked_app_icons(i);
309 if (linked_app_icon_info.has_url() && linked_app_icon_info.has_size()) {
310 LinkedAppIconInfo linked_icon;
311 linked_icon.url = GURL(linked_app_icon_info.url());
312 linked_icon.size = linked_app_icon_info.size();
313 linked_icons_.push_back(linked_icon);
317 return true;
320 void ExtensionSyncData::set_uninstalled(bool uninstalled) {
321 uninstalled_ = uninstalled;
324 bool ExtensionSyncData::PopulateFromSyncData(
325 const syncer::SyncData& sync_data) {
326 const sync_pb::EntitySpecifics& entity_specifics = sync_data.GetSpecifics();
328 if (entity_specifics.has_app())
329 return PopulateFromAppSpecifics(entity_specifics.app());
331 if (entity_specifics.has_extension())
332 return PopulateFromExtensionSpecifics(entity_specifics.extension());
334 LOG(ERROR) << "Attempt to sync bad EntitySpecifics: no extension data.";
335 RecordBadSyncData(NO_EXTENSION_SPECIFICS);
336 return false;
339 } // namespace extensions