diff options
author | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-07 04:31:35 +0000 |
---|---|---|
committer | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-07 04:31:35 +0000 |
commit | b504899244b4264994d4daae2bee660706dba652 (patch) | |
tree | eda190cb35dfff7f700a4c636730ff2487cb35d8 /chrome/browser/bookmarks/bookmark_html_writer.cc | |
parent | e562de106eeab2667eeb6922ddf2d771a0efa55d (diff) | |
download | chromium_src-b504899244b4264994d4daae2bee660706dba652.zip chromium_src-b504899244b4264994d4daae2bee660706dba652.tar.gz chromium_src-b504899244b4264994d4daae2bee660706dba652.tar.bz2 |
Adds import/export of bookmarks to bookmarks.html file.
BUG=1649
TEST=bring up bookmark manager and try out import/export from the
tools menu. Note that import ALWAYS creates a new folder under
the 'Other bookmarks folder' with the name of Imported (x). This
is by design.
Review URL: http://codereview.chromium.org/9471
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4968 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/bookmarks/bookmark_html_writer.cc')
-rw-r--r-- | chrome/browser/bookmarks/bookmark_html_writer.cc | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc new file mode 100644 index 0000000..b6c26ef --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_html_writer.cc @@ -0,0 +1,332 @@ +// Copyright (c) 2006-2008 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_html_writer.h" + +#include "base/message_loop.h" +#include "base/scoped_handle.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/values.h" +#include "chrome/browser/bookmarks/bookmark_codec.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/history/history_types.h" +#include "net/base/escape.h" + +namespace bookmark_html_writer { + +namespace { + +// File header. +const char kHeader[] = + "<!DOCTYPE NETSCAPE-Bookmark-file-1>\r\n" + "<!-- This is an automatically generated file.\r\n" + " It will be read and overwritten.\r\n" + " DO NOT EDIT! -->\r\n" + "<META HTTP-EQUIV=\"Content-Type\"" + " CONTENT=\"text/html; charset=UTF-8\">\r\n" + "<TITLE>Bookmarks</TITLE>\r\n" + "<H1>Bookmarks</H1>\r\n" + "<DL><p>\r\n"; + +// Newline separator. +const char kNewline[] = "\r\n"; + +// The following are used for bookmarks. + +// Start of a bookmark. +const char kBookmarkStart[] = "<DT><A HREF=\""; +// After kBookmarkStart. +const char kAddDate[] = "\" ADD_DATE=\""; +// After kAddDate. +const char kBookmarkAttributeEnd[] = "\">"; +// End of a bookmark. +const char kBookmarkEnd[] = "</A>"; + +// The following are used when writing folders. + +// Start of a folder. +const char kFolderStart[] = "<DT><H3 ADD_DATE=\""; +// After kFolderStart. +const char kLastModified[] = "\" LAST_MODIFIED=\""; +// After kLastModified when writing the bookmark bar. +const char kBookmarkBar[] = "\" PERSONAL_TOOLBAR_FOLDER=\"true\">"; +// After kLastModified when writing a user created folder. +const char kFolderAttributeEnd[] = "\">"; +// End of the folder. +const char kFolderEnd[] = "</H3>"; +// Start of the children of a folder. +const char kFolderChildren[] = "<DL><p>"; +// End of the children for a folder. +const char kFolderChildrenEnd[] = "</DL><p>"; + +// Number of characters to indent by. +const size_t kIndentSize = 4; + +// Class responsible for the actual writing. +class Writer : public Task { + public: + Writer(Value* bookmarks, const std::wstring& path) + : bookmarks_(bookmarks), + path_(path) { + } + + virtual void Run() { + if (!OpenFile()) + return; + + Value* roots; + if (!Write(kHeader) || + bookmarks_->GetType() != Value::TYPE_DICTIONARY || + !static_cast<DictionaryValue*>(bookmarks_.get())->Get( + BookmarkCodec::kRootsKey, &roots) || + roots->GetType() != Value::TYPE_DICTIONARY) { + NOTREACHED(); + return; + } + + DictionaryValue* roots_d_value = static_cast<DictionaryValue*>(roots); + Value* root_folder_value; + Value* other_folder_value; + if (!roots_d_value->Get(BookmarkCodec::kRootFolderNameKey, + &root_folder_value) || + root_folder_value->GetType() != Value::TYPE_DICTIONARY || + !roots_d_value->Get(BookmarkCodec::kOtherBookmarFolderNameKey, + &other_folder_value) || + other_folder_value->GetType() != Value::TYPE_DICTIONARY) { + NOTREACHED(); + return; // Invalid type for root folder and/or other folder. + } + + IncrementIndent(); + + if (!WriteNode(*static_cast<DictionaryValue*>(root_folder_value), + history::StarredEntry::BOOKMARK_BAR) || + !WriteNode(*static_cast<DictionaryValue*>(other_folder_value), + history::StarredEntry::OTHER)) { + return; + } + + DecrementIndent(); + + Write(kFolderChildrenEnd); + Write(kNewline); + } + + private: + // Types of text being written out. The type dictates how the text is + // escaped. + enum TextType { + // The text is the value of an html attribute, eg foo in + // <a href="foo">. + ATTRIBUTE_VALUE, + + // Actual content, eg foo in <h1>foo</h2>. + CONTENT + }; + + // Opens the file, returning true on success. + bool OpenFile() { + handle_.Set( + CreateFile(path_.c_str(), GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); + if (!handle_.IsValid()) + return false; + return true; + } + + // Increments the indent. + void IncrementIndent() { + indent_.resize(indent_.size() + kIndentSize, ' '); + } + + // Decrements the indent. + void DecrementIndent() { + DCHECK(!indent_.empty()); + indent_.resize(indent_.size() - kIndentSize, ' '); + } + + // Writes raw text out returning true on success. This does not escape + // the text in anyway. + bool Write(const std::string& text) { + DWORD wrote; + bool result = + (WriteFile(handle_, text.c_str(), text.length(), &wrote, NULL) && + wrote == text.length()); + DCHECK(result); + return result; + } + + // Writes out the text string (as UTF8). The text is escaped based on + // type. + bool Write(const std::wstring& text, TextType type) { + std::string utf8_string; + + switch (type) { + case ATTRIBUTE_VALUE: + // Convert " to \" + if (text.find(L"\"") != std::wstring::npos) { + std::wstring replaced_string = text; + ReplaceSubstringsAfterOffset(&replaced_string, 0, L"\"", L"\\\""); + utf8_string = WideToUTF8(replaced_string); + } else { + utf8_string = WideToUTF8(text); + } + break; + + case CONTENT: + utf8_string = WideToUTF8(EscapeForHTML(text)); + break; + + default: + NOTREACHED(); + } + + return Write(utf8_string); + } + + // Indents the current line. + bool WriteIndent() { + return Write(indent_); + } + + // Converts a time string written to the JSON codec into a time_t string + // (used by bookmarks.html) and writes it. + bool WriteTime(const std::wstring& time_string) { + base::Time time = + base::Time::FromInternalValue(StringToInt64(time_string)); + return Write(Int64ToString(time.ToTimeT())); + } + + // Writes the node and all its children, returning true on success. + bool WriteNode(const DictionaryValue& value, + history::StarredEntry::Type folder_type) { + std::wstring title, date_added_string, type_string; + if (!value.GetString(BookmarkCodec::kNameKey, &title) || + !value.GetString(BookmarkCodec::kDateAddedKey, &date_added_string) || + !value.GetString(BookmarkCodec::kTypeKey, &type_string) || + (type_string != BookmarkCodec::kTypeURL && + type_string != BookmarkCodec::kTypeFolder)) { + NOTREACHED(); + return false; + } + + if (type_string == BookmarkCodec::kTypeURL) { + std::wstring url_string; + if (!value.GetString(BookmarkCodec::kURLKey, &url_string)) { + NOTREACHED(); + return false; + } + if (!WriteIndent() || + !Write(kBookmarkStart) || + !Write(url_string, ATTRIBUTE_VALUE) || + !Write(kAddDate) || + !WriteTime(date_added_string) || + !Write(kBookmarkAttributeEnd) || + !Write(title, CONTENT) || + !Write(kBookmarkEnd) || + !Write(kNewline)) { + return false; + } + return true; + } + + // Folder. + std::wstring last_modified_date; + Value* child_values; + if (!value.GetString(BookmarkCodec::kDateModifiedKey, + &last_modified_date) || + !value.Get(BookmarkCodec::kChildrenKey, &child_values) || + child_values->GetType() != Value::TYPE_LIST) { + NOTREACHED(); + return false; + } + if (folder_type != history::StarredEntry::OTHER) { + // The other folder name is not written out. This gives the effect of + // making the contents of the 'other folder' be a sibling to the bookmark + // bar folder. + if (!WriteIndent() || + !Write(kFolderStart) || + !WriteTime(date_added_string) || + !Write(kLastModified) || + !WriteTime(last_modified_date)) { + return false; + } + if (folder_type == history::StarredEntry::BOOKMARK_BAR) { + if (!Write(kBookmarkBar)) + return false; + title = L"Bookmark Bar"; + } else if (!Write(kFolderAttributeEnd)) { + return false; + } + if (!Write(title, CONTENT) || + !Write(kFolderEnd) || + !Write(kNewline) || + !WriteIndent() || + !Write(kFolderChildren) || + !Write(kNewline)) { + return false; + } + IncrementIndent(); + } + + // Write the children. + ListValue* children = static_cast<ListValue*>(child_values); + for (size_t i = 0; i < children->GetSize(); ++i) { + Value* child_value; + if (!children->Get(i, &child_value) || + child_value->GetType() != Value::TYPE_DICTIONARY) { + NOTREACHED(); + return false; + } + if (!WriteNode(*static_cast<DictionaryValue*>(child_value), + history::StarredEntry::USER_GROUP)) { + return false; + } + } + if (folder_type != history::StarredEntry::OTHER) { + // Close out the folder. + DecrementIndent(); + if (!WriteIndent() || + !Write(kFolderChildrenEnd) || + !Write(kNewline)) { + return false; + } + } + return true; + } + + // The BookmarkModel as a Value. This value was generated from the + // BookmarkCodec. + scoped_ptr<Value> bookmarks_; + + // Path we're writing to. + std::wstring path_; + + // File we're writing to. + ScopedHandle handle_; + + // How much we indent when writing a bookmark/folder. This is modified + // via IncrementIndent and DecrementIndent. + std::string indent_; +}; + +} // namespace + +void WriteBookmarks(MessageLoop* thread, + BookmarkModel* model, + const std::wstring& path) { + // BookmarkModel isn't thread safe (nor would we want to lock it down + // for the duration of the write), as such we make a copy of the + // BookmarkModel using BookmarkCodec then write from that. + BookmarkCodec codec; + scoped_ptr<Writer> writer(new Writer(codec.Encode(model), path)); + if (thread) + thread->PostTask(FROM_HERE, writer.release()); + else + writer->Run(); +} + +} // namespace bookmark_html_writer |