Home | History | Annotate | Download | only in syncable
      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