1 // Copyright (c) 2011 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.
4
5 #include "chrome/browser/sync/syncable/model_type.h"
6
7 #include "base/metrics/histogram.h"
8 #include "base/values.h"
9 #include "chrome/browser/sync/engine/syncproto.h"
10 #include "chrome/browser/sync/protocol/app_specifics.pb.h"
11 #include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
12 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
13 #include "chrome/browser/sync/protocol/extension_specifics.pb.h"
14 #include "chrome/browser/sync/protocol/nigori_specifics.pb.h"
15 #include "chrome/browser/sync/protocol/password_specifics.pb.h"
16 #include "chrome/browser/sync/protocol/preference_specifics.pb.h"
17 #include "chrome/browser/sync/protocol/session_specifics.pb.h"
18 #include "chrome/browser/sync/protocol/sync.pb.h"
19 #include "chrome/browser/sync/protocol/theme_specifics.pb.h"
20 #include "chrome/browser/sync/protocol/typed_url_specifics.pb.h"
21
22 namespace syncable {
23
AddDefaultExtensionValue(syncable::ModelType datatype,sync_pb::EntitySpecifics * specifics)24 void AddDefaultExtensionValue(syncable::ModelType datatype,
25 sync_pb::EntitySpecifics* specifics) {
26 switch (datatype) {
27 case BOOKMARKS:
28 specifics->MutableExtension(sync_pb::bookmark);
29 break;
30 case PASSWORDS:
31 specifics->MutableExtension(sync_pb::password);
32 break;
33 case PREFERENCES:
34 specifics->MutableExtension(sync_pb::preference);
35 break;
36 case AUTOFILL:
37 specifics->MutableExtension(sync_pb::autofill);
38 break;
39 case AUTOFILL_PROFILE:
40 specifics->MutableExtension(sync_pb::autofill_profile);
41 break;
42 case THEMES:
43 specifics->MutableExtension(sync_pb::theme);
44 break;
45 case TYPED_URLS:
46 specifics->MutableExtension(sync_pb::typed_url);
47 break;
48 case EXTENSIONS:
49 specifics->MutableExtension(sync_pb::extension);
50 break;
51 case NIGORI:
52 specifics->MutableExtension(sync_pb::nigori);
53 break;
54 case SESSIONS:
55 specifics->MutableExtension(sync_pb::session);
56 break;
57 case APPS:
58 specifics->MutableExtension(sync_pb::app);
59 break;
60 default:
61 NOTREACHED() << "No known extension for model type.";
62 }
63 }
64
GetModelTypeFromExtensionFieldNumber(int field_number)65 ModelType GetModelTypeFromExtensionFieldNumber(int field_number) {
66 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
67 ModelType model_type = ModelTypeFromInt(i);
68 if (GetExtensionFieldNumberFromModelType(model_type) == field_number)
69 return model_type;
70 }
71 NOTREACHED();
72 return UNSPECIFIED;
73 }
74
GetExtensionFieldNumberFromModelType(ModelType model_type)75 int GetExtensionFieldNumberFromModelType(ModelType model_type) {
76 switch (model_type) {
77 case BOOKMARKS:
78 return sync_pb::kBookmarkFieldNumber;
79 break;
80 case PASSWORDS:
81 return sync_pb::kPasswordFieldNumber;
82 break;
83 case PREFERENCES:
84 return sync_pb::kPreferenceFieldNumber;
85 break;
86 case AUTOFILL:
87 return sync_pb::kAutofillFieldNumber;
88 break;
89 case AUTOFILL_PROFILE:
90 return sync_pb::kAutofillProfileFieldNumber;
91 break;
92 case THEMES:
93 return sync_pb::kThemeFieldNumber;
94 break;
95 case TYPED_URLS:
96 return sync_pb::kTypedUrlFieldNumber;
97 break;
98 case EXTENSIONS:
99 return sync_pb::kExtensionFieldNumber;
100 break;
101 case NIGORI:
102 return sync_pb::kNigoriFieldNumber;
103 break;
104 case SESSIONS:
105 return sync_pb::kSessionFieldNumber;
106 break;
107 case APPS:
108 return sync_pb::kAppFieldNumber;
109 break;
110 default:
111 NOTREACHED() << "No known extension for model type.";
112 return 0;
113 }
114 NOTREACHED() << "Needed for linux_keep_shadow_stacks because of "
115 << "http://gcc.gnu.org/bugzilla/show_bug.cgi?id=20681";
116 return 0;
117 }
118
119 // Note: keep this consistent with GetModelType in syncable.cc!
GetModelType(const sync_pb::SyncEntity & sync_pb_entity)120 ModelType GetModelType(const sync_pb::SyncEntity& sync_pb_entity) {
121 const browser_sync::SyncEntity& sync_entity =
122 static_cast<const browser_sync::SyncEntity&>(sync_pb_entity);
123 DCHECK(!sync_entity.id().IsRoot()); // Root shouldn't ever go over the wire.
124
125 if (sync_entity.deleted())
126 return UNSPECIFIED;
127
128 // Backwards compatibility with old (pre-specifics) protocol.
129 if (sync_entity.has_bookmarkdata())
130 return BOOKMARKS;
131
132 ModelType specifics_type = GetModelTypeFromSpecifics(sync_entity.specifics());
133 if (specifics_type != UNSPECIFIED)
134 return specifics_type;
135
136 // Loose check for server-created top-level folders that aren't
137 // bound to a particular model type.
138 if (!sync_entity.server_defined_unique_tag().empty() &&
139 sync_entity.IsFolder()) {
140 return TOP_LEVEL_FOLDER;
141 }
142
143 // This is an item of a datatype we can't understand. Maybe it's
144 // from the future? Either we mis-encoded the object, or the
145 // server sent us entries it shouldn't have.
146 NOTREACHED() << "Unknown datatype in sync proto.";
147 return UNSPECIFIED;
148 }
149
GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics & specifics)150 ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
151 if (specifics.HasExtension(sync_pb::bookmark))
152 return BOOKMARKS;
153
154 if (specifics.HasExtension(sync_pb::password))
155 return PASSWORDS;
156
157 if (specifics.HasExtension(sync_pb::preference))
158 return PREFERENCES;
159
160 if (specifics.HasExtension(sync_pb::autofill))
161 return AUTOFILL;
162
163 if (specifics.HasExtension(sync_pb::autofill_profile))
164 return AUTOFILL_PROFILE;
165
166 if (specifics.HasExtension(sync_pb::theme))
167 return THEMES;
168
169 if (specifics.HasExtension(sync_pb::typed_url))
170 return TYPED_URLS;
171
172 if (specifics.HasExtension(sync_pb::extension))
173 return EXTENSIONS;
174
175 if (specifics.HasExtension(sync_pb::nigori))
176 return NIGORI;
177
178 if (specifics.HasExtension(sync_pb::app))
179 return APPS;
180
181 if (specifics.HasExtension(sync_pb::session))
182 return SESSIONS;
183
184 return UNSPECIFIED;
185 }
186
ModelTypeToString(ModelType model_type)187 std::string ModelTypeToString(ModelType model_type) {
188 switch (model_type) {
189 case BOOKMARKS:
190 return "Bookmarks";
191 case PREFERENCES:
192 return "Preferences";
193 case PASSWORDS:
194 return "Passwords";
195 case AUTOFILL:
196 return "Autofill";
197 case THEMES:
198 return "Themes";
199 case TYPED_URLS:
200 return "Typed URLs";
201 case EXTENSIONS:
202 return "Extensions";
203 case NIGORI:
204 return "Encryption keys";
205 case SESSIONS:
206 return "Sessions";
207 case APPS:
208 return "Apps";
209 case AUTOFILL_PROFILE:
210 return "Autofill Profiles";
211 default:
212 break;
213 }
214 NOTREACHED() << "No known extension for model type.";
215 return "INVALID";
216 }
217
ModelTypeToValue(ModelType model_type)218 StringValue* ModelTypeToValue(ModelType model_type) {
219 if (model_type >= syncable::FIRST_REAL_MODEL_TYPE) {
220 return Value::CreateStringValue(ModelTypeToString(model_type));
221 } else if (model_type == syncable::TOP_LEVEL_FOLDER) {
222 return Value::CreateStringValue("Top-level folder");
223 } else if (model_type == syncable::UNSPECIFIED) {
224 return Value::CreateStringValue("Unspecified");
225 }
226 NOTREACHED();
227 return Value::CreateStringValue("");
228 }
229
ModelTypeSetToString(const ModelTypeSet & model_types)230 std::string ModelTypeSetToString(const ModelTypeSet& model_types) {
231 std::string result;
232 for (ModelTypeSet::const_iterator iter = model_types.begin();
233 iter != model_types.end();) {
234 result += ModelTypeToString(*iter);
235 if (++iter != model_types.end())
236 result += ", ";
237 }
238 return result;
239 }
240
ModelTypeFromString(const std::string & model_type_string)241 ModelType ModelTypeFromString(const std::string& model_type_string) {
242 if (model_type_string == "Bookmarks")
243 return BOOKMARKS;
244 else if (model_type_string == "Preferences")
245 return PREFERENCES;
246 else if (model_type_string == "Passwords")
247 return PASSWORDS;
248 else if (model_type_string == "Autofill")
249 return AUTOFILL;
250 else if (model_type_string == "Autofill Profiles")
251 return AUTOFILL_PROFILE;
252 else if (model_type_string == "Themes")
253 return THEMES;
254 else if (model_type_string == "Typed URLs")
255 return TYPED_URLS;
256 else if (model_type_string == "Extensions")
257 return EXTENSIONS;
258 else if (model_type_string == "Encryption keys")
259 return NIGORI;
260 else if (model_type_string == "Sessions")
261 return SESSIONS;
262 else if (model_type_string == "Apps")
263 return APPS;
264 else
265 NOTREACHED() << "No known model type corresponding to "
266 << model_type_string << ".";
267 return UNSPECIFIED;
268 }
269
ModelTypeBitSetFromString(const std::string & model_type_bitset_string,ModelTypeBitSet * model_types)270 bool ModelTypeBitSetFromString(
271 const std::string& model_type_bitset_string,
272 ModelTypeBitSet* model_types) {
273 DCHECK(model_types);
274 if (model_type_bitset_string.length() != MODEL_TYPE_COUNT)
275 return false;
276 if (model_type_bitset_string.find_first_not_of("01") != std::string::npos)
277 return false;
278 *model_types = ModelTypeBitSet(model_type_bitset_string);
279 return true;
280 }
281
ModelTypeBitSetFromSet(const ModelTypeSet & set)282 ModelTypeBitSet ModelTypeBitSetFromSet(const ModelTypeSet& set) {
283 ModelTypeBitSet bitset;
284 for (ModelTypeSet::const_iterator iter = set.begin(); iter != set.end();
285 ++iter) {
286 bitset.set(*iter);
287 }
288 return bitset;
289 }
290
ModelTypeBitSetToValue(const ModelTypeBitSet & model_types)291 ListValue* ModelTypeBitSetToValue(const ModelTypeBitSet& model_types) {
292 ListValue* value = new ListValue();
293 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
294 if (model_types[i]) {
295 value->Append(
296 Value::CreateStringValue(ModelTypeToString(ModelTypeFromInt(i))));
297 }
298 }
299 return value;
300 }
301
ModelTypeSetToValue(const ModelTypeSet & model_types)302 ListValue* ModelTypeSetToValue(const ModelTypeSet& model_types) {
303 ListValue* value = new ListValue();
304 for (ModelTypeSet::const_iterator i = model_types.begin();
305 i != model_types.end(); ++i) {
306 value->Append(Value::CreateStringValue(ModelTypeToString(*i)));
307 }
308 return value;
309 }
310
311 // TODO(zea): remove all hardcoded tags in model associators and have them use
312 // this instead.
ModelTypeToRootTag(ModelType type)313 std::string ModelTypeToRootTag(ModelType type) {
314 switch (type) {
315 case BOOKMARKS:
316 return "google_chrome_bookmarks";
317 case PREFERENCES:
318 return "google_chrome_preferences";
319 case PASSWORDS:
320 return "google_chrome_passwords";
321 case AUTOFILL:
322 return "google_chrome_autofill";
323 case THEMES:
324 return "google_chrome_themes";
325 case TYPED_URLS:
326 return "google_chrome_typed_urls";
327 case EXTENSIONS:
328 return "google_chrome_extensions";
329 case NIGORI:
330 return "google_chrome_nigori";
331 case SESSIONS:
332 return "google_chrome_sessions";
333 case APPS:
334 return "google_chrome_apps";
335 case AUTOFILL_PROFILE:
336 return "google_chrome_autofill_profiles";
337 default:
338 break;
339 }
340 NOTREACHED() << "No known extension for model type.";
341 return "INVALID";
342 }
343
344 // For now, this just implements UMA_HISTOGRAM_LONG_TIMES. This can be adjusted
345 // if we feel the min, max, or bucket count amount are not appropriate.
346 #define SYNC_FREQ_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES( \
347 name, time, base::TimeDelta::FromMilliseconds(1), \
348 base::TimeDelta::FromHours(1), 50)
349
PostTimeToTypeHistogram(ModelType model_type,base::TimeDelta time)350 void PostTimeToTypeHistogram(ModelType model_type, base::TimeDelta time) {
351 switch (model_type) {
352 case BOOKMARKS: {
353 SYNC_FREQ_HISTOGRAM("Sync.FreqBookmarks", time);
354 return;
355 }
356 case PREFERENCES: {
357 SYNC_FREQ_HISTOGRAM("Sync.FreqPreferences", time);
358 return;
359 }
360 case PASSWORDS: {
361 SYNC_FREQ_HISTOGRAM("Sync.FreqPasswords", time);
362 return;
363 }
364 case AUTOFILL: {
365 SYNC_FREQ_HISTOGRAM("Sync.FreqAutofill", time);
366 return;
367 }
368 case AUTOFILL_PROFILE: {
369 SYNC_FREQ_HISTOGRAM("Sync.FreqAutofillProfiles", time);
370 return;
371 }
372 case THEMES: {
373 SYNC_FREQ_HISTOGRAM("Sync.FreqThemes", time);
374 return;
375 }
376 case TYPED_URLS: {
377 SYNC_FREQ_HISTOGRAM("Sync.FreqTypedUrls", time);
378 return;
379 }
380 case EXTENSIONS: {
381 SYNC_FREQ_HISTOGRAM("Sync.FreqExtensions", time);
382 return;
383 }
384 case NIGORI: {
385 SYNC_FREQ_HISTOGRAM("Sync.FreqNigori", time);
386 return;
387 }
388 case SESSIONS: {
389 SYNC_FREQ_HISTOGRAM("Sync.FreqSessions", time);
390 return;
391 }
392 case APPS: {
393 SYNC_FREQ_HISTOGRAM("Sync.FreqApps", time);
394 return;
395 }
396 default:
397 LOG(ERROR) << "No known extension for model type.";
398 }
399 }
400
401 #undef SYNC_FREQ_HISTOGRAM
402
403 // TODO(akalin): Figure out a better way to do these mappings.
404
405 namespace {
406 const char kBookmarkNotificationType[] = "BOOKMARK";
407 const char kPreferenceNotificationType[] = "PREFERENCE";
408 const char kPasswordNotificationType[] = "PASSWORD";
409 const char kAutofillNotificationType[] = "AUTOFILL";
410 const char kThemeNotificationType[] = "THEME";
411 const char kTypedUrlNotificationType[] = "TYPED_URL";
412 const char kExtensionNotificationType[] = "EXTENSION";
413 const char kNigoriNotificationType[] = "NIGORI";
414 const char kAppNotificationType[] = "APP";
415 const char kSessionNotificationType[] = "SESSION";
416 const char kAutofillProfileNotificationType[] = "AUTOFILL_PROFILE";
417 } // namespace
418
RealModelTypeToNotificationType(ModelType model_type,std::string * notification_type)419 bool RealModelTypeToNotificationType(ModelType model_type,
420 std::string* notification_type) {
421 switch (model_type) {
422 case BOOKMARKS:
423 *notification_type = kBookmarkNotificationType;
424 return true;
425 case PREFERENCES:
426 *notification_type = kPreferenceNotificationType;
427 return true;
428 case PASSWORDS:
429 *notification_type = kPasswordNotificationType;
430 return true;
431 case AUTOFILL:
432 *notification_type = kAutofillNotificationType;
433 return true;
434 case THEMES:
435 *notification_type = kThemeNotificationType;
436 return true;
437 case TYPED_URLS:
438 *notification_type = kTypedUrlNotificationType;
439 return true;
440 case EXTENSIONS:
441 *notification_type = kExtensionNotificationType;
442 return true;
443 case NIGORI:
444 *notification_type = kNigoriNotificationType;
445 return true;
446 case APPS:
447 *notification_type = kAppNotificationType;
448 return true;
449 case SESSIONS:
450 *notification_type = kSessionNotificationType;
451 return true;
452 case AUTOFILL_PROFILE:
453 *notification_type = kAutofillProfileNotificationType;
454 return true;
455 default:
456 break;
457 }
458 notification_type->clear();
459 return false;
460 }
461
NotificationTypeToRealModelType(const std::string & notification_type,ModelType * model_type)462 bool NotificationTypeToRealModelType(const std::string& notification_type,
463 ModelType* model_type) {
464 if (notification_type == kBookmarkNotificationType) {
465 *model_type = BOOKMARKS;
466 return true;
467 } else if (notification_type == kPreferenceNotificationType) {
468 *model_type = PREFERENCES;
469 return true;
470 } else if (notification_type == kPasswordNotificationType) {
471 *model_type = PASSWORDS;
472 return true;
473 } else if (notification_type == kAutofillNotificationType) {
474 *model_type = AUTOFILL;
475 return true;
476 } else if (notification_type == kThemeNotificationType) {
477 *model_type = THEMES;
478 return true;
479 } else if (notification_type == kTypedUrlNotificationType) {
480 *model_type = TYPED_URLS;
481 return true;
482 } else if (notification_type == kExtensionNotificationType) {
483 *model_type = EXTENSIONS;
484 return true;
485 } else if (notification_type == kNigoriNotificationType) {
486 *model_type = NIGORI;
487 return true;
488 } else if (notification_type == kAppNotificationType) {
489 *model_type = APPS;
490 return true;
491 } else if (notification_type == kSessionNotificationType) {
492 *model_type = SESSIONS;
493 return true;
494 } else if (notification_type == kAutofillProfileNotificationType) {
495 *model_type = AUTOFILL_PROFILE;
496 return true;
497 }
498 *model_type = UNSPECIFIED;
499 return false;
500 }
501
502 } // namespace syncable
503