blob: 48a90eaf650714c9ff8f90cf4e7ab9d54bcfb3d9 [file] [log] [blame]
// Copyright (c) 2012 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 "ios/net/cookies/cookie_creation_time_manager.h"
#include <Foundation/Foundation.h>
#include <stddef.h>
#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
#include "base/time/time.h"
// Key holding the creation-time in NSHTTPCookie properties.
// This key is undocumented, and its value has type NSNumber.
NSString* const kHTTPCookieCreated = @"Created";
namespace {
// Gets the creation date of a NSHTTPCookie, reading a undocumented property.
base::Time GetCreationTimeFromObject(NSHTTPCookie* cookie) {
// The "Created" key is not documented.
// Return a null time if the key is missing.
id created = [[cookie properties] objectForKey:kHTTPCookieCreated];
DCHECK(created && [created isKindOfClass:[NSNumber class]]);
if (!created || ![created isKindOfClass:[NSNumber class]])
return base::Time();
// created is the time from January 1st, 2001 in seconds.
CFAbsoluteTime absolute_time = [(NSNumber*)created doubleValue];
// If the cookie has been created using |-initWithProperties:|, the creation
// date property is (incorrectly) set to 1.0. Treat that as an invalid time.
if (absolute_time < 2.0)
return base::Time();
return base::Time::FromCFAbsoluteTime(absolute_time);
}
// Gets a string that can be used as a unique identifier for |cookie|.
std::string GetCookieUniqueID(NSHTTPCookie* cookie) {
return base::SysNSStringToUTF8([cookie name]) + ";" +
base::SysNSStringToUTF8([cookie domain]) + ";" +
base::SysNSStringToUTF8([cookie path]);
}
} // namespace
namespace net {
CookieCreationTimeManager::CookieCreationTimeManager() {
}
CookieCreationTimeManager::~CookieCreationTimeManager() {
}
void CookieCreationTimeManager::SetCreationTime(
NSHTTPCookie* cookie,
const base::Time& creation_time) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(unique_times_.find(creation_time) == unique_times_.end());
// If the cookie overrides an existing cookie, remove its creation time.
auto it = creation_times_.find(GetCookieUniqueID(cookie));
if (it != creation_times_.end()) {
size_t erased = unique_times_.erase(it->second);
DCHECK_EQ(1u, erased);
}
unique_times_.insert(creation_time);
creation_times_[GetCookieUniqueID(cookie)] = creation_time;
}
base::Time CookieCreationTimeManager::MakeUniqueCreationTime(
const base::Time& creation_time) {
DCHECK(thread_checker_.CalledOnValidThread());
auto it = unique_times_.find(creation_time);
if (it == unique_times_.end())
return creation_time;
// If the time already exist, increment until we find a time available.
// |unique_times_| is sorted according to time, so we can traverse it from
// |it|.
base::Time time = creation_time;
do {
DCHECK(time == *it);
++it;
time = base::Time::FromInternalValue(time.ToInternalValue() + 1);
} while (it != unique_times_.end() && *it == time);
return time;
}
base::Time CookieCreationTimeManager::GetCreationTime(NSHTTPCookie* cookie) {
DCHECK(thread_checker_.CalledOnValidThread());
base::hash_map<std::string, base::Time>::iterator it =
creation_times_.find(GetCookieUniqueID(cookie));
if (it != creation_times_.end())
return it->second;
base::Time native_creation_time = GetCreationTimeFromObject(cookie);
native_creation_time = MakeUniqueCreationTime(native_creation_time);
SetCreationTime(cookie, native_creation_time);
return native_creation_time;
}
void CookieCreationTimeManager::DeleteCreationTime(NSHTTPCookie* cookie) {
DCHECK(thread_checker_.CalledOnValidThread());
auto it = creation_times_.find(GetCookieUniqueID(cookie));
if (it != creation_times_.end()) {
size_t erased = unique_times_.erase(it->second);
DCHECK_EQ(1u, erased);
creation_times_.erase(it);
}
}
void CookieCreationTimeManager::Clear() {
DCHECK(thread_checker_.CalledOnValidThread());
creation_times_.clear();
unique_times_.clear();
}
} // namespace net