[go: nahoru, domu]

blob: bf62c6bd68c3a2986aa7f11d76e21b0fa662f91b [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/public/session/crw_session_storage.h"
#import "base/apple/foundation_util.h"
#import "base/memory/ptr_util.h"
#import "base/metrics/histogram_functions.h"
#import "base/strings/utf_string_conversions.h"
#import "base/time/time.h"
#import "ios/web/common/features.h"
#import "ios/web/navigation/nscoder_util.h"
#import "ios/web/public/session/crw_navigation_item_storage.h"
#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
#import "ios/web/public/session/crw_session_user_data.h"
#import "ios/web/public/session/proto/metadata.pb.h"
#import "ios/web/public/session/proto/navigation.pb.h"
#import "ios/web/public/session/proto/proto_util.h"
#import "ios/web/public/session/proto/storage.pb.h"
namespace {
// Serialization keys used in NSCoding functions.
NSString* const kCertificatePolicyCacheStorageKey =
@"certificatePolicyCacheStorage";
NSString* const kCertificatePolicyCacheStorageDeprecatedKey =
@"certificatePolicyManager";
NSString* const kItemStoragesKey = @"entries";
NSString* const kHasOpenerKey = @"openedByDOM";
NSString* const kLastCommittedItemIndexKey = @"lastCommittedItemIndex";
NSString* const kUserAgentKey = @"userAgentKey";
NSString* const kStableIdentifierKey = @"stableIdentifier";
NSString* const kUniqueIdentifierKey = @"uniqueIdentifier";
NSString* const kSerializedUserDataKey = @"serializedUserData";
NSString* const kLastActiveTimeKey = @"lastActiveTime";
NSString* const kCreationTimeKey = @"creationTime";
// Deprecated, used for backward compatibility.
// TODO(crbug.com/1278308): Remove this key.
NSString* const kLastCommittedItemIndexDeprecatedKey =
@"currentNavigationIndex";
// Deprecated, used for backward compatibility for reading the stable
// identifier from the serializable user data as it was stored by the
// external tab helper.
// TODO(crbug.com/1278308): Remove this key.
NSString* const kTabIdKey = @"TabId";
}
@implementation CRWSessionStorage {
// The unique identifier, stored as the underlying type since SessionID
// has not public default constructor, thus cannot be an ivar/property.
SessionID::id_type _uniqueIdentifier;
}
- (instancetype)initWithProto:(const web::proto::WebStateStorage&)storage {
if ((self = [super init])) {
// As the protobuf message does not contain the unique or stable
// identifiers, generate new random values.
_uniqueIdentifier = SessionID::NewUnique().id();
_stableIdentifier = [[NSUUID UUID] UUIDString];
_hasOpener = storage.has_opener();
_userAgentType = web::UserAgentTypeFromProto(storage.user_agent());
_certPolicyCacheStorage = [[CRWSessionCertificatePolicyCacheStorage alloc]
initWithProto:storage.certs_cache()];
const web::proto::NavigationStorage& navigationStorage =
storage.navigation();
_lastCommittedItemIndex = navigationStorage.last_committed_item_index();
NSMutableArray<CRWNavigationItemStorage*>* itemStorages =
[[NSMutableArray alloc]
initWithCapacity:navigationStorage.items_size()];
for (const web::proto::NavigationItemStorage& itemStorage :
navigationStorage.items()) {
[itemStorages addObject:[[CRWNavigationItemStorage alloc]
initWithProto:itemStorage]];
}
_itemStorages = [itemStorages copy];
const web::proto::WebStateMetadataStorage& metadataStorage =
storage.metadata();
_creationTime = web::TimeFromProto(metadataStorage.creation_time());
_lastActiveTime = web::TimeFromProto(metadataStorage.last_active_time());
}
return self;
}
- (void)serializeToProto:(web::proto::WebStateStorage&)storage {
storage.set_has_opener(_hasOpener);
storage.set_user_agent(web::UserAgentTypeToProto(_userAgentType));
[_certPolicyCacheStorage serializeToProto:*storage.mutable_certs_cache()];
web::proto::NavigationStorage* navigationStorage =
storage.mutable_navigation();
navigationStorage->set_last_committed_item_index(_lastCommittedItemIndex);
for (CRWNavigationItemStorage* itemStorage in _itemStorages) {
[itemStorage serializeToProto:*navigationStorage->add_items()];
}
[self serializeMetadataToProto:*storage.mutable_metadata()];
}
- (void)serializeMetadataToProto:
(web::proto::WebStateMetadataStorage&)metadata {
web::SerializeTimeToProto(_creationTime, *metadata.mutable_creation_time());
web::SerializeTimeToProto(_lastActiveTime,
*metadata.mutable_last_active_time());
metadata.set_navigation_item_count(_itemStorages.count);
if (_lastCommittedItemIndex >= 0) {
NSUInteger const activePageIndex =
static_cast<NSUInteger>(_lastCommittedItemIndex);
if (activePageIndex < _itemStorages.count) {
CRWNavigationItemStorage* const activePageItem =
_itemStorages[activePageIndex];
web::proto::PageMetadataStorage* pageMetadataStorage =
metadata.mutable_active_page();
pageMetadataStorage->set_page_title(
base::UTF16ToUTF8(activePageItem.title));
GURL pageURL = activePageItem.virtualURL;
if (!pageURL.is_valid()) {
pageURL = activePageItem.URL;
}
pageMetadataStorage->set_page_url(pageURL.spec());
}
}
}
#pragma mark - NSCoding
- (instancetype)initWithCoder:(nonnull NSCoder*)decoder {
self = [super init];
if (self) {
_hasOpener = [decoder decodeBoolForKey:kHasOpenerKey];
if ([decoder containsValueForKey:kLastCommittedItemIndexKey]) {
_lastCommittedItemIndex =
[decoder decodeIntForKey:kLastCommittedItemIndexKey];
} else {
// Backward compatibility.
_lastCommittedItemIndex =
[decoder decodeIntForKey:kLastCommittedItemIndexDeprecatedKey];
}
_itemStorages = [[NSMutableArray alloc]
initWithArray:[decoder decodeObjectForKey:kItemStoragesKey]];
// Prior to M34, 0 was used as "no index" instead of -1; adjust for that.
if (!_itemStorages.count)
_lastCommittedItemIndex = -1;
_certPolicyCacheStorage =
[decoder decodeObjectForKey:kCertificatePolicyCacheStorageKey];
if (!_certPolicyCacheStorage) {
// If the cert policy cache was not found, attempt to decode using the
// deprecated serialization key.
// TODO(crbug.com/1278308): Remove this deprecated key once we remove
// support for legacy class conversions.
_certPolicyCacheStorage = [decoder
decodeObjectForKey:kCertificatePolicyCacheStorageDeprecatedKey];
}
id<NSCoding, NSObject> userData =
[decoder decodeObjectForKey:kSerializedUserDataKey];
if ([userData isKindOfClass:[CRWSessionUserData class]]) {
_userData = base::apple::ObjCCastStrict<CRWSessionUserData>(userData);
} else if ([userData isKindOfClass:[NSDictionary class]]) {
// Before M99, the user data was serialized by a C++ class that did
// serialize a NSDictionary<NSString*, id<NSCoding>>* directly.
// TODO(crbug.com/1278308): Remove this deprecated logic when we remove
// support for loading legacy sessions.
NSDictionary<NSString*, id<NSCoding>>* dictionary =
base::apple::ObjCCastStrict<NSDictionary>(userData);
_userData = [[CRWSessionUserData alloc] init];
for (NSString* key in dictionary) {
[_userData setObject:dictionary[key] forKey:key];
}
}
if ([decoder containsValueForKey:kUserAgentKey]) {
std::string userAgentDescription =
web::nscoder_util::DecodeString(decoder, kUserAgentKey);
_userAgentType =
web::GetUserAgentTypeWithDescription(userAgentDescription);
} else {
// Prior to M85, the UserAgent wasn't stored.
// TODO(crbug.com/1278308): Remove this deprecated logic when we
// remove support for loading legacy sessions.
_userAgentType = web::UserAgentType::AUTOMATIC;
}
_stableIdentifier = [decoder decodeObjectForKey:kStableIdentifierKey];
if (!_stableIdentifier.length) {
// Before M99, the stable identifier was managed by a tab helper and
// saved as part of the serializable user data. To support migration
// of pre M99 session, read the data from there if not found.
// If "TabId" is set, clear it and initialise the `stableIdentifier`
// from it (if it is a NSString and non empty, otherwise a new value
// will be created below).
id<NSCoding> tabIdValue = [_userData objectForKey:kTabIdKey];
if (tabIdValue) {
[_userData removeObjectForKey:kTabIdKey];
// If the value is not an NSString or is empty, a random identifier
// will be generated below.
_stableIdentifier = base::apple::ObjCCast<NSString>(tabIdValue);
}
}
// If no stable identifier was read, generate a new one (this simplify
// WebState session restoration code as it can assume that the property
// is never nil).
if (!_stableIdentifier.length) {
_stableIdentifier = [[NSUUID UUID] UUIDString];
}
// If no unique identifier was read, or it was invalid, generate a
// new one.
static_assert(sizeof(_uniqueIdentifier) == sizeof(int32_t));
_uniqueIdentifier = [decoder decodeInt32ForKey:kUniqueIdentifierKey];
if (!SessionID::IsValidValue(_uniqueIdentifier)) {
_uniqueIdentifier = SessionID::NewUnique().id();
}
// Force conversion to NSString if `_stableIdentifier` happens to be a
// NSMutableString (to prevent this value from being mutated).
_stableIdentifier = [_stableIdentifier copy];
DCHECK(_stableIdentifier.length);
if ([decoder containsValueForKey:kLastActiveTimeKey]) {
_lastActiveTime = base::Time::FromDeltaSinceWindowsEpoch(
base::Microseconds([decoder decodeInt64ForKey:kLastActiveTimeKey]));
}
if ([decoder containsValueForKey:kCreationTimeKey]) {
_creationTime = base::Time::FromDeltaSinceWindowsEpoch(
base::Microseconds([decoder decodeInt64ForKey:kCreationTimeKey]));
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder*)coder {
[coder encodeBool:self.hasOpener forKey:kHasOpenerKey];
[coder encodeInt:self.lastCommittedItemIndex
forKey:kLastCommittedItemIndexKey];
[coder encodeObject:self.itemStorages forKey:kItemStoragesKey];
size_t previous_cert_policy_bytes = web::GetCertPolicyBytesEncoded();
[coder encodeObject:self.certPolicyCacheStorage
forKey:kCertificatePolicyCacheStorageKey];
base::UmaHistogramCounts100000(
"Session.WebStates.SerializedCertPolicyCacheSize",
web::GetCertPolicyBytesEncoded() - previous_cert_policy_bytes / 1024);
if (_userData) {
[coder encodeObject:_userData forKey:kSerializedUserDataKey];
}
web::UserAgentType userAgentType = _userAgentType;
web::nscoder_util::EncodeString(
coder, kUserAgentKey, web::GetUserAgentTypeDescription(userAgentType));
[coder encodeObject:_stableIdentifier forKey:kStableIdentifierKey];
if (!_lastActiveTime.is_null()) {
[coder
encodeInt64:_lastActiveTime.ToDeltaSinceWindowsEpoch().InMicroseconds()
forKey:kLastActiveTimeKey];
}
if (!_creationTime.is_null()) {
[coder encodeInt64:_creationTime.ToDeltaSinceWindowsEpoch().InMicroseconds()
forKey:kCreationTimeKey];
}
if (SessionID::IsValidValue(_uniqueIdentifier)) {
static_assert(sizeof(_uniqueIdentifier) == sizeof(int32_t));
[coder encodeInt32:_uniqueIdentifier forKey:kUniqueIdentifierKey];
}
}
#pragma mark - Properties
- (SessionID)uniqueIdentifier {
DCHECK(SessionID::IsValidValue(_uniqueIdentifier));
return SessionID::FromSerializedValue(_uniqueIdentifier);
}
- (void)setUniqueIdentifier:(SessionID)uniqueIdentifier {
DCHECK(uniqueIdentifier.is_valid());
_uniqueIdentifier = uniqueIdentifier.id();
}
@end