| Index: chrome/browser/bookmarks/bookmark_html_writer.cc
|
| ===================================================================
|
| --- chrome/browser/bookmarks/bookmark_html_writer.cc (revision 0)
|
| +++ chrome/browser/bookmarks/bookmark_html_writer.cc (revision 0)
|
| @@ -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
|
|
|
| Property changes on: chrome\browser\bookmarks\bookmark_html_writer.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|