- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / bookmarks / bookmark_codec.cc
1 // Copyright (c) 2012 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/bookmarks/bookmark_codec.h"
6
7 #include <algorithm>
8
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/values.h"
12 #include "chrome/browser/bookmarks/bookmark_model.h"
13 #include "grit/generated_resources.h"
14 #include "ui/base/l10n/l10n_util.h"
15 #include "url/gurl.h"
16
17 using base::Time;
18
19 const char* BookmarkCodec::kRootsKey = "roots";
20 const char* BookmarkCodec::kRootFolderNameKey = "bookmark_bar";
21 const char* BookmarkCodec::kOtherBookmarkFolderNameKey = "other";
22 // The value is left as 'synced' for historical reasons.
23 const char* BookmarkCodec::kMobileBookmarkFolderNameKey = "synced";
24 const char* BookmarkCodec::kVersionKey = "version";
25 const char* BookmarkCodec::kChecksumKey = "checksum";
26 const char* BookmarkCodec::kIdKey = "id";
27 const char* BookmarkCodec::kTypeKey = "type";
28 const char* BookmarkCodec::kNameKey = "name";
29 const char* BookmarkCodec::kDateAddedKey = "date_added";
30 const char* BookmarkCodec::kURLKey = "url";
31 const char* BookmarkCodec::kDateModifiedKey = "date_modified";
32 const char* BookmarkCodec::kChildrenKey = "children";
33 const char* BookmarkCodec::kMetaInfo = "meta_info";
34 const char* BookmarkCodec::kTypeURL = "url";
35 const char* BookmarkCodec::kTypeFolder = "folder";
36
37 // Current version of the file.
38 static const int kCurrentVersion = 1;
39
40 BookmarkCodec::BookmarkCodec()
41     : ids_reassigned_(false),
42       ids_valid_(true),
43       maximum_id_(0) {
44 }
45
46 BookmarkCodec::~BookmarkCodec() {}
47
48 Value* BookmarkCodec::Encode(BookmarkModel* model) {
49   return Encode(model->bookmark_bar_node(),
50                 model->other_node(),
51                 model->mobile_node(),
52                 model->root_node()->meta_info_str());
53 }
54
55 Value* BookmarkCodec::Encode(const BookmarkNode* bookmark_bar_node,
56                              const BookmarkNode* other_folder_node,
57                              const BookmarkNode* mobile_folder_node,
58                              const std::string& model_meta_info) {
59   ids_reassigned_ = false;
60   InitializeChecksum();
61   DictionaryValue* roots = new DictionaryValue();
62   roots->Set(kRootFolderNameKey, EncodeNode(bookmark_bar_node));
63   roots->Set(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node));
64   roots->Set(kMobileBookmarkFolderNameKey, EncodeNode(mobile_folder_node));
65   if (!model_meta_info.empty())
66     roots->SetString(kMetaInfo, model_meta_info);
67   DictionaryValue* main = new DictionaryValue();
68   main->SetInteger(kVersionKey, kCurrentVersion);
69   FinalizeChecksum();
70   // We are going to store the computed checksum. So set stored checksum to be
71   // the same as computed checksum.
72   stored_checksum_ = computed_checksum_;
73   main->Set(kChecksumKey, new base::StringValue(computed_checksum_));
74   main->Set(kRootsKey, roots);
75   return main;
76 }
77
78 bool BookmarkCodec::Decode(BookmarkNode* bb_node,
79                            BookmarkNode* other_folder_node,
80                            BookmarkNode* mobile_folder_node,
81                            int64* max_id,
82                            const Value& value) {
83   ids_.clear();
84   ids_reassigned_ = false;
85   ids_valid_ = true;
86   maximum_id_ = 0;
87   stored_checksum_.clear();
88   InitializeChecksum();
89   bool success = DecodeHelper(bb_node, other_folder_node, mobile_folder_node,
90                               value);
91   FinalizeChecksum();
92   // If either the checksums differ or some IDs were missing/not unique,
93   // reassign IDs.
94   if (!ids_valid_ || computed_checksum() != stored_checksum())
95     ReassignIDs(bb_node, other_folder_node, mobile_folder_node);
96   *max_id = maximum_id_ + 1;
97   return success;
98 }
99
100 Value* BookmarkCodec::EncodeNode(const BookmarkNode* node) {
101   DictionaryValue* value = new DictionaryValue();
102   std::string id = base::Int64ToString(node->id());
103   value->SetString(kIdKey, id);
104   const string16& title = node->GetTitle();
105   value->SetString(kNameKey, title);
106   value->SetString(kDateAddedKey,
107                    base::Int64ToString(node->date_added().ToInternalValue()));
108   if (node->is_url()) {
109     value->SetString(kTypeKey, kTypeURL);
110     std::string url = node->url().possibly_invalid_spec();
111     value->SetString(kURLKey, url);
112     UpdateChecksumWithUrlNode(id, title, url);
113   } else {
114     value->SetString(kTypeKey, kTypeFolder);
115     value->SetString(kDateModifiedKey,
116                      base::Int64ToString(node->date_folder_modified().
117                                    ToInternalValue()));
118     UpdateChecksumWithFolderNode(id, title);
119
120     ListValue* child_values = new ListValue();
121     value->Set(kChildrenKey, child_values);
122     for (int i = 0; i < node->child_count(); ++i)
123       child_values->Append(EncodeNode(node->GetChild(i)));
124   }
125   if (!node->meta_info_str().empty())
126     value->SetString(kMetaInfo, node->meta_info_str());
127   return value;
128 }
129
130 bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
131                                  BookmarkNode* other_folder_node,
132                                  BookmarkNode* mobile_folder_node,
133                                  const Value& value) {
134   if (value.GetType() != Value::TYPE_DICTIONARY)
135     return false;  // Unexpected type.
136
137   const DictionaryValue& d_value = static_cast<const DictionaryValue&>(value);
138
139   int version;
140   if (!d_value.GetInteger(kVersionKey, &version) || version != kCurrentVersion)
141     return false;  // Unknown version.
142
143   const Value* checksum_value;
144   if (d_value.Get(kChecksumKey, &checksum_value)) {
145     if (checksum_value->GetType() != Value::TYPE_STRING)
146       return false;
147     if (!checksum_value->GetAsString(&stored_checksum_))
148       return false;
149   }
150
151   const Value* roots;
152   if (!d_value.Get(kRootsKey, &roots))
153     return false;  // No roots.
154
155   if (roots->GetType() != Value::TYPE_DICTIONARY)
156     return false;  // Invalid type for roots.
157
158   const DictionaryValue* roots_d_value =
159       static_cast<const DictionaryValue*>(roots);
160   const Value* root_folder_value;
161   const Value* other_folder_value = NULL;
162   if (!roots_d_value->Get(kRootFolderNameKey, &root_folder_value) ||
163       root_folder_value->GetType() != Value::TYPE_DICTIONARY ||
164       !roots_d_value->Get(kOtherBookmarkFolderNameKey, &other_folder_value) ||
165       other_folder_value->GetType() != Value::TYPE_DICTIONARY) {
166     return false;  // Invalid type for root folder and/or other
167                    // folder.
168   }
169   DecodeNode(*static_cast<const DictionaryValue*>(root_folder_value), NULL,
170              bb_node);
171   DecodeNode(*static_cast<const DictionaryValue*>(other_folder_value), NULL,
172              other_folder_node);
173
174   // Fail silently if we can't deserialize mobile bookmarks. We can't require
175   // them to exist in order to be backwards-compatible with older versions of
176   // chrome.
177   const Value* mobile_folder_value;
178   if (roots_d_value->Get(kMobileBookmarkFolderNameKey, &mobile_folder_value) &&
179       mobile_folder_value->GetType() == Value::TYPE_DICTIONARY) {
180     DecodeNode(*static_cast<const DictionaryValue*>(mobile_folder_value), NULL,
181                mobile_folder_node);
182   } else {
183     // If we didn't find the mobile folder, we're almost guaranteed to have a
184     // duplicate id when we add the mobile folder. Consequently, if we don't
185     // intend to reassign ids in the future (ids_valid_ is still true), then at
186     // least reassign the mobile bookmarks to avoid it colliding with anything
187     // else.
188     if (ids_valid_)
189       ReassignIDsHelper(mobile_folder_node);
190   }
191
192   roots_d_value->GetString(kMetaInfo, &model_meta_info_);
193
194   // Need to reset the type as decoding resets the type to FOLDER. Similarly
195   // we need to reset the title as the title is persisted and restored from
196   // the file.
197   bb_node->set_type(BookmarkNode::BOOKMARK_BAR);
198   other_folder_node->set_type(BookmarkNode::OTHER_NODE);
199   mobile_folder_node->set_type(BookmarkNode::MOBILE);
200   bb_node->SetTitle(l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_FOLDER_NAME));
201   other_folder_node->SetTitle(
202       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME));
203   mobile_folder_node->SetTitle(
204         l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME));
205
206   return true;
207 }
208
209 bool BookmarkCodec::DecodeChildren(const ListValue& child_value_list,
210                                    BookmarkNode* parent) {
211   for (size_t i = 0; i < child_value_list.GetSize(); ++i) {
212     const Value* child_value;
213     if (!child_value_list.Get(i, &child_value))
214       return false;
215
216     if (child_value->GetType() != Value::TYPE_DICTIONARY)
217       return false;
218
219     DecodeNode(*static_cast<const DictionaryValue*>(child_value), parent, NULL);
220   }
221   return true;
222 }
223
224 bool BookmarkCodec::DecodeNode(const DictionaryValue& value,
225                                BookmarkNode* parent,
226                                BookmarkNode* node) {
227   // If no |node| is specified, we'll create one and add it to the |parent|.
228   // Therefore, in that case, |parent| must be non-NULL.
229   if (!node && !parent) {
230     NOTREACHED();
231     return false;
232   }
233
234   std::string id_string;
235   int64 id = 0;
236   if (ids_valid_) {
237     if (!value.GetString(kIdKey, &id_string) ||
238         !base::StringToInt64(id_string, &id) ||
239         ids_.count(id) != 0) {
240       ids_valid_ = false;
241     } else {
242       ids_.insert(id);
243     }
244   }
245
246   maximum_id_ = std::max(maximum_id_, id);
247
248   string16 title;
249   value.GetString(kNameKey, &title);
250
251   std::string date_added_string;
252   if (!value.GetString(kDateAddedKey, &date_added_string))
253     date_added_string = base::Int64ToString(Time::Now().ToInternalValue());
254   int64 internal_time;
255   base::StringToInt64(date_added_string, &internal_time);
256
257   std::string type_string;
258   if (!value.GetString(kTypeKey, &type_string))
259     return false;
260
261   if (type_string != kTypeURL && type_string != kTypeFolder)
262     return false;  // Unknown type.
263
264   if (type_string == kTypeURL) {
265     std::string url_string;
266     if (!value.GetString(kURLKey, &url_string))
267       return false;
268
269     GURL url = GURL(url_string);
270     if (!node && url.is_valid())
271       node = new BookmarkNode(id, url);
272     else
273       return false;  // Node invalid.
274
275     if (parent)
276       parent->Add(node, parent->child_count());
277     node->set_type(BookmarkNode::URL);
278     UpdateChecksumWithUrlNode(id_string, title, url_string);
279   } else {
280     std::string last_modified_date;
281     if (!value.GetString(kDateModifiedKey, &last_modified_date))
282       last_modified_date = base::Int64ToString(Time::Now().ToInternalValue());
283
284     const Value* child_values;
285     if (!value.Get(kChildrenKey, &child_values))
286       return false;
287
288     if (child_values->GetType() != Value::TYPE_LIST)
289       return false;
290
291     if (!node) {
292       node = new BookmarkNode(id, GURL());
293     } else {
294       // If a new node is not created, explicitly assign ID to the existing one.
295       node->set_id(id);
296     }
297
298     node->set_type(BookmarkNode::FOLDER);
299     int64 internal_time;
300     base::StringToInt64(last_modified_date, &internal_time);
301     node->set_date_folder_modified(Time::FromInternalValue(internal_time));
302
303     if (parent)
304       parent->Add(node, parent->child_count());
305
306     UpdateChecksumWithFolderNode(id_string, title);
307
308     if (!DecodeChildren(*static_cast<const ListValue*>(child_values), node))
309       return false;
310   }
311
312   node->SetTitle(title);
313   node->set_date_added(base::Time::FromInternalValue(internal_time));
314
315   std::string meta_info;
316   if (value.GetString(kMetaInfo, &meta_info))
317     node->set_meta_info_str(meta_info);
318
319   return true;
320 }
321
322 void BookmarkCodec::ReassignIDs(BookmarkNode* bb_node,
323                                 BookmarkNode* other_node,
324                                 BookmarkNode* mobile_node) {
325   maximum_id_ = 0;
326   ReassignIDsHelper(bb_node);
327   ReassignIDsHelper(other_node);
328   ReassignIDsHelper(mobile_node);
329   ids_reassigned_ = true;
330 }
331
332 void BookmarkCodec::ReassignIDsHelper(BookmarkNode* node) {
333   DCHECK(node);
334   node->set_id(++maximum_id_);
335   for (int i = 0; i < node->child_count(); ++i)
336     ReassignIDsHelper(node->GetChild(i));
337 }
338
339 void BookmarkCodec::UpdateChecksum(const std::string& str) {
340   base::MD5Update(&md5_context_, str);
341 }
342
343 void BookmarkCodec::UpdateChecksum(const string16& str) {
344   base::MD5Update(&md5_context_,
345                   base::StringPiece(
346                       reinterpret_cast<const char*>(str.data()),
347                       str.length() * sizeof(str[0])));
348 }
349
350 void BookmarkCodec::UpdateChecksumWithUrlNode(const std::string& id,
351                                               const string16& title,
352                                               const std::string& url) {
353   DCHECK(IsStringUTF8(url));
354   UpdateChecksum(id);
355   UpdateChecksum(title);
356   UpdateChecksum(kTypeURL);
357   UpdateChecksum(url);
358 }
359
360 void BookmarkCodec::UpdateChecksumWithFolderNode(const std::string& id,
361                                                  const string16& title) {
362   UpdateChecksum(id);
363   UpdateChecksum(title);
364   UpdateChecksum(kTypeFolder);
365 }
366
367 void BookmarkCodec::InitializeChecksum() {
368   base::MD5Init(&md5_context_);
369 }
370
371 void BookmarkCodec::FinalizeChecksum() {
372   base::MD5Digest digest;
373   base::MD5Final(&digest, &md5_context_);
374   computed_checksum_ = base::MD5DigestToBase16(digest);
375 }