summaryrefslogtreecommitdiffstats
path: root/chrome/browser/bookmarks/bookmark_codec.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/bookmarks/bookmark_codec.cc')
-rw-r--r--chrome/browser/bookmarks/bookmark_codec.cc334
1 files changed, 334 insertions, 0 deletions
diff --git a/chrome/browser/bookmarks/bookmark_codec.cc b/chrome/browser/bookmarks/bookmark_codec.cc
new file mode 100644
index 0000000..e64c365
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_codec.cc
@@ -0,0 +1,334 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/bookmarks/bookmark_codec.h"
+
+#include <algorithm>
+
+#include "app/l10n_util.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "googleurl/src/gurl.h"
+#include "grit/generated_resources.h"
+
+using base::Time;
+
+const wchar_t* BookmarkCodec::kRootsKey = L"roots";
+const wchar_t* BookmarkCodec::kRootFolderNameKey = L"bookmark_bar";
+const wchar_t* BookmarkCodec::kOtherBookmarkFolderNameKey = L"other";
+const wchar_t* BookmarkCodec::kVersionKey = L"version";
+const wchar_t* BookmarkCodec::kChecksumKey = L"checksum";
+const wchar_t* BookmarkCodec::kIdKey = L"id";
+const wchar_t* BookmarkCodec::kTypeKey = L"type";
+const wchar_t* BookmarkCodec::kNameKey = L"name";
+const wchar_t* BookmarkCodec::kDateAddedKey = L"date_added";
+const wchar_t* BookmarkCodec::kURLKey = L"url";
+const wchar_t* BookmarkCodec::kDateModifiedKey = L"date_modified";
+const wchar_t* BookmarkCodec::kChildrenKey = L"children";
+const char* BookmarkCodec::kTypeURL = "url";
+const char* BookmarkCodec::kTypeFolder = "folder";
+
+// Current version of the file.
+static const int kCurrentVersion = 1;
+
+BookmarkCodec::BookmarkCodec()
+ : ids_reassigned_(false),
+ ids_valid_(true),
+ maximum_id_(0) {
+}
+
+Value* BookmarkCodec::Encode(BookmarkModel* model) {
+ return Encode(model->GetBookmarkBarNode(), model->other_node());
+}
+
+Value* BookmarkCodec::Encode(const BookmarkNode* bookmark_bar_node,
+ const BookmarkNode* other_folder_node) {
+ ids_reassigned_ = false;
+ InitializeChecksum();
+ DictionaryValue* roots = new DictionaryValue();
+ roots->Set(kRootFolderNameKey, EncodeNode(bookmark_bar_node));
+ roots->Set(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node));
+
+ DictionaryValue* main = new DictionaryValue();
+ main->SetInteger(kVersionKey, kCurrentVersion);
+ FinalizeChecksum();
+ // We are going to store the computed checksum. So set stored checksum to be
+ // the same as computed checksum.
+ stored_checksum_ = computed_checksum_;
+ main->Set(kChecksumKey, Value::CreateStringValue(computed_checksum_));
+ main->Set(kRootsKey, roots);
+ return main;
+}
+
+bool BookmarkCodec::Decode(BookmarkNode* bb_node,
+ BookmarkNode* other_folder_node,
+ int64* max_id,
+ const Value& value) {
+ ids_.clear();
+ ids_reassigned_ = false;
+ ids_valid_ = true;
+ maximum_id_ = 0;
+ stored_checksum_.clear();
+ InitializeChecksum();
+ bool success = DecodeHelper(bb_node, other_folder_node, value);
+ FinalizeChecksum();
+ // If either the checksums differ or some IDs were missing/not unique,
+ // reassign IDs.
+ if (!ids_valid_ || computed_checksum() != stored_checksum())
+ ReassignIDs(bb_node, other_folder_node);
+ *max_id = maximum_id_ + 1;
+ return success;
+}
+
+Value* BookmarkCodec::EncodeNode(const BookmarkNode* node) {
+ DictionaryValue* value = new DictionaryValue();
+ std::string id = Int64ToString(node->id());
+ value->SetString(kIdKey, id);
+ const string16& title = node->GetTitleAsString16();
+ value->SetStringFromUTF16(kNameKey, title);
+ value->SetString(kDateAddedKey,
+ Int64ToString(node->date_added().ToInternalValue()));
+ if (node->type() == BookmarkNode::URL) {
+ value->SetString(kTypeKey, kTypeURL);
+ std::string url = node->GetURL().possibly_invalid_spec();
+ value->SetString(kURLKey, url);
+ UpdateChecksumWithUrlNode(id, title, url);
+ } else {
+ value->SetString(kTypeKey, kTypeFolder);
+ value->SetString(kDateModifiedKey,
+ Int64ToString(node->date_group_modified().
+ ToInternalValue()));
+ UpdateChecksumWithFolderNode(id, title);
+
+ ListValue* child_values = new ListValue();
+ value->Set(kChildrenKey, child_values);
+ for (int i = 0; i < node->GetChildCount(); ++i)
+ child_values->Append(EncodeNode(node->GetChild(i)));
+ }
+ return value;
+}
+
+bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
+ BookmarkNode* other_folder_node,
+ const Value& value) {
+ if (value.GetType() != Value::TYPE_DICTIONARY)
+ return false; // Unexpected type.
+
+ const DictionaryValue& d_value = static_cast<const DictionaryValue&>(value);
+
+ int version;
+ if (!d_value.GetInteger(kVersionKey, &version) || version != kCurrentVersion)
+ return false; // Unknown version.
+
+ Value* checksum_value;
+ if (d_value.Get(kChecksumKey, &checksum_value)) {
+ if (checksum_value->GetType() != Value::TYPE_STRING)
+ return false;
+ StringValue* checksum_value_str = static_cast<StringValue*>(checksum_value);
+ if (!checksum_value_str->GetAsString(&stored_checksum_))
+ return false;
+ }
+
+ Value* roots;
+ if (!d_value.Get(kRootsKey, &roots))
+ return false; // No roots.
+
+ if (roots->GetType() != Value::TYPE_DICTIONARY)
+ return false; // Invalid type for roots.
+
+ DictionaryValue* roots_d_value = static_cast<DictionaryValue*>(roots);
+ Value* root_folder_value;
+ Value* other_folder_value;
+ if (!roots_d_value->Get(kRootFolderNameKey, &root_folder_value) ||
+ root_folder_value->GetType() != Value::TYPE_DICTIONARY ||
+ !roots_d_value->Get(kOtherBookmarkFolderNameKey, &other_folder_value) ||
+ other_folder_value->GetType() != Value::TYPE_DICTIONARY)
+ return false; // Invalid type for root folder and/or other folder.
+
+ DecodeNode(*static_cast<DictionaryValue*>(root_folder_value), NULL,
+ bb_node);
+ DecodeNode(*static_cast<DictionaryValue*>(other_folder_value), NULL,
+ other_folder_node);
+ // Need to reset the type as decoding resets the type to FOLDER. Similarly
+ // we need to reset the title as the title is persisted and restored from
+ // the file.
+ bb_node->set_type(BookmarkNode::BOOKMARK_BAR);
+ other_folder_node->set_type(BookmarkNode::OTHER_NODE);
+ bb_node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_FOLDER_NAME));
+ other_folder_node->SetTitle(
+ l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME));
+
+ return true;
+}
+
+bool BookmarkCodec::DecodeChildren(const ListValue& child_value_list,
+ BookmarkNode* parent) {
+ for (size_t i = 0; i < child_value_list.GetSize(); ++i) {
+ Value* child_value;
+ if (!child_value_list.Get(i, &child_value))
+ return false;
+
+ if (child_value->GetType() != Value::TYPE_DICTIONARY)
+ return false;
+
+ DecodeNode(*static_cast<DictionaryValue*>(child_value), parent, NULL);
+ }
+ return true;
+}
+
+bool BookmarkCodec::DecodeNode(const DictionaryValue& value,
+ BookmarkNode* parent,
+ BookmarkNode* node) {
+ // If no |node| is specified, we'll create one and add it to the |parent|.
+ // Therefore, in that case, |parent| must be non-NULL.
+ if (!node && !parent) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::string id_string;
+ int64 id = 0;
+ if (ids_valid_) {
+ if (!value.GetString(kIdKey, &id_string) ||
+ !StringToInt64(id_string, &id) ||
+ ids_.count(id) != 0) {
+ ids_valid_ = false;
+ } else {
+ ids_.insert(id);
+ }
+ }
+
+ maximum_id_ = std::max(maximum_id_, id);
+
+ string16 title;
+ value.GetStringAsUTF16(kNameKey, &title);
+
+ std::string date_added_string;
+ if (!value.GetString(kDateAddedKey, &date_added_string))
+ date_added_string = Int64ToString(Time::Now().ToInternalValue());
+ base::Time date_added = base::Time::FromInternalValue(
+ StringToInt64(date_added_string));
+#if !defined(OS_WIN)
+ // We changed the epoch for dates on Mac & Linux from 1970 to the Windows
+ // one of 1601. We assume any number we encounter from before 1970 is using
+ // the old format, so we need to add the delta to it.
+ //
+ // This code should be removed at some point:
+ // http://code.google.com/p/chromium/issues/detail?id=20264
+ if (date_added.ToInternalValue() <
+ base::Time::kWindowsEpochDeltaMicroseconds) {
+ date_added = base::Time::FromInternalValue(date_added.ToInternalValue() +
+ base::Time::kWindowsEpochDeltaMicroseconds);
+ }
+#endif
+
+ std::string type_string;
+ if (!value.GetString(kTypeKey, &type_string))
+ return false;
+
+ if (type_string != kTypeURL && type_string != kTypeFolder)
+ return false; // Unknown type.
+
+ if (type_string == kTypeURL) {
+ std::string url_string;
+ if (!value.GetString(kURLKey, &url_string))
+ return false;
+
+ GURL url = GURL(url_string);
+ if (!node && url.is_valid())
+ node = new BookmarkNode(id, url);
+ else
+ return false; // Node invalid.
+
+ if (parent)
+ parent->Add(parent->GetChildCount(), node);
+ node->set_type(BookmarkNode::URL);
+ UpdateChecksumWithUrlNode(id_string, title, url_string);
+ } else {
+ std::string last_modified_date;
+ if (!value.GetString(kDateModifiedKey, &last_modified_date))
+ last_modified_date = Int64ToString(Time::Now().ToInternalValue());
+
+ Value* child_values;
+ if (!value.Get(kChildrenKey, &child_values))
+ return false;
+
+ if (child_values->GetType() != Value::TYPE_LIST)
+ return false;
+
+ if (!node) {
+ node = new BookmarkNode(id, GURL());
+ } else {
+ // If a new node is not created, explicitly assign ID to the existing one.
+ node->set_id(id);
+ }
+
+ node->set_type(BookmarkNode::FOLDER);
+ node->set_date_group_modified(Time::FromInternalValue(
+ StringToInt64(last_modified_date)));
+
+ if (parent)
+ parent->Add(parent->GetChildCount(), node);
+
+ UpdateChecksumWithFolderNode(id_string, title);
+
+ if (!DecodeChildren(*static_cast<ListValue*>(child_values), node))
+ return false;
+ }
+
+ node->SetTitle(title);
+ node->set_date_added(date_added);
+ return true;
+}
+
+void BookmarkCodec::ReassignIDs(BookmarkNode* bb_node,
+ BookmarkNode* other_node) {
+ maximum_id_ = 0;
+ ReassignIDsHelper(bb_node);
+ ReassignIDsHelper(other_node);
+ ids_reassigned_ = true;
+}
+
+void BookmarkCodec::ReassignIDsHelper(BookmarkNode* node) {
+ DCHECK(node);
+ node->set_id(++maximum_id_);
+ for (int i = 0; i < node->GetChildCount(); ++i)
+ ReassignIDsHelper(node->GetChild(i));
+}
+
+void BookmarkCodec::UpdateChecksum(const std::string& str) {
+ MD5Update(&md5_context_, str.data(), str.length() * sizeof(char));
+}
+
+void BookmarkCodec::UpdateChecksum(const string16& str) {
+ MD5Update(&md5_context_, str.data(), str.length() * sizeof(char16));
+}
+
+void BookmarkCodec::UpdateChecksumWithUrlNode(const std::string& id,
+ const string16& title,
+ const std::string& url) {
+ DCHECK(IsStringUTF8(url));
+ UpdateChecksum(id);
+ UpdateChecksum(title);
+ UpdateChecksum(kTypeURL);
+ UpdateChecksum(url);
+}
+
+void BookmarkCodec::UpdateChecksumWithFolderNode(const std::string& id,
+ const string16& title) {
+ UpdateChecksum(id);
+ UpdateChecksum(title);
+ UpdateChecksum(kTypeFolder);
+}
+
+void BookmarkCodec::InitializeChecksum() {
+ MD5Init(&md5_context_);
+}
+
+void BookmarkCodec::FinalizeChecksum() {
+ MD5Digest digest;
+ MD5Final(&digest, &md5_context_);
+ computed_checksum_ = MD5DigestToBase16(digest);
+}