1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "ios/web/navigation/crw_session_entry.h"
7 #include "base/mac/objc_property_releaser.h"
8 #include "base/mac/scoped_nsobject.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "ios/web/navigation/navigation_item_impl.h"
12 #include "ios/web/navigation/nscoder_util.h"
13 #include "ios/web/public/navigation_item.h"
14 #include "ios/web/public/web_state/page_display_state.h"
15 #import "net/base/mac/url_conversions.h"
18 // Keys used to serialize web::PageScrollState properties.
19 NSString* const kSessionEntryPageScrollStateKey = @"state";
20 NSString* const kSessionEntryScrollOffsetXKey = @"scrollX";
21 NSString* const kSessionEntryScrollOffsetYKey = @"scrollY";
22 NSString* const kSessionEntryMinimumZoomScaleKey = @"minZoom";
23 NSString* const kSessionEntryMaximumZoomScaleKey = @"maxZoom";
24 NSString* const kSessionEntryZoomScaleKey = @"zoom";
26 // Keys used to serialize navigation properties.
27 NSString* const kSessionEntryURLKey = @"virtualUrlString";
28 NSString* const kSessionEntryURLDeperecatedKey = @"virtualUrl";
29 NSString* const kSessionEntryReferrerURLKey = @"referrerUrlString";
30 NSString* const kSessionEntryReferrerURLDeprecatedKey = @"referrer";
31 NSString* const kSessionEntryReferrerPolicyKey = @"referrerPolicy";
32 NSString* const kSessionEntryTimestampKey = @"timestamp";
33 NSString* const kSessionEntryTitleKey = @"title";
34 NSString* const kSessionEntryPOSTDataKey = @"POSTData";
35 NSString* const kSessionEntryHTTPRequestHeadersKey = @"httpHeaders";
36 NSString* const kSessionEntrySkipResubmitConfirmationKey =
37 @"skipResubmitDataConfirmation";
38 NSString* const kSessionEntryUseDesktopUserAgentKey = @"useDesktopUserAgent";
41 @interface CRWSessionEntry () {
42 // The original URL of the page. In cases where a redirect occurred, |url_|
43 // will contain the final post-redirect URL, and |originalUrl_| will contain
44 // the pre-redirect URL. This field is not persisted to disk.
47 // The NavigationItemImpl corresponding to this CRWSessionEntry.
48 // TODO(stuartmorgan): Move ownership to NavigationManagerImpl.
49 scoped_ptr<web::NavigationItemImpl> _navigationItem;
51 base::mac::ObjCPropertyReleaser _propertyReleaser_CRWSessionEntry;
53 // Redefine originalUrl to be read-write.
54 @property(nonatomic, readwrite) const GURL& originalUrl;
56 // Converts a serialized NSDictionary to a web::PageDisplayState.
57 + (web::PageDisplayState)pageDisplayStateFromDictionary:
58 (NSDictionary*)dictionary;
59 // Serializes a web::PageDisplayState to an NSDictionary.
60 + (NSDictionary*)dictionaryFromPageDisplayState:
61 (const web::PageDisplayState&)displayState;
62 // Returns a readable description of |displayState|.
63 + (NSString*)descriptionForPageDisplayState:
64 (const web::PageDisplayState&)displayState;
67 @implementation CRWSessionEntry
69 @synthesize originalUrl = _originalUrl;
71 - (instancetype)initWithNavigationItem:(scoped_ptr<web::NavigationItem>)item {
74 _propertyReleaser_CRWSessionEntry.Init(self, [CRWSessionEntry class]);
75 _navigationItem.reset(
76 static_cast<web::NavigationItemImpl*>(item.release()));
77 self.originalUrl = _navigationItem->GetURL();
82 - (instancetype)initWithCoder:(NSCoder*)aDecoder {
85 _propertyReleaser_CRWSessionEntry.Init(self, [CRWSessionEntry class]);
86 _navigationItem.reset(new web::NavigationItemImpl());
88 // Desktop chrome only persists virtualUrl_ and uses it to feed the url
89 // when creating a NavigationEntry.
91 if ([aDecoder containsValueForKey:web::kSessionEntryURLKey]) {
93 web::nscoder_util::DecodeString(aDecoder, web::kSessionEntryURLKey));
95 // Backward compatibility.
96 url = net::GURLWithNSURL(
97 [aDecoder decodeObjectForKey:web::kSessionEntryURLDeperecatedKey]);
99 _navigationItem->SetURL(url);
100 self.originalUrl = url;
102 if ([aDecoder containsValueForKey:web::kSessionEntryReferrerURLKey]) {
103 const std::string referrerString(web::nscoder_util::DecodeString(
104 aDecoder, web::kSessionEntryReferrerURLKey));
105 web::ReferrerPolicy referrerPolicy = static_cast<web::ReferrerPolicy>(
106 [aDecoder decodeIntForKey:web::kSessionEntryReferrerPolicyKey]);
107 _navigationItem->SetReferrer(
108 web::Referrer(GURL(referrerString), referrerPolicy));
110 // Backward compatibility.
111 NSURL* referrer = [aDecoder
112 decodeObjectForKey:web::kSessionEntryReferrerURLDeprecatedKey];
113 _navigationItem->SetReferrer(web::Referrer(
114 net::GURLWithNSURL(referrer), web::ReferrerPolicyDefault));
117 if ([aDecoder containsValueForKey:web::kSessionEntryTimestampKey]) {
118 int64 us = [aDecoder decodeInt64ForKey:web::kSessionEntryTimestampKey];
119 _navigationItem->SetTimestamp(base::Time::FromInternalValue(us));
122 NSString* title = [aDecoder decodeObjectForKey:web::kSessionEntryTitleKey];
123 // Use a transition type of reload so that we don't incorrectly increase
124 // the typed count. This is what desktop chrome does.
125 _navigationItem->SetTitle(base::SysNSStringToUTF16(title));
126 _navigationItem->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
127 _navigationItem->SetPageDisplayState([[self class]
128 pageDisplayStateFromDictionary:
130 decodeObjectForKey:web::kSessionEntryPageScrollStateKey]]);
131 _navigationItem->SetShouldSkipResubmitDataConfirmation([aDecoder
132 decodeBoolForKey:web::kSessionEntrySkipResubmitConfirmationKey]);
133 _navigationItem->SetIsOverridingUserAgent(
134 [aDecoder decodeBoolForKey:web::kSessionEntryUseDesktopUserAgentKey]);
135 _navigationItem->SetPostData(
136 [aDecoder decodeObjectForKey:web::kSessionEntryPOSTDataKey]);
137 _navigationItem->AddHttpRequestHeaders(
138 [aDecoder decodeObjectForKey:web::kSessionEntryHTTPRequestHeadersKey]);
143 - (void)encodeWithCoder:(NSCoder*)aCoder {
144 // Desktop Chrome doesn't persist |url_| or |originalUrl_|, only
146 web::nscoder_util::EncodeString(aCoder, web::kSessionEntryURLKey,
147 _navigationItem->GetVirtualURL().spec());
148 web::nscoder_util::EncodeString(aCoder, web::kSessionEntryReferrerURLKey,
149 _navigationItem->GetReferrer().url.spec());
150 [aCoder encodeInt:_navigationItem->GetReferrer().policy
151 forKey:web::kSessionEntryReferrerPolicyKey];
152 [aCoder encodeInt64:_navigationItem->GetTimestamp().ToInternalValue()
153 forKey:web::kSessionEntryTimestampKey];
155 [aCoder encodeObject:base::SysUTF16ToNSString(_navigationItem->GetTitle())
156 forKey:web::kSessionEntryTitleKey];
157 [aCoder encodeObject:[[self class] dictionaryFromPageDisplayState:
158 _navigationItem->GetPageDisplayState()]
159 forKey:web::kSessionEntryPageScrollStateKey];
160 [aCoder encodeBool:_navigationItem->ShouldSkipResubmitDataConfirmation()
161 forKey:web::kSessionEntrySkipResubmitConfirmationKey];
162 [aCoder encodeBool:_navigationItem->IsOverridingUserAgent()
163 forKey:web::kSessionEntryUseDesktopUserAgentKey];
164 [aCoder encodeObject:_navigationItem->GetPostData()
165 forKey:web::kSessionEntryPOSTDataKey];
166 [aCoder encodeObject:_navigationItem->GetHttpRequestHeaders()
167 forKey:web::kSessionEntryHTTPRequestHeadersKey];
170 // TODO(ios): Shall we overwrite EqualTo:?
172 - (instancetype)copyWithZone:(NSZone*)zone {
173 CRWSessionEntry* copy = [[[self class] alloc] init];
174 copy->_propertyReleaser_CRWSessionEntry.Init(copy, [CRWSessionEntry class]);
175 copy->_navigationItem.reset(
176 new web::NavigationItemImpl(*_navigationItem.get()));
177 copy->_originalUrl = _originalUrl;
181 - (NSString*)description {
184 @"url:%@ originalurl:%@ title:%@ transition:%d displayState:%@ "
186 base::SysUTF8ToNSString(_navigationItem->GetURL().spec()),
187 base::SysUTF8ToNSString(self.originalUrl.spec()),
188 base::SysUTF16ToNSString(_navigationItem->GetTitle()),
189 _navigationItem->GetTransitionType(),
190 [[self class] descriptionForPageDisplayState:
191 _navigationItem->GetPageDisplayState()],
192 _navigationItem->IsOverridingUserAgent()];
195 - (web::NavigationItem*)navigationItem {
196 return _navigationItem.get();
199 - (web::NavigationItemImpl*)navigationItemImpl {
200 return _navigationItem.get();
203 #pragma mark - Serialization helpers
205 + (web::PageDisplayState)pageDisplayStateFromDictionary:
206 (NSDictionary*)dictionary {
207 NSNumber* serializedValue = nil;
208 web::PageScrollState scrollState;
209 if ((serializedValue = dictionary[web::kSessionEntryScrollOffsetXKey]))
210 scrollState.set_offset_x([serializedValue doubleValue]);
211 if ((serializedValue = dictionary[web::kSessionEntryScrollOffsetYKey]))
212 scrollState.set_offset_y([serializedValue doubleValue]);
213 web::PageZoomState zoomState;
214 if ((serializedValue = dictionary[web::kSessionEntryMinimumZoomScaleKey]))
215 zoomState.set_minimum_zoom_scale([serializedValue doubleValue]);
216 if ((serializedValue = dictionary[web::kSessionEntryMaximumZoomScaleKey]))
217 zoomState.set_maximum_zoom_scale([serializedValue doubleValue]);
218 if ((serializedValue = dictionary[web::kSessionEntryZoomScaleKey]))
219 zoomState.set_zoom_scale([serializedValue doubleValue]);
220 return web::PageDisplayState(scrollState, zoomState);
223 + (NSDictionary*)dictionaryFromPageDisplayState:
224 (const web::PageDisplayState&)displayState {
226 web::kSessionEntryScrollOffsetXKey :
227 @(displayState.scroll_state().offset_x()),
228 web::kSessionEntryScrollOffsetYKey :
229 @(displayState.scroll_state().offset_y()),
230 web::kSessionEntryMinimumZoomScaleKey :
231 @(displayState.zoom_state().minimum_zoom_scale()),
232 web::kSessionEntryMaximumZoomScaleKey :
233 @(displayState.zoom_state().maximum_zoom_scale()),
234 web::kSessionEntryZoomScaleKey :
235 @(displayState.zoom_state().zoom_scale())
239 + (NSString*)descriptionForPageDisplayState:
240 (const web::PageDisplayState&)displayState {
241 NSString* const kPageScrollStateDescriptionFormat =
242 @"{ scrollOffset:(%0.2f, %0.2f), zoomScaleRange:(%0.2f, %0.2f), "
243 @"zoomScale:%0.2f }";
245 [NSString stringWithFormat:kPageScrollStateDescriptionFormat,
246 displayState.scroll_state().offset_x(),
247 displayState.scroll_state().offset_y(),
248 displayState.zoom_state().minimum_zoom_scale(),
249 displayState.zoom_state().maximum_zoom_scale(),
250 displayState.zoom_state().zoom_scale()];