summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--base/mac_util.h3
-rw-r--r--base/mac_util.mm14
-rw-r--r--base/mac_util_unittest.cc7
-rw-r--r--chrome/browser/history/history_types.h2
-rw-r--r--chrome/browser/importer/importer.cc8
-rw-r--r--chrome/browser/importer/safari_importer.h63
-rw-r--r--chrome/browser/importer/safari_importer.mm223
-rw-r--r--chrome/browser/importer/safari_importer_unittest.mm63
-rw-r--r--chrome/chrome.gyp3
-rw-r--r--chrome/test/data/safari_import/Preferences/com.apple.Safari.plistbin0 -> 3636 bytes
-rw-r--r--chrome/test/data/safari_import/README6
-rw-r--r--chrome/test/data/safari_import/Safari/History.plistbin0 -> 414 bytes
13 files changed, 396 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index d4d5635..67dbc2e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,13 @@
/chrome/Release
/chrome/app/theme/google_chrome/
/chrome/installer/linux/
-/chrome/test/data/
+/chrome/test/data/firefox2_profile/searchplugins/
+/chrome/test/data/firefox2_searchplugins/
+/chrome/test/data/firefox3_profile/searchplugins/
+/chrome/test/data/firefox3_searchplugins/
+/chrome/test/data/osdd/
+/chrome/test/data/plugin/
+/chrome/test/data/workers/LayoutTests/
/data/
/googleurl/
/native_client/
diff --git a/base/mac_util.h b/base/mac_util.h
index 371aa9e..43f0e5c 100644
--- a/base/mac_util.h
+++ b/base/mac_util.h
@@ -29,6 +29,9 @@ bool AmIBundled();
// aren't a bundle.
NSBundle* MainAppBundle();
+// Returns the ~/Library directory.
+FilePath GetUserLibraryPath();
+
// Set the bundle that MainAppBundle will return, overriding the default value
// (Restore the default by calling SetOverrideAppBundle(nil)).
void SetOverrideAppBundle(NSBundle* bundle);
diff --git a/base/mac_util.mm b/base/mac_util.mm
index 156cb1c..5304b75 100644
--- a/base/mac_util.mm
+++ b/base/mac_util.mm
@@ -53,6 +53,20 @@ NSBundle* MainAppBundle() {
return [NSBundle mainBundle];
}
+FilePath GetUserLibraryPath() {
+ NSArray* dirs = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
+ NSUserDomainMask, YES);
+ if ([dirs count] == 0)
+ return FilePath();
+
+ NSString* library_dir = [dirs objectAtIndex:0];
+ const char* library_dir_path = [library_dir fileSystemRepresentation];
+ if (!library_dir_path)
+ return FilePath();
+
+ return FilePath(library_dir_path);
+}
+
void SetOverrideAppBundle(NSBundle* bundle) {
[g_override_app_bundle release];
g_override_app_bundle = [bundle retain];
diff --git a/base/mac_util_unittest.cc b/base/mac_util_unittest.cc
index 538b1e7..18b8b5e 100644
--- a/base/mac_util_unittest.cc
+++ b/base/mac_util_unittest.cc
@@ -6,6 +6,7 @@
#include <ApplicationServices/ApplicationServices.h>
+#include "base/file_path.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
@@ -18,3 +19,9 @@ TEST_F(MacUtilTest, TestFSRef) {
ASSERT_TRUE(mac_util::FSRefFromPath(path, &ref));
EXPECT_EQ(path, mac_util::PathFromFSRef(ref));
}
+
+TEST_F(MacUtilTest, TestLibraryPath) {
+ FilePath library_dir = mac_util::GetUserLibraryPath();
+ // Make sure the string isn't empty.
+ EXPECT_FALSE(library_dir.value().empty());
+}
diff --git a/chrome/browser/history/history_types.h b/chrome/browser/history/history_types.h
index 38561b5..f23af42 100644
--- a/chrome/browser/history/history_types.h
+++ b/chrome/browser/history/history_types.h
@@ -95,6 +95,7 @@ class URLRow {
visit_count_ = visit_count;
}
+ // Number of times the URL was typed in the Omnibox.
int typed_count() const {
return typed_count_;
}
@@ -109,6 +110,7 @@ class URLRow {
last_visit_ = last_visit;
}
+ // If this is set, we won't autocomplete this URL.
bool hidden() const {
return hidden_;
}
diff --git a/chrome/browser/importer/importer.cc b/chrome/browser/importer/importer.cc
index df32899..904361a 100644
--- a/chrome/browser/importer/importer.cc
+++ b/chrome/browser/importer/importer.cc
@@ -14,6 +14,7 @@
#endif
#include "base/file_util.h"
#include "base/gfx/png_encoder.h"
+#include "base/mac_util.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
@@ -28,6 +29,9 @@
#if defined(OS_WIN)
#include "chrome/browser/importer/ie_importer.h"
#endif
+#if defined(OS_MACOSX)
+#include "chrome/browser/importer/safari_importer.h"
+#endif
#include "chrome/browser/importer/toolbar_importer.h"
#if defined(OS_WIN)
#include "chrome/browser/password_manager/ie7_password.h"
@@ -652,9 +656,7 @@ Importer* ImporterHost::CreateImporterByType(ProfileType type) {
return new Toolbar5Importer();
#if defined(OS_MACOSX)
case SAFARI:
- // TODO(jeremy): Implement.
- NOTIMPLEMENTED();
- return NULL;
+ return new SafariImporter(mac_util::GetUserLibraryPath());
#endif // OS_MACOSX
}
NOTREACHED();
diff --git a/chrome/browser/importer/safari_importer.h b/chrome/browser/importer/safari_importer.h
new file mode 100644
index 0000000..28b939f
--- /dev/null
+++ b/chrome/browser/importer/safari_importer.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2009 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_IMPORTER_SAFARI_IMPORTER_H_
+#define CHROME_BROWSER_IMPORTER_SAFARI_IMPORTER_H_
+
+#include "chrome/browser/importer/importer.h"
+
+#if __OBJC__
+@class NSDictionary;
+@class NSString;
+#else
+class NSDictionary;
+class NSString;
+#endif
+
+// Importer for Safari on OS X.
+class SafariImporter : public Importer {
+ public:
+ // |library_dir| is the full path to the ~/Library directory,
+ // We pass it in as a parameter for testing purposes.
+ explicit SafariImporter(const FilePath& library_dir);
+ virtual ~SafariImporter();
+
+ // Importer methods.
+ virtual void StartImport(ProfileInfo profile_info,
+ uint16 items,
+ ProfileWriter* writer,
+ MessageLoop* delegate_loop,
+ ImporterHost* host);
+
+ private:
+ FRIEND_TEST(SafariImporterTest, HistoryImport);
+
+ void ImportBookmarks();
+ void ImportSearchEngines();
+ void ImportPasswords();
+ void ImportHistory();
+ void ImportHomepage();
+
+ // Converts history time stored by Safari as a double serialized as a string,
+ // to seconds-since-UNIX-Ephoch-format used by Chrome.
+ double HistoryTimeToEpochTime(NSString* history_time);
+
+ // Parses Safari's history and loads it into the input array.
+ void ParseHistoryItems(std::vector<history::URLRow>* history_items);
+
+ // Given the URL of a page and a favicon data URL, adds an appropriate record
+ // to the given favicon usage vector. Will do nothing if the favicon is not
+ // valid.
+ static void DataURLToFaviconUsage(
+ const GURL& link_url,
+ const GURL& favicon_data,
+ std::vector<history::ImportedFavIconUsage>* favicons);
+
+ ProfileWriter* writer_;
+ FilePath library_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafariImporter);
+};
+
+#endif // CHROME_BROWSER_IMPORTER_SAFARI_IMPORTER_H_
diff --git a/chrome/browser/importer/safari_importer.mm b/chrome/browser/importer/safari_importer.mm
new file mode 100644
index 0000000..78e1111
--- /dev/null
+++ b/chrome/browser/importer/safari_importer.mm
@@ -0,0 +1,223 @@
+// Copyright (c) 2009 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 <Cocoa/Cocoa.h>
+
+#include "chrome/browser/importer/safari_importer.h"
+
+#include <vector>
+
+#include "base/message_loop.h"
+#include "base/scoped_nsobject.h"
+#include "base/string16.h"
+#include "base/sys_string_conversions.h"
+#include "base/time.h"
+#include "chrome/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+#include "grit/generated_resources.h"
+#include "net/base/data_url.h"
+
+namespace {
+
+// A function like this is used by other importers in order to filter out
+// URLS we don't want to import.
+// For now it's pretty basic, but I've split it out so it's easy to slot
+// in necessary logic for filtering URLS, should we need it.
+bool CanImportSafariURL(const GURL& url) {
+ // The URL is not valid.
+ if (!url.is_valid())
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+SafariImporter::SafariImporter(const FilePath& library_dir)
+ : writer_(NULL), library_dir_(library_dir) {
+}
+
+SafariImporter::~SafariImporter() {
+}
+
+void SafariImporter::StartImport(ProfileInfo profile_info,
+ uint16 items, ProfileWriter* writer,
+ MessageLoop* delegate_loop,
+ ImporterHost* host) {
+ writer_ = writer;
+ importer_host_ = host;
+
+ // The order here is important!
+ NotifyStarted();
+ if ((items & HOME_PAGE) && !cancelled())
+ ImportHomepage(); // Doesn't have a UI item.
+ if ((items & FAVORITES) && !cancelled()) {
+ NotifyItemStarted(FAVORITES);
+ ImportBookmarks();
+ NotifyItemEnded(FAVORITES);
+ }
+ if ((items & SEARCH_ENGINES) && !cancelled()) {
+ NotifyItemStarted(SEARCH_ENGINES);
+ ImportSearchEngines();
+ NotifyItemEnded(SEARCH_ENGINES);
+ }
+ if ((items & PASSWORDS) && !cancelled()) {
+ NotifyItemStarted(PASSWORDS);
+ ImportPasswords();
+ NotifyItemEnded(PASSWORDS);
+ }
+ if ((items & HISTORY) && !cancelled()) {
+ NotifyItemStarted(HISTORY);
+ ImportHistory();
+ NotifyItemEnded(HISTORY);
+ }
+ NotifyEnded();
+}
+
+void SafariImporter::ImportBookmarks() {
+ NOTIMPLEMENTED();
+}
+
+void SafariImporter::ImportSearchEngines() {
+ NOTIMPLEMENTED();
+}
+
+void SafariImporter::ImportPasswords() {
+ // Safari stores it's passwords in the Keychain, same as us so we don't need
+ // to import them.
+ // Note: that we don't automatically pick them up, there is some logic around
+ // the user needing to explicitly input his username in a page and bluring
+ // the field before we pick it up, but the details of that are beyond the
+ // scope of this comment.
+}
+
+void SafariImporter::ImportHistory() {
+ std::vector<history::URLRow> rows;
+ ParseHistoryItems(&rows);
+
+ if (!rows.empty() && !cancelled()) {
+ main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
+ &ProfileWriter::AddHistoryPage, rows));
+ }
+}
+
+double SafariImporter::HistoryTimeToEpochTime(NSString* history_time) {
+ DCHECK(history_time);
+ // Add Difference between Unix epoch and CFAbsoluteTime epoch in seconds.
+ // Unix epoch is 1970-01-01 00:00:00.0 UTC,
+ // CF epoch is 2001-01-01 00:00:00.0 UTC.
+ return CFStringGetDoubleValue(reinterpret_cast<CFStringRef>(history_time)) +
+ kCFAbsoluteTimeIntervalSince1970;
+}
+
+void SafariImporter::ParseHistoryItems(
+ std::vector<history::URLRow>* history_items) {
+ DCHECK(history_items);
+
+ // Construct ~/Library/Safari/History.plist path
+ NSString* library_dir = [NSString
+ stringWithUTF8String:library_dir_.value().c_str()];
+ NSString* safari_dir = [library_dir
+ stringByAppendingPathComponent:@"Safari"];
+ NSString* history_plist = [safari_dir
+ stringByAppendingPathComponent:@"History.plist"];
+
+ // Load the plist file.
+ NSDictionary* history_dict = [NSDictionary
+ dictionaryWithContentsOfFile:history_plist];
+
+ NSArray* safari_history_items = [history_dict valueForKey:@"WebHistoryDates"];
+
+ for (NSDictionary* history_item in safari_history_items) {
+ using base::SysNSStringToUTF8;
+ using base::SysNSStringToWide;
+ NSString* url_ns = [history_item valueForKey:@""];
+ if (!url_ns)
+ continue;
+
+ GURL url(SysNSStringToUTF8(url_ns));
+
+ if (!CanImportSafariURL(url))
+ continue;
+
+ history::URLRow row(url);
+ NSString* title_ns = [history_item valueForKey:@"title"];
+
+ // Sometimes items don't have a title, in which case we just substitue
+ // the url.
+ if (!title_ns)
+ title_ns = url_ns;
+
+ row.set_title(SysNSStringToWide(title_ns));
+ int visit_count = [[history_item valueForKey:@"visitCount"]
+ intValue];
+ row.set_visit_count(visit_count);
+ // Include imported URLs in autocompletion - don't hide them.
+ row.set_hidden(0);
+ // Item was never typed before in the omnibox.
+ row.set_typed_count(0);
+
+ NSString* last_visit_str = [history_item valueForKey:@"lastVisitedDate"];
+ // The last visit time should always be in the history item, but if not
+ /// just continue without this item.
+ DCHECK(last_visit_str);
+ if (!last_visit_str)
+ continue;
+
+ // Convert Safari's last visit time to Unix Epoch time.
+ double seconds_since_unix_epoch = HistoryTimeToEpochTime(last_visit_str);
+ row.set_last_visit(base::Time::FromDoubleT(seconds_since_unix_epoch));
+
+ history_items->push_back(row);
+ }
+}
+
+void SafariImporter::ImportHomepage() {
+ const scoped_nsobject<NSString> homepage_ns(
+ reinterpret_cast<const NSString*>(
+ CFPreferencesCopyAppValue(CFSTR("HomePage"),
+ CFSTR("com.apple.Safari"))));
+ if (!homepage_ns.get())
+ return;
+
+ string16 hompeage_str = base::SysNSStringToUTF16(homepage_ns.get());
+ GURL homepage(hompeage_str);
+ if (homepage.is_valid()) {
+ main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_,
+ &ProfileWriter::AddHomepage, homepage));
+ }
+}
+
+// TODO(jeremy): This is temporary, just copied from the FF import code in case
+// we need it, clean this up when writing favicon import code.
+// static
+void SafariImporter::DataURLToFaviconUsage(
+ const GURL& link_url,
+ const GURL& favicon_data,
+ std::vector<history::ImportedFavIconUsage>* favicons) {
+ if (!link_url.is_valid() || !favicon_data.is_valid() ||
+ !favicon_data.SchemeIs(chrome::kDataScheme))
+ return;
+
+ // Parse the data URL.
+ std::string mime_type, char_set, data;
+ if (!net::DataURL::Parse(favicon_data, &mime_type, &char_set, &data) ||
+ data.empty())
+ return;
+
+ history::ImportedFavIconUsage usage;
+ if (!ReencodeFavicon(reinterpret_cast<const unsigned char*>(&data[0]),
+ data.size(), &usage.png_data))
+ return; // Unable to decode.
+
+ // We need to make up a URL for the favicon. We use a version of the page's
+ // URL so that we can be sure it will not collide.
+ usage.favicon_url = GURL(std::string("made-up-favicon:") + link_url.spec());
+
+ // We only have one URL per favicon for Firefox 2 bookmarks.
+ usage.urls.insert(link_url);
+
+ favicons->push_back(usage);
+}
+
diff --git a/chrome/browser/importer/safari_importer_unittest.mm b/chrome/browser/importer/safari_importer_unittest.mm
new file mode 100644
index 0000000..af0ae9e
--- /dev/null
+++ b/chrome/browser/importer/safari_importer_unittest.mm
@@ -0,0 +1,63 @@
+// Copyright (c) 2009 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/safari_importer.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/common/chrome_paths.h"
+#include "testing/platform_test.h"
+
+// In order to test the Safari import functionality effectively, we store a
+// simulated Library directory containing dummy data files in the same
+// structure as ~/Library in the Chrome test data directory.
+// This function returns the path to that directory.
+FilePath GetTestSafariLibraryPath() {
+ std::wstring test_dir_wstring;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_dir_wstring);
+ FilePath test_dir = FilePath::FromWStringHack(test_dir_wstring);
+
+ // Our simulated ~/Library directory
+ test_dir = test_dir.Append("safari_import");
+ return test_dir;
+}
+
+class SafariImporterTest : public PlatformTest {};
+
+TEST_F(SafariImporterTest, HistoryImport) {
+ FilePath test_library_dir = GetTestSafariLibraryPath();
+ ASSERT_TRUE(file_util::PathExists(test_library_dir)) <<
+ "Missing test data directory";
+
+ scoped_refptr<SafariImporter> importer(
+ new SafariImporter(test_library_dir));
+
+ std::vector<history::URLRow> history_items;
+ importer->ParseHistoryItems(&history_items);
+
+ // Should be 2 history items.
+ ASSERT_EQ(history_items.size(), 2U);
+
+ history::URLRow& it1 = history_items[0];
+ EXPECT_EQ(it1.url(), GURL("http://www.firsthistoryitem.com/"));
+ EXPECT_EQ(it1.title(), L"First History Item Title");
+ EXPECT_EQ(it1.visit_count(), 1);
+ EXPECT_EQ(it1.hidden(), 0);
+ EXPECT_EQ(it1.typed_count(), 0);
+ EXPECT_EQ(it1.last_visit().ToDoubleT(),
+ importer->HistoryTimeToEpochTime(@"270598264.4"));
+
+ history::URLRow& it2 = history_items[1];
+ std::string second_item_title("http://www.secondhistoryitem.com/");
+ EXPECT_EQ(it2.url(), GURL(second_item_title));
+ // The second item lacks a title so we expect the URL to be substituted.
+ EXPECT_EQ(base::SysWideToUTF8(it2.title()), second_item_title.c_str());
+ EXPECT_EQ(it2.visit_count(), 55);
+ EXPECT_EQ(it2.hidden(), 0);
+ EXPECT_EQ(it2.typed_count(), 0);
+ EXPECT_EQ(it2.last_visit().ToDoubleT(),
+ importer->HistoryTimeToEpochTime(@"270598231.4"));
+}
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 67391ed..f623c1e 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1254,6 +1254,8 @@
'browser/importer/nss_decryptor_mac.mm',
'browser/importer/nss_decryptor_win.cc',
'browser/importer/nss_decryptor_win.h',
+ 'browser/importer/safari_importer.h',
+ 'browser/importer/safari_importer.mm',
'browser/importer/toolbar_importer.cc',
'browser/importer/toolbar_importer.h',
'browser/in_process_webkit/browser_webkitclient_impl.cc',
@@ -3835,6 +3837,7 @@
'browser/history/visit_tracker_unittest.cc',
'browser/importer/firefox_importer_unittest.cc',
'browser/importer/importer_unittest.cc',
+ 'browser/importer/safari_importer_unittest.mm',
'browser/importer/toolbar_importer_unittest.cc',
'browser/importer/firefox_profile_lock_unittest.cc',
'browser/in_process_webkit/dom_storage_dispatcher_host_unittest.cc',
diff --git a/chrome/test/data/safari_import/Preferences/com.apple.Safari.plist b/chrome/test/data/safari_import/Preferences/com.apple.Safari.plist
new file mode 100644
index 0000000..dbd55e5
--- /dev/null
+++ b/chrome/test/data/safari_import/Preferences/com.apple.Safari.plist
Binary files differ
diff --git a/chrome/test/data/safari_import/README b/chrome/test/data/safari_import/README
new file mode 100644
index 0000000..e167778
--- /dev/null
+++ b/chrome/test/data/safari_import/README
@@ -0,0 +1,6 @@
+This directory contains test data for the Safari importer, the files in
+this directory were written by Safari v4.0.2 on OS X 10.5.7. In the
+future we may want to version this directory if the data formats change.
+
+This directory is passed in to the test code as if it where ~/Library,
+so the directory structure mimics that of the Library directory. \ No newline at end of file
diff --git a/chrome/test/data/safari_import/Safari/History.plist b/chrome/test/data/safari_import/Safari/History.plist
new file mode 100644
index 0000000..356404e
--- /dev/null
+++ b/chrome/test/data/safari_import/Safari/History.plist
Binary files differ