| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "components/bookmarks/core/browser/bookmark_codec.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/json/json_string_value_serializer.h" | |
| 10 #include "base/strings/string_number_conversions.h" | |
| 11 #include "base/strings/string_util.h" | |
| 12 #include "base/values.h" | |
| 13 #include "components/bookmarks/core/browser/bookmark_model.h" | |
| 14 #include "grit/component_strings.h" | |
| 15 #include "ui/base/l10n/l10n_util.h" | |
| 16 #include "url/gurl.h" | |
| 17 | |
| 18 using base::Time; | |
| 19 | |
| 20 const char* BookmarkCodec::kRootsKey = "roots"; | |
| 21 const char* BookmarkCodec::kRootFolderNameKey = "bookmark_bar"; | |
| 22 const char* BookmarkCodec::kOtherBookmarkFolderNameKey = "other"; | |
| 23 // The value is left as 'synced' for historical reasons. | |
| 24 const char* BookmarkCodec::kMobileBookmarkFolderNameKey = "synced"; | |
| 25 const char* BookmarkCodec::kVersionKey = "version"; | |
| 26 const char* BookmarkCodec::kChecksumKey = "checksum"; | |
| 27 const char* BookmarkCodec::kIdKey = "id"; | |
| 28 const char* BookmarkCodec::kTypeKey = "type"; | |
| 29 const char* BookmarkCodec::kNameKey = "name"; | |
| 30 const char* BookmarkCodec::kDateAddedKey = "date_added"; | |
| 31 const char* BookmarkCodec::kURLKey = "url"; | |
| 32 const char* BookmarkCodec::kDateModifiedKey = "date_modified"; | |
| 33 const char* BookmarkCodec::kChildrenKey = "children"; | |
| 34 const char* BookmarkCodec::kMetaInfo = "meta_info"; | |
| 35 const char* BookmarkCodec::kSyncTransactionVersion = "sync_transaction_version"; | |
| 36 const char* BookmarkCodec::kTypeURL = "url"; | |
| 37 const char* BookmarkCodec::kTypeFolder = "folder"; | |
| 38 | |
| 39 // Current version of the file. | |
| 40 static const int kCurrentVersion = 1; | |
| 41 | |
| 42 BookmarkCodec::BookmarkCodec() | |
| 43 : ids_reassigned_(false), | |
| 44 ids_valid_(true), | |
| 45 maximum_id_(0), | |
| 46 model_sync_transaction_version_( | |
| 47 BookmarkNode::kInvalidSyncTransactionVersion) { | |
| 48 } | |
| 49 | |
| 50 BookmarkCodec::~BookmarkCodec() {} | |
| 51 | |
| 52 base::Value* BookmarkCodec::Encode(BookmarkModel* model) { | |
| 53 return Encode(model->bookmark_bar_node(), | |
| 54 model->other_node(), | |
| 55 model->mobile_node(), | |
| 56 model->root_node()->GetMetaInfoMap(), | |
| 57 model->root_node()->sync_transaction_version()); | |
| 58 } | |
| 59 | |
| 60 base::Value* BookmarkCodec::Encode( | |
| 61 const BookmarkNode* bookmark_bar_node, | |
| 62 const BookmarkNode* other_folder_node, | |
| 63 const BookmarkNode* mobile_folder_node, | |
| 64 const BookmarkNode::MetaInfoMap* model_meta_info_map, | |
| 65 int64 sync_transaction_version) { | |
| 66 ids_reassigned_ = false; | |
| 67 InitializeChecksum(); | |
| 68 base::DictionaryValue* roots = new base::DictionaryValue(); | |
| 69 roots->Set(kRootFolderNameKey, EncodeNode(bookmark_bar_node)); | |
| 70 roots->Set(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node)); | |
| 71 roots->Set(kMobileBookmarkFolderNameKey, EncodeNode(mobile_folder_node)); | |
| 72 if (model_meta_info_map) | |
| 73 roots->Set(kMetaInfo, EncodeMetaInfo(*model_meta_info_map)); | |
| 74 if (sync_transaction_version != | |
| 75 BookmarkNode::kInvalidSyncTransactionVersion) { | |
| 76 roots->SetString(kSyncTransactionVersion, | |
| 77 base::Int64ToString(sync_transaction_version)); | |
| 78 } | |
| 79 base::DictionaryValue* main = new base::DictionaryValue(); | |
| 80 main->SetInteger(kVersionKey, kCurrentVersion); | |
| 81 FinalizeChecksum(); | |
| 82 // We are going to store the computed checksum. So set stored checksum to be | |
| 83 // the same as computed checksum. | |
| 84 stored_checksum_ = computed_checksum_; | |
| 85 main->Set(kChecksumKey, new base::StringValue(computed_checksum_)); | |
| 86 main->Set(kRootsKey, roots); | |
| 87 return main; | |
| 88 } | |
| 89 | |
| 90 bool BookmarkCodec::Decode(BookmarkNode* bb_node, | |
| 91 BookmarkNode* other_folder_node, | |
| 92 BookmarkNode* mobile_folder_node, | |
| 93 int64* max_id, | |
| 94 const base::Value& value) { | |
| 95 ids_.clear(); | |
| 96 ids_reassigned_ = false; | |
| 97 ids_valid_ = true; | |
| 98 maximum_id_ = 0; | |
| 99 stored_checksum_.clear(); | |
| 100 InitializeChecksum(); | |
| 101 bool success = DecodeHelper(bb_node, other_folder_node, mobile_folder_node, | |
| 102 value); | |
| 103 FinalizeChecksum(); | |
| 104 // If either the checksums differ or some IDs were missing/not unique, | |
| 105 // reassign IDs. | |
| 106 if (!ids_valid_ || computed_checksum() != stored_checksum()) | |
| 107 ReassignIDs(bb_node, other_folder_node, mobile_folder_node); | |
| 108 *max_id = maximum_id_ + 1; | |
| 109 return success; | |
| 110 } | |
| 111 | |
| 112 base::Value* BookmarkCodec::EncodeNode(const BookmarkNode* node) { | |
| 113 base::DictionaryValue* value = new base::DictionaryValue(); | |
| 114 std::string id = base::Int64ToString(node->id()); | |
| 115 value->SetString(kIdKey, id); | |
| 116 const base::string16& title = node->GetTitle(); | |
| 117 value->SetString(kNameKey, title); | |
| 118 value->SetString(kDateAddedKey, | |
| 119 base::Int64ToString(node->date_added().ToInternalValue())); | |
| 120 if (node->is_url()) { | |
| 121 value->SetString(kTypeKey, kTypeURL); | |
| 122 std::string url = node->url().possibly_invalid_spec(); | |
| 123 value->SetString(kURLKey, url); | |
| 124 UpdateChecksumWithUrlNode(id, title, url); | |
| 125 } else { | |
| 126 value->SetString(kTypeKey, kTypeFolder); | |
| 127 value->SetString(kDateModifiedKey, | |
| 128 base::Int64ToString(node->date_folder_modified(). | |
| 129 ToInternalValue())); | |
| 130 UpdateChecksumWithFolderNode(id, title); | |
| 131 | |
| 132 base::ListValue* child_values = new base::ListValue(); | |
| 133 value->Set(kChildrenKey, child_values); | |
| 134 for (int i = 0; i < node->child_count(); ++i) | |
| 135 child_values->Append(EncodeNode(node->GetChild(i))); | |
| 136 } | |
| 137 const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap(); | |
| 138 if (meta_info_map) | |
| 139 value->Set(kMetaInfo, EncodeMetaInfo(*meta_info_map)); | |
| 140 if (node->sync_transaction_version() != | |
| 141 BookmarkNode::kInvalidSyncTransactionVersion) { | |
| 142 value->SetString(kSyncTransactionVersion, | |
| 143 base::Int64ToString(node->sync_transaction_version())); | |
| 144 } | |
| 145 return value; | |
| 146 } | |
| 147 | |
| 148 base::Value* BookmarkCodec::EncodeMetaInfo( | |
| 149 const BookmarkNode::MetaInfoMap& meta_info_map) { | |
| 150 base::DictionaryValue* meta_info = new base::DictionaryValue; | |
| 151 for (BookmarkNode::MetaInfoMap::const_iterator it = meta_info_map.begin(); | |
| 152 it != meta_info_map.end(); ++it) { | |
| 153 meta_info->SetStringWithoutPathExpansion(it->first, it->second); | |
| 154 } | |
| 155 return meta_info; | |
| 156 } | |
| 157 | |
| 158 bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node, | |
| 159 BookmarkNode* other_folder_node, | |
| 160 BookmarkNode* mobile_folder_node, | |
| 161 const base::Value& value) { | |
| 162 if (value.GetType() != base::Value::TYPE_DICTIONARY) | |
| 163 return false; // Unexpected type. | |
| 164 | |
| 165 const base::DictionaryValue& d_value = | |
| 166 static_cast<const base::DictionaryValue&>(value); | |
| 167 | |
| 168 int version; | |
| 169 if (!d_value.GetInteger(kVersionKey, &version) || version != kCurrentVersion) | |
| 170 return false; // Unknown version. | |
| 171 | |
| 172 const base::Value* checksum_value; | |
| 173 if (d_value.Get(kChecksumKey, &checksum_value)) { | |
| 174 if (checksum_value->GetType() != base::Value::TYPE_STRING) | |
| 175 return false; | |
| 176 if (!checksum_value->GetAsString(&stored_checksum_)) | |
| 177 return false; | |
| 178 } | |
| 179 | |
| 180 const base::Value* roots; | |
| 181 if (!d_value.Get(kRootsKey, &roots)) | |
| 182 return false; // No roots. | |
| 183 | |
| 184 if (roots->GetType() != base::Value::TYPE_DICTIONARY) | |
| 185 return false; // Invalid type for roots. | |
| 186 | |
| 187 const base::DictionaryValue* roots_d_value = | |
| 188 static_cast<const base::DictionaryValue*>(roots); | |
| 189 const base::Value* root_folder_value; | |
| 190 const base::Value* other_folder_value = NULL; | |
| 191 if (!roots_d_value->Get(kRootFolderNameKey, &root_folder_value) || | |
| 192 root_folder_value->GetType() != base::Value::TYPE_DICTIONARY || | |
| 193 !roots_d_value->Get(kOtherBookmarkFolderNameKey, &other_folder_value) || | |
| 194 other_folder_value->GetType() != base::Value::TYPE_DICTIONARY) { | |
| 195 return false; // Invalid type for root folder and/or other | |
| 196 // folder. | |
| 197 } | |
| 198 DecodeNode(*static_cast<const base::DictionaryValue*>(root_folder_value), | |
| 199 NULL, bb_node); | |
| 200 DecodeNode(*static_cast<const base::DictionaryValue*>(other_folder_value), | |
| 201 NULL, other_folder_node); | |
| 202 | |
| 203 // Fail silently if we can't deserialize mobile bookmarks. We can't require | |
| 204 // them to exist in order to be backwards-compatible with older versions of | |
| 205 // chrome. | |
| 206 const base::Value* mobile_folder_value; | |
| 207 if (roots_d_value->Get(kMobileBookmarkFolderNameKey, &mobile_folder_value) && | |
| 208 mobile_folder_value->GetType() == base::Value::TYPE_DICTIONARY) { | |
| 209 DecodeNode(*static_cast<const base::DictionaryValue*>(mobile_folder_value), | |
| 210 NULL, mobile_folder_node); | |
| 211 } else { | |
| 212 // If we didn't find the mobile folder, we're almost guaranteed to have a | |
| 213 // duplicate id when we add the mobile folder. Consequently, if we don't | |
| 214 // intend to reassign ids in the future (ids_valid_ is still true), then at | |
| 215 // least reassign the mobile bookmarks to avoid it colliding with anything | |
| 216 // else. | |
| 217 if (ids_valid_) | |
| 218 ReassignIDsHelper(mobile_folder_node); | |
| 219 } | |
| 220 | |
| 221 if (!DecodeMetaInfo(*roots_d_value, &model_meta_info_map_, | |
| 222 &model_sync_transaction_version_)) | |
| 223 return false; | |
| 224 | |
| 225 std::string sync_transaction_version_str; | |
| 226 if (roots_d_value->GetString(kSyncTransactionVersion, | |
| 227 &sync_transaction_version_str) && | |
| 228 !base::StringToInt64(sync_transaction_version_str, | |
| 229 &model_sync_transaction_version_)) | |
| 230 return false; | |
| 231 | |
| 232 // Need to reset the type as decoding resets the type to FOLDER. Similarly | |
| 233 // we need to reset the title as the title is persisted and restored from | |
| 234 // the file. | |
| 235 bb_node->set_type(BookmarkNode::BOOKMARK_BAR); | |
| 236 other_folder_node->set_type(BookmarkNode::OTHER_NODE); | |
| 237 mobile_folder_node->set_type(BookmarkNode::MOBILE); | |
| 238 bb_node->SetTitle(l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_FOLDER_NAME)); | |
| 239 other_folder_node->SetTitle( | |
| 240 l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME)); | |
| 241 mobile_folder_node->SetTitle( | |
| 242 l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME)); | |
| 243 | |
| 244 return true; | |
| 245 } | |
| 246 | |
| 247 bool BookmarkCodec::DecodeChildren(const base::ListValue& child_value_list, | |
| 248 BookmarkNode* parent) { | |
| 249 for (size_t i = 0; i < child_value_list.GetSize(); ++i) { | |
| 250 const base::Value* child_value; | |
| 251 if (!child_value_list.Get(i, &child_value)) | |
| 252 return false; | |
| 253 | |
| 254 if (child_value->GetType() != base::Value::TYPE_DICTIONARY) | |
| 255 return false; | |
| 256 | |
| 257 DecodeNode(*static_cast<const base::DictionaryValue*>(child_value), | |
| 258 parent, NULL); | |
| 259 } | |
| 260 return true; | |
| 261 } | |
| 262 | |
| 263 bool BookmarkCodec::DecodeNode(const base::DictionaryValue& value, | |
| 264 BookmarkNode* parent, | |
| 265 BookmarkNode* node) { | |
| 266 // If no |node| is specified, we'll create one and add it to the |parent|. | |
| 267 // Therefore, in that case, |parent| must be non-NULL. | |
| 268 if (!node && !parent) { | |
| 269 NOTREACHED(); | |
| 270 return false; | |
| 271 } | |
| 272 | |
| 273 std::string id_string; | |
| 274 int64 id = 0; | |
| 275 if (ids_valid_) { | |
| 276 if (!value.GetString(kIdKey, &id_string) || | |
| 277 !base::StringToInt64(id_string, &id) || | |
| 278 ids_.count(id) != 0) { | |
| 279 ids_valid_ = false; | |
| 280 } else { | |
| 281 ids_.insert(id); | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 maximum_id_ = std::max(maximum_id_, id); | |
| 286 | |
| 287 base::string16 title; | |
| 288 value.GetString(kNameKey, &title); | |
| 289 | |
| 290 std::string date_added_string; | |
| 291 if (!value.GetString(kDateAddedKey, &date_added_string)) | |
| 292 date_added_string = base::Int64ToString(Time::Now().ToInternalValue()); | |
| 293 int64 internal_time; | |
| 294 base::StringToInt64(date_added_string, &internal_time); | |
| 295 | |
| 296 std::string type_string; | |
| 297 if (!value.GetString(kTypeKey, &type_string)) | |
| 298 return false; | |
| 299 | |
| 300 if (type_string != kTypeURL && type_string != kTypeFolder) | |
| 301 return false; // Unknown type. | |
| 302 | |
| 303 if (type_string == kTypeURL) { | |
| 304 std::string url_string; | |
| 305 if (!value.GetString(kURLKey, &url_string)) | |
| 306 return false; | |
| 307 | |
| 308 GURL url = GURL(url_string); | |
| 309 if (!node && url.is_valid()) | |
| 310 node = new BookmarkNode(id, url); | |
| 311 else | |
| 312 return false; // Node invalid. | |
| 313 | |
| 314 if (parent) | |
| 315 parent->Add(node, parent->child_count()); | |
| 316 node->set_type(BookmarkNode::URL); | |
| 317 UpdateChecksumWithUrlNode(id_string, title, url_string); | |
| 318 } else { | |
| 319 std::string last_modified_date; | |
| 320 if (!value.GetString(kDateModifiedKey, &last_modified_date)) | |
| 321 last_modified_date = base::Int64ToString(Time::Now().ToInternalValue()); | |
| 322 | |
| 323 const base::Value* child_values; | |
| 324 if (!value.Get(kChildrenKey, &child_values)) | |
| 325 return false; | |
| 326 | |
| 327 if (child_values->GetType() != base::Value::TYPE_LIST) | |
| 328 return false; | |
| 329 | |
| 330 if (!node) { | |
| 331 node = new BookmarkNode(id, GURL()); | |
| 332 } else { | |
| 333 // If a new node is not created, explicitly assign ID to the existing one. | |
| 334 node->set_id(id); | |
| 335 } | |
| 336 | |
| 337 node->set_type(BookmarkNode::FOLDER); | |
| 338 int64 internal_time; | |
| 339 base::StringToInt64(last_modified_date, &internal_time); | |
| 340 node->set_date_folder_modified(Time::FromInternalValue(internal_time)); | |
| 341 | |
| 342 if (parent) | |
| 343 parent->Add(node, parent->child_count()); | |
| 344 | |
| 345 UpdateChecksumWithFolderNode(id_string, title); | |
| 346 | |
| 347 if (!DecodeChildren(*static_cast<const base::ListValue*>(child_values), | |
| 348 node)) { | |
| 349 return false; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 node->SetTitle(title); | |
| 354 node->set_date_added(base::Time::FromInternalValue(internal_time)); | |
| 355 | |
| 356 int64 sync_transaction_version = node->sync_transaction_version(); | |
| 357 BookmarkNode::MetaInfoMap meta_info_map; | |
| 358 if (!DecodeMetaInfo(value, &meta_info_map, &sync_transaction_version)) | |
| 359 return false; | |
| 360 node->SetMetaInfoMap(meta_info_map); | |
| 361 | |
| 362 std::string sync_transaction_version_str; | |
| 363 if (value.GetString(kSyncTransactionVersion, &sync_transaction_version_str) && | |
| 364 !base::StringToInt64(sync_transaction_version_str, | |
| 365 &sync_transaction_version)) | |
| 366 return false; | |
| 367 | |
| 368 node->set_sync_transaction_version(sync_transaction_version); | |
| 369 | |
| 370 return true; | |
| 371 } | |
| 372 | |
| 373 bool BookmarkCodec::DecodeMetaInfo(const base::DictionaryValue& value, | |
| 374 BookmarkNode::MetaInfoMap* meta_info_map, | |
| 375 int64* sync_transaction_version) { | |
| 376 DCHECK(meta_info_map); | |
| 377 DCHECK(sync_transaction_version); | |
| 378 meta_info_map->clear(); | |
| 379 | |
| 380 const base::Value* meta_info; | |
| 381 if (!value.Get(kMetaInfo, &meta_info)) | |
| 382 return true; | |
| 383 | |
| 384 scoped_ptr<base::Value> deserialized_holder; | |
| 385 | |
| 386 // Meta info used to be stored as a serialized dictionary, so attempt to | |
| 387 // parse the value as one. | |
| 388 if (meta_info->IsType(base::Value::TYPE_STRING)) { | |
| 389 std::string meta_info_str; | |
| 390 meta_info->GetAsString(&meta_info_str); | |
| 391 JSONStringValueSerializer serializer(meta_info_str); | |
| 392 deserialized_holder.reset(serializer.Deserialize(NULL, NULL)); | |
| 393 if (!deserialized_holder) | |
| 394 return false; | |
| 395 meta_info = deserialized_holder.get(); | |
| 396 } | |
| 397 // meta_info is now either the kMetaInfo node, or the deserialized node if it | |
| 398 // was stored as a string. Either way it should now be a (possibly nested) | |
| 399 // dictionary of meta info values. | |
| 400 const base::DictionaryValue* meta_info_dict; | |
| 401 if (!meta_info->GetAsDictionary(&meta_info_dict)) | |
| 402 return false; | |
| 403 DecodeMetaInfoHelper(*meta_info_dict, std::string(), meta_info_map); | |
| 404 | |
| 405 // Previously sync transaction version was stored in the meta info field | |
| 406 // using this key. If the key is present when decoding, set the sync | |
| 407 // transaction version to its value, then delete the field. | |
| 408 if (deserialized_holder) { | |
| 409 const char kBookmarkTransactionVersionKey[] = "sync.transaction_version"; | |
| 410 BookmarkNode::MetaInfoMap::iterator it = | |
| 411 meta_info_map->find(kBookmarkTransactionVersionKey); | |
| 412 if (it != meta_info_map->end()) { | |
| 413 base::StringToInt64(it->second, sync_transaction_version); | |
| 414 meta_info_map->erase(it); | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 return true; | |
| 419 } | |
| 420 | |
| 421 void BookmarkCodec::DecodeMetaInfoHelper( | |
| 422 const base::DictionaryValue& dict, | |
| 423 const std::string& prefix, | |
| 424 BookmarkNode::MetaInfoMap* meta_info_map) { | |
| 425 for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) { | |
| 426 if (it.value().IsType(base::Value::TYPE_DICTIONARY)) { | |
| 427 const base::DictionaryValue* subdict; | |
| 428 it.value().GetAsDictionary(&subdict); | |
| 429 DecodeMetaInfoHelper(*subdict, prefix + it.key() + ".", meta_info_map); | |
| 430 } else if (it.value().IsType(base::Value::TYPE_STRING)) { | |
| 431 it.value().GetAsString(&(*meta_info_map)[prefix + it.key()]); | |
| 432 } | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 void BookmarkCodec::ReassignIDs(BookmarkNode* bb_node, | |
| 437 BookmarkNode* other_node, | |
| 438 BookmarkNode* mobile_node) { | |
| 439 maximum_id_ = 0; | |
| 440 ReassignIDsHelper(bb_node); | |
| 441 ReassignIDsHelper(other_node); | |
| 442 ReassignIDsHelper(mobile_node); | |
| 443 ids_reassigned_ = true; | |
| 444 } | |
| 445 | |
| 446 void BookmarkCodec::ReassignIDsHelper(BookmarkNode* node) { | |
| 447 DCHECK(node); | |
| 448 node->set_id(++maximum_id_); | |
| 449 for (int i = 0; i < node->child_count(); ++i) | |
| 450 ReassignIDsHelper(node->GetChild(i)); | |
| 451 } | |
| 452 | |
| 453 void BookmarkCodec::UpdateChecksum(const std::string& str) { | |
| 454 base::MD5Update(&md5_context_, str); | |
| 455 } | |
| 456 | |
| 457 void BookmarkCodec::UpdateChecksum(const base::string16& str) { | |
| 458 base::MD5Update(&md5_context_, | |
| 459 base::StringPiece( | |
| 460 reinterpret_cast<const char*>(str.data()), | |
| 461 str.length() * sizeof(str[0]))); | |
| 462 } | |
| 463 | |
| 464 void BookmarkCodec::UpdateChecksumWithUrlNode(const std::string& id, | |
| 465 const base::string16& title, | |
| 466 const std::string& url) { | |
| 467 DCHECK(base::IsStringUTF8(url)); | |
| 468 UpdateChecksum(id); | |
| 469 UpdateChecksum(title); | |
| 470 UpdateChecksum(kTypeURL); | |
| 471 UpdateChecksum(url); | |
| 472 } | |
| 473 | |
| 474 void BookmarkCodec::UpdateChecksumWithFolderNode(const std::string& id, | |
| 475 const base::string16& title) { | |
| 476 UpdateChecksum(id); | |
| 477 UpdateChecksum(title); | |
| 478 UpdateChecksum(kTypeFolder); | |
| 479 } | |
| 480 | |
| 481 void BookmarkCodec::InitializeChecksum() { | |
| 482 base::MD5Init(&md5_context_); | |
| 483 } | |
| 484 | |
| 485 void BookmarkCodec::FinalizeChecksum() { | |
| 486 base::MD5Digest digest; | |
| 487 base::MD5Final(&digest, &md5_context_); | |
| 488 computed_checksum_ = base::MD5DigestToBase16(digest); | |
| 489 } | |
| OLD | NEW |