OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #import "ios/web/navigation/crw_session_entry.h" |
| 6 |
| 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_scroll_state.h" |
| 15 #import "net/base/mac/url_conversions.h" |
| 16 |
| 17 namespace { |
| 18 // Keys used to serialize web::PageScrollState properties. |
| 19 NSString* const kScrollOffsetXKey = @"scrollX"; |
| 20 NSString* const kScrollOffsetYKey = @"scrollY"; |
| 21 NSString* const kMinimumZoomScaleKey = @"minZoom"; |
| 22 NSString* const kMaximumZoomScaleKey = @"maxZoom"; |
| 23 NSString* const kZoomScaleKey = @"zoom"; |
| 24 } |
| 25 |
| 26 @interface CRWSessionEntry () { |
| 27 // The index in the CRWSessionController. |
| 28 // |
| 29 // This is used when determining the selected CRWSessionEntry and only useful |
| 30 // to the SessionServiceIOS. |
| 31 NSInteger _index; |
| 32 |
| 33 // The original URL of the page. In cases where a redirect occurred, |url_| |
| 34 // will contain the final post-redirect URL, and |originalUrl_| will contain |
| 35 // the pre-redirect URL. This field is not persisted to disk. |
| 36 GURL _originalUrl; |
| 37 |
| 38 // Headers passed along with the request. For POST requests, these are |
| 39 // persisted, to be able to resubmit them. Some specialized non-POST requests |
| 40 // may also pass custom headers. |
| 41 base::scoped_nsobject<NSMutableDictionary> _httpHeaders; |
| 42 |
| 43 // Data submitted with a POST request, persisted for resubmits. |
| 44 NSData* _POSTData; |
| 45 |
| 46 // Serialized representation of the state object that was used in conjunction |
| 47 // with a JavaScript window.history.pushState() or |
| 48 // window.history.replaceState() call that created or modified this |
| 49 // CRWSessionEntry. Intended to be used for JavaScript history operations and |
| 50 // will be nil in most cases. |
| 51 NSString* _serializedStateObject; |
| 52 |
| 53 // Whether or not this entry was created by calling history.pushState(). |
| 54 BOOL _createdFromPushState; |
| 55 |
| 56 // If |YES| use a desktop user agent in HTTP requests and UIWebView. |
| 57 BOOL _useDesktopUserAgent; |
| 58 |
| 59 // If |YES| the page was last fetched through the data reduction proxy. |
| 60 BOOL _usedDataReductionProxy; |
| 61 |
| 62 // Whether or not to bypass showing the resubmit data confirmation when |
| 63 // loading a POST request. Set to YES for browser-generated POST requests such |
| 64 // as search-by-image requests. |
| 65 BOOL _skipResubmitDataConfirmation; |
| 66 |
| 67 // The NavigationItemImpl corresponding to this CRWSessionEntry. |
| 68 // TODO(stuartmorgan): Move ownership to NavigationManagerImpl. |
| 69 scoped_ptr<web::NavigationItemImpl> _navigationItem; |
| 70 |
| 71 base::mac::ObjCPropertyReleaser _propertyReleaser_CRWSessionEntry; |
| 72 } |
| 73 // Redefine originalUrl to be read-write. |
| 74 @property(nonatomic, readwrite) const GURL& originalUrl; |
| 75 |
| 76 // Converts a serialized NSDictionary to a web::PageScrollState. |
| 77 + (web::PageScrollState)scrollStateFromDictionary:(NSDictionary*)dictionary; |
| 78 // Serializes a web::PageScrollState to an NSDictionary. |
| 79 + (NSDictionary*)dictionaryFromScrollState: |
| 80 (const web::PageScrollState&)scrollState; |
| 81 // Returns a readable description of |scrollState|. |
| 82 + (NSString*)scrollStateDescription:(const web::PageScrollState&)scrollState; |
| 83 @end |
| 84 |
| 85 @implementation CRWSessionEntry |
| 86 |
| 87 @synthesize POSTData = _POSTData; |
| 88 @synthesize originalUrl = _originalUrl; |
| 89 @synthesize useDesktopUserAgent = _useDesktopUserAgent; |
| 90 @synthesize usedDataReductionProxy = _usedDataReductionProxy; |
| 91 @synthesize index = _index; |
| 92 @synthesize serializedStateObject = _serializedStateObject; |
| 93 @synthesize createdFromPushState = _createdFromPushState; |
| 94 @synthesize skipResubmitDataConfirmation = _skipResubmitDataConfirmation; |
| 95 |
| 96 // Creates a new session entry. These may be nil. |
| 97 - (instancetype)initWithUrl:(const GURL&)url |
| 98 referrer:(const web::Referrer&)referrer |
| 99 transition:(ui::PageTransition)transition |
| 100 useDesktopUserAgent:(BOOL)useDesktopUserAgent |
| 101 rendererInitiated:(BOOL)rendererInitiated { |
| 102 self = [super init]; |
| 103 if (self) { |
| 104 _propertyReleaser_CRWSessionEntry.Init(self, [CRWSessionEntry class]); |
| 105 _navigationItem.reset(new web::NavigationItemImpl()); |
| 106 |
| 107 _navigationItem->SetURL(url); |
| 108 _navigationItem->SetReferrer(referrer); |
| 109 _navigationItem->SetTransitionType(transition); |
| 110 _navigationItem->set_is_renderer_initiated(rendererInitiated); |
| 111 |
| 112 self.originalUrl = url; |
| 113 self.useDesktopUserAgent = useDesktopUserAgent; |
| 114 } |
| 115 return self; |
| 116 } |
| 117 |
| 118 - (instancetype)initWithNavigationItem:(scoped_ptr<web::NavigationItem>)item |
| 119 index:(int)index { |
| 120 self = [super init]; |
| 121 if (self) { |
| 122 _propertyReleaser_CRWSessionEntry.Init(self, [CRWSessionEntry class]); |
| 123 _navigationItem.reset( |
| 124 static_cast<web::NavigationItemImpl*>(item.release())); |
| 125 |
| 126 self.index = index; |
| 127 self.originalUrl = _navigationItem->GetURL(); |
| 128 self.useDesktopUserAgent = NO; |
| 129 } |
| 130 return self; |
| 131 } |
| 132 |
| 133 - (instancetype)initWithCoder:(NSCoder*)aDecoder { |
| 134 self = [super init]; |
| 135 if (self) { |
| 136 _propertyReleaser_CRWSessionEntry.Init(self, [CRWSessionEntry class]); |
| 137 _navigationItem.reset(new web::NavigationItemImpl()); |
| 138 |
| 139 // Desktop chrome only persists virtualUrl_ and uses it to feed the url |
| 140 // when creating a NavigationEntry. |
| 141 GURL url; |
| 142 if ([aDecoder containsValueForKey:@"virtualUrlString"]) { |
| 143 url = GURL( |
| 144 web::nscoder_util::DecodeString(aDecoder, @"virtualUrlString")); |
| 145 } else { |
| 146 // Backward compatibility. |
| 147 url = net::GURLWithNSURL([aDecoder decodeObjectForKey:@"virtualUrl"]); |
| 148 } |
| 149 _navigationItem->SetURL(url); |
| 150 self.originalUrl = url; |
| 151 |
| 152 if ([aDecoder containsValueForKey:@"referrerUrlString"]) { |
| 153 const std::string referrerString(web::nscoder_util::DecodeString( |
| 154 aDecoder, @"referrerUrlString")); |
| 155 web::ReferrerPolicy referrerPolicy = |
| 156 static_cast<web::ReferrerPolicy>( |
| 157 [aDecoder decodeIntForKey:@"referrerPolicy"]); |
| 158 _navigationItem->SetReferrer( |
| 159 web::Referrer(GURL(referrerString), referrerPolicy)); |
| 160 } else { |
| 161 // Backward compatibility. |
| 162 NSURL* referrer = [aDecoder decodeObjectForKey:@"referrer"]; |
| 163 _navigationItem->SetReferrer(web::Referrer( |
| 164 net::GURLWithNSURL(referrer), web::ReferrerPolicyDefault)); |
| 165 } |
| 166 |
| 167 if ([aDecoder containsValueForKey:@"timestamp"]) { |
| 168 int64 us = [aDecoder decodeInt64ForKey:@"timestamp"]; |
| 169 _navigationItem->SetTimestamp(base::Time::FromInternalValue(us)); |
| 170 } |
| 171 |
| 172 NSString* title = [aDecoder decodeObjectForKey:@"title"]; |
| 173 // Use a transition type of reload so that we don't incorrectly increase |
| 174 // the typed count. This is what desktop chrome does. |
| 175 _navigationItem->SetPageID(-1); |
| 176 _navigationItem->SetTitle(base::SysNSStringToUTF16(title)); |
| 177 _navigationItem->SetTransitionType(ui::PAGE_TRANSITION_RELOAD); |
| 178 _navigationItem->SetPageScrollState([[self class] |
| 179 scrollStateFromDictionary:[aDecoder decodeObjectForKey:@"state"]]); |
| 180 self.index = [aDecoder decodeIntForKey:@"index"]; |
| 181 self.useDesktopUserAgent = |
| 182 [aDecoder decodeBoolForKey:@"useDesktopUserAgent"]; |
| 183 self.usedDataReductionProxy = |
| 184 [aDecoder decodeBoolForKey:@"usedDataReductionProxy"]; |
| 185 [self addHTTPHeaders:[aDecoder decodeObjectForKey:@"httpHeaders"]]; |
| 186 self.POSTData = [aDecoder decodeObjectForKey:@"POSTData"]; |
| 187 self.skipResubmitDataConfirmation = |
| 188 [aDecoder decodeBoolForKey:@"skipResubmitDataConfirmation"]; |
| 189 } |
| 190 return self; |
| 191 } |
| 192 |
| 193 - (void)encodeWithCoder:(NSCoder*)aCoder { |
| 194 // Desktop Chrome doesn't persist |url_| or |originalUrl_|, only |
| 195 // |virtualUrl_|. |
| 196 [aCoder encodeInt:self.index forKey:@"index"]; |
| 197 web::nscoder_util::EncodeString(aCoder, @"virtualUrlString", |
| 198 _navigationItem->GetVirtualURL().spec()); |
| 199 web::nscoder_util::EncodeString(aCoder, @"referrerUrlString", |
| 200 _navigationItem->GetReferrer().url.spec()); |
| 201 [aCoder encodeInt:_navigationItem->GetReferrer().policy |
| 202 forKey:@"referrerPolicy"]; |
| 203 [aCoder encodeInt64:_navigationItem->GetTimestamp().ToInternalValue() |
| 204 forKey:@"timestamp"]; |
| 205 |
| 206 [aCoder encodeObject:base::SysUTF16ToNSString(_navigationItem->GetTitle()) |
| 207 forKey:@"title"]; |
| 208 [aCoder encodeObject:[[self class] dictionaryFromScrollState: |
| 209 _navigationItem->GetPageScrollState()] |
| 210 forKey:@"state"]; |
| 211 [aCoder encodeBool:self.useDesktopUserAgent forKey:@"useDesktopUserAgent"]; |
| 212 [aCoder encodeBool:self.usedDataReductionProxy |
| 213 forKey:@"usedDataReductionProxy"]; |
| 214 [aCoder encodeObject:self.httpHeaders forKey:@"httpHeaders"]; |
| 215 [aCoder encodeObject:self.POSTData forKey:@"POSTData"]; |
| 216 [aCoder encodeBool:self.skipResubmitDataConfirmation |
| 217 forKey:@"skipResubmitDataConfirmation"]; |
| 218 } |
| 219 |
| 220 // TODO(ios): Shall we overwrite EqualTo:? |
| 221 |
| 222 - (instancetype)copyWithZone:(NSZone*)zone { |
| 223 CRWSessionEntry* copy = [[[self class] alloc] init]; |
| 224 copy->_propertyReleaser_CRWSessionEntry.Init(copy, [CRWSessionEntry class]); |
| 225 copy->_navigationItem.reset( |
| 226 new web::NavigationItemImpl(*_navigationItem.get())); |
| 227 copy->_index = _index; |
| 228 copy->_originalUrl = _originalUrl; |
| 229 copy->_useDesktopUserAgent = _useDesktopUserAgent; |
| 230 copy->_usedDataReductionProxy = _usedDataReductionProxy; |
| 231 copy->_POSTData = [_POSTData copy]; |
| 232 copy->_httpHeaders.reset([_httpHeaders mutableCopy]); |
| 233 copy->_skipResubmitDataConfirmation = _skipResubmitDataConfirmation; |
| 234 return copy; |
| 235 } |
| 236 |
| 237 - (NSString*)description { |
| 238 return [NSString |
| 239 stringWithFormat: |
| 240 @"url:%@ originalurl:%@ title:%@ transition:%d scrollState:%@ " |
| 241 @"desktopUA:%d " @"proxy:%d", |
| 242 base::SysUTF8ToNSString(_navigationItem->GetURL().spec()), |
| 243 base::SysUTF8ToNSString(self.originalUrl.spec()), |
| 244 base::SysUTF16ToNSString(_navigationItem->GetTitle()), |
| 245 _navigationItem->GetTransitionType(), |
| 246 [[self class] |
| 247 scrollStateDescription:_navigationItem->GetPageScrollState()], |
| 248 _useDesktopUserAgent, _usedDataReductionProxy]; |
| 249 } |
| 250 |
| 251 - (web::NavigationItem*)navigationItem { |
| 252 return _navigationItem.get(); |
| 253 } |
| 254 |
| 255 - (NSDictionary*)httpHeaders { |
| 256 return _httpHeaders ? [NSDictionary dictionaryWithDictionary:_httpHeaders] |
| 257 : nil; |
| 258 } |
| 259 |
| 260 - (void)addHTTPHeaders:(NSDictionary*)moreHTTPHeaders { |
| 261 if (_httpHeaders) |
| 262 [_httpHeaders addEntriesFromDictionary:moreHTTPHeaders]; |
| 263 else |
| 264 _httpHeaders.reset([moreHTTPHeaders mutableCopy]); |
| 265 } |
| 266 |
| 267 - (void)removeHTTPHeaderForKey:(NSString*)key { |
| 268 [_httpHeaders removeObjectForKey:key]; |
| 269 if (![_httpHeaders count]) |
| 270 _httpHeaders.reset(); |
| 271 } |
| 272 |
| 273 - (void)resetHTTPHeaders { |
| 274 _httpHeaders.reset(); |
| 275 } |
| 276 |
| 277 #pragma mark - Serialization helpers |
| 278 |
| 279 + (web::PageScrollState)scrollStateFromDictionary:(NSDictionary*)dictionary { |
| 280 web::PageScrollState scrollState; |
| 281 NSNumber* serializedValue = nil; |
| 282 if ((serializedValue = dictionary[kScrollOffsetXKey])) |
| 283 scrollState.set_scroll_offset_x([serializedValue doubleValue]); |
| 284 if ((serializedValue = dictionary[kScrollOffsetYKey])) |
| 285 scrollState.set_scroll_offset_y([serializedValue doubleValue]); |
| 286 if ((serializedValue = dictionary[kMinimumZoomScaleKey])) |
| 287 scrollState.set_minimum_zoom_scale([serializedValue doubleValue]); |
| 288 if ((serializedValue = dictionary[kMaximumZoomScaleKey])) |
| 289 scrollState.set_maximum_zoom_scale([serializedValue doubleValue]); |
| 290 if ((serializedValue = dictionary[kZoomScaleKey])) |
| 291 scrollState.set_zoom_scale([serializedValue doubleValue]); |
| 292 return scrollState; |
| 293 } |
| 294 |
| 295 + (NSDictionary*)dictionaryFromScrollState: |
| 296 (const web::PageScrollState&)scrollState { |
| 297 return @{ |
| 298 kScrollOffsetXKey : @(scrollState.scroll_offset_x()), |
| 299 kScrollOffsetYKey : @(scrollState.scroll_offset_y()), |
| 300 kMinimumZoomScaleKey : @(scrollState.minimum_zoom_scale()), |
| 301 kMaximumZoomScaleKey : @(scrollState.maximum_zoom_scale()), |
| 302 kZoomScaleKey : @(scrollState.zoom_scale()) |
| 303 }; |
| 304 } |
| 305 |
| 306 + (NSString*)scrollStateDescription:(const web::PageScrollState&)scrollState { |
| 307 NSString* const kPageScrollStateDescriptionFormat = |
| 308 @"{ scrollOffset:(%0.2f, %0.2f), zoomScaleRange:(%0.2f, %0.2f), " |
| 309 @"zoomScale:%0.2f }"; |
| 310 return [NSString stringWithFormat:kPageScrollStateDescriptionFormat, |
| 311 scrollState.scroll_offset_x(), |
| 312 scrollState.scroll_offset_y(), |
| 313 scrollState.minimum_zoom_scale(), |
| 314 scrollState.maximum_zoom_scale(), |
| 315 scrollState.zoom_scale()]; |
| 316 } |
| 317 |
| 318 @end |
OLD | NEW |