diff options
-rw-r--r-- | base/time.h | 9 | ||||
-rw-r--r-- | base/time_mac.cc | 34 | ||||
-rw-r--r-- | base/time_posix.cc | 76 | ||||
-rw-r--r-- | base/time_unittest.cc | 30 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_codec.cc | 18 | ||||
-rw-r--r-- | chrome/browser/history/history_backend.cc | 13 | ||||
-rw-r--r-- | chrome/browser/history/history_database.cc | 51 | ||||
-rw-r--r-- | chrome/browser/history/history_database.h | 21 | ||||
-rw-r--r-- | chrome/browser/password_manager/password_store_mac_unittest.cc | 4 | ||||
-rw-r--r-- | chrome/test/data/profiles/typical_history/Default/History | bin | 13770752 -> 13783040 bytes | |||
-rw-r--r-- | net/base/cookie_monster_unittest.cc | 11 |
11 files changed, 205 insertions, 62 deletions
diff --git a/base/time.h b/base/time.h index beeca27..a57c04f 100644 --- a/base/time.h +++ b/base/time.h @@ -178,6 +178,15 @@ class Time { static const int64 kNanosecondsPerSecond = kNanosecondsPerMicrosecond * kMicrosecondsPerSecond; +#if !defined(OS_WIN) + // On Mac & Linux, this value is the delta from the Windows epoch of 1601 to + // the Posix delta of 1970. This is used for migrating between the old + // 1970-based epochs to the new 1601-based ones. It should be removed from + // this global header and put in the platform-specific ones when we remove the + // migration code. + static const int64 kWindowsEpochDeltaMicroseconds; +#endif + // Represents an exploded time that can be formatted nicely. This is kind of // like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few // additions and changes to prevent errors. diff --git a/base/time_mac.cc b/base/time_mac.cc index 3e5e14a..6b46b95 100644 --- a/base/time_mac.cc +++ b/base/time_mac.cc @@ -24,20 +24,33 @@ namespace base { // Time ----------------------------------------------------------------------- -// The internal representation of Time uses a 64-bit microsecond count -// from 1970-01-01 00:00:00 UTC. Core Foundation uses a double second count -// since 2001-01-01 00:00:00 UTC. +// Core Foundation uses a double second count since 2001-01-01 00:00:00 UTC. +// The UNIX epoch is 1970-01-01 00:00:00 UTC. +// Windows uses a Gregorian epoch of 1601. We need to match this internally +// so that our time representations match across all platforms. See bug 14734. +// irb(main):010:0> Time.at(0).getutc() +// => Thu Jan 01 00:00:00 UTC 1970 +// irb(main):011:0> Time.at(-11644473600).getutc() +// => Mon Jan 01 00:00:00 UTC 1601 +static const int64 kWindowsEpochDeltaSeconds = GG_INT64_C(11644473600); +static const int64 kWindowsEpochDeltaMilliseconds = + kWindowsEpochDeltaSeconds * Time::kMillisecondsPerSecond; -// Some functions in time.cc use time_t directly, so we provide a zero offset -// for them. The epoch is 1970-01-01 00:00:00 UTC. // static -const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0); +const int64 Time::kWindowsEpochDeltaMicroseconds = + kWindowsEpochDeltaSeconds * Time::kMicrosecondsPerSecond; + +// Some functions in time.cc use time_t directly, so we provide an offset +// to convert from time_t (Unix epoch) and internal (Windows epoch). +// static +const int64 Time::kTimeTToMicrosecondsOffset = kWindowsEpochDeltaMicroseconds; // static Time Time::Now() { CFAbsoluteTime now = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970; - return Time(static_cast<int64>(now * kMicrosecondsPerSecond)); + return Time(static_cast<int64>(now * kMicrosecondsPerSecond) + + kWindowsEpochDeltaMicroseconds); } // static @@ -61,13 +74,14 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) { time_zone(is_local ? CFTimeZoneCopySystem() : NULL); CFAbsoluteTime seconds = CFGregorianDateGetAbsoluteTime(date, time_zone) + kCFAbsoluteTimeIntervalSince1970; - return Time(static_cast<int64>(seconds * kMicrosecondsPerSecond)); + return Time(static_cast<int64>(seconds * kMicrosecondsPerSecond) + + kWindowsEpochDeltaMicroseconds); } void Time::Explode(bool is_local, Exploded* exploded) const { CFAbsoluteTime seconds = - (static_cast<double>(us_) / kMicrosecondsPerSecond) - - kCFAbsoluteTimeIntervalSince1970; + (static_cast<double>((us_ - kWindowsEpochDeltaMicroseconds) / + kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970); scoped_cftyperef<CFTimeZoneRef> time_zone(is_local ? CFTimeZoneCopySystem() : NULL); diff --git a/base/time_posix.cc b/base/time_posix.cc index 8b04be9..66f41d3 100644 --- a/base/time_posix.cc +++ b/base/time_posix.cc @@ -4,9 +4,6 @@ #include "base/time.h" -#ifdef OS_MACOSX -#include <mach/mach_time.h> -#endif #include <sys/time.h> #include <time.h> @@ -23,10 +20,24 @@ namespace base { // Time ----------------------------------------------------------------------- -// Some functions in time.cc use time_t directly, so we provide a zero offset -// for them. The epoch is 1970-01-01 00:00:00 UTC. +// Windows uses a Gregorian epoch of 1601. We need to match this internally +// so that our time representations match across all platforms. See bug 14734. +// irb(main):010:0> Time.at(0).getutc() +// => Thu Jan 01 00:00:00 UTC 1970 +// irb(main):011:0> Time.at(-11644473600).getutc() +// => Mon Jan 01 00:00:00 UTC 1601 +static const int64 kWindowsEpochDeltaSeconds = GG_INT64_C(11644473600); +static const int64 kWindowsEpochDeltaMilliseconds = + kWindowsEpochDeltaSeconds * Time::kMillisecondsPerSecond; + +// static +const int64 Time::kWindowsEpochDeltaMicroseconds = + kWindowsEpochDeltaSeconds * Time::kMicrosecondsPerSecond; + +// Some functions in time.cc use time_t directly, so we provide an offset +// to convert from time_t (Unix epoch) and internal (Windows epoch). // static -const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0); +const int64 Time::kTimeTToMicrosecondsOffset = kWindowsEpochDeltaMicroseconds; // static Time Time::Now() { @@ -36,8 +47,10 @@ Time Time::Now() { DCHECK(0) << "Could not determine time of day"; } // Combine seconds and microseconds in a 64-bit field containing microseconds - // since the epoch. That's enough for nearly 600 centuries. - return tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec; + // since the epoch. That's enough for nearly 600 centuries. Adjust from + // Unix (1970) to Windows (1601) epoch. + return Time((tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec) + + kWindowsEpochDeltaMicroseconds); } // static @@ -100,13 +113,17 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) { milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond; } - return Time(milliseconds * kMicrosecondsPerMillisecond); + // Adjust from Unix (1970) to Windows (1601) epoch. + return Time((milliseconds * kMicrosecondsPerMillisecond) + + kWindowsEpochDeltaMicroseconds); } void Time::Explode(bool is_local, Exploded* exploded) const { // Time stores times with microsecond resolution, but Exploded only carries - // millisecond resolution, so begin by being lossy. - int64 milliseconds = us_ / kMicrosecondsPerMillisecond; + // millisecond resolution, so begin by being lossy. Adjust from Windows + // epoch (1601) to Unix epoch (1970); + int64 milliseconds = (us_ - kWindowsEpochDeltaMicroseconds) / + kMicrosecondsPerMillisecond; time_t seconds = milliseconds / kMillisecondsPerSecond; struct tm timestruct; @@ -127,38 +144,13 @@ void Time::Explode(bool is_local, Exploded* exploded) const { // TimeTicks ------------------------------------------------------------------ +#if defined(OS_POSIX) && \ + defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 + // static TimeTicks TimeTicks::Now() { uint64_t absolute_micro; -#if defined(OS_MACOSX) - static mach_timebase_info_data_t timebase_info; - if (timebase_info.denom == 0) { - // Zero-initialization of statics guarantees that denom will be 0 before - // calling mach_timebase_info. mach_timebase_info will never set denom to - // 0 as that would be invalid, so the zero-check can be used to determine - // whether mach_timebase_info has already been called. This is - // recommended by Apple's QA1398. - kern_return_t kr = mach_timebase_info(&timebase_info); - DCHECK(kr == KERN_SUCCESS); - } - - // mach_absolute_time is it when it comes to ticks on the Mac. Other calls - // with less precision (such as TickCount) just call through to - // mach_absolute_time. - - // timebase_info converts absolute time tick units into nanoseconds. Convert - // to microseconds up front to stave off overflows. - absolute_micro = mach_absolute_time() / Time::kNanosecondsPerMicrosecond * - timebase_info.numer / timebase_info.denom; - - // Don't bother with the rollover handling that the Windows version does. - // With numer and denom = 1 (the expected case), the 64-bit absolute time - // reported in nanoseconds is enough to last nearly 585 years. - -#elif defined(OS_POSIX) && \ - defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 - struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { NOTREACHED() << "clock_gettime(CLOCK_MONOTONIC) failed."; @@ -169,13 +161,13 @@ TimeTicks TimeTicks::Now() { (static_cast<int64>(ts.tv_sec) * Time::kMicrosecondsPerSecond) + (static_cast<int64>(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond); + return TimeTicks(absolute_micro); +} + #else // _POSIX_MONOTONIC_CLOCK #error No usable tick clock function on this platform. #endif // _POSIX_MONOTONIC_CLOCK - return TimeTicks(absolute_micro); -} - // static TimeTicks TimeTicks::HighResNow() { return Now(); diff --git a/base/time_unittest.cc b/base/time_unittest.cc index ebe69eb..f8a62cb 100644 --- a/base/time_unittest.cc +++ b/base/time_unittest.cc @@ -66,9 +66,10 @@ TEST(Time, LocalExplode) { Time b = Time::FromLocalExploded(exploded); - // The exploded structure doesn't have microseconds, so the result will be - // rounded to the nearest millisecond. - EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1)); + // The exploded structure doesn't have microseconds, and on Mac & Linux, the + // internal OS conversion uses seconds, which will cause truncation. So we + // can only make sure that the delta is within one second. + EXPECT_TRUE((a - b) < TimeDelta::FromSeconds(1)); } TEST(Time, UTCExplode) { @@ -77,7 +78,7 @@ TEST(Time, UTCExplode) { a.UTCExplode(&exploded); Time b = Time::FromUTCExploded(exploded); - EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1)); + EXPECT_TRUE((a - b) < TimeDelta::FromSeconds(1)); } TEST(Time, LocalMidnight) { @@ -140,3 +141,24 @@ TEST(TimeDelta, FromAndIn) { EXPECT_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF()); EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).InMicroseconds()); } + +// Our internal time format is serialized in things like databases, so it's +// important that it's consistent across all our platforms. We use the 1601 +// Windows epoch as the internal format across all platforms. +TEST(TimeDelta, WindowsEpoch) { + Time::Exploded exploded; + exploded.year = 1970; + exploded.month = 1; + exploded.day_of_week = 0; // Should be unusued. + exploded.day_of_month = 1; + exploded.hour = 0; + exploded.minute = 0; + exploded.second = 0; + exploded.millisecond = 0; + Time t = Time::FromUTCExploded(exploded); + // Unix 1970 epoch. + EXPECT_EQ(GG_INT64_C(11644473600000000), t.ToInternalValue()); + + // We can't test 1601 epoch, since the system time functions on Linux + // only compute years starting from 1900. +} diff --git a/chrome/browser/bookmarks/bookmark_codec.cc b/chrome/browser/bookmarks/bookmark_codec.cc index cae5f22..ebf48db 100644 --- a/chrome/browser/bookmarks/bookmark_codec.cc +++ b/chrome/browser/bookmarks/bookmark_codec.cc @@ -200,6 +200,21 @@ bool BookmarkCodec::DecodeNode(const DictionaryValue& value, std::wstring date_added_string; if (!value.GetString(kDateAddedKey, &date_added_string)) date_added_string = Int64ToWString(Time::Now().ToInternalValue()); + base::Time date_added = base::Time::FromInternalValue( + StringToInt64(WideToUTF16Hack(date_added_string))); +#if !defined(OS_WIN) + // We changed the epoch for dates on Mac & Linux from 1970 to the Windows + // one of 1601. We assume any number we encounter from before 1970 is using + // the old format, so we need to add the delta to it. + // + // This code should be removed at some point: + // http://code.google.com/p/chromium/issues/detail?id=20264 + if (date_added.ToInternalValue() < + base::Time::kWindowsEpochDeltaMicroseconds) { + date_added = base::Time::FromInternalValue(date_added.ToInternalValue() + + base::Time::kWindowsEpochDeltaMicroseconds); + } +#endif std::wstring type_string; if (!value.GetString(kTypeKey, &type_string)) @@ -255,8 +270,7 @@ bool BookmarkCodec::DecodeNode(const DictionaryValue& value, } node->SetTitle(title); - node->set_date_added(Time::FromInternalValue( - StringToInt64(WideToUTF16Hack(date_added_string)))); + node->set_date_added(date_added); return true; } diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc index 1ccd7cf..0d01496 100644 --- a/chrome/browser/history/history_backend.cc +++ b/chrome/browser/history/history_backend.cc @@ -529,6 +529,13 @@ void HistoryBackend::InitImpl() { LOG(WARNING) << "Text database initialization failed, running without it."; text_database_.reset(); } + if (db_->needs_version_17_migration()) { + // See needs_version_17_migration() decl for more. In this case, we want + // to erase all the text database files. This must be done after the text + // database manager has been initialized, since it knows about all the + // files it manages. + text_database_->DeleteAll(); + } // Thumbnail database. thumbnail_db_.reset(new ThumbnailDatabase()); @@ -544,6 +551,12 @@ void HistoryBackend::InitImpl() { } // Archived database. + if (db_->needs_version_17_migration()) { + // See needs_version_17_migration() decl for more. In this case, we want + // to delete the archived database and need to do so before we try to + // open the file. We can ignore any error (maybe the file doesn't exist). + file_util::Delete(archived_name, false); + } archived_db_.reset(new ArchivedDatabase()); if (!archived_db_->Init(archived_name)) { LOG(WARNING) << "Could not initialize the archived database."; diff --git a/chrome/browser/history/history_database.cc b/chrome/browser/history/history_database.cc index 5a929b1..198c6a4 100644 --- a/chrome/browser/history/history_database.cc +++ b/chrome/browser/history/history_database.cc @@ -16,8 +16,10 @@ namespace history { namespace { -// Current version number. -static const int kCurrentVersionNumber = 16; +// Current version number. We write databases at the "current" version number, +// but any previous version that can read the "compatible" one can make do with +// or database without *too* many bad effects. +static const int kCurrentVersionNumber = 17; static const int kCompatibleVersionNumber = 16; static const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold"; @@ -25,7 +27,8 @@ static const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold"; HistoryDatabase::HistoryDatabase() : transaction_nesting_(0), - db_(NULL) { + db_(NULL), + needs_version_17_migration_(false) { } HistoryDatabase::~HistoryDatabase() { @@ -235,6 +238,20 @@ InitStatus HistoryDatabase::EnsureCurrentVersion( std::min(cur_version, kCompatibleVersionNumber)); } + if (cur_version == 16) { +#if !defined(OS_WIN) + // In this version we bring the time format on Mac & Linux in sync with the + // Windows version so that profiles can be moved between computers. + MigrateTimeEpoch(); +#endif + // On all platforms we bump the version number, so on Windows this + // migration is a NOP. We keep the compatible version at 16 since things + // will basically still work, just history will be in the future if an + // old version reads it. + ++cur_version; + meta_table_.SetVersionNumber(cur_version); + } + // When the version is too old, we just try to continue anyway, there should // not be a released product that makes a database too old for us to handle. LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << @@ -243,4 +260,32 @@ InitStatus HistoryDatabase::EnsureCurrentVersion( return INIT_OK; } +#if !defined(OS_WIN) +void HistoryDatabase::MigrateTimeEpoch() { + // Update all the times in the URLs and visits table in the main database. + // For visits, clear the indexed flag since we'll delete the FTS databases in + // the next step. + sqlite3_exec(GetDB(), + "UPDATE urls " + "SET last_visit_time = last_visit_time + 11644473600000000 " + "WHERE id IN (SELECT id FROM urls WHERE last_visit_time > 0);", + NULL, NULL, NULL); + sqlite3_exec(GetDB(), + "UPDATE visits " + "SET visit_time = visit_time + 11644473600000000, is_indexed = 0 " + "WHERE id IN (SELECT id FROM visits WHERE visit_time > 0);", + NULL, NULL, NULL); + sqlite3_exec(GetDB(), + "UPDATE segment_usage " + "SET time_slot = time_slot + 11644473600000000 " + "WHERE id IN (SELECT id FROM segment_usage WHERE time_slot > 0);", + NULL, NULL, NULL); + + // Erase all the full text index files. These will take a while to update and + // are less important, so we just blow them away. Same with the archived + // database. + needs_version_17_migration_ = true; +} +#endif + } // namespace history diff --git a/chrome/browser/history/history_database.h b/chrome/browser/history/history_database.h index 35d8595..9351c26 100644 --- a/chrome/browser/history/history_database.h +++ b/chrome/browser/history/history_database.h @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_HISTORY_HISTORY_DATABASE_H_ #define CHROME_BROWSER_HISTORY_HISTORY_DATABASE_H_ +#include "build/build_config.h" #include "chrome/browser/history/download_database.h" #include "chrome/browser/history/history_types.h" #include "chrome/browser/history/starred_url_database.h" @@ -110,6 +111,17 @@ class HistoryDatabase : public DownloadDatabase, // unused space in the file. It can be VERY SLOW. void Vacuum(); + // Returns true if the history backend should erase the full text search + // and archived history files as part of version 16 -> 17 migration. The + // time format changed in this revision, and these files would be much slower + // to migrate. Since the data is less important, they should be deleted. + // + // This flag will be valid after Init() is called. It will always be false + // when running on Windows. + bool needs_version_17_migration() const { + return needs_version_17_migration_; + } + // Visit table functions ---------------------------------------------------- // Update the segment id of a visit. Return true on success. @@ -144,6 +156,12 @@ class HistoryDatabase : public DownloadDatabase, // may commit the transaction and start a new one if migration requires it. InitStatus EnsureCurrentVersion(const FilePath& tmp_bookmarks_path); +#if !defined(OS_WIN) + // Converts the time epoch in the database from being 1970-based to being + // 1601-based which corresponds to the change in Time.internal_value_. + void MigrateTimeEpoch(); +#endif + // --------------------------------------------------------------------------- // How many nested transactions are pending? When this gets to 0, we commit. @@ -162,6 +180,9 @@ class HistoryDatabase : public DownloadDatabase, MetaTableHelper meta_table_; base::Time cached_early_expiration_threshold_; + // See the getter above. + bool needs_version_17_migration_; + DISALLOW_COPY_AND_ASSIGN(HistoryDatabase); }; diff --git a/chrome/browser/password_manager/password_store_mac_unittest.cc b/chrome/browser/password_manager/password_store_mac_unittest.cc index c96a1a4..6eeeac7 100644 --- a/chrome/browser/password_manager/password_store_mac_unittest.cc +++ b/chrome/browser/password_manager/password_store_mac_unittest.cc @@ -232,9 +232,11 @@ TEST_F(PasswordStoreMacTest, TestKeychainToFormTranslation) { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security", "https://some.domain.com/", L"digest_auth_user", L"digest", true, 1998, 3, 30, 10, 0, 0 }, + // This one gives us an invalid date, which we will treat as a "NULL" date + // which is 1601. { PasswordForm::SCHEME_OTHER, "http://a.server.com/", "http://a.server.com/", L"abc", L"123", false, - 1970, 1, 1, 0, 0, 0 }, + 1601, 1, 1, 0, 0, 0 }, }; for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) { diff --git a/chrome/test/data/profiles/typical_history/Default/History b/chrome/test/data/profiles/typical_history/Default/History Binary files differindex b3d68c7..3251af2 100644 --- a/chrome/test/data/profiles/typical_history/Default/History +++ b/chrome/test/data/profiles/typical_history/Default/History diff --git a/net/base/cookie_monster_unittest.cc b/net/base/cookie_monster_unittest.cc index bb36585..1f0b45d 100644 --- a/net/base/cookie_monster_unittest.cc +++ b/net/base/cookie_monster_unittest.cc @@ -688,6 +688,17 @@ TEST(CookieMonsterTest, TestCookieDeletion) { std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); EXPECT_EQ("", cm.GetCookies(url_google)); + + // Create a persistent cookie. + EXPECT_TRUE(cm.SetCookie(url_google, + std::string(kValidCookieLine) + + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); + EXPECT_EQ("A=B", cm.GetCookies(url_google)); + // Delete it via Expires, with a unix epoch of 0. + EXPECT_TRUE(cm.SetCookie(url_google, + std::string(kValidCookieLine) + + "; expires=Thu, 1-Jan-1970 00:00:00 GMT")); + EXPECT_EQ("", cm.GetCookies(url_google)); } TEST(CookieMonsterTest, TestCookieDeleteAll) { |