summaryrefslogtreecommitdiffstats
path: root/chrome/browser/importer
diff options
context:
space:
mode:
authorbrg@chromium.com <brg@chromium.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-16 22:59:02 +0000
committerbrg@chromium.com <brg@chromium.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-16 22:59:02 +0000
commit9b7ef69113c8a5c7eb6c1e51af506547c32d6fe4 (patch)
tree6abfdc143b6050a4b0d480baed150260dbdade57 /chrome/browser/importer
parent6256397c0cbb5d88e47676a7414f71669ddc638a (diff)
downloadchromium_src-9b7ef69113c8a5c7eb6c1e51af506547c32d6fe4.zip
chromium_src-9b7ef69113c8a5c7eb6c1e51af506547c32d6fe4.tar.gz
chromium_src-9b7ef69113c8a5c7eb6c1e51af506547c32d6fe4.tar.bz2
Re-introducing the toolbar importer code.This is the same code that was removed in CL http://codereview.chromium.org/18550.The UI will be reworked in a CL immediately following review/submission of this code.Please contact Nick Baum for information regarding this change.
Review URL: http://codereview.chromium.org/66065 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13895 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/importer')
-rw-r--r--chrome/browser/importer/importer.cc44
-rw-r--r--chrome/browser/importer/importer.h2
-rw-r--r--chrome/browser/importer/toolbar_importer.cc592
-rw-r--r--chrome/browser/importer/toolbar_importer.h169
-rw-r--r--chrome/browser/importer/toolbar_importer_unittest.cc453
5 files changed, 1260 insertions, 0 deletions
diff --git a/chrome/browser/importer/importer.cc b/chrome/browser/importer/importer.cc
index 6e29b59..7710c83 100644
--- a/chrome/browser/importer/importer.cc
+++ b/chrome/browser/importer/importer.cc
@@ -22,6 +22,7 @@
#if defined(OS_WIN)
#include "chrome/browser/importer/ie_importer.h"
#endif
+#include "chrome/browser/importer/toolbar_importer.h"
#include "chrome/browser/search_engines/template_url_model.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/tab_contents/site_instance.h"
@@ -512,6 +513,32 @@ void ImporterHost::StartImportSettings(const ProfileInfo& profile_info,
}
}
+ #if defined(OS_WIN)
+ // For google toolbar import, we need the user to log in and store their GAIA
+ // credentials.
+ if (profile_info.browser_type == GOOGLE_TOOLBAR5) {
+ if (!toolbar_importer_utils::IsGoogleGAIACookieInstalled()) {
+ win_util::MessageBox(
+ NULL,
+ l10n_util::GetString(IDS_IMPORTER_GOOGLE_LOGIN_TEXT).c_str(),
+ L"",
+ MB_OK | MB_TOPMOST);
+
+ GURL url("https://www.google.com/accounts/ServiceLogin");
+ BrowsingInstance* instance = new BrowsingInstance(writer_->GetProfile());
+ SiteInstance* site = instance->GetSiteInstanceForURL(url);
+ Browser* browser = BrowserList::GetLastActive();
+ browser->AddTabWithURL(url, GURL(), PageTransition::TYPED, true, -1,
+ site);
+
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &ImporterHost::OnLockViewEnd, false));
+
+ is_source_readable_ = false;
+ }
+ }
+#endif
+
// BookmarkModel should be loaded before adding IE favorites. So we observe
// the BookmarkModel if needed, and start the task after it has been loaded.
if ((items & FAVORITES) && !writer_->BookmarkModelIsLoaded()) {
@@ -582,6 +609,8 @@ Importer* ImporterHost::CreateImporterByType(ProfileType type) {
return new Firefox2Importer();
case FIREFOX3:
return new Firefox3Importer();
+ case GOOGLE_TOOLBAR5:
+ return new Toolbar5Importer();
}
NOTREACHED();
return NULL;
@@ -612,6 +641,8 @@ void ImporterHost::DetectSourceProfiles() {
DetectIEProfiles();
DetectFirefoxProfiles();
}
+ // TODO(brg) : Current UI requires win_util.
+ DetectGoogleToolbarProfiles();
#else
DetectFirefoxProfiles();
#endif
@@ -707,3 +738,16 @@ void ImporterHost::DetectFirefoxProfiles() {
source_profiles_.push_back(firefox);
}
}
+
+void ImporterHost::DetectGoogleToolbarProfiles() {
+ if (!FirstRun::IsChromeFirstRun()) {
+ ProfileInfo* google_toolbar = new ProfileInfo();
+ google_toolbar->browser_type = GOOGLE_TOOLBAR5;
+ google_toolbar->description = l10n_util::GetString(
+ IDS_IMPORT_FROM_GOOGLE_TOOLBAR);
+ google_toolbar->source_path.clear();
+ google_toolbar->app_path.clear();
+ google_toolbar->services_supported = FAVORITES;
+ source_profiles_.push_back(google_toolbar);
+ }
+}
diff --git a/chrome/browser/importer/importer.h b/chrome/browser/importer/importer.h
index f4f4f07..46ce03e 100644
--- a/chrome/browser/importer/importer.h
+++ b/chrome/browser/importer/importer.h
@@ -32,6 +32,7 @@ enum ProfileType {
#endif
FIREFOX2,
FIREFOX3,
+ GOOGLE_TOOLBAR5,
// Identifies a 'bookmarks.html' file.
BOOKMARKS_HTML
};
@@ -284,6 +285,7 @@ class ImporterHost : public base::RefCounted<ImporterHost>,
void DetectIEProfiles();
#endif
void DetectFirefoxProfiles();
+ void DetectGoogleToolbarProfiles();
// The list of profiles with the default one first.
std::vector<ProfileInfo*> source_profiles_;
diff --git a/chrome/browser/importer/toolbar_importer.cc b/chrome/browser/importer/toolbar_importer.cc
new file mode 100644
index 0000000..22594e6
--- /dev/null
+++ b/chrome/browser/importer/toolbar_importer.cc
@@ -0,0 +1,592 @@
+// 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/importer/toolbar_importer.h"
+
+#include <limits>
+
+#include "base/string_util.h"
+#include "base/rand_util.h"
+#include "chrome/browser/first_run.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/libxml_utils.h"
+#include "grit/generated_resources.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/data_url.h"
+#include "net/url_request/url_request_context.h"
+
+//
+// ToolbarImporterUtils
+//
+static const char* kGoogleDomainUrl = "http://.google.com/";
+static const wchar_t kSplitStringToken = L';';
+static const char* kGoogleDomainSecureCookieId = "SID=";
+
+bool toolbar_importer_utils::IsGoogleGAIACookieInstalled() {
+ URLRequestContext* context = Profile::GetDefaultRequestContext();
+ net::CookieMonster* store = context->cookie_store();
+ GURL url(kGoogleDomainUrl);
+ net::CookieMonster::CookieOptions options;
+ options.set_include_httponly(); // The SID cookie might be httponly.
+ std::string cookies = store->GetCookiesWithOptions(url, options);
+ std::vector<std::string> cookie_list;
+ SplitString(cookies, kSplitStringToken, &cookie_list);
+ for (std::vector<std::string>::iterator current = cookie_list.begin();
+ current != cookie_list.end();
+ ++current) {
+ size_t position = (*current).find(kGoogleDomainSecureCookieId);
+ if (0 == position)
+ return true;
+ }
+ return false;
+}
+
+//
+// Toolbar5Importer
+//
+const char Toolbar5Importer::kXmlApiReplyXmlTag[] = "xml_api_reply";
+const char Toolbar5Importer::kBookmarksXmlTag[] = "bookmarks";
+const char Toolbar5Importer::kBookmarkXmlTag[] = "bookmark";
+const char Toolbar5Importer::kTitleXmlTag[] = "title";
+const char Toolbar5Importer::kUrlXmlTag[] = "url";
+const char Toolbar5Importer::kTimestampXmlTag[] = "timestamp";
+const char Toolbar5Importer::kLabelsXmlTag[] = "labels";
+const char Toolbar5Importer::kLabelsXmlCloseTag[] = "/labels";
+const char Toolbar5Importer::kLabelXmlTag[] = "label";
+const char Toolbar5Importer::kAttributesXmlTag[] = "attributes";
+
+const char Toolbar5Importer::kRandomNumberToken[] = "{random_number}";
+const char Toolbar5Importer::kAuthorizationToken[] = "{auth_token}";
+const char Toolbar5Importer::kAuthorizationTokenPrefix[] = "/*";
+const char Toolbar5Importer::kAuthorizationTokenSuffix[] = "*/";
+const char Toolbar5Importer::kMaxNumToken[] = "{max_num}";
+const char Toolbar5Importer::kMaxTimestampToken[] = "{max_timestamp}";
+
+const char Toolbar5Importer::kT5AuthorizationTokenUrl[] =
+ "http://www.google.com/notebook/token?zx={random_number}";
+const char Toolbar5Importer::kT5FrontEndUrlTemplate[] =
+ "http://www.google.com/notebook/toolbar?cmd=list&tok={auth_token}&"
+ "num={max_num}&min={max_timestamp}&all=0&zx={random_number}";
+
+// Importer methods.
+
+// The constructor should set the initial state to NOT_USED.
+Toolbar5Importer::Toolbar5Importer()
+ : writer_(NULL),
+ state_(NOT_USED),
+ items_to_import_(NONE),
+ token_fetcher_(NULL),
+ data_fetcher_(NULL) {
+}
+
+// The destructor insures that the fetchers are currently not being used, as
+// their thread-safe implementation requires that they are cancelled from the
+// thread in which they were constructed.
+Toolbar5Importer::~Toolbar5Importer() {
+ DCHECK(!token_fetcher_);
+ DCHECK(!data_fetcher_);
+}
+
+void Toolbar5Importer::StartImport(ProfileInfo profile_info,
+ uint16 items,
+ ProfileWriter* writer,
+ MessageLoop* delagate_loop,
+ ImporterHost* host) {
+ DCHECK(writer);
+ DCHECK(host);
+
+ importer_host_ = host;
+ delagate_loop_ = delagate_loop;
+ writer_ = writer;
+ items_to_import_ = items;
+ state_ = INITIALIZED;
+
+ NotifyStarted();
+ ContinueImport();
+}
+
+// The public cancel method serves two functions, as a callback from the UI
+// as well as an internal callback in case of cancel. An internal callback
+// is required since the URLFetcher must be destroyed from the thread it was
+// created.
+void Toolbar5Importer::Cancel() {
+ // In the case when the thread is not importing messages we are to
+ // cancel as soon as possible.
+ Importer::Cancel();
+
+ // If we are conducting network operations, post a message to the importer
+ // thread for synchronization.
+ if (NULL != delagate_loop_) {
+ if (delagate_loop_ != MessageLoop::current()) {
+ delagate_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &Toolbar5Importer::Cancel));
+ } else {
+ EndImport();
+ }
+ }
+}
+
+void Toolbar5Importer::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data) {
+ if (cancelled()) {
+ EndImport();
+ return;
+ }
+
+ if (200 != response_code) { // HTTP/Ok
+ // Cancelling here will update the UI and bypass the rest of bookmark
+ // import.
+ EndImportBookmarks();
+ return;
+ }
+
+ switch (state_) {
+ case GET_AUTHORIZATION_TOKEN:
+ GetBookmarkDataFromServer(data);
+ break;
+ case GET_BOOKMARKS:
+ GetBookmarksFromServerDataResponse(data);
+ break;
+ default:
+ NOTREACHED() << "Invalid state.";
+ EndImportBookmarks();
+ break;
+ }
+}
+
+void Toolbar5Importer::ContinueImport() {
+ DCHECK((items_to_import_ == FAVORITES) || (items_to_import_ == NONE)) <<
+ "The items requested are not supported";
+
+ // The order here is important. Each Begin... will clear the flag
+ // of its item before its task finishes and re-enters this method.
+ if (NONE == items_to_import_) {
+ EndImport();
+ return;
+ }
+ if ((items_to_import_ & FAVORITES) && !cancelled()) {
+ items_to_import_ &= ~FAVORITES;
+ BeginImportBookmarks();
+ return;
+ }
+ // TODO(brg): Import history, autocomplete, other toolbar information
+ // in a future release.
+
+ // This code should not be reached, but gracefully handles the possibility
+ // that StartImport was called with unsupported items_to_import.
+ if (!cancelled())
+ EndImport();
+}
+
+void Toolbar5Importer::EndImport() {
+ if (state_ != DONE) {
+ state_ = DONE;
+ // By spec the fetchers must be destroyed within the same
+ // thread they are created. The importer is destroyed in the ui_thread
+ // so when we complete in the file_thread we destroy them first.
+ if (NULL != token_fetcher_) {
+ delete token_fetcher_;
+ token_fetcher_ = NULL;
+ }
+
+ if (NULL != data_fetcher_) {
+ delete data_fetcher_;
+ data_fetcher_ = NULL;
+ }
+
+ NotifyEnded();
+ }
+}
+
+void Toolbar5Importer::BeginImportBookmarks() {
+ NotifyItemStarted(FAVORITES);
+ GetAuthenticationFromServer();
+}
+
+void Toolbar5Importer::EndImportBookmarks() {
+ NotifyItemEnded(FAVORITES);
+ ContinueImport();
+}
+
+
+// Notebook front-end connection manager implementation follows.
+void Toolbar5Importer::GetAuthenticationFromServer() {
+ if (cancelled()) {
+ EndImport();
+ return;
+ }
+
+ // Authentication is a token string retrieved from the authentication server
+ // To access it we call the url below with a random number replacing the
+ // value in the string.
+ state_ = GET_AUTHORIZATION_TOKEN;
+
+ // Random number construction.
+ int random = base::RandInt(0, std::numeric_limits<int>::max());
+ std::string random_string = UintToString(random);
+
+ // Retrieve authorization token from the network.
+ std::string url_string(kT5AuthorizationTokenUrl);
+ url_string.replace(url_string.find(kRandomNumberToken),
+ arraysize(kRandomNumberToken) - 1,
+ random_string);
+ GURL url(url_string);
+
+ token_fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
+ token_fetcher_->set_request_context(Profile::GetDefaultRequestContext());
+ token_fetcher_->Start();
+}
+
+void Toolbar5Importer::GetBookmarkDataFromServer(const std::string& response) {
+ if (cancelled()) {
+ EndImport();
+ return;
+ }
+
+ state_ = GET_BOOKMARKS;
+
+ // Parse and verify the authorization token from the response.
+ std::string token;
+ if (!ParseAuthenticationTokenResponse(response, &token)) {
+ EndImportBookmarks();
+ return;
+ }
+
+ // Build the Toolbar FE connection string, and call the server for
+ // the xml blob. We must tag the connection string with a random number.
+ std::string conn_string = kT5FrontEndUrlTemplate;
+ int random = base::RandInt(0, std::numeric_limits<int>::max());
+ std::string random_string = UintToString(random);
+ conn_string.replace(conn_string.find(kRandomNumberToken),
+ arraysize(kRandomNumberToken) - 1,
+ random_string);
+ conn_string.replace(conn_string.find(kAuthorizationToken),
+ arraysize(kAuthorizationToken) - 1,
+ token);
+ GURL url(conn_string);
+
+ data_fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
+ data_fetcher_->set_request_context(Profile::GetDefaultRequestContext());
+ data_fetcher_->Start();
+}
+
+void Toolbar5Importer::GetBookmarksFromServerDataResponse(
+ const std::string& response) {
+ if (cancelled()) {
+ EndImport();
+ return;
+ }
+
+ state_ = PARSE_BOOKMARKS;
+
+ XmlReader reader;
+ if (reader.Load(response) && !cancelled()) {
+ // Construct Bookmarks
+ std::vector<ProfileWriter::BookmarkEntry> bookmarks;
+ if (ParseBookmarksFromReader(&reader, &bookmarks))
+ AddBookmarksToChrome(bookmarks);
+ }
+ EndImportBookmarks();
+}
+
+bool Toolbar5Importer::ParseAuthenticationTokenResponse(
+ const std::string& response,
+ std::string* token) {
+ DCHECK(token);
+
+ *token = response;
+ size_t position = token->find(kAuthorizationTokenPrefix);
+ if (0 != position)
+ return false;
+ token->replace(position, arraysize(kAuthorizationTokenPrefix) - 1, "");
+
+ position = token->find(kAuthorizationTokenSuffix);
+ if (token->size() != (position + (arraysize(kAuthorizationTokenSuffix) - 1)))
+ return false;
+ token->replace(position, arraysize(kAuthorizationTokenSuffix) - 1, "");
+
+ return true;
+}
+
+// Parsing
+bool Toolbar5Importer::ParseBookmarksFromReader(
+ XmlReader* reader,
+ std::vector<ProfileWriter::BookmarkEntry>* bookmarks) {
+ DCHECK(reader);
+ DCHECK(bookmarks);
+
+ // The XML blob returned from the server is described in the
+ // Toolbar-Notebook/Bookmarks Protocol document located at
+ // https://docs.google.com/a/google.com/Doc?docid=cgt3m7dr_24djt62m&hl=en
+ // We are searching for the section with structure
+ // <bookmarks><bookmark>...</bookmark><bookmark>...</bookmark></bookmarks>
+
+ // Locate the |bookmarks| blob.
+ if (!reader->SkipToElement())
+ return false;
+
+ if (!LocateNextTagByName(reader, kBookmarksXmlTag))
+ return false;
+
+ // Parse each |bookmark| blob
+ while (LocateNextTagWithStopByName(reader, kBookmarkXmlTag,
+ kBookmarksXmlTag)) {
+ ProfileWriter::BookmarkEntry bookmark_entry;
+ std::vector<BookmarkFolderType> folders;
+ if (ExtractBookmarkInformation(reader, &bookmark_entry, &folders)) {
+ // For each folder we create a new bookmark entry. Duplicates will
+ // be detected when we attempt to create the bookmark in the profile.
+ for (std::vector<BookmarkFolderType>::iterator folder = folders.begin();
+ folder != folders.end();
+ ++folder) {
+ bookmark_entry.path = *folder;
+ bookmarks->push_back(bookmark_entry);
+ }
+ }
+ }
+
+ if (0 == bookmarks->size())
+ return false;
+
+ return true;
+}
+
+bool Toolbar5Importer::LocateNextOpenTag(XmlReader* reader) {
+ DCHECK(reader);
+
+ while (!reader->SkipToElement()) {
+ if (!reader->Read())
+ return false;
+ }
+ return true;
+}
+
+bool Toolbar5Importer::LocateNextTagByName(XmlReader* reader,
+ const std::string& tag) {
+ DCHECK(reader);
+
+ // Locate the |tag| blob.
+ while (tag != reader->NodeName()) {
+ if (!reader->Read() || !LocateNextOpenTag(reader))
+ return false;
+ }
+ return true;
+}
+
+bool Toolbar5Importer::LocateNextTagWithStopByName(XmlReader* reader,
+ const std::string& tag,
+ const std::string& stop) {
+ DCHECK(reader);
+
+ DCHECK_NE(tag, stop);
+ // Locate the |tag| blob.
+ while (tag != reader->NodeName()) {
+ // Move to the next open tag.
+ if (!reader->Read() || !LocateNextOpenTag(reader))
+ return false;
+ // If we encounter the stop word return false.
+ if (stop == reader->NodeName())
+ return false;
+ }
+ return true;
+}
+
+bool Toolbar5Importer::ExtractBookmarkInformation(
+ XmlReader* reader,
+ ProfileWriter::BookmarkEntry* bookmark_entry,
+ std::vector<BookmarkFolderType>* bookmark_folders) {
+ DCHECK(reader);
+ DCHECK(bookmark_entry);
+ DCHECK(bookmark_folders);
+
+ // The following is a typical bookmark entry.
+ // The reader should be pointing to the <title> tag at the moment.
+ //
+ // <bookmark>
+ // <title>MyTitle</title>
+ // <url>http://www.sohu.com/</url>
+ // <timestamp>1153328691085181</timestamp>
+ // <id>N123nasdf239</id>
+ // <notebook_id>Bxxxxxxx</notebook_id> (for bookmarks, a special id is used)
+ // <section_id>Sxxxxxx</section_id>
+ // <has_highlight>0</has_highlight>
+ // <labels>
+ // <label>China</label>
+ // <label>^k</label> (if this special label is present, the note is deleted)
+ // </labels>
+ // <attributes>
+ // <attribute>
+ // <name>favicon_url</name>
+ // <value>http://www.sohu.com/favicon.ico</value>
+ // </attribute>
+ // <attribute>
+ // <name>favicon_timestamp</name>
+ // <value>1153328653</value>
+ // </attribute>
+ // <attribute>
+ // <name>notebook_name</name>
+ // <value>My notebook 0</value>
+ // </attribute>
+ // <attribute>
+ // <name>section_name</name>
+ // <value>My section 0</value>
+ // </attribute>
+ // </attributes>
+ // </bookmark>
+ //
+ // We parse the blob in order, title->url->timestamp etc. Any failure
+ // causes us to skip this bookmark.
+
+ if (!ExtractTitleFromXmlReader(reader, bookmark_entry))
+ return false;
+ if (!ExtractUrlFromXmlReader(reader, bookmark_entry))
+ return false;
+ if (!ExtractTimeFromXmlReader(reader, bookmark_entry))
+ return false;
+ if (!ExtractFoldersFromXmlReader(reader, bookmark_folders))
+ return false;
+
+ return true;
+}
+
+bool Toolbar5Importer::ExtractNamedValueFromXmlReader(XmlReader* reader,
+ const std::string& name,
+ std::string* buffer) {
+ DCHECK(reader);
+ DCHECK(buffer);
+
+ if (name != reader->NodeName())
+ return false;
+ if (!reader->ReadElementContent(buffer))
+ return false;
+ return true;
+}
+
+bool Toolbar5Importer::ExtractTitleFromXmlReader(
+ XmlReader* reader,
+ ProfileWriter::BookmarkEntry* entry) {
+ DCHECK(reader);
+ DCHECK(entry);
+
+ if (!LocateNextTagWithStopByName(reader, kTitleXmlTag, kUrlXmlTag))
+ return false;
+ std::string buffer;
+ if (!ExtractNamedValueFromXmlReader(reader, kTitleXmlTag, &buffer)) {
+ return false;
+ }
+ entry->title = UTF8ToWide(buffer);
+ return true;
+}
+
+bool Toolbar5Importer::ExtractUrlFromXmlReader(
+ XmlReader* reader,
+ ProfileWriter::BookmarkEntry* entry) {
+ DCHECK(reader);
+ DCHECK(entry);
+
+ if (!LocateNextTagWithStopByName(reader, kUrlXmlTag, kTimestampXmlTag))
+ return false;
+ std::string buffer;
+ if (!ExtractNamedValueFromXmlReader(reader, kUrlXmlTag, &buffer)) {
+ return false;
+ }
+ entry->url = GURL(buffer);
+ return true;
+}
+
+bool Toolbar5Importer::ExtractTimeFromXmlReader(
+ XmlReader* reader,
+ ProfileWriter::BookmarkEntry* entry) {
+ DCHECK(reader);
+ DCHECK(entry);
+ if (!LocateNextTagWithStopByName(reader, kTimestampXmlTag, kLabelsXmlTag))
+ return false;
+ std::string buffer;
+ if (!ExtractNamedValueFromXmlReader(reader, kTimestampXmlTag, &buffer)) {
+ return false;
+ }
+ int64 timestamp;
+ if (!StringToInt64(buffer, &timestamp)) {
+ return false;
+ }
+ entry->creation_time = base::Time::FromTimeT(timestamp);
+ return true;
+}
+
+bool Toolbar5Importer::ExtractFoldersFromXmlReader(
+ XmlReader* reader,
+ std::vector<BookmarkFolderType>* bookmark_folders) {
+ DCHECK(reader);
+ DCHECK(bookmark_folders);
+
+ // Read in the labels for this bookmark from the xml. There may be many
+ // labels for any one bookmark.
+ if (!LocateNextTagWithStopByName(reader, kLabelsXmlTag, kAttributesXmlTag))
+ return false;
+
+ // It is within scope to have an empty labels section, so we do not
+ // return false if the labels are empty.
+ if (!reader->Read() || !LocateNextOpenTag(reader))
+ return false;
+
+ std::vector<std::wstring> label_vector;
+ while (kLabelXmlTag == reader->NodeName()) {
+ std::string label_buffer;
+ if (!reader->ReadElementContent(&label_buffer)) {
+ label_buffer = "";
+ }
+ label_vector.push_back(UTF8ToWide(label_buffer));
+ LocateNextOpenTag(reader);
+ }
+
+ if (0 == label_vector.size()) {
+ if (!FirstRun::IsChromeFirstRun()) {
+ bookmark_folders->resize(1);
+ (*bookmark_folders)[0].push_back(
+ l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_GOOGLE_TOOLBAR));
+ }
+ return true;
+ }
+
+ // We will be making one bookmark folder for each label.
+ bookmark_folders->resize(label_vector.size());
+
+ for (size_t index = 0; index < label_vector.size(); ++index) {
+ // If this is the first run then we place favorites with no labels
+ // in the title bar. Else they are placed in the "Google Toolbar" folder.
+ if (!FirstRun::IsChromeFirstRun() || !label_vector[index].empty()) {
+ (*bookmark_folders)[index].push_back(
+ l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_GOOGLE_TOOLBAR));
+ }
+
+ // If the label and is in the form "xxx:yyy:zzz" this was created from an
+ // IE or Firefox folder. We undo the label creation and recreate the
+ // correct folder.
+ std::vector<std::wstring> folder_names;
+ SplitString(label_vector[index], L':', &folder_names);
+ (*bookmark_folders)[index].insert((*bookmark_folders)[index].end(),
+ folder_names.begin(), folder_names.end());
+ }
+
+ return true;
+}
+
+// Bookmark creation
+void Toolbar5Importer::AddBookmarksToChrome(
+ const std::vector<ProfileWriter::BookmarkEntry>& bookmarks) {
+ if (!bookmarks.empty() && !cancelled()) {
+ 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/importer/toolbar_importer.h b/chrome/browser/importer/toolbar_importer.h
new file mode 100644
index 0000000..24b5b28
--- /dev/null
+++ b/chrome/browser/importer/toolbar_importer.h
@@ -0,0 +1,169 @@
+// 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.
+
+// The functionality provided here allows the user to import their bookmarks
+// (favorites) from Google Toolbar.
+
+#ifndef CHROME_BROWSER_IMPORTER_TOOLBAR_IMPORTER_H_
+#define CHROME_BROWSER_IMPORTER_TOOLBAR_IMPORTER_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/importer/importer.h"
+#include "chrome/browser/net/url_fetcher.h"
+
+class XmlReader;
+
+// Currently the only configuration information we need is to check whether or
+// not the user currently has their GAIA cookie. This is done by the function
+// exposed through the ToolbarImportUtils namespace.
+namespace toolbar_importer_utils {
+bool IsGoogleGAIACookieInstalled();
+} // namespace toolbar_importer_utils
+
+// Toolbar5Importer is a class which exposes the functionality needed to
+// communicate with the Google Toolbar v5 front-end, negotiate the download of
+// Toolbar bookmarks, parse them, and install them on the client.
+// Toolbar5Importer should not have StartImport called more than once. Futher
+// if StartImport is called, then the class must not be destroyed until it
+// has either completed or Toolbar5Importer->Cancel() has been called.
+class Toolbar5Importer : public URLFetcher::Delegate, public Importer {
+ public:
+ Toolbar5Importer();
+ virtual ~Toolbar5Importer();
+
+ // Importer view calls this method to begin the process. The items parameter
+ // should only either be NONE or FAVORITES, since as of right now these are
+ // the only items this importer supports. This method provides implementation
+ // of Importer::StartImport.
+ virtual void StartImport(ProfileInfo profile_info,
+ uint16 items,
+ ProfileWriter* writer,
+ MessageLoop* delegate_loop,
+ ImporterHost* host);
+
+ // Importer view call this method when the user clicks the cancel button
+ // in the ImporterView UI. We need to post a message to our loop
+ // to cancel network retrieval.
+ virtual void Cancel();
+
+ // URLFetcher::Delegate method called back from the URLFetcher object.
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+
+ private:
+ FRIEND_TEST(Toolbar5ImporterTest, BookmarkParse);
+
+ // Internal states of the toolbar importer.
+ enum InternalStateEnum {
+ NOT_USED = -1,
+ INITIALIZED,
+ GET_AUTHORIZATION_TOKEN,
+ GET_BOOKMARKS,
+ PARSE_BOOKMARKS,
+ DONE
+ };
+
+ typedef std::vector<std::wstring> BookmarkFolderType;
+
+ // URLs for connecting to the toolbar front end are defined below.
+ static const char kT5AuthorizationTokenUrl[];
+ static const char kT5FrontEndUrlTemplate[];
+
+ // Token replacement tags are defined below.
+ static const char kRandomNumberToken[];
+ static const char kAuthorizationToken[];
+ static const char kAuthorizationTokenPrefix[];
+ static const char kAuthorizationTokenSuffix[];
+ static const char kMaxNumToken[];
+ static const char kMaxTimestampToken[];
+
+ // XML tag names are defined below.
+ static const char kXmlApiReplyXmlTag[];
+ static const char kBookmarksXmlTag[];
+ static const char kBookmarkXmlTag[];
+ static const char kTitleXmlTag[];
+ static const char kUrlXmlTag[];
+ static const char kTimestampXmlTag[];
+ static const char kLabelsXmlTag[];
+ static const char kLabelsXmlCloseTag[];
+ static const char kLabelXmlTag[];
+ static const char kAttributesXmlTag[];
+
+ // Flow control for asynchronous import is controlled by the methods below.
+ // ContinueImport is called back by each import action taken. BeginXXX
+ // and EndXXX are responsible for updating the state of the asynchronous
+ // import. EndImport is responsible for state cleanup and notifying the
+ // caller that import has completed.
+ void ContinueImport();
+ void EndImport();
+ void BeginImportBookmarks();
+ void EndImportBookmarks();
+
+ // Network I/O is done by the methods below. These three methods are called
+ // in the order provided. The last two are called back with the HTML
+ // response provided by the Toolbar server.
+ void GetAuthenticationFromServer();
+ void GetBookmarkDataFromServer(const std::string& response);
+ void GetBookmarksFromServerDataResponse(const std::string& response);
+
+ // XML Parsing is implemented with the methods below.
+ bool ParseAuthenticationTokenResponse(const std::string& response,
+ std::string* token);
+
+ static bool ParseBookmarksFromReader(
+ XmlReader* reader,
+ std::vector<ProfileWriter::BookmarkEntry>* bookmarks);
+
+ static bool LocateNextOpenTag(XmlReader* reader);
+ static bool LocateNextTagByName(XmlReader* reader, const std::string& tag);
+ static bool LocateNextTagWithStopByName(
+ XmlReader* reader,
+ const std::string& tag,
+ const std::string& stop);
+
+ static bool ExtractBookmarkInformation(
+ XmlReader* reader,
+ ProfileWriter::BookmarkEntry* bookmark_entry,
+ std::vector<BookmarkFolderType>* bookmark_folders);
+ static bool ExtractNamedValueFromXmlReader(XmlReader* reader,
+ const std::string& name,
+ std::string* buffer);
+ static bool ExtractTitleFromXmlReader(XmlReader* reader,
+ ProfileWriter::BookmarkEntry* entry);
+ static bool ExtractUrlFromXmlReader(XmlReader* reader,
+ ProfileWriter::BookmarkEntry* entry);
+ static bool ExtractTimeFromXmlReader(XmlReader* reader,
+ ProfileWriter::BookmarkEntry* entry);
+ static bool ExtractFoldersFromXmlReader(
+ XmlReader* reader,
+ std::vector<BookmarkFolderType>* bookmark_folders);
+
+ // Bookmark creation is done by the method below.
+ void AddBookmarksToChrome(
+ const std::vector<ProfileWriter::BookmarkEntry>& bookmarks);
+
+ // The writer used in this importer is stored in writer_.
+ ProfileWriter* writer_;
+
+ // Internal state is stored in state_.
+ InternalStateEnum state_;
+
+ // Bitmask of Importer::ImportItem is stored in items_to_import_.
+ uint16 items_to_import_;
+
+ // The fetchers need to be available to cancel the network call on user cancel
+ // hence they are stored as member variables.
+ URLFetcher* token_fetcher_;
+ URLFetcher* data_fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(Toolbar5Importer);
+};
+
+#endif // CHROME_BROWSER_IMPORTER_TOOLBAR_IMPORTER_H_
diff --git a/chrome/browser/importer/toolbar_importer_unittest.cc b/chrome/browser/importer/toolbar_importer_unittest.cc
new file mode 100644
index 0000000..7f9ec16
--- /dev/null
+++ b/chrome/browser/importer/toolbar_importer_unittest.cc
@@ -0,0 +1,453 @@
+// 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 <string>
+#include <vector>
+
+#include "chrome/browser/importer/importer.h"
+#include "chrome/browser/importer/toolbar_importer.h"
+#include "chrome/common/libxml_utils.h"
+#include "googleurl/src/gurl.h"
+
+
+namespace toolbar_importer_unittest {
+static const wchar_t* kTitle = L"MyTitle";
+static const char* kUrl = "http://www.google.com/";
+static const wchar_t* kFolder = L"Google";
+static const wchar_t* kFolder2 = L"Homepage";
+static const wchar_t* kFolderArray[3] = {L"Google", L"Search", L"Page"};
+
+static const wchar_t* kOtherTitle = L"MyOtherTitle";
+static const char* kOtherUrl = "http://www.google.com/mail";
+static const wchar_t* kOtherFolder = L"Mail";
+
+// Since the following is very dense to read I enumerate the test cases here.
+// 1. Correct bookmark structure with one label.
+// 2. Correct bookmark structure with no labels.
+// 3. Correct bookmark structure with two labels.
+// 4. Correct bookmark structure with a folder->label translation by toolbar.
+// 5. Correct bookmark structure with no favicon.
+// 6. Two correct bookmarks.
+// The following are error cases by removing sections from the xml:
+// 7. Empty string passed as xml.
+// 8. No <bookmarks> section in the xml.
+// 9. No <bookmark> section below the <bookmarks> section.
+// 10. No <title> in a <bookmark> section.
+// 11. No <url> in a <bookmark> section.
+// 12. No <timestamp> in a <bookmark> section.
+// 13. No <labels> in a <bookmark> section.
+static const char* kGoodBookmark =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kGoodBookmarkNoLabel =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kGoodBookmarkTwoLabels =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> <label>Homepage</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kGoodBookmarkFolderLabel =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google:Search:Page</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kGoodBookmarkNoFavicon =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kGoodBookmark2Items =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark>"
+ " <bookmark> "
+ "<title>MyOtherTitle</title> "
+ "<url>http://www.google.com/mail</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Mail</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name>"
+ "<value>http://www.google.com/mail/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1253328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark>"
+ "</bookmarks>";
+static const char* kEmptyString = "";
+static const char* kBadBookmarkNoBookmarks =
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kBadBookmarkNoBookmark =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kBadBookmarkNoTitle =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kBadBookmarkNoUrl =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kBadBookmarkNoTimestamp =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<labels> <label>Google</label> </labels> "
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+static const char* kBadBookmarkNoLabels =
+ "<?xml version=\"1.0\" ?> <xml_api_reply version=\"1\"> <bookmarks>"
+ " <bookmark> "
+ "<title>MyTitle</title> "
+ "<url>http://www.google.com/</url> "
+ "<timestamp>1153328691085181</timestamp> "
+ "<id>N123nasdf239</id> <notebook_id>Bxxxxxxx</notebook_id> "
+ "<section_id>Sxxxxxx</section_id> <has_highlight>0</has_highlight>"
+ "<attributes> "
+ "<attribute> "
+ "<name>favicon_url</name> <value>http://www.google.com/favicon.ico</value> "
+ "</attribute> "
+ "<attribute> "
+ "<name>favicon_timestamp</name> <value>1153328653</value> "
+ "</attribute> "
+ "<attribute> <name>notebook_name</name> <value>My notebook 0</value> "
+ "</attribute> "
+ "<attribute> <name>section_name</name> <value>My section 0 "
+ "</value> </attribute> </attributes> "
+ "</bookmark> </bookmarks>";
+} // namespace toolbar_importer_unittest
+
+// The parsing tests for Toolbar5Importer use the string above. For a
+// description of all the tests run please see the comments immediately before
+// the string constants above.
+TEST(Toolbar5ImporterTest, BookmarkParse) {
+ XmlReader reader;
+ std::string bookmark_xml;
+ std::vector<ProfileWriter::BookmarkEntry> bookmarks;
+
+ const GURL url(toolbar_importer_unittest::kUrl);
+ const GURL other_url(toolbar_importer_unittest::kOtherUrl);
+
+ // Test case 1 is parsing a basic bookmark with a single label.
+ bookmark_xml = toolbar_importer_unittest::kGoodBookmark;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_TRUE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ EXPECT_EQ(bookmarks.size(), (size_t) 1);
+ EXPECT_FALSE(bookmarks[0].in_toolbar);
+ EXPECT_EQ(toolbar_importer_unittest::kTitle, bookmarks[0].title);
+ EXPECT_EQ(url, bookmarks[0].url);
+ EXPECT_EQ(bookmarks[0].path.size(), (size_t) 2);
+ EXPECT_EQ(toolbar_importer_unittest::kFolder, bookmarks[0].path[1]);
+
+ // Test case 2 is parsing a single bookmark with no label.
+ bookmark_xml = toolbar_importer_unittest::kGoodBookmarkNoLabel;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_TRUE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ EXPECT_EQ(bookmarks.size(), (size_t) 1);
+ EXPECT_FALSE(bookmarks[0].in_toolbar);
+ EXPECT_EQ(toolbar_importer_unittest::kTitle, bookmarks[0].title);
+ EXPECT_EQ(bookmarks[0].url, url);
+ EXPECT_EQ(bookmarks[0].path.size(), (size_t) 1);
+
+ // Test case 3 is parsing a single bookmark with two labels.
+ bookmark_xml = toolbar_importer_unittest::kGoodBookmarkTwoLabels;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_TRUE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ EXPECT_EQ(bookmarks.size(), (size_t) 2);
+ EXPECT_FALSE(bookmarks[0].in_toolbar);
+ EXPECT_FALSE(bookmarks[1].in_toolbar);
+ EXPECT_EQ(toolbar_importer_unittest::kTitle, bookmarks[0].title);
+ EXPECT_EQ(toolbar_importer_unittest::kTitle, bookmarks[1].title);
+ EXPECT_EQ(bookmarks[0].url, url);
+ EXPECT_EQ(bookmarks[1].url, url);
+ EXPECT_EQ(toolbar_importer_unittest::kFolder, bookmarks[0].path[1]);
+ EXPECT_EQ(toolbar_importer_unittest::kFolder2, bookmarks[1].path[1]);
+
+ // Test case 4 is parsing a single bookmark which has a label with a colon,
+ // this test file name translation between Toolbar and Chrome.
+ bookmark_xml = toolbar_importer_unittest::kGoodBookmarkFolderLabel;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_TRUE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ EXPECT_EQ(bookmarks.size(), (size_t) 1);
+ EXPECT_FALSE(bookmarks[0].in_toolbar);
+ EXPECT_EQ(toolbar_importer_unittest::kTitle, bookmarks[0].title);
+ EXPECT_EQ(bookmarks[0].url, url);
+ EXPECT_EQ(bookmarks[0].path.size(), (size_t) 4);
+ EXPECT_TRUE(toolbar_importer_unittest::kFolderArray[0] ==
+ bookmarks[0].path[1]);
+ EXPECT_TRUE(toolbar_importer_unittest::kFolderArray[1] ==
+ bookmarks[0].path[2]);
+ EXPECT_TRUE(toolbar_importer_unittest::kFolderArray[2] ==
+ bookmarks[0].path[3]);
+
+ // Test case 5 is parsing a single bookmark without a favicon URL.
+ bookmark_xml = toolbar_importer_unittest::kGoodBookmarkNoFavicon;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_TRUE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ EXPECT_EQ(bookmarks.size(), (size_t) 1);
+ EXPECT_FALSE(bookmarks[0].in_toolbar);
+ EXPECT_EQ(toolbar_importer_unittest::kTitle, bookmarks[0].title);
+ EXPECT_EQ(bookmarks[0].url, url);
+ EXPECT_EQ(bookmarks[0].path.size(), (size_t) 2);
+ EXPECT_EQ(toolbar_importer_unittest::kFolder, bookmarks[0].path[1]);
+
+ // Test case 6 is parsing two bookmarks.
+ bookmark_xml = toolbar_importer_unittest::kGoodBookmark2Items;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_TRUE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ EXPECT_EQ(bookmarks.size(), (size_t) 2);
+ EXPECT_FALSE(bookmarks[0].in_toolbar);
+ EXPECT_FALSE(bookmarks[1].in_toolbar);
+ EXPECT_EQ(toolbar_importer_unittest::kTitle, bookmarks[0].title);
+ EXPECT_EQ(toolbar_importer_unittest::kOtherTitle, bookmarks[1].title);
+ EXPECT_EQ(bookmarks[0].url, url);
+ EXPECT_EQ(bookmarks[1].url, other_url);
+ EXPECT_EQ(bookmarks[0].path.size(), (size_t) 2);
+ EXPECT_EQ(toolbar_importer_unittest::kFolder, bookmarks[0].path[1]);
+ EXPECT_EQ(bookmarks[0].path.size(), (size_t) 2);
+ EXPECT_EQ(toolbar_importer_unittest::kOtherFolder, bookmarks[1].path[1]);
+
+ // Test case 7 is parsing an empty string for bookmarks.
+ bookmark_xml = toolbar_importer_unittest::kEmptyString;
+ bookmarks.clear();
+ EXPECT_FALSE(reader.Load(bookmark_xml));
+
+ // Test case 8 is testing the error when no <bookmarks> section is present.
+ bookmark_xml = toolbar_importer_unittest::kBadBookmarkNoBookmarks;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_FALSE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ // Test case 9 tests when no <bookmark> section is present.
+ bookmark_xml = toolbar_importer_unittest::kBadBookmarkNoBookmark;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_FALSE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+
+ // Test case 10 tests when a bookmark has no <title> section.
+ bookmark_xml = toolbar_importer_unittest::kBadBookmarkNoTitle;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_FALSE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+
+ // Test case 11 tests when a bookmark has no <url> section.
+ bookmark_xml = toolbar_importer_unittest::kBadBookmarkNoUrl;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_FALSE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ // Test case 12 tests when a bookmark has no <timestamp> section.
+ bookmark_xml = toolbar_importer_unittest::kBadBookmarkNoTimestamp;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_FALSE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+
+ // Test case 13 tests when a bookmark has no <labels> section.
+ bookmark_xml = toolbar_importer_unittest::kBadBookmarkNoLabels;
+ bookmarks.clear();
+ EXPECT_TRUE(reader.Load(bookmark_xml));
+ EXPECT_FALSE(Toolbar5Importer::ParseBookmarksFromReader(&reader, &bookmarks));
+}