diff options
-rw-r--r-- | .gitignore | 8 | ||||
-rw-r--r-- | base/mac_util.h | 3 | ||||
-rw-r--r-- | base/mac_util.mm | 14 | ||||
-rw-r--r-- | base/mac_util_unittest.cc | 7 | ||||
-rw-r--r-- | chrome/browser/history/history_types.h | 2 | ||||
-rw-r--r-- | chrome/browser/importer/importer.cc | 8 | ||||
-rw-r--r-- | chrome/browser/importer/safari_importer.h | 63 | ||||
-rw-r--r-- | chrome/browser/importer/safari_importer.mm | 223 | ||||
-rw-r--r-- | chrome/browser/importer/safari_importer_unittest.mm | 63 | ||||
-rw-r--r-- | chrome/chrome.gyp | 3 | ||||
-rw-r--r-- | chrome/test/data/safari_import/Preferences/com.apple.Safari.plist | bin | 0 -> 3636 bytes | |||
-rw-r--r-- | chrome/test/data/safari_import/README | 6 | ||||
-rw-r--r-- | chrome/test/data/safari_import/Safari/History.plist | bin | 0 -> 414 bytes |
13 files changed, 396 insertions, 4 deletions
@@ -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 Binary files differnew file mode 100644 index 0000000..dbd55e5 --- /dev/null +++ b/chrome/test/data/safari_import/Preferences/com.apple.Safari.plist 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 Binary files differnew file mode 100644 index 0000000..356404e --- /dev/null +++ b/chrome/test/data/safari_import/Safari/History.plist |