15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_html_writer.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/base64.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind_helpers.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
129ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/platform_file.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_codec.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_model.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_model_factory.h"
207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/favicon/favicon_service.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/favicon/favicon_service_factory.h"
239ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "chrome/common/favicon/favicon_types.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/file_stream.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/favicon_size.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static BookmarkFaviconFetcher* fetcher = NULL;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// File header.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kHeader[] =
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "<!DOCTYPE NETSCAPE-Bookmark-file-1>\r\n"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "<!-- This is an automatically generated file.\r\n"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "     It will be read and overwritten.\r\n"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "     DO NOT EDIT! -->\r\n"
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "<META HTTP-EQUIV=\"Content-Type\""
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    " CONTENT=\"text/html; charset=UTF-8\">\r\n"
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "<TITLE>Bookmarks</TITLE>\r\n"
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "<H1>Bookmarks</H1>\r\n"
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "<DL><p>\r\n";
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Newline separator.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kNewline[] = "\r\n";
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The following are used for bookmarks.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Start of a bookmark.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kBookmarkStart[] = "<DT><A HREF=\"";
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// After kBookmarkStart.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kAddDate[] = "\" ADD_DATE=\"";
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// After kAddDate.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kIcon[] = "\" ICON=\"";
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// After kIcon.
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kBookmarkAttributeEnd[] = "\">";
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// End of a bookmark.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kBookmarkEnd[] = "</A>";
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The following are used when writing folders.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Start of a folder.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kFolderStart[] = "<DT><H3 ADD_DATE=\"";
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// After kFolderStart.
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kLastModified[] = "\" LAST_MODIFIED=\"";
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// After kLastModified when writing the bookmark bar.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kBookmarkBar[] = "\" PERSONAL_TOOLBAR_FOLDER=\"true\">";
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// After kLastModified when writing a user created folder.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kFolderAttributeEnd[] = "\">";
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// End of the folder.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kFolderEnd[] = "</H3>";
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Start of the children of a folder.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kFolderChildren[] = "<DL><p>";
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// End of the children for a folder.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kFolderChildrenEnd[] = "</DL><p>";
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Number of characters to indent by.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const size_t kIndentSize = 4;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Class responsible for the actual writing. Takes ownership of favicons_map.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Writer : public base::RefCountedThreadSafe<Writer> {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Writer(base::Value* bookmarks,
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         const base::FilePath& path,
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         BookmarkFaviconFetcher::URLFaviconMap* favicons_map,
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         BookmarksExportObserver* observer)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : bookmarks_(bookmarks),
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        path_(path),
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        favicons_map_(favicons_map),
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        observer_(observer) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Writing bookmarks and favicons data to file.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void DoWrite() {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!OpenFile())
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Value* roots = NULL;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!Write(kHeader) ||
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bookmarks_->GetType() != Value::TYPE_DICTIONARY ||
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !static_cast<DictionaryValue*>(bookmarks_.get())->Get(
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            BookmarkCodec::kRootsKey, &roots) ||
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        roots->GetType() != Value::TYPE_DICTIONARY) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryValue* roots_d_value = static_cast<DictionaryValue*>(roots);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Value* root_folder_value;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Value* other_folder_value = NULL;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Value* mobile_folder_value = NULL;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!roots_d_value->Get(BookmarkCodec::kRootFolderNameKey,
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &root_folder_value) ||
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        root_folder_value->GetType() != Value::TYPE_DICTIONARY ||
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !roots_d_value->Get(BookmarkCodec::kOtherBookmarkFolderNameKey,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &other_folder_value) ||
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        other_folder_value->GetType() != Value::TYPE_DICTIONARY ||
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !roots_d_value->Get(BookmarkCodec::kMobileBookmarkFolderNameKey,
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &mobile_folder_value) ||
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mobile_folder_value->GetType() != Value::TYPE_DICTIONARY) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;  // Invalid type for root folder and/or other folder.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IncrementIndent();
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!WriteNode(*static_cast<DictionaryValue*>(root_folder_value),
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   BookmarkNode::BOOKMARK_BAR) ||
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !WriteNode(*static_cast<DictionaryValue*>(other_folder_value),
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   BookmarkNode::OTHER_NODE) ||
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !WriteNode(*static_cast<DictionaryValue*>(mobile_folder_value),
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   BookmarkNode::MOBILE)) {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DecrementIndent();
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Write(kFolderChildrenEnd);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Write(kNewline);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // File stream close is forced so that unit test could read it.
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_stream_.reset();
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NotifyOnFinish();
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  friend class base::RefCountedThreadSafe<Writer>;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Types of text being written out. The type dictates how the text is
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // escaped.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  enum TextType {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The text is the value of an html attribute, eg foo in
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // <a href="foo">.
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ATTRIBUTE_VALUE,
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Actual content, eg foo in <h1>foo</h2>.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CONTENT
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~Writer() {}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Opens the file, returning true on success.
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool OpenFile() {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_stream_.reset(new net::FileStream(NULL));
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (file_stream_->OpenSync(path_, flags) == net::OK);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Increments the indent.
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void IncrementIndent() {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    indent_.resize(indent_.size() + kIndentSize, ' ');
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Decrements the indent.
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void DecrementIndent() {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!indent_.empty());
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    indent_.resize(indent_.size() - kIndentSize, ' ');
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Called at the end of the export process.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void NotifyOnFinish() {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (observer_ != NULL) {
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      observer_->OnExportFinished();
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Writes raw text out returning true on success. This does not escape
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the text in anyway.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool Write(const std::string& text) {
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // net::FileStream does not allow 0-byte writes.
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!text.length())
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t wrote = file_stream_->WriteSync(text.c_str(), text.length());
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool result = (wrote == text.length());
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(result);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return result;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Writes out the text string (as UTF8). The text is escaped based on
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // type.
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool Write(const std::string& text, TextType type) {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(IsStringUTF8(text));
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string utf8_string;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (type) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ATTRIBUTE_VALUE:
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Convert " to &quot;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        utf8_string = text;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ReplaceSubstringsAfterOffset(&utf8_string, 0, "\"", "&quot;");
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case CONTENT:
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        utf8_string = net::EscapeForHTML(text);
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Write(utf8_string);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Indents the current line.
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool WriteIndent() {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Write(indent_);
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Converts a time string written to the JSON codec into a time_t string
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (used by bookmarks.html) and writes it.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool WriteTime(const std::string& time_string) {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 internal_value;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::StringToInt64(time_string, &internal_value);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Write(base::Int64ToString(
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Time::FromInternalValue(internal_value).ToTimeT()));
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Writes the node and all its children, returning true on success.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool WriteNode(const DictionaryValue& value,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                BookmarkNode::Type folder_type) {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string title, date_added_string, type_string;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!value.GetString(BookmarkCodec::kNameKey, &title) ||
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !value.GetString(BookmarkCodec::kDateAddedKey, &date_added_string) ||
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !value.GetString(BookmarkCodec::kTypeKey, &type_string) ||
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (type_string != BookmarkCodec::kTypeURL &&
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         type_string != BookmarkCodec::kTypeFolder))  {
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (type_string == BookmarkCodec::kTypeURL) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string url_string;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!value.GetString(BookmarkCodec::kURLKey, &url_string)) {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string favicon_string;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BookmarkFaviconFetcher::URLFaviconMap::iterator itr =
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          favicons_map_->find(url_string);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (itr != favicons_map_->end()) {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        scoped_refptr<base::RefCountedMemory> data(itr->second.get());
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::string favicon_data;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        favicon_data.assign(reinterpret_cast<const char*>(data->front()),
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            data->size());
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::string favicon_base64_encoded;
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (base::Base64Encode(favicon_data, &favicon_base64_encoded)) {
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GURL favicon_url("data:image/png;base64," + favicon_base64_encoded);
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          favicon_string = favicon_url.spec();
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!WriteIndent() ||
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kBookmarkStart) ||
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(url_string, ATTRIBUTE_VALUE) ||
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kAddDate) ||
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !WriteTime(date_added_string) ||
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (!favicon_string.empty() &&
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              (!Write(kIcon) ||
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               !Write(favicon_string, ATTRIBUTE_VALUE))) ||
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kBookmarkAttributeEnd) ||
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(title, CONTENT) ||
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kBookmarkEnd) ||
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kNewline)) {
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Folder.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string last_modified_date;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Value* child_values = NULL;
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!value.GetString(BookmarkCodec::kDateModifiedKey,
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         &last_modified_date) ||
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !value.Get(BookmarkCodec::kChildrenKey, &child_values) ||
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        child_values->GetType() != Value::TYPE_LIST) {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (folder_type != BookmarkNode::OTHER_NODE &&
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        folder_type != BookmarkNode::MOBILE) {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The other/mobile folder name are not written out. This gives the effect
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // of making the contents of the 'other folder' be a sibling to the
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // bookmark bar folder.
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!WriteIndent() ||
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kFolderStart) ||
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !WriteTime(date_added_string) ||
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kLastModified) ||
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !WriteTime(last_modified_date)) {
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (folder_type == BookmarkNode::BOOKMARK_BAR) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!Write(kBookmarkBar))
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return false;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        title = l10n_util::GetStringUTF8(IDS_BOOKMARK_BAR_FOLDER_NAME);
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (!Write(kFolderAttributeEnd)) {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!Write(title, CONTENT) ||
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kFolderEnd) ||
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kNewline) ||
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !WriteIndent() ||
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kFolderChildren) ||
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kNewline)) {
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IncrementIndent();
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Write the children.
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ListValue* children = static_cast<const ListValue*>(child_values);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < children->GetSize(); ++i) {
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const Value* child_value;
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!children->Get(i, &child_value) ||
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          child_value->GetType() != Value::TYPE_DICTIONARY) {
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!WriteNode(*static_cast<const DictionaryValue*>(child_value),
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     BookmarkNode::FOLDER)) {
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (folder_type != BookmarkNode::OTHER_NODE &&
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        folder_type != BookmarkNode::MOBILE) {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Close out the folder.
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DecrementIndent();
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!WriteIndent() ||
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kFolderChildrenEnd) ||
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !Write(kNewline)) {
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The BookmarkModel as a Value. This value was generated from the
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // BookmarkCodec.
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<Value> bookmarks_;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Path we're writing to.
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath path_;
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Map that stores favicon per URL.
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<BookmarkFaviconFetcher::URLFaviconMap> favicons_map_;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Observer to be notified on finish.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarksExportObserver* observer_;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // File we're writing to.
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<net::FileStream> file_stream_;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // How much we indent when writing a bookmark/folder. This is modified
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // via IncrementIndent and DecrementIndent.
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string indent_;
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(Writer);
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BookmarkFaviconFetcher::BookmarkFaviconFetcher(
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile,
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& path,
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BookmarksExportObserver* observer)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : profile_(profile),
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path_(path),
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      observer_(observer) {
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  favicons_map_.reset(new URLFaviconMap());
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this,
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 chrome::NOTIFICATION_PROFILE_DESTROYED,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile_));
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BookmarkFaviconFetcher::~BookmarkFaviconFetcher() {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkFaviconFetcher::ExportBookmarks() {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractUrls(BookmarkModelFactory::GetForProfile(
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_)->bookmark_bar_node());
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractUrls(BookmarkModelFactory::GetForProfile(profile_)->other_node());
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractUrls(BookmarkModelFactory::GetForProfile(profile_)->mobile_node());
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!bookmark_urls_.empty())
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FetchNextFavicon();
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExecuteWriter();
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkFaviconFetcher::Observe(
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (chrome::NOTIFICATION_PROFILE_DESTROYED == type && fetcher != NULL) {
42090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    base::MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fetcher = NULL;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkFaviconFetcher::ExtractUrls(const BookmarkNode* node) {
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (node->is_url()) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string url = node->url().spec();
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!url.empty())
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bookmark_urls_.push_back(url);
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < node->child_count(); ++i)
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExtractUrls(node->GetChild(i));
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkFaviconFetcher::ExecuteWriter() {
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // BookmarkModel isn't thread safe (nor would we want to lock it down
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for the duration of the write), as such we make a copy of the
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // BookmarkModel using BookmarkCodec then write from that.
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BookmarkCodec codec;
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::FILE, FROM_HERE,
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&Writer::DoWrite,
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 new Writer(codec.Encode(BookmarkModelFactory::GetForProfile(
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                profile_)),
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            path_, favicons_map_.release(), observer_)));
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (fetcher != NULL) {
44890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    base::MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher);
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fetcher = NULL;
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BookmarkFaviconFetcher::FetchNextFavicon() {
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (bookmark_urls_.empty()) {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  do {
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string url = bookmark_urls_.front();
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Filter out urls that we've already got favicon for.
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    URLFaviconMap::const_iterator iter = favicons_map_->find(url);
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (favicons_map_->end() == iter) {
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          profile_, Profile::EXPLICIT_ACCESS);
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      favicon_service->GetRawFaviconForURL(
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          FaviconService::FaviconForURLParams(
46690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)              profile_, GURL(url), chrome::FAVICON, gfx::kFaviconSize),
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ui::SCALE_FACTOR_100P,
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::Bind(&BookmarkFaviconFetcher::OnFaviconDataAvailable,
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     base::Unretained(this)),
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          &cancelable_task_tracker_);
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bookmark_urls_.pop_front();
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } while (!bookmark_urls_.empty());
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkFaviconFetcher::OnFaviconDataAvailable(
48090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const chrome::FaviconBitmapResult& bitmap_result) {
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL url;
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!bookmark_urls_.empty()) {
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url = GURL(bookmark_urls_.front());
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bookmark_urls_.pop_front();
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (bitmap_result.is_valid() && !url.is_empty()) {
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    favicons_map_->insert(
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        make_pair(url.spec(), bitmap_result.bitmap_data));
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FetchNextFavicon()) {
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExecuteWriter();
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace bookmark_html_writer {
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WriteBookmarks(Profile* profile,
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    const base::FilePath& path,
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    BookmarksExportObserver* observer) {
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // BookmarkModel isn't thread safe (nor would we want to lock it down
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for the duration of the write), as such we make a copy of the
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // BookmarkModel using BookmarkCodec then write from that.
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (fetcher == NULL) {
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fetcher = new BookmarkFaviconFetcher(profile, path, observer);
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fetcher->ExportBookmarks();
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace bookmark_html_writer
512