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