blob: 736be29ce654290e757f8131d25697535b7743ac [file] [log] [blame]
// Copyright 2017 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_navigation_item_storage.h"
#import "base/apple/foundation_util.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "ios/web/navigation/nscoder_util.h"
#import "ios/web/navigation/proto_util.h"
#import "ios/web/public/session/proto/navigation.pb.h"
#import "ios/web/public/session/proto/proto_util.h"
#import "ios/web/public/web_client.h"
#import "net/base/apple/url_conversions.h"
namespace web {
// Keys used to serialize navigation properties.
NSString* const kNavigationItemStorageURLKey = @"urlString";
NSString* const kNavigationItemStorageVirtualURLKey = @"virtualUrlString";
NSString* const kNavigationItemStorageReferrerURLKey = @"referrerUrlString";
NSString* const kNavigationItemStorageReferrerURLDeprecatedKey = @"referrer";
NSString* const kNavigationItemStorageReferrerPolicyKey = @"referrerPolicy";
NSString* const kNavigationItemStorageTimestampKey = @"timestamp";
NSString* const kNavigationItemStorageTitleKey = @"title";
NSString* const kNavigationItemStorageHTTPRequestHeadersKey = @"httpHeaders";
NSString* const kNavigationItemStorageUserAgentTypeKey = @"userAgentType";
} // namespace web
@implementation CRWNavigationItemStorage {
GURL _URL;
GURL _virtualURL;
std::u16string _title;
}
- (instancetype)initWithProto:
(const web::proto::NavigationItemStorage&)storage {
if ((self = [super init])) {
_URL = GURL(storage.url());
_virtualURL = GURL(storage.virtual_url());
_title = base::UTF8ToUTF16(storage.title());
_timestamp = web::TimeFromProto(storage.timestamp());
_userAgentType = web::UserAgentTypeFromProto(storage.user_agent());
_referrer = web::ReferrerFromProto(storage.referrer());
_HTTPRequestHeaders =
web::HttpRequestHeadersFromProto(storage.http_request_headers());
}
return self;
}
- (void)serializeToProto:(web::proto::NavigationItemStorage&)storage {
if (_URL.is_valid()) {
storage.set_url(_URL.spec());
}
if (_virtualURL.is_valid()) {
storage.set_virtual_url(_virtualURL.spec());
}
storage.set_title(base::UTF16ToUTF8(_title));
web::SerializeTimeToProto(_timestamp, *storage.mutable_timestamp());
storage.set_user_agent(web::UserAgentTypeToProto(_userAgentType));
// To reduce disk usage, NavigationItemImpl does not serialize invalid
// referrer or empty HTTP header map. The helper function responsible
// for the serialisation enforces this with assertion, so skip items
// that should not be serialised.
if (_referrer.url.is_valid()) {
web::SerializeReferrerToProto(_referrer, *storage.mutable_referrer());
}
if (_HTTPRequestHeaders.count) {
web::SerializeHttpRequestHeadersToProto(
_HTTPRequestHeaders, *storage.mutable_http_request_headers());
}
}
#pragma mark - NSObject
- (BOOL)isEqual:(NSObject*)object {
CRWNavigationItemStorage* other =
base::apple::ObjCCast<CRWNavigationItemStorage>(object);
return [other cr_isEqualSameClass:self];
}
- (NSString*)description {
NSMutableString* description =
[NSMutableString stringWithString:[super description]];
[description appendFormat:@"URL : %s, ", _URL.spec().c_str()];
[description appendFormat:@"virtualURL : %s, ", _virtualURL.spec().c_str()];
[description appendFormat:@"referrer : %s, ", _referrer.url.spec().c_str()];
[description appendFormat:@"timestamp : %f, ", _timestamp.ToCFAbsoluteTime()];
[description appendFormat:@"title : %@, ", base::SysUTF16ToNSString(_title)];
[description
appendFormat:@"userAgentType : %s, ",
web::GetUserAgentTypeDescription(_userAgentType).c_str()];
[description appendFormat:@"HTTPRequestHeaders : %@", _HTTPRequestHeaders];
return description;
}
#pragma mark - NSCoding
- (instancetype)initWithCoder:(NSCoder*)aDecoder {
self = [super init];
if (self) {
// Desktop chrome only persists virtualUrl_ and uses it to feed the url
// when creating a NavigationEntry. Chrome on iOS is also storing _url.
if ([aDecoder
containsValueForKey:web::kNavigationItemStorageVirtualURLKey]) {
_virtualURL = GURL(web::nscoder_util::DecodeString(
aDecoder, web::kNavigationItemStorageVirtualURLKey));
}
if ([aDecoder containsValueForKey:web::kNavigationItemStorageURLKey]) {
_URL = GURL(web::nscoder_util::DecodeString(
aDecoder, web::kNavigationItemStorageURLKey));
}
if ([aDecoder
containsValueForKey:web::kNavigationItemStorageReferrerURLKey]) {
const std::string referrerString(web::nscoder_util::DecodeString(
aDecoder, web::kNavigationItemStorageReferrerURLKey));
web::ReferrerPolicy referrerPolicy =
static_cast<web::ReferrerPolicy>([aDecoder
decodeIntForKey:web::kNavigationItemStorageReferrerPolicyKey]);
_referrer = web::Referrer(GURL(referrerString), referrerPolicy);
} else {
// Backward compatibility.
NSURL* referrerURL =
[aDecoder decodeObjectForKey:
web::kNavigationItemStorageReferrerURLDeprecatedKey];
_referrer = web::Referrer(net::GURLWithNSURL(referrerURL),
web::ReferrerPolicyDefault);
}
if ([aDecoder
containsValueForKey:web::kNavigationItemStorageTimestampKey]) {
int64_t us =
[aDecoder decodeInt64ForKey:web::kNavigationItemStorageTimestampKey];
_timestamp = base::Time::FromInternalValue(us);
}
if ([aDecoder
containsValueForKey:web::kNavigationItemStorageUserAgentTypeKey]) {
std::string userAgentDescription = web::nscoder_util::DecodeString(
aDecoder, web::kNavigationItemStorageUserAgentTypeKey);
_userAgentType =
web::GetUserAgentTypeWithDescription(userAgentDescription);
} else if (web::GetWebClient()->IsAppSpecificURL(_virtualURL)) {
// Legacy CRWNavigationItemStorages didn't have the concept of a NONE
// user agent for app-specific URLs, so check decoded virtual URL before
// attempting to decode the deprecated key.
_userAgentType = web::UserAgentType::NONE;
}
NSString* title =
[aDecoder decodeObjectForKey:web::kNavigationItemStorageTitleKey];
// Use a transition type of reload so that we don't incorrectly increase
// the typed count. This is what desktop chrome does.
_title = base::SysNSStringToUTF16(title);
_HTTPRequestHeaders = [aDecoder
decodeObjectForKey:web::kNavigationItemStorageHTTPRequestHeadersKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder*)aCoder {
// Desktop Chrome doesn't persist `url_` or `originalUrl_`, only
// `virtualUrl_`. Chrome on iOS is persisting `url_`.
if (_virtualURL != _URL && _virtualURL.is_valid()) {
// In most cases _virtualURL is the same as URL. Not storing virtual URL
// will save memory during unarchiving.
const std::string& virtualURLSpec = _virtualURL.spec();
web::nscoder_util::EncodeString(
aCoder, web::kNavigationItemStorageVirtualURLKey, virtualURLSpec);
}
if (_URL.is_valid()) {
web::nscoder_util::EncodeString(aCoder, web::kNavigationItemStorageURLKey,
_URL.spec());
}
if (_referrer.url.is_valid()) {
web::nscoder_util::EncodeString(aCoder,
web::kNavigationItemStorageReferrerURLKey,
_referrer.url.spec());
}
[aCoder encodeInt:_referrer.policy
forKey:web::kNavigationItemStorageReferrerPolicyKey];
[aCoder encodeInt64:_timestamp.ToInternalValue()
forKey:web::kNavigationItemStorageTimestampKey];
[aCoder encodeObject:base::SysUTF16ToNSString(_title)
forKey:web::kNavigationItemStorageTitleKey];
web::nscoder_util::EncodeString(
aCoder, web::kNavigationItemStorageUserAgentTypeKey,
web::GetUserAgentTypeDescription(_userAgentType));
[aCoder encodeObject:_HTTPRequestHeaders
forKey:web::kNavigationItemStorageHTTPRequestHeadersKey];
}
#pragma mark - Properties
- (const GURL&)URL {
return _URL;
}
- (void)setURL:(const GURL&)URL {
_URL = URL;
}
- (const GURL&)virtualURL {
// virtualURL is not stored (see -encodeWithCoder:) if it's the same as URL.
// This logic repeats NavigationItemImpl::GetURL to store virtualURL only when
// different from URL.
return _virtualURL.is_empty() ? _URL : _virtualURL;
}
- (void)setVirtualURL:(const GURL&)virtualURL {
_virtualURL = virtualURL;
}
- (const std::u16string&)title {
return _title;
}
- (void)setTitle:(const std::u16string&)title {
_title = title;
}
#pragma mark Private
- (BOOL)cr_isEqualSameClass:(CRWNavigationItemStorage*)other {
if (_URL != other.URL) {
return NO;
}
// -virtualURL getter is complex and does not always return `_virtualURL`,
// so use the property for both `self` and `other` to ensure correctness.
if (self.virtualURL != other.virtualURL) {
return NO;
}
if (_referrer != other.referrer) {
return NO;
}
if (_timestamp != other.timestamp) {
return NO;
}
if (_title != other.title) {
return NO;
}
if (_userAgentType != other.userAgentType) {
return NO;
}
if (_HTTPRequestHeaders != other.HTTPRequestHeaders &&
![_HTTPRequestHeaders isEqual:other.HTTPRequestHeaders]) {
return NO;
}
return YES;
}
@end