// 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 "app/l10n_util.h" #include "base/file_path.h" #include "base/message_loop.h" #include "base/platform_file.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/chrome_thread.h" #include "chrome/browser/history/history_types.h" #include "grit/generated_resources.h" #include "net/base/escape.h" #include "net/base/file_stream.h" #include "net/base/net_errors.h" namespace bookmark_html_writer { namespace { // File header. const char kHeader[] = "\r\n" "\r\n" "\r\n" "
\r\n"; // Newline separator. const char kNewline[] = "\r\n"; // The following are used for bookmarks. // Start of a bookmark. const char kBookmarkStart[] = "
"; // End of the children for a folder. const char kFolderChildrenEnd[] = "
";
// 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 FilePath& path)
: bookmarks_(bookmarks),
path_(path) {
}
virtual void Run() {
if (!OpenFile())
return;
Value* roots;
if (!Write(kHeader) ||
bookmarks_->GetType() != Value::TYPE_DICTIONARY ||
!static_castfoo.
CONTENT
};
// Opens the file, returning true on success.
bool OpenFile() {
int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
return (file_stream_.Open(path_, flags) == net::OK);
}
// 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) {
size_t wrote = file_stream_.Write(text.c_str(), text.length(), NULL);
bool result = (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) {
string16 replaced_string = WideToUTF16Hack(text);
ReplaceSubstringsAfterOffset(&replaced_string, 0,
ASCIIToUTF16("\""),
ASCIIToUTF16("""));
utf8_string = UTF16ToUTF8(replaced_string);
} else {
utf8_string = WideToUTF8(text);
}
break;
case CONTENT:
utf8_string = UTF16ToUTF8(EscapeForHTML(WideToUTF16Hack(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(WideToUTF16Hack(time_string)));
return Write(Int64ToString(time.ToTimeT()));
}
// Writes the node and all its children, returning true on success.
bool WriteNode(const DictionaryValue& value,
BookmarkNode::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 != BookmarkNode::OTHER_NODE) {
// 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 == BookmarkNode::BOOKMARK_BAR) {
if (!Write(kBookmarkBar))
return false;
title = l10n_util::GetString(IDS_BOOMARK_BAR_FOLDER_NAME);
} 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