Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: chrome/browser/bookmarks/bookmark_html_writer.cc

Issue 9471: Adds import/export of bookmarks to bookmarks.html file.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 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_html_writer.h"
6
7 #include "base/message_loop.h"
8 #include "base/scoped_handle.h"
9 #include "base/scoped_ptr.h"
10 #include "base/string_util.h"
11 #include "base/time.h"
12 #include "base/values.h"
13 #include "chrome/browser/bookmarks/bookmark_codec.h"
14 #include "chrome/browser/bookmarks/bookmark_model.h"
15 #include "chrome/browser/history/history_types.h"
16 #include "net/base/escape.h"
17
18 namespace bookmark_html_writer {
19
20 namespace {
21
22 // File header.
23 const char kHeader[] =
24 "<!DOCTYPE NETSCAPE-Bookmark-file-1>\r\n"
25 "<!-- This is an automatically generated file.\r\n"
26 " It will be read and overwritten.\r\n"
27 " DO NOT EDIT! -->\r\n"
28 "<META HTTP-EQUIV=\"Content-Type\""
29 " CONTENT=\"text/html; charset=UTF-8\">\r\n"
30 "<TITLE>Bookmarks</TITLE>\r\n"
31 "<H1>Bookmarks</H1>\r\n"
32 "<DL><p>\r\n";
33
34 // Newline separator.
35 const char kNewline[] = "\r\n";
36
37 // The following are used for bookmarks.
38
39 // Start of a bookmark.
40 const char kBookmarkStart[] = "<DT><A HREF=\"";
41 // After kBookmarkStart.
42 const char kAddDate[] = "\" ADD_DATE=\"";
43 // After kAddDate.
44 const char kBookmarkAttributeEnd[] = "\">";
45 // End of a bookmark.
46 const char kBookmarkEnd[] = "</A>";
47
48 // The following are used when writing folders.
49
50 // Start of a folder.
51 const char kFolderStart[] = "<DT><H3 ADD_DATE=\"";
52 // After kFolderStart.
53 const char kLastModified[] = "\" LAST_MODIFIED=\"";
54 // After kLastModified when writing the bookmark bar.
55 const char kBookmarkBar[] = "\" PERSONAL_TOOLBAR_FOLDER=\"true\">";
56 // After kLastModified when writing a user created folder.
57 const char kFolderAttributeEnd[] = "\">";
58 // End of the folder.
59 const char kFolderEnd[] = "</H3>";
60 // Start of the children of a folder.
61 const char kFolderChildren[] = "<DL><p>";
62 // End of the children for a folder.
63 const char kFolderChildrenEnd[] = "</DL><p>";
64
65 // Number of characters to indent by.
66 const size_t kIndentSize = 4;
67
68 // Class responsible for the actual writing.
69 class Writer : public Task {
70 public:
71 Writer(Value* bookmarks, const std::wstring& path)
72 : bookmarks_(bookmarks),
73 path_(path) {
74 }
75
76 virtual void Run() {
77 if (!OpenFile())
78 return;
79
80 Value* roots;
81 if (!Write(kHeader) ||
82 bookmarks_->GetType() != Value::TYPE_DICTIONARY ||
83 !static_cast<DictionaryValue*>(bookmarks_.get())->Get(
84 BookmarkCodec::kRootsKey, &roots) ||
85 roots->GetType() != Value::TYPE_DICTIONARY) {
86 NOTREACHED();
87 return;
88 }
89
90 DictionaryValue* roots_d_value = static_cast<DictionaryValue*>(roots);
91 Value* root_folder_value;
92 Value* other_folder_value;
93 if (!roots_d_value->Get(BookmarkCodec::kRootFolderNameKey,
94 &root_folder_value) ||
95 root_folder_value->GetType() != Value::TYPE_DICTIONARY ||
96 !roots_d_value->Get(BookmarkCodec::kOtherBookmarFolderNameKey,
97 &other_folder_value) ||
98 other_folder_value->GetType() != Value::TYPE_DICTIONARY) {
99 NOTREACHED();
100 return; // Invalid type for root folder and/or other folder.
101 }
102
103 IncrementIndent();
104
105 if (!WriteNode(*static_cast<DictionaryValue*>(root_folder_value),
106 history::StarredEntry::BOOKMARK_BAR) ||
107 !WriteNode(*static_cast<DictionaryValue*>(other_folder_value),
108 history::StarredEntry::OTHER)) {
109 return;
110 }
111
112 DecrementIndent();
113
114 Write(kFolderChildrenEnd);
115 Write(kNewline);
116 }
117
118 private:
119 // Types of text being written out. The type dictates how the text is
120 // escaped.
121 enum TextType {
122 // The text is the value of an html attribute, eg foo in
123 // <a href="foo">.
124 ATTRIBUTE_VALUE,
125
126 // Actual content, eg foo in <h1>foo</h2>.
127 CONTENT
128 };
129
130 // Opens the file, returning true on success.
131 bool OpenFile() {
132 handle_.Set(
133 CreateFile(path_.c_str(), GENERIC_WRITE, 0, NULL,
134 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
135 if (!handle_.IsValid())
136 return false;
137 return true;
138 }
139
140 // Increments the indent.
141 void IncrementIndent() {
142 indent_.resize(indent_.size() + kIndentSize, ' ');
143 }
144
145 // Decrements the indent.
146 void DecrementIndent() {
147 DCHECK(!indent_.empty());
148 indent_.resize(indent_.size() - kIndentSize, ' ');
149 }
150
151 // Writes raw text out returning true on success. This does not escape
152 // the text in anyway.
153 bool Write(const std::string& text) {
154 DWORD wrote;
155 bool result =
156 (WriteFile(handle_, text.c_str(), text.length(), &wrote, NULL) &&
157 wrote == text.length());
158 DCHECK(result);
159 return result;
160 }
161
162 // Writes out the text string (as UTF8). The text is escaped based on
163 // type.
164 bool Write(const std::wstring& text, TextType type) {
165 std::string utf8_string;
166
167 switch (type) {
168 case ATTRIBUTE_VALUE:
169 // Convert " to \"
170 if (text.find(L"\"") != std::wstring::npos) {
171 std::wstring replaced_string = text;
172 ReplaceSubstringsAfterOffset(&replaced_string, 0, L"\"", L"\\\"");
173 utf8_string = WideToUTF8(replaced_string);
174 } else {
175 utf8_string = WideToUTF8(text);
176 }
177 break;
178
179 case CONTENT:
180 utf8_string = WideToUTF8(EscapeForHTML(text));
181 break;
182
183 default:
184 NOTREACHED();
185 }
186
187 return Write(utf8_string);
188 }
189
190 // Indents the current line.
191 bool WriteIndent() {
192 return Write(indent_);
193 }
194
195 // Converts a time string written to the JSON codec into a time_t string
196 // (used by bookmarks.html) and writes it.
197 bool WriteTime(const std::wstring& time_string) {
198 base::Time time =
199 base::Time::FromInternalValue(StringToInt64(time_string));
200 return Write(Int64ToString(time.ToTimeT()));
201 }
202
203 // Writes the node and all its children, returning true on success.
204 bool WriteNode(const DictionaryValue& value,
205 history::StarredEntry::Type folder_type) {
206 std::wstring title, date_added_string, type_string;
207 if (!value.GetString(BookmarkCodec::kNameKey, &title) ||
208 !value.GetString(BookmarkCodec::kDateAddedKey, &date_added_string) ||
209 !value.GetString(BookmarkCodec::kTypeKey, &type_string) ||
210 (type_string != BookmarkCodec::kTypeURL &&
211 type_string != BookmarkCodec::kTypeFolder)) {
212 NOTREACHED();
213 return false;
214 }
215
216 if (type_string == BookmarkCodec::kTypeURL) {
217 std::wstring url_string;
218 if (!value.GetString(BookmarkCodec::kURLKey, &url_string)) {
219 NOTREACHED();
220 return false;
221 }
222 if (!WriteIndent() ||
223 !Write(kBookmarkStart) ||
224 !Write(url_string, ATTRIBUTE_VALUE) ||
225 !Write(kAddDate) ||
226 !WriteTime(date_added_string) ||
227 !Write(kBookmarkAttributeEnd) ||
228 !Write(title, CONTENT) ||
229 !Write(kBookmarkEnd) ||
230 !Write(kNewline)) {
231 return false;
232 }
233 return true;
234 }
235
236 // Folder.
237 std::wstring last_modified_date;
238 Value* child_values;
239 if (!value.GetString(BookmarkCodec::kDateModifiedKey,
240 &last_modified_date) ||
241 !value.Get(BookmarkCodec::kChildrenKey, &child_values) ||
242 child_values->GetType() != Value::TYPE_LIST) {
243 NOTREACHED();
244 return false;
245 }
246 if (folder_type != history::StarredEntry::OTHER) {
247 // The other folder name is not written out. This gives the effect of
248 // making the contents of the 'other folder' be a sibling to the bookmark
249 // bar folder.
250 if (!WriteIndent() ||
251 !Write(kFolderStart) ||
252 !WriteTime(date_added_string) ||
253 !Write(kLastModified) ||
254 !WriteTime(last_modified_date)) {
255 return false;
256 }
257 if (folder_type == history::StarredEntry::BOOKMARK_BAR) {
258 if (!Write(kBookmarkBar))
259 return false;
260 title = L"Bookmark Bar";
261 } else if (!Write(kFolderAttributeEnd)) {
262 return false;
263 }
264 if (!Write(title, CONTENT) ||
265 !Write(kFolderEnd) ||
266 !Write(kNewline) ||
267 !WriteIndent() ||
268 !Write(kFolderChildren) ||
269 !Write(kNewline)) {
270 return false;
271 }
272 IncrementIndent();
273 }
274
275 // Write the children.
276 ListValue* children = static_cast<ListValue*>(child_values);
277 for (size_t i = 0; i < children->GetSize(); ++i) {
278 Value* child_value;
279 if (!children->Get(i, &child_value) ||
280 child_value->GetType() != Value::TYPE_DICTIONARY) {
281 NOTREACHED();
282 return false;
283 }
284 if (!WriteNode(*static_cast<DictionaryValue*>(child_value),
285 history::StarredEntry::USER_GROUP)) {
286 return false;
287 }
288 }
289 if (folder_type != history::StarredEntry::OTHER) {
290 // Close out the folder.
291 DecrementIndent();
292 if (!WriteIndent() ||
293 !Write(kFolderChildrenEnd) ||
294 !Write(kNewline)) {
295 return false;
296 }
297 }
298 return true;
299 }
300
301 // The BookmarkModel as a Value. This value was generated from the
302 // BookmarkCodec.
303 scoped_ptr<Value> bookmarks_;
304
305 // Path we're writing to.
306 std::wstring path_;
307
308 // File we're writing to.
309 ScopedHandle handle_;
310
311 // How much we indent when writing a bookmark/folder. This is modified
312 // via IncrementIndent and DecrementIndent.
313 std::string indent_;
314 };
315
316 } // namespace
317
318 void WriteBookmarks(MessageLoop* thread,
319 BookmarkModel* model,
320 const std::wstring& path) {
321 // BookmarkModel isn't thread safe (nor would we want to lock it down
322 // for the duration of the write), as such we make a copy of the
323 // BookmarkModel using BookmarkCodec then write from that.
324 BookmarkCodec codec;
325 scoped_ptr<Writer> writer(new Writer(codec.Encode(model), path));
326 if (thread)
327 thread->PostTask(FROM_HERE, writer.release());
328 else
329 writer->Run();
330 }
331
332 } // namespace bookmark_html_writer
OLDNEW
« no previous file with comments | « chrome/browser/bookmarks/bookmark_html_writer.h ('k') | chrome/browser/bookmarks/bookmark_html_writer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698