summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/task.h14
-rw-r--r--chrome/browser/bookmarks/bookmark_codec.cc27
-rw-r--r--chrome/browser/bookmarks/bookmark_codec.h16
-rw-r--r--chrome/browser/bookmarks/bookmark_context_menu.cc1
-rw-r--r--chrome/browser/bookmarks/bookmark_html_writer.cc332
-rw-r--r--chrome/browser/bookmarks/bookmark_html_writer.h27
-rw-r--r--chrome/browser/bookmarks/bookmark_html_writer_unittest.cc125
-rw-r--r--chrome/browser/browser.scons1
-rw-r--r--chrome/browser/browser.vcproj8
-rw-r--r--chrome/browser/importer/firefox2_importer.cc96
-rw-r--r--chrome/browser/importer/firefox2_importer.h16
-rw-r--r--chrome/browser/importer/firefox3_importer.cc4
-rw-r--r--chrome/browser/importer/ie_importer.cc5
-rw-r--r--chrome/browser/importer/importer.cc134
-rw-r--r--chrome/browser/importer/importer.h56
-rw-r--r--chrome/browser/importer/importer_unittest.cc13
-rw-r--r--chrome/browser/importer/toolbar_importer.cc8
-rw-r--r--chrome/browser/shell_dialogs.h14
-rw-r--r--chrome/browser/views/bookmark_manager_view.cc172
-rw-r--r--chrome/browser/views/bookmark_manager_view.h27
-rw-r--r--chrome/browser/views/importing_progress_view.cc35
-rw-r--r--chrome/browser/views/importing_progress_view.h9
-rw-r--r--chrome/browser/views/shell_dialogs.cc37
-rw-r--r--chrome/test/unit/unit_tests.scons1
-rw-r--r--chrome/test/unit/unittests.vcproj8
-rw-r--r--chrome/views/tree_view.cc24
-rw-r--r--chrome/views/tree_view.h8
-rw-r--r--net/base/escape.h1
28 files changed, 1066 insertions, 153 deletions
diff --git a/base/task.h b/base/task.h
index 9d4e6e2..c1b25cf 100644
--- a/base/task.h
+++ b/base/task.h
@@ -373,6 +373,20 @@ inline CancelableTask* NewRunnableMethod(T* object, Method method,
MakeTuple(a, b, c, d, e));
}
+template <class T, class Method, class A, class B, class C, class D, class E,
+ class F>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+ const A& a, const B& b,
+ const C& c, const D& d, const E& e,
+ const F& f) {
+ return new RunnableMethod<T,
+ Method,
+ Tuple6<A, B, C, D, E, F> >(object,
+ method,
+ MakeTuple(a, b, c, d, e,
+ f));
+}
+
// RunnableFunction and NewRunnableFunction implementation ---------------------
template <class Function, class Params>
diff --git a/chrome/browser/bookmarks/bookmark_codec.cc b/chrome/browser/bookmarks/bookmark_codec.cc
index 1bea43e..70aa410 100644
--- a/chrome/browser/bookmarks/bookmark_codec.cc
+++ b/chrome/browser/bookmarks/bookmark_codec.cc
@@ -14,21 +14,18 @@
using base::Time;
-// Key names.
-static const wchar_t* kRootsKey = L"roots";
-static const wchar_t* kRootFolderNameKey = L"bookmark_bar";
-static const wchar_t* kOtherBookmarFolderNameKey = L"other";
-static const wchar_t* kVersionKey = L"version";
-static const wchar_t* kTypeKey = L"type";
-static const wchar_t* kNameKey = L"name";
-static const wchar_t* kDateAddedKey = L"date_added";
-static const wchar_t* kURLKey = L"url";
-static const wchar_t* kDateModifiedKey = L"date_modified";
-static const wchar_t* kChildrenKey = L"children";
-
-// Possible values for kTypeKey.
-static const wchar_t* kTypeURL = L"url";
-static const wchar_t* kTypeFolder = L"folder";
+const wchar_t* BookmarkCodec::kRootsKey = L"roots";
+const wchar_t* BookmarkCodec::kRootFolderNameKey = L"bookmark_bar";
+const wchar_t* BookmarkCodec::kOtherBookmarFolderNameKey = L"other";
+const wchar_t* BookmarkCodec::kVersionKey = L"version";
+const wchar_t* BookmarkCodec::kTypeKey = L"type";
+const wchar_t* BookmarkCodec::kNameKey = L"name";
+const wchar_t* BookmarkCodec::kDateAddedKey = L"date_added";
+const wchar_t* BookmarkCodec::kURLKey = L"url";
+const wchar_t* BookmarkCodec::kDateModifiedKey = L"date_modified";
+const wchar_t* BookmarkCodec::kChildrenKey = L"children";
+const wchar_t* BookmarkCodec::kTypeURL = L"url";
+const wchar_t* BookmarkCodec::kTypeFolder = L"folder";
// Current version of the file.
static const int kCurrentVersion = 1;
diff --git a/chrome/browser/bookmarks/bookmark_codec.h b/chrome/browser/bookmarks/bookmark_codec.h
index 2865514..4e1fb81 100644
--- a/chrome/browser/bookmarks/bookmark_codec.h
+++ b/chrome/browser/bookmarks/bookmark_codec.h
@@ -43,6 +43,22 @@ class BookmarkCodec {
// nodes.
bool Decode(BookmarkModel* model, const Value& value);
+ // Names of the various keys written to the Value.
+ static const wchar_t* kRootsKey;
+ static const wchar_t* kRootFolderNameKey;
+ static const wchar_t* kOtherBookmarFolderNameKey;
+ static const wchar_t* kVersionKey;
+ static const wchar_t* kTypeKey;
+ static const wchar_t* kNameKey;
+ static const wchar_t* kDateAddedKey;
+ static const wchar_t* kURLKey;
+ static const wchar_t* kDateModifiedKey;
+ static const wchar_t* kChildrenKey;
+
+ // Possible values for kTypeKey.
+ static const wchar_t* kTypeURL;
+ static const wchar_t* kTypeFolder;
+
private:
// Encodes node and all its children into a Value object and returns it.
// The caller takes ownership of the returned object.
diff --git a/chrome/browser/bookmarks/bookmark_context_menu.cc b/chrome/browser/bookmarks/bookmark_context_menu.cc
index e25f4fa..1841f2b 100644
--- a/chrome/browser/bookmarks/bookmark_context_menu.cc
+++ b/chrome/browser/bookmarks/bookmark_context_menu.cc
@@ -409,6 +409,7 @@ void BookmarkContextMenu::ExecuteCommand(int id) {
break;
case IDS_BOOKMARK_MANAGER:
+ UserMetrics::RecordAction(L"ShowBookmarkManager", profile_);
BookmarkManagerView::Show(profile_);
break;
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
diff --git a/chrome/browser/bookmarks/bookmark_html_writer.h b/chrome/browser/bookmarks/bookmark_html_writer.h
new file mode 100644
index 0000000..f6b1830
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_html_writer.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef CHROME_BROWSER_BOOKMARKS_BOOKMARK_HTML_WRITER_H_
+#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_HTML_WRITER_H_
+
+#include <string>
+
+class BookmarkModel;
+class MessageLoop;
+
+namespace bookmark_html_writer {
+
+// Writes the bookmarks out in the 'bookmarks.html' format understood by
+// Firefox and IE. The results are written to the file at |path|.
+// If |thread| is non-null, writing is done on that thread, otherwise
+// writing is synchronous.
+//
+// TODO(sky): need a callback on failure.
+void WriteBookmarks(MessageLoop* thread,
+ BookmarkModel* model,
+ const std::wstring& path);
+
+}
+
+#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_HTML_WRITER_H_
diff --git a/chrome/browser/bookmarks/bookmark_html_writer_unittest.cc b/chrome/browser/bookmarks/bookmark_html_writer_unittest.cc
new file mode 100644
index 0000000..403ad16
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_html_writer_unittest.cc
@@ -0,0 +1,125 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/time.h"
+#include "chrome/browser/bookmarks/bookmark_html_writer.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/importer/firefox2_importer.h"
+
+class BookmarkHTMLWriterTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &path_));
+ file_util::AppendToPath(&path_, L"bookmarks.html");
+ file_util::Delete(path_, true);
+
+ }
+
+ virtual void TearDown() {
+ if (!path_.empty())
+ file_util::Delete(path_, true);
+ }
+
+ void AssertBookmarkEntryEquals(const ProfileWriter::BookmarkEntry& entry,
+ bool on_toolbar,
+ const GURL& url,
+ const std::wstring& title,
+ base::Time creation_time,
+ const std::wstring& f1,
+ const std::wstring& f2,
+ const std::wstring& f3) {
+ EXPECT_EQ(on_toolbar, entry.in_toolbar);
+ EXPECT_TRUE(url == entry.url);
+ EXPECT_EQ(title, entry.title);
+ EXPECT_TRUE(creation_time.ToTimeT() == entry.creation_time.ToTimeT());
+ size_t path_count = 0;
+ if (!f3.empty())
+ path_count = 3;
+ else if (!f2.empty())
+ path_count = 2;
+ else if (!f1.empty())
+ path_count = 1;
+ // The first path element should always be 'x', as that is what we passed
+ // to the importer.
+ ASSERT_EQ(path_count + 1, entry.path.size());
+ EXPECT_EQ(L"x", entry.path[0]);
+ EXPECT_TRUE(path_count < 1 || entry.path[1] == f1);
+ EXPECT_TRUE(path_count < 2 || entry.path[2] == f2);
+ EXPECT_TRUE(path_count < 3 || entry.path[3] == f3);
+ }
+
+ std::wstring path_;
+};
+
+// Tests bookmark_html_writer by populating a BookmarkModel, writing it out by
+// way of bookmark_html_writer, then using the importer to read it back in.
+TEST_F(BookmarkHTMLWriterTest, Test) {
+ // Populate the BookmarkModel. This creates the following bookmark structure:
+ // Bookmark Bar
+ // F1
+ // url1
+ // F2
+ // url2
+ // url3
+ // Other
+ // url1
+ // url2
+ // F3
+ // F4
+ // url1
+ std::wstring f1_title = L"F\"&;<1\"";
+ std::wstring f2_title = L"F2";
+ std::wstring f3_title = L"F 3";
+ std::wstring f4_title = L"F4";
+ std::wstring url1_title = L"url 1";
+ std::wstring url2_title = L"url&2";
+ std::wstring url3_title = L"url\"3";
+ GURL url1("http://url1");
+ GURL url2("http://url2");
+ GURL url3("http://url3");
+ BookmarkModel model(NULL);
+ base::Time t1(base::Time::Now());
+ base::Time t2(t1 + base::TimeDelta::FromHours(1));
+ base::Time t3(t1 + base::TimeDelta::FromHours(1));
+ BookmarkNode* f1 = model.AddGroup(model.GetBookmarkBarNode(), 0, f1_title);
+ model.AddURLWithCreationTime(f1, 0, url1_title, url1, t1);
+ BookmarkNode* f2 = model.AddGroup(f1, 1, f2_title);
+ model.AddURLWithCreationTime(f2, 0, url2_title, url2, t2);
+ model.AddURLWithCreationTime(model.GetBookmarkBarNode(), 1, url3_title, url3,
+ t3);
+
+ model.AddURLWithCreationTime(model.other_node(), 0, url1_title, url1, t1);
+ model.AddURLWithCreationTime(model.other_node(), 1, url2_title, url2, t2);
+ BookmarkNode* f3 = model.AddGroup(model.other_node(), 2, f3_title);
+ BookmarkNode* f4 = model.AddGroup(f3, 0, f4_title);
+ model.AddURLWithCreationTime(f4, 0, url1_title, url1, t1);
+
+ // Write to a temp file.
+ bookmark_html_writer::WriteBookmarks(NULL, &model, path_);
+
+ // Read the bookmarks back in.
+ std::vector<ProfileWriter::BookmarkEntry> parsed_bookmarks;
+ Firefox2Importer::ImportBookmarksFile(path_, std::set<GURL>(), false,
+ L"x", NULL, &parsed_bookmarks, NULL,
+ NULL);
+
+ // Verify we got back what we wrote.
+ ASSERT_EQ(6, parsed_bookmarks.size());
+ AssertBookmarkEntryEquals(parsed_bookmarks[0], false, url1, url1_title, t1,
+ L"Bookmark Bar", f1_title, std::wstring());
+ AssertBookmarkEntryEquals(parsed_bookmarks[1], false, url2, url2_title, t2,
+ L"Bookmark Bar", f1_title, f2_title);
+ AssertBookmarkEntryEquals(parsed_bookmarks[2], false, url3, url3_title, t3,
+ L"Bookmark Bar", std::wstring(), std::wstring());
+ AssertBookmarkEntryEquals(parsed_bookmarks[3], false, url1, url1_title, t1,
+ std::wstring(), std::wstring(), std::wstring());
+ AssertBookmarkEntryEquals(parsed_bookmarks[4], false, url2, url2_title, t2,
+ std::wstring(), std::wstring(), std::wstring());
+ AssertBookmarkEntryEquals(parsed_bookmarks[5], false, url1, url1_title, t1,
+ f3_title, f4_title, std::wstring());
+}
diff --git a/chrome/browser/browser.scons b/chrome/browser/browser.scons
index 0b9bb44..42633fa 100644
--- a/chrome/browser/browser.scons
+++ b/chrome/browser/browser.scons
@@ -119,6 +119,7 @@ if env['PLATFORM'] == 'win32':
'bookmarks/bookmark_drag_data.cc',
'bookmarks/bookmark_drop_info.cc',
'bookmarks/bookmark_folder_tree_model.cc',
+ 'bookmarks/bookmark_html_writer.cc',
'bookmarks/bookmark_model.cc',
'bookmarks/bookmark_storage.cc',
'bookmarks/bookmark_table_model.cc',
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj
index 921f371..f83558f 100644
--- a/chrome/browser/browser.vcproj
+++ b/chrome/browser/browser.vcproj
@@ -766,6 +766,14 @@
>
</File>
<File
+ RelativePath=".\bookmarks\bookmark_html_writer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\bookmarks\bookmark_html_writer.cc"
+ >
+ </File>
+ <File
RelativePath=".\bookmarks\bookmark_model.h"
>
</File>
diff --git a/chrome/browser/importer/firefox2_importer.cc b/chrome/browser/importer/firefox2_importer.cc
index e85d347..aef2ba2 100644
--- a/chrome/browser/importer/firefox2_importer.cc
+++ b/chrome/browser/importer/firefox2_importer.cc
@@ -22,7 +22,7 @@ using base::Time;
// Firefox2Importer.
-Firefox2Importer::Firefox2Importer() {
+Firefox2Importer::Firefox2Importer() : parsing_bookmarks_html_file_(false) {
}
Firefox2Importer::~Firefox2Importer() {
@@ -37,6 +37,8 @@ void Firefox2Importer::StartImport(ProfileInfo profile_info,
app_path_ = profile_info.app_path;
importer_host_ = host;
+ parsing_bookmarks_html_file_ = (profile_info.browser_type == BOOKMARKS_HTML);
+
// The order here is important!
NotifyStarted();
if ((items & HOME_PAGE) && !cancelled())
@@ -120,30 +122,29 @@ TemplateURL* Firefox2Importer::CreateTemplateURL(const std::wstring& title,
return t_url;
}
-void Firefox2Importer::ImportBookmarks() {
- // Read the whole file.
- std::wstring file = source_path_;
- file_util::AppendToPath(&file, L"bookmarks.html");
+// static
+void Firefox2Importer::ImportBookmarksFile(
+ const std::wstring& file_path,
+ const std::set<GURL>& default_urls,
+ bool first_run,
+ const std::wstring& first_folder_name,
+ Importer* importer,
+ std::vector<ProfileWriter::BookmarkEntry>* bookmarks,
+ std::vector<TemplateURL*>* template_urls,
+ std::vector<history::ImportedFavIconUsage>* favicons) {
std::string content;
- file_util::ReadFileToString(file, &content);
+ file_util::ReadFileToString(file_path, &content);
std::vector<std::string> lines;
SplitString(content, '\n', &lines);
-
- // Load the default bookmarks.
- std::set<GURL> default_urls;
- LoadDefaultBookmarks(app_path_, &default_urls);
-
- // Parse the bookmarks.html file.
- std::vector<ProfileWriter::BookmarkEntry> bookmarks, toolbar_bookmarks;
- std::vector<TemplateURL*> template_urls;
- std::vector<history::ImportedFavIconUsage> favicons;
- std::wstring last_folder
- = l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_FIREFOX);
+
+ std::vector<ProfileWriter::BookmarkEntry> toolbar_bookmarks;
+ std::wstring last_folder = first_folder_name;
bool last_folder_on_toolbar = false;
std::vector<std::wstring> path;
size_t toolbar_folder = 0;
std::string charset;
- for (size_t i = 0; i < lines.size() && !cancelled(); ++i) {
+ for (size_t i = 0; i < lines.size() && (!importer || !importer->cancelled());
+ ++i) {
std::string line;
TrimString(lines[i], " ", &line);
@@ -179,7 +180,7 @@ void Firefox2Importer::ImportBookmarks() {
entry.url = url;
entry.title = title;
- if (first_run() && toolbar_folder) {
+ if (first_run && toolbar_folder) {
// Flatten the items in toolbar.
entry.in_toolbar = true;
entry.path.assign(path.begin() + toolbar_folder, path.end());
@@ -188,20 +189,23 @@ void Firefox2Importer::ImportBookmarks() {
// Insert the item into the "Imported from Firefox" folder after
// the first run.
entry.path.assign(path.begin(), path.end());
- if (first_run())
+ if (first_run)
entry.path.erase(entry.path.begin());
- bookmarks.push_back(entry);
+ bookmarks->push_back(entry);
}
// Save the favicon. DataURLToFaviconUsage will handle the case where
// there is no favicon.
- DataURLToFaviconUsage(url, favicon, &favicons);
-
- // If there is a SHORTCUT attribute for this bookmark, we
- // add it as our keywords.
- TemplateURL* t_url = CreateTemplateURL(title, shortcut, url);
- if (t_url)
- template_urls.push_back(t_url);
+ if (favicons)
+ DataURLToFaviconUsage(url, favicon, favicons);
+
+ if (template_urls) {
+ // If there is a SHORTCUT attribute for this bookmark, we
+ // add it as our keywords.
+ TemplateURL* t_url = CreateTemplateURL(title, shortcut, url);
+ if (t_url)
+ template_urls->push_back(t_url);
+ }
continue;
}
@@ -221,14 +225,42 @@ void Firefox2Importer::ImportBookmarks() {
}
}
+ bookmarks->insert(bookmarks->begin(), toolbar_bookmarks.begin(),
+ toolbar_bookmarks.end());
+}
+
+void Firefox2Importer::ImportBookmarks() {
+ // Load the default bookmarks.
+ std::set<GURL> default_urls;
+ if (!parsing_bookmarks_html_file_)
+ LoadDefaultBookmarks(app_path_, &default_urls);
+
+ // Parse the bookmarks.html file.
+ std::vector<ProfileWriter::BookmarkEntry> bookmarks, toolbar_bookmarks;
+ std::vector<TemplateURL*> template_urls;
+ std::vector<history::ImportedFavIconUsage> favicons;
+ std::wstring file = source_path_;
+ if (!parsing_bookmarks_html_file_)
+ file_util::AppendToPath(&file, L"bookmarks.html");
+ std::wstring first_folder_name;
+ if (parsing_bookmarks_html_file_)
+ first_folder_name = l10n_util::GetString(IDS_BOOKMARK_GROUP);
+ else
+ first_folder_name = l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_FIREFOX);
+
+ ImportBookmarksFile(file, default_urls, first_run(),
+ first_folder_name, this, &bookmarks, &template_urls,
+ &favicons);
+
// Write data into profile.
- bookmarks.insert(bookmarks.begin(), toolbar_bookmarks.begin(),
- toolbar_bookmarks.end());
if (!bookmarks.empty() && !cancelled()) {
main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
- &ProfileWriter::AddBookmarkEntry, bookmarks, false));
+ &ProfileWriter::AddBookmarkEntry, bookmarks,
+ first_folder_name,
+ first_run() ? ProfileWriter::FIRST_RUN : 0));
}
- if (!template_urls.empty() && !cancelled()) {
+ if (!parsing_bookmarks_html_file_ && !template_urls.empty() &&
+ !cancelled()) {
main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
&ProfileWriter::AddKeywords, template_urls, -1, false));
} else {
diff --git a/chrome/browser/importer/firefox2_importer.h b/chrome/browser/importer/firefox2_importer.h
index f14438d..eb02cb7 100644
--- a/chrome/browser/importer/firefox2_importer.h
+++ b/chrome/browser/importer/firefox2_importer.h
@@ -33,6 +33,19 @@ class Firefox2Importer : public Importer {
const std::wstring& keyword,
const GURL& url);
+ // Imports the bookmarks from the specified file. |template_urls| and
+ // |favicons| may be null, in which case TemplateURLs and favicons are
+ // not parsed. Any bookmarks in |default_urls| are ignored.
+ static void ImportBookmarksFile(
+ const std::wstring& file_path,
+ const std::set<GURL>& default_urls,
+ bool first_run,
+ const std::wstring& first_folder_name,
+ Importer* importer,
+ std::vector<ProfileWriter::BookmarkEntry>* bookmarks,
+ std::vector<TemplateURL*>* template_urls,
+ std::vector<history::ImportedFavIconUsage>* favicons);
+
private:
FRIEND_TEST(FirefoxImporterTest, Firefox2BookmarkParse);
FRIEND_TEST(FirefoxImporterTest, Firefox2CookesParse);
@@ -105,9 +118,10 @@ class Firefox2Importer : public Importer {
ProfileWriter* writer_;
std::wstring source_path_;
std::wstring app_path_;
+ // If true, we only parse the bookmarks.html file specified as source_path_.
+ bool parsing_bookmarks_html_file_;
DISALLOW_EVIL_CONSTRUCTORS(Firefox2Importer);
};
#endif // CHROME_BROWSER_IMPORTER_FIREFOX2_IMPORTER_H_
-
diff --git a/chrome/browser/importer/firefox3_importer.cc b/chrome/browser/importer/firefox3_importer.cc
index 965ba15..ea470d5 100644
--- a/chrome/browser/importer/firefox3_importer.cc
+++ b/chrome/browser/importer/firefox3_importer.cc
@@ -247,7 +247,9 @@ void Firefox3Importer::ImportBookmarks() {
// Write into profile.
if (!bookmarks.empty() && !cancelled()) {
main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
- &ProfileWriter::AddBookmarkEntry, bookmarks, false));
+ &ProfileWriter::AddBookmarkEntry, bookmarks,
+ l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_FIREFOX),
+ first_run() ? ProfileWriter::FIRST_RUN : 0));
}
if (!template_urls.empty() && !cancelled()) {
main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
diff --git a/chrome/browser/importer/ie_importer.cc b/chrome/browser/importer/ie_importer.cc
index c952603..2e5d919 100644
--- a/chrome/browser/importer/ie_importer.cc
+++ b/chrome/browser/importer/ie_importer.cc
@@ -109,7 +109,9 @@ void IEImporter::ImportFavorites() {
if (!bookmarks.empty() && !cancelled()) {
main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
- &ProfileWriter::AddBookmarkEntry, bookmarks, false));
+ &ProfileWriter::AddBookmarkEntry, bookmarks,
+ l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_IE),
+ first_run() ? ProfileWriter::FIRST_RUN : 0));
}
}
@@ -552,4 +554,3 @@ int IEImporter::CurrentIEVersion() const {
}
return version;
}
-
diff --git a/chrome/browser/importer/importer.cc b/chrome/browser/importer/importer.cc
index 16552e3..3ce8996 100644
--- a/chrome/browser/importer/importer.cc
+++ b/chrome/browser/importer/importer.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/importer/importer.h"
#include <map>
+#include <set>
#include "base/file_util.h"
#include "base/gfx/image_operations.h"
@@ -81,10 +82,15 @@ void ProfileWriter::AddHomepage(const GURL& home_page) {
void ProfileWriter::AddBookmarkEntry(
const std::vector<BookmarkEntry>& bookmark,
- bool check_uniqueness) {
+ const std::wstring& first_folder_name,
+ int options) {
BookmarkModel* model = profile_->GetBookmarkModel();
DCHECK(model->IsLoaded());
+ bool first_run = (options & FIRST_RUN) != 0;
+ std::wstring real_first_folder = first_run ? first_folder_name :
+ GenerateUniqueFolderName(model, first_folder_name);
+
bool show_bookmark_toolbar = false;
std::set<BookmarkNode*> groups_added_to;
for (std::vector<BookmarkEntry>::const_iterator it = bookmark.begin();
@@ -92,49 +98,13 @@ void ProfileWriter::AddBookmarkEntry(
// Don't insert this url if it isn't valid.
if (!it->url.is_valid())
continue;
-
+
// We suppose that bookmarks are unique by Title, URL, and Folder. Since
// checking for uniqueness may not be always the user's intention we have
// this as an option.
- if (check_uniqueness) {
- std::vector<BookmarkModel::TitleMatch> matches;
- model->GetBookmarksMatchingText((*it).title, 32, &matches); // 32 enough?
- if (!matches.empty()) {
- bool found_match = false;
- for (std::vector<BookmarkModel::TitleMatch>::iterator match_it =
- matches.begin();
- match_it != matches.end() && !found_match;
- ++match_it) {
- if ((*it).title != (*match_it).node->GetTitle())
- continue;
- if ((*it).url != (*match_it).node->GetURL())
- continue;
-
- // Check the folder path for uniqueness as well
- found_match = true;
- BookmarkNode* node = (*match_it).node->GetParent();
- for(std::vector<std::wstring>::const_reverse_iterator path_it =
- (*it).path.rbegin();
- (path_it != (*it).path.rend()) && found_match;
- ++path_it) {
- if (NULL == node || (*path_it != node->GetTitle()))
- found_match = false;
- if (found_match)
- node = node->GetParent();
- }
-
- // We need a post test to differentiate checks such as
- // /home/hello and /hello. Note that here the current parent
- // should be the "Other bookmarks" node, its parent should be the
- // root with title "", and it's parent is finally NULL.
- if (NULL == node->GetParent() ||
- NULL != node->GetParent()->GetParent())
- found_match = false;
- }
-
- if (found_match)
- continue;
- }
+ if (options & ADD_IF_UNIQUE &&
+ DoesBookmarkExist(model, *it, real_first_folder, first_run)) {
+ continue;
}
// Set up groups in BookmarkModel in such a way that path[i] is
@@ -146,17 +116,21 @@ void ProfileWriter::AddBookmarkEntry(
for (std::vector<std::wstring>::const_iterator i = it->path.begin();
i != it->path.end(); ++i) {
BookmarkNode* child = NULL;
+ const std::wstring& folder_name =
+ (!first_run && !it->in_toolbar && (i == it->path.begin())) ?
+ real_first_folder : *i;
+
for (int index = 0; index < parent->GetChildCount(); ++index) {
BookmarkNode* node = parent->GetChild(index);
if ((node->GetType() == history::StarredEntry::BOOKMARK_BAR ||
node->GetType() == history::StarredEntry::USER_GROUP) &&
- node->GetTitle() == *i) {
+ node->GetTitle() == folder_name) {
child = node;
break;
}
}
if (child == NULL)
- child = model->AddGroup(parent, parent->GetChildCount(), *i);
+ child = model->AddGroup(parent, parent->GetChildCount(), folder_name);
parent = child;
}
groups_added_to.insert(parent);
@@ -321,6 +295,79 @@ void ProfileWriter::ShowBookmarkBar() {
}
}
+std::wstring ProfileWriter::GenerateUniqueFolderName(
+ BookmarkModel* model,
+ const std::wstring& folder_name) {
+ // Build a set containing the folder names of the other folder.
+ std::set<std::wstring> other_folder_names;
+ BookmarkNode* other = model->other_node();
+ for (int i = 0; i < other->GetChildCount(); ++i) {
+ BookmarkNode* node = other->GetChild(i);
+ if (node->is_folder())
+ other_folder_names.insert(node->GetTitle());
+ }
+
+ if (other_folder_names.find(folder_name) == other_folder_names.end())
+ return folder_name; // Name is unique, use it.
+
+ // Otherwise iterate until we find a unique name.
+ for (int i = 1; i < 100; ++i) {
+ std::wstring name = folder_name + StringPrintf(L" (%d)", i);
+ if (other_folder_names.find(name) == other_folder_names.end())
+ return name;
+ }
+
+ return folder_name;
+}
+
+bool ProfileWriter::DoesBookmarkExist(
+ BookmarkModel* model,
+ const BookmarkEntry& entry,
+ const std::wstring& first_folder_name,
+ bool first_run) {
+ std::vector<BookmarkNode*> nodes_with_same_url;
+ model->GetNodesByURL(entry.url, &nodes_with_same_url);
+ if (nodes_with_same_url.empty())
+ return false;
+
+ for (size_t i = 0; i < nodes_with_same_url.size(); ++i) {
+ BookmarkNode* node = nodes_with_same_url[i];
+ if (entry.title != node->GetTitle())
+ continue;
+
+ // Does the path match?
+ bool found_match = true;
+ BookmarkNode* parent = node->GetParent();
+ for (std::vector<std::wstring>::const_reverse_iterator path_it =
+ entry.path.rbegin();
+ (path_it != entry.path.rend()) && found_match; ++path_it) {
+ const std::wstring& folder_name =
+ (!first_run && path_it + 1 == entry.path.rend()) ?
+ first_folder_name : *path_it;
+ if (NULL == parent || *path_it != folder_name)
+ found_match = false;
+ else
+ parent = parent->GetParent();
+ }
+
+ // We need a post test to differentiate checks such as
+ // /home/hello and /hello. The parent should either by the other folder
+ // node, or the bookmarks bar, depending upon first_run and
+ // entry.in_toolbar.
+ if (found_match &&
+ ((first_run && entry.in_toolbar && parent !=
+ model->GetBookmarkBarNode()) ||
+ ((!first_run || !entry.in_toolbar) &&
+ parent != model->other_node()))) {
+ found_match = false;
+ }
+
+ if (found_match)
+ return true; // Found a match with the same url path and title.
+ }
+ return false;
+}
+
// Importer.
// static
@@ -539,6 +586,7 @@ Importer* ImporterHost::CreateImporterByType(ProfileType type) {
switch (type) {
case MS_IE:
return new IEImporter();
+ case BOOKMARKS_HTML:
case FIREFOX2:
return new Firefox2Importer();
case FIREFOX3:
diff --git a/chrome/browser/importer/importer.h b/chrome/browser/importer/importer.h
index a597879..4e57b64 100644
--- a/chrome/browser/importer/importer.h
+++ b/chrome/browser/importer/importer.h
@@ -27,7 +27,9 @@ enum ProfileType {
MS_IE = 0,
FIREFOX2,
FIREFOX3,
- GOOGLE_TOOLBAR5
+ GOOGLE_TOOLBAR5,
+ // Identifies a 'bookmarks.html' file.
+ BOOKMARKS_HTML
};
// An enumeration of the type of data we want to import.
@@ -57,6 +59,18 @@ class Importer;
// This object must be invoked on UI thread.
class ProfileWriter : public base::RefCounted<ProfileWriter> {
public:
+ // Used to identify how the bookmarks are added.
+ enum BookmarkOptions {
+ // Indicates the bookmark should only be added if unique. Uniqueness
+ // is done by title, url and path. That is, if this is passed to
+ // AddBookmarkEntry the bookmark is added only if there is no other
+ // URL with the same url, path and title.
+ ADD_IF_UNIQUE = 1 << 0,
+
+ // Indicates the bookmarks are being added during first run.
+ FIRST_RUN = 1 << 1
+ };
+
explicit ProfileWriter(Profile* profile) : profile_(profile) { }
virtual ~ProfileWriter() { }
@@ -86,9 +100,21 @@ class ProfileWriter : public base::RefCounted<ProfileWriter> {
virtual void AddIE7PasswordInfo(const IE7PasswordInfo& info);
virtual void AddHistoryPage(const std::vector<history::URLRow>& page);
virtual void AddHomepage(const GURL& homepage);
- virtual void AddBookmarkEntry(
- const std::vector<BookmarkEntry>& bookmark,
- bool check_uniqueness);
+ // Adds the bookmarks to the BookmarkModel.
+ // |options| is a bitmask of BookmarkOptions and dictates how and
+ // which bookmarks are added. If the bitmask contains FIRST_RUN,
+ // then any entries with a value of true for in_toolbar are added to
+ // the bookmark bar. If the bitmask does not contain FIRST_RUN then
+ // the folder name the bookmarks are added to is uniqued based on
+ // |first_folder_name|. For example, if |first_folder_name| is 'foo'
+ // and a folder with the name 'foo' already exists in the other
+ // bookmarks folder, then the folder name 'foo 2' is used.
+ // If |options| contains ADD_IF_UNIQUE, then the bookmark is added only
+ // if another bookmarks does not exist with the same title, path and
+ // url.
+ virtual void AddBookmarkEntry(const std::vector<BookmarkEntry>& bookmark,
+ const std::wstring& first_folder_name,
+ int options);
virtual void AddFavicons(
const std::vector<history::ImportedFavIconUsage>& favicons);
// Add the TemplateURLs in |template_urls| to the local store and make the
@@ -111,6 +137,19 @@ class ProfileWriter : public base::RefCounted<ProfileWriter> {
Profile* GetProfile() const { return profile_; }
private:
+ // Generates a unique folder name. If folder_name is not unique, then this
+ // repeatedly tests for '|folder_name| + (i)' until a unique name is found.
+ std::wstring GenerateUniqueFolderName(BookmarkModel* model,
+ const std::wstring& folder_name);
+
+ // Returns true if a bookmark exists with the same url, title and path
+ // as |entry|. |first_folder_name| is the name to use for the first
+ // path entry if |first_run| is true.
+ bool DoesBookmarkExist(BookmarkModel* model,
+ const BookmarkEntry& entry,
+ const std::wstring& first_folder_name,
+ bool first_run);
+
Profile* profile_;
DISALLOW_EVIL_CONSTRUCTORS(ProfileWriter);
@@ -290,12 +329,15 @@ class Importer : public base::RefCounted<Importer> {
void set_first_run(bool first_run) { first_run_ = first_run; }
+ bool cancelled() const { return cancelled_; }
+
protected:
Importer()
: main_loop_(MessageLoop::current()),
delagate_loop_(NULL),
importer_host_(NULL),
- cancelled_(false) {}
+ cancelled_(false),
+ first_run_(false) {}
// Notifies the coordinator that the collection of data for the specified
// item has begun.
@@ -329,8 +371,6 @@ class Importer : public base::RefCounted<Importer> {
static bool ReencodeFavicon(const unsigned char* src_data, size_t src_len,
std::vector<unsigned char>* png_data);
- bool cancelled() const { return cancelled_; }
-
bool first_run() const { return first_run_; }
// The importer should know the main thread so that ProfileWriter
@@ -359,6 +399,7 @@ class ImportObserver {
public:
virtual ~ImportObserver() {}
// The import operation was canceled by the user.
+ // TODO (4164): this is never invoked, either rip it out or invoke it.
virtual void ImportCanceled() = 0;
// The import operation was completed successfully.
@@ -380,4 +421,3 @@ void StartImportingWithUI(HWND parent_window,
bool first_run);
#endif // CHROME_BROWSER_IMPORTER_IMPORTER_H_
-
diff --git a/chrome/browser/importer/importer_unittest.cc b/chrome/browser/importer/importer_unittest.cc
index f75aa4f..a6094fc 100644
--- a/chrome/browser/importer/importer_unittest.cc
+++ b/chrome/browser/importer/importer_unittest.cc
@@ -189,9 +189,9 @@ class TestObserver : public ProfileWriter,
++history_count_;
}
- virtual void AddBookmarkEntry(
- const std::vector<BookmarkEntry>& bookmark,
- bool check_duplicates) {
+ virtual void AddBookmarkEntry(const std::vector<BookmarkEntry>& bookmark,
+ const std::wstring& first_folder_name,
+ int options) {
// Importer should import the IE Favorites folder the same as the list.
for (size_t i = 0; i < bookmark.size(); ++i) {
if (FindBookmarkEntry(bookmark[i], kIEBookmarks,
@@ -545,7 +545,8 @@ class FirefoxObserver : public ProfileWriter,
}
virtual void AddBookmarkEntry(const std::vector<BookmarkEntry>& bookmark,
- bool check_duplicates) {
+ const std::wstring& first_folder_name,
+ int options) {
for (size_t i = 0; i < bookmark.size(); ++i) {
if (FindBookmarkEntry(bookmark[i], kFirefox2Bookmarks,
arraysize(kFirefox2Bookmarks)))
@@ -745,7 +746,8 @@ class Firefox3Observer : public ProfileWriter,
}
virtual void AddBookmarkEntry(const std::vector<BookmarkEntry>& bookmark,
- bool check_duplicates) {
+ const std::wstring& first_folder_name,
+ int options) {
for (size_t i = 0; i < bookmark.size(); ++i) {
if (FindBookmarkEntry(bookmark[i], kFirefox3Bookmarks,
arraysize(kFirefox3Bookmarks)))
@@ -829,4 +831,3 @@ TEST_F(ImporterTest, Firefox3Importer) {
HISTORY | PASSWORDS | FAVORITES | SEARCH_ENGINES, observer, true));
loop->Run();
}
-
diff --git a/chrome/browser/importer/toolbar_importer.cc b/chrome/browser/importer/toolbar_importer.cc
index 8813807..e96435f 100644
--- a/chrome/browser/importer/toolbar_importer.cc
+++ b/chrome/browser/importer/toolbar_importer.cc
@@ -568,7 +568,11 @@ bool Toolbar5Importer::ExtractFoldersFromXmlReader(
void Toolbar5Importer::AddBookMarksToChrome(
const std::vector<ProfileWriter::BookmarkEntry>& bookmarks) {
if (!bookmarks.empty() && !cancelled()) {
- main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
- &ProfileWriter::AddBookmarkEntry, bookmarks, true));
+ int options = ProfileWriter::ADD_IF_UNIQUE |
+ (first_run() ? ProfileWriter::FIRST_RUN : 0);
+ main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
+ &ProfileWriter::AddBookmarkEntry, bookmarks,
+ l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_GOOGLE_TOOLBAR),
+ options));
}
}
diff --git a/chrome/browser/shell_dialogs.h b/chrome/browser/shell_dialogs.h
index 7e57c7b..74cf66c 100644
--- a/chrome/browser/shell_dialogs.h
+++ b/chrome/browser/shell_dialogs.h
@@ -73,17 +73,30 @@ class SelectFileDialog
// the dialog. This only works for SELECT_SAVEAS_FILE and SELECT_OPEN_FILE.
// Can be an empty string to indicate Windows should choose the default to
// show.
+ // |filter| is a null (\0) separated list of alternating filter description
+ // and filters and terminated with two nulls.
// |owning_hwnd| is the window the dialog is modal to, or NULL for a modeless
// dialog.
// |params| is data from the calling context which will be passed through to
// the listener. Can be NULL.
// NOTE: only one instance of any shell dialog can be shown per owning_hwnd
// at a time (for obvious reasons).
+ // TODO: convert all callers to this and rip out the old.
virtual void SelectFile(Type type,
const std::wstring& title,
const std::wstring& default_path,
+ const std::wstring& filter,
HWND owning_hwnd,
void* params) = 0;
+
+ void SelectFile(Type type,
+ const std::wstring& title,
+ const std::wstring& default_path,
+ HWND owning_hwnd,
+ void* params) {
+ SelectFile(type, title, default_path, std::wstring(),
+ owning_hwnd, params);
+ }
};
// Shows a dialog box for selecting a font.
@@ -137,4 +150,3 @@ class SelectFontDialog
};
#endif // #ifndef CHROME_BROWSER_SHELL_DIALOGS_H_
-
diff --git a/chrome/browser/views/bookmark_manager_view.cc b/chrome/browser/views/bookmark_manager_view.cc
index 9650e92..76ac3d7 100644
--- a/chrome/browser/views/bookmark_manager_view.cc
+++ b/chrome/browser/views/bookmark_manager_view.cc
@@ -9,12 +9,15 @@
#include "base/gfx/skia_utils.h"
#include "chrome/app/locales/locale_settings.h"
#include "chrome/browser/bookmarks/bookmark_folder_tree_model.h"
+#include "chrome/browser/bookmarks/bookmark_html_writer.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/bookmarks/bookmark_table_model.h"
#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/importer/importer.h"
#include "chrome/browser/profile.h"
+#include "chrome/browser/user_metrics.h"
#include "chrome/browser/views/bookmark_editor_view.h"
#include "chrome/browser/views/bookmark_folder_tree_view.h"
#include "chrome/browser/views/bookmark_table_view.h"
@@ -40,6 +43,53 @@ static BookmarkManagerView* manager = NULL;
// Delay, in ms, between when the user types and when we run the search.
static const int kSearchDelayMS = 200;
+static const int kOrganizeMenuButtonID = 1;
+static const int kToolsMenuButtonID = 2;
+
+namespace {
+
+// Observer installed on the importer. When done importing the newly created
+// folder is selected in the bookmark manager.
+class ImportObserverImpl : public ImportObserver {
+ public:
+ explicit ImportObserverImpl(Profile* profile) : profile_(profile) {
+ BookmarkModel* model = profile->GetBookmarkModel();
+ initial_other_count_ = model->other_node()->GetChildCount();
+ }
+
+ virtual void ImportCanceled() {
+ delete this;
+ }
+
+ virtual void ImportComplete() {
+ // We aren't needed anymore.
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+
+ BookmarkManagerView* manager = BookmarkManagerView::current();
+ if (!manager || manager->profile() != profile_)
+ return;
+
+ BookmarkModel* model = profile_->GetBookmarkModel();
+ int other_count = model->other_node()->GetChildCount();
+ if (other_count == initial_other_count_ + 1) {
+ BookmarkNode* imported_node =
+ model->other_node()->GetChild(initial_other_count_);
+ manager->SelectInTree(imported_node);
+ manager->ExpandAll(imported_node);
+ }
+ }
+
+ private:
+ Profile* profile_;
+ // Number of children in the other bookmarks folder at the time we were
+ // created.
+ int initial_other_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImportObserverImpl);
+};
+
+} // namespace
+
BookmarkManagerView::BookmarkManagerView(Profile* profile)
: profile_(profile->GetOriginalProfile()),
table_view_(NULL),
@@ -59,6 +109,12 @@ BookmarkManagerView::BookmarkManagerView(Profile* profile)
views::MenuButton* organize_menu_button = new views::MenuButton(
l10n_util::GetString(IDS_BOOKMARK_MANAGER_ORGANIZE_MENU),
this, true);
+ organize_menu_button->SetID(kOrganizeMenuButtonID);
+
+ views::MenuButton* tools_menu_button = new views::MenuButton(
+ l10n_util::GetString(IDS_BOOKMARK_MANAGER_TOOLS_MENU),
+ this, true);
+ tools_menu_button->SetID(kToolsMenuButtonID);
split_view_ = new views::SingleSplitView(tree_view_, table_view_);
@@ -66,10 +122,13 @@ BookmarkManagerView::BookmarkManagerView(Profile* profile)
SetLayoutManager(layout);
const int top_id = 1;
const int split_cs_id = 2;
- layout->SetInsets(kPanelVertMargin, 0, 0, 0);
+ layout->SetInsets(2, 0, 0, 0);
views::ColumnSet* column_set = layout->AddColumnSet(top_id);
column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
- 1, views::GridLayout::USE_PREF, 0, 0);
+ 0, views::GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
+ 0, views::GridLayout::USE_PREF, 0, 0);
column_set->AddPaddingColumn(1, kUnrelatedControlHorizontalSpacing);
column_set->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
1, views::GridLayout::USE_PREF, 0, 0);
@@ -81,6 +140,7 @@ BookmarkManagerView::BookmarkManagerView(Profile* profile)
layout->StartRow(0, top_id);
layout->AddView(organize_menu_button);
+ layout->AddView(tools_menu_button);
layout->AddView(search_tf_);
layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
@@ -94,6 +154,9 @@ BookmarkManagerView::BookmarkManagerView(Profile* profile)
}
BookmarkManagerView::~BookmarkManagerView() {
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+
if (!GetBookmarkModel()->IsLoaded()) {
GetBookmarkModel()->RemoveObserver(this);
} else {
@@ -164,6 +227,16 @@ void BookmarkManagerView::SelectInTree(BookmarkNode* node) {
}
}
+void BookmarkManagerView::ExpandAll(BookmarkNode* node) {
+ BookmarkNode* parent = node->is_url() ? node->GetParent() : node;
+ FolderNode* folder_node = tree_model_->GetFolderNodeForBookmarkNode(parent);
+ if (!folder_node) {
+ NOTREACHED();
+ return;
+ }
+ tree_view_->ExpandAll(folder_node);
+}
+
BookmarkNode* BookmarkManagerView::GetSelectedFolder() {
return tree_view_->GetSelectedBookmarkNode();
}
@@ -339,8 +412,62 @@ void BookmarkManagerView::RunMenu(views::View* source,
// TODO(glen): when you change the buttons around and what not, futz with
// this to make it look good. If you end up keeping padding numbers make them
// constants.
- ShowMenu(hwnd, pt.x - source->width() + 5, pt.y + 2,
- BookmarkContextMenu::BOOKMARK_MANAGER_ORGANIZE_MENU);
+ if (!GetBookmarkModel()->IsLoaded())
+ return;
+
+ if (source->GetID() == kOrganizeMenuButtonID) {
+ ShowMenu(hwnd, pt.x - source->width() + 5, pt.y + 2,
+ BookmarkContextMenu::BOOKMARK_MANAGER_ORGANIZE_MENU);
+ } else if (source->GetID() == kToolsMenuButtonID) {
+ ShowToolsMenu(hwnd, pt.x - source->width() + 5, pt.y + 2);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void BookmarkManagerView::ExecuteCommand(int id) {
+ switch (id) {
+ case IDS_BOOKMARK_MANAGER_IMPORT_MENU:
+ UserMetrics::RecordAction(L"BookmarkManager_Import", profile_);
+ ShowImportBookmarksFileChooser();
+ break;
+
+ case IDS_BOOKMARK_MANAGER_EXPORT_MENU:
+ UserMetrics::RecordAction(L"BookmarkManager_Export", profile_);
+ ShowExportBookmarksFileChooser();
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void BookmarkManagerView::FileSelected(const std::wstring& path,
+ void* params) {
+ int id = reinterpret_cast<int>(params);
+ if (id == IDS_BOOKMARK_MANAGER_IMPORT_MENU) {
+ // ImporterHost is ref counted and will delete itself when done.
+ ImporterHost* host = new ImporterHost();
+ ProfileInfo profile_info;
+ profile_info.browser_type = BOOKMARKS_HTML;
+ profile_info.source_path = path;
+ StartImportingWithUI(GetContainer()->GetHWND(), FAVORITES, host,
+ profile_info, profile_,
+ new ImportObserverImpl(profile()), false);
+ } else if (id == IDS_BOOKMARK_MANAGER_EXPORT_MENU) {
+ if (g_browser_process->io_thread()) {
+ bookmark_html_writer::WriteBookmarks(
+ g_browser_process->io_thread()->message_loop(), GetBookmarkModel(),
+ path);
+ }
+ } else {
+ NOTREACHED();
+ }
+}
+
+void BookmarkManagerView::FileSelectionCanceled(void* params) {
+ select_file_dialog_ = NULL;
}
BookmarkTableModel* BookmarkManagerView::CreateSearchTableModel() {
@@ -456,3 +583,40 @@ void BookmarkManagerView::ShowMenu(
menu.RunMenuAt(x, y);
}
}
+
+void BookmarkManagerView::ShowToolsMenu(HWND host, int x, int y) {
+ views::MenuItemView menu(this);
+ menu.AppendMenuItemWithLabel(
+ IDS_BOOKMARK_MANAGER_IMPORT_MENU,
+ l10n_util::GetString(IDS_BOOKMARK_MANAGER_IMPORT_MENU));
+ menu.AppendMenuItemWithLabel(
+ IDS_BOOKMARK_MANAGER_EXPORT_MENU,
+ l10n_util::GetString(IDS_BOOKMARK_MANAGER_EXPORT_MENU));
+ menu.RunMenuAt(GetContainer()->GetHWND(), gfx::Rect(x, y, 0, 0),
+ views::MenuItemView::TOPLEFT, true);
+}
+
+void BookmarkManagerView::ShowImportBookmarksFileChooser() {
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+
+ // TODO(sky): need a textual description here once we can add new
+ // strings.
+ std::wstring filter_string(L"*.html\0*.html\0\0");
+ select_file_dialog_ = SelectFileDialog::Create(this);
+ select_file_dialog_->SelectFile(
+ SelectFileDialog::SELECT_OPEN_FILE, std::wstring(), std::wstring(),
+ filter_string, GetContainer()->GetHWND(),
+ reinterpret_cast<void*>(IDS_BOOKMARK_MANAGER_IMPORT_MENU));
+}
+
+void BookmarkManagerView::ShowExportBookmarksFileChooser() {
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+
+ select_file_dialog_ = SelectFileDialog::Create(this);
+ select_file_dialog_->SelectFile(
+ SelectFileDialog::SELECT_SAVEAS_FILE, std::wstring(), std::wstring(),
+ std::wstring(), GetContainer()->GetHWND(),
+ reinterpret_cast<void*>(IDS_BOOKMARK_MANAGER_EXPORT_MENU));
+}
diff --git a/chrome/browser/views/bookmark_manager_view.h b/chrome/browser/views/bookmark_manager_view.h
index 8a28eac..0aa83dd 100644
--- a/chrome/browser/views/bookmark_manager_view.h
+++ b/chrome/browser/views/bookmark_manager_view.h
@@ -5,9 +5,11 @@
#ifndef CHROME_BROWSER_VIEWS_BOOKMARK_MANAGER_VIEW_H_
#define CHROME_BROWSER_VIEWS_BOOKMARK_MANAGER_VIEW_H_
+#include "base/ref_counted.h"
#include "base/task.h"
#include "chrome/browser/bookmarks/bookmark_context_menu.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/shell_dialogs.h"
#include "chrome/views/table_view.h"
#include "chrome/views/text_field.h"
#include "chrome/views/tree_view.h"
@@ -39,7 +41,9 @@ class BookmarkManagerView : public views::View,
public views::TextField::Controller,
public BookmarkModelObserver,
public views::ContextMenuController,
- public views::ViewMenuDelegate {
+ public views::ViewMenuDelegate,
+ public views::MenuDelegate,
+ public SelectFileDialog::Listener {
public:
explicit BookmarkManagerView(Profile* profile);
virtual ~BookmarkManagerView();
@@ -56,6 +60,9 @@ class BookmarkManagerView : public views::View,
// selected and node is selected in the table.
void SelectInTree(BookmarkNode* node);
+ // Expands all the children of the selected folder.
+ void ExpandAll(BookmarkNode* node);
+
// Returns the selected folder, which may be null.
BookmarkNode* GetSelectedFolder();
@@ -137,6 +144,13 @@ class BookmarkManagerView : public views::View,
// ViewMenuDelegate.
virtual void RunMenu(views::View* source, const CPoint& pt, HWND hwnd);
+ // MenuDelegate.
+ virtual void ExecuteCommand(int id);
+
+ // SelectFileDialog::Listener.
+ virtual void FileSelected(const std::wstring& path, void* params);
+ virtual void FileSelectionCanceled(void* params);
+
// Creates the table model to use when searching. This returns NULL if there
// is no search text.
BookmarkTableModel* CreateSearchTableModel();
@@ -167,6 +181,14 @@ class BookmarkManagerView : public views::View,
int y,
BookmarkContextMenu::ConfigurationType config);
+ // Shows the tools menu.
+ void ShowToolsMenu(HWND host, int x, int y);
+
+ // Shows the import/export file chooser. These invoke
+ // FileSelected/FileSelectionCanceled when done.
+ void ShowImportBookmarksFileChooser();
+ void ShowExportBookmarksFileChooser();
+
Profile* profile_;
BookmarkTableView* table_view_;
BookmarkFolderTreeView* tree_view_;
@@ -175,6 +197,9 @@ class BookmarkManagerView : public views::View,
views::TextField* search_tf_;
views::SingleSplitView* split_view_;
+ // Import/export file dialog.
+ scoped_refptr<SelectFileDialog> select_file_dialog_;
+
// Factory used for delaying search.
ScopedRunnableMethodFactory<BookmarkManagerView> search_factory_;
diff --git a/chrome/browser/views/importing_progress_view.cc b/chrome/browser/views/importing_progress_view.cc
index 3d759ef..783a83c 100644
--- a/chrome/browser/views/importing_progress_view.cc
+++ b/chrome/browser/views/importing_progress_view.cc
@@ -22,14 +22,13 @@ ImportingProgressView::ImportingProgressView(const std::wstring& source_name,
int16 items,
ImporterHost* coordinator,
ImportObserver* observer,
- HWND parent_window)
+ HWND parent_window,
+ bool bookmarks_import)
: state_bookmarks_(new views::CheckmarkThrobber),
state_searches_(new views::CheckmarkThrobber),
state_passwords_(new views::CheckmarkThrobber),
state_history_(new views::CheckmarkThrobber),
state_cookies_(new views::CheckmarkThrobber),
- label_info_(new views::Label(l10n_util::GetStringF(
- IDS_IMPORT_PROGRESS_INFO, source_name))),
label_bookmarks_(new views::Label(
l10n_util::GetString(IDS_IMPORT_PROGRESS_STATUS_BOOKMARKS))),
label_searches_(new views::Label(
@@ -44,7 +43,12 @@ ImportingProgressView::ImportingProgressView(const std::wstring& source_name,
coordinator_(coordinator),
import_observer_(observer),
items_(items),
- importing_(true) {
+ importing_(true),
+ bookmarks_import_(bookmarks_import) {
+ std::wstring info_text = bookmarks_import ?
+ l10n_util::GetString(IDS_IMPORT_BOOKMARKS) :
+ l10n_util::GetStringF(IDS_IMPORT_PROGRESS_INFO, source_name);
+ label_info_ = new views::Label(info_text);
coordinator_->SetObserver(this);
label_info_->SetMultiLine(true);
label_info_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
@@ -78,6 +82,17 @@ ImportingProgressView::~ImportingProgressView() {
RemoveChildView(label_passwords_.get());
RemoveChildView(label_history_.get());
RemoveChildView(label_cookies_.get());
+
+ if (importing_) {
+ // We're being deleted while importing, clean up state so that the importer
+ // doesn't have a reference to us and cancel the import. We can get here
+ // if our parent window is closed, which closes our window and deletes us.
+ importing_ = false;
+ coordinator_->SetObserver(NULL);
+ coordinator_->Cancel();
+ if (import_observer_)
+ import_observer_->ImportComplete();
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -217,6 +232,11 @@ void ImportingProgressView::InitControlLayout() {
const int single_column_view_set_id = 0;
ColumnSet* column_set = layout->AddColumnSet(single_column_view_set_id);
+ if (bookmarks_import_) {
+ column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
+ GridLayout::FIXED, ps.width(), 0);
+ column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
+ }
column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
GridLayout::USE_PREF, 0, 0);
const int double_column_view_set_id = 1;
@@ -230,10 +250,12 @@ void ImportingProgressView::InitControlLayout() {
column_set->AddPaddingColumn(0, kUnrelatedControlLargeHorizontalSpacing);
layout->StartRow(0, single_column_view_set_id);
+ if (bookmarks_import_)
+ layout->AddView(state_bookmarks_.get());
layout->AddView(label_info_);
layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing);
- if (items_ & FAVORITES) {
+ if (items_ & FAVORITES && !bookmarks_import_) {
layout->StartRow(0, double_column_view_set_id);
layout->AddView(state_bookmarks_.get());
layout->AddView(label_bookmarks_.get());
@@ -277,7 +299,8 @@ void StartImportingWithUI(HWND parent_window,
bool first_run) {
DCHECK(items != 0);
ImportingProgressView* v = new ImportingProgressView(
- source_profile.description, items, coordinator, observer, parent_window);
+ source_profile.description, items, coordinator, observer, parent_window,
+ source_profile.browser_type == BOOKMARKS_HTML);
views::Window* window =
views::Window::CreateChromeWindow(parent_window, gfx::Rect(), v);
diff --git a/chrome/browser/views/importing_progress_view.h b/chrome/browser/views/importing_progress_view.h
index c7f97c0..61ec12f 100644
--- a/chrome/browser/views/importing_progress_view.h
+++ b/chrome/browser/views/importing_progress_view.h
@@ -19,11 +19,15 @@ class ImportingProgressView : public views::View,
public views::DialogDelegate,
public ImporterHost::Observer {
public:
+ // |items| is a bitmask of ImportItems being imported.
+ // |bookmark_import| is true if we're importing bookmarks from a
+ // bookmarks.html file.
ImportingProgressView(const std::wstring& source_name,
int16 items,
ImporterHost* coordinator,
ImportObserver* observer,
- HWND parent_window);
+ HWND parent_window,
+ bool bookmarks_import);
virtual ~ImportingProgressView();
protected:
@@ -79,6 +83,9 @@ class ImportingProgressView : public views::View,
// True if the import operation is in progress.
bool importing_;
+ // Are we importing a bookmarks.html file?
+ bool bookmarks_import_;
+
DISALLOW_EVIL_CONSTRUCTORS(ImportingProgressView);
};
diff --git a/chrome/browser/views/shell_dialogs.cc b/chrome/browser/views/shell_dialogs.cc
index b2dbf1c..feadbed 100644
--- a/chrome/browser/views/shell_dialogs.cc
+++ b/chrome/browser/views/shell_dialogs.cc
@@ -192,7 +192,9 @@ class SelectFileDialogImpl : public SelectFileDialog,
// SelectFileDialog implementation:
virtual void SelectFile(Type type, const std::wstring& title,
- const std::wstring& default_path, HWND owning_hwnd,
+ const std::wstring& default_path,
+ const std::wstring& filter,
+ HWND owning_hwnd,
void* params);
virtual bool IsRunning(HWND owning_hwnd) const;
virtual void ListenerDestroyed();
@@ -203,6 +205,7 @@ class SelectFileDialogImpl : public SelectFileDialog,
void ExecuteSelectFile(Type type,
const std::wstring& title,
const std::wstring& default_path,
+ const std::wstring& filter,
RunState run_state,
void* params);
@@ -225,6 +228,7 @@ class SelectFileDialogImpl : public SelectFileDialog,
// Runs an Open file dialog box, with similar semantics for input paramaters
// as RunSelectFolderDialog.
bool RunOpenFileDialog(const std::wstring& title,
+ const std::wstring& filters,
HWND owner,
std::wstring* path);
@@ -245,12 +249,13 @@ SelectFileDialogImpl::~SelectFileDialogImpl() {
void SelectFileDialogImpl::SelectFile(Type type,
const std::wstring& title,
const std::wstring& default_path,
+ const std::wstring& filter,
HWND owner,
void* params) {
RunState run_state = BeginRun(owner);
run_state.dialog_thread->message_loop()->PostTask(FROM_HERE,
NewRunnableMethod(this, &SelectFileDialogImpl::ExecuteSelectFile, type,
- title, default_path, run_state, params));
+ title, default_path, filter, run_state, params));
}
bool SelectFileDialogImpl::IsRunning(HWND owning_hwnd) const {
@@ -263,11 +268,13 @@ void SelectFileDialogImpl::ListenerDestroyed() {
listener_ = NULL;
}
-void SelectFileDialogImpl::ExecuteSelectFile(Type type,
- const std::wstring& title,
- const std::wstring& default_path,
- RunState run_state,
- void* params) {
+void SelectFileDialogImpl::ExecuteSelectFile(
+ Type type,
+ const std::wstring& title,
+ const std::wstring& default_path,
+ const std::wstring& filter,
+ RunState run_state,
+ void* params) {
std::wstring path = default_path;
bool success = false;
if (type == SELECT_FOLDER) {
@@ -276,7 +283,7 @@ void SelectFileDialogImpl::ExecuteSelectFile(Type type,
success = win_util::SaveFileAs(run_state.owner, default_path, &path);
DisableOwner(run_state.owner);
} else if (type == SELECT_OPEN_FILE) {
- success = RunOpenFileDialog(title, run_state.owner, &path);
+ success = RunOpenFileDialog(title, filter, run_state.owner, &path);
}
if (success) {
@@ -331,9 +338,11 @@ bool SelectFileDialogImpl::RunSelectFolderDialog(const std::wstring& title,
return false;
}
-bool SelectFileDialogImpl::RunOpenFileDialog(const std::wstring& title,
- HWND owner,
- std::wstring* path) {
+bool SelectFileDialogImpl::RunOpenFileDialog(
+ const std::wstring& title,
+ const std::wstring& filter,
+ HWND owner,
+ std::wstring* path) {
OPENFILENAME ofn;
// We must do this otherwise the ofn's FlagsEx may be initialized to random
// junk in release builds which can cause the Places Bar not to show up!
@@ -350,8 +359,9 @@ bool SelectFileDialogImpl::RunOpenFileDialog(const std::wstring& title,
// without having to close Chrome first.
ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
- // TODO(beng): (http://b/issue?id=1126563) edit the filter options in the
- // dropdown list.
+ if (!filter.empty()) {
+ ofn.lpstrFilter = filter.c_str();
+ }
bool success = !!GetOpenFileName(&ofn);
DisableOwner(owner);
if (success)
@@ -527,4 +537,3 @@ void SelectFontDialogImpl::FontNotSelected(void* params, RunState run_state) {
SelectFontDialog* SelectFontDialog::Create(Listener* listener) {
return new SelectFontDialogImpl(listener);
}
-
diff --git a/chrome/test/unit/unit_tests.scons b/chrome/test/unit/unit_tests.scons
index df137a2..3129c16 100644
--- a/chrome/test/unit/unit_tests.scons
+++ b/chrome/test/unit/unit_tests.scons
@@ -146,6 +146,7 @@ if env['PLATFORM'] == 'win32':
'$CHROME_DIR/browser/bookmarks/bookmark_context_menu_test.cc',
'$CHROME_DIR/browser/bookmarks/bookmark_drag_data_unittest.cc',
'$CHROME_DIR/browser/bookmarks/bookmark_folder_tree_model_unittest.cc',
+ '$CHROME_DIR/browser/bookmarks/bookmark_html_writer_unittest.cc',
'$CHROME_DIR/browser/bookmarks/bookmark_model_unittest.cc',
'$CHROME_DIR/browser/bookmarks/bookmark_table_model_unittest.cc',
'$CHROME_DIR/browser/cache_manager_host_unittest.cc',
diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj
index 850fc8a..d39295b 100644
--- a/chrome/test/unit/unittests.vcproj
+++ b/chrome/test/unit/unittests.vcproj
@@ -231,6 +231,14 @@
</File>
</Filter>
<Filter
+ Name="TestBookmarkHTMLWriter"
+ >
+ <File
+ RelativePath="..\..\browser\bookmarks\bookmark_html_writer_unittest.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
Name="TestGoogleURLTracker"
>
<File
diff --git a/chrome/views/tree_view.cc b/chrome/views/tree_view.cc
index 8987042..913b845 100644
--- a/chrome/views/tree_view.cc
+++ b/chrome/views/tree_view.cc
@@ -157,6 +157,18 @@ void TreeView::ExpandAll() {
ExpandAll(model_->GetRoot());
}
+void TreeView::ExpandAll(TreeModelNode* node) {
+ DCHECK(node);
+ // Expand the node.
+ if (node != model_->GetRoot() || root_shown_)
+ TreeView_Expand(tree_view_, GetNodeDetails(node)->tree_item, TVE_EXPAND);
+ // And recursively expand all the children.
+ for (int i = model_->GetChildCount(node) - 1; i >= 0; --i) {
+ TreeModelNode* child = model_->GetChild(node, i);
+ ExpandAll(child);
+ }
+}
+
bool TreeView::IsExpanded(TreeModelNode* node) {
TreeModelNode* parent = model_->GetParent(node);
if (!parent)
@@ -470,18 +482,6 @@ HTREEITEM TreeView::GetTreeItemForNode(TreeModelNode* node) {
return details ? details->tree_item : NULL;
}
-void TreeView::ExpandAll(TreeModelNode* node) {
- DCHECK(node);
- // Expand the node.
- if (node != model_->GetRoot() || root_shown_)
- TreeView_Expand(tree_view_, GetNodeDetails(node)->tree_item, TVE_EXPAND);
- // And recursively expand all the children.
- for (int i = model_->GetChildCount(node) - 1; i >= 0; --i) {
- TreeModelNode* child = model_->GetChild(node, i);
- ExpandAll(child);
- }
-}
-
void TreeView::DeleteRootItems() {
HTREEITEM root = TreeView_GetRoot(tree_view_);
if (root) {
diff --git a/chrome/views/tree_view.h b/chrome/views/tree_view.h
index fb3f50e..d0d083e 100644
--- a/chrome/views/tree_view.h
+++ b/chrome/views/tree_view.h
@@ -147,6 +147,10 @@ class TreeView : public NativeControl, TreeModelObserver {
// Convenience to expand ALL nodes in the tree.
void ExpandAll();
+ // Invoked from ExpandAll(). Expands the supplied node and recursively
+ // invokes itself with all children.
+ void ExpandAll(TreeModelNode* node);
+
// Returns true if the specified node is expanded.
bool IsExpanded(TreeModelNode* node);
@@ -253,10 +257,6 @@ class TreeView : public NativeControl, TreeModelObserver {
bool loaded_children;
};
- // Invoked from ExpandAll(). Expands the supplied node and recursively
- // invokes itself with all children.
- void ExpandAll(TreeModelNode* node);
-
// Deletes the root items from the treeview. This is used when the model
// changes.
void DeleteRootItems();
diff --git a/net/base/escape.h b/net/base/escape.h
index 90614f5..14f46c6 100644
--- a/net/base/escape.h
+++ b/net/base/escape.h
@@ -30,6 +30,7 @@ void AppendEscapedCharForHTML(char c, std::string* output);
// Escape chars that might cause this text to be interpretted as HTML tags.
std::string EscapeForHTML(const std::string& text);
+std::wstring EscapeForHTML(const std::wstring& text);
// Unescaping ------------------------------------------------------------------