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 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 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 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! 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 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 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 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 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 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 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 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 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 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. 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 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 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 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