| // Copyright 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. |
| |
| #import "ios/web/navigation/crw_session_entry.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/mac/scoped_nsobject.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "ios/web/navigation/navigation_item_impl.h" |
| #include "ios/web/navigation/nscoder_util.h" |
| #include "ios/web/public/navigation_item.h" |
| #include "ios/web/public/web_state/page_display_state.h" |
| #import "net/base/mac/url_conversions.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace web { |
| // Keys used to serialize web::PageScrollState properties. |
| NSString* const kSessionEntryPageScrollStateKey = @"state"; |
| NSString* const kSessionEntryScrollOffsetXKey = @"scrollX"; |
| NSString* const kSessionEntryScrollOffsetYKey = @"scrollY"; |
| NSString* const kSessionEntryMinimumZoomScaleKey = @"minZoom"; |
| NSString* const kSessionEntryMaximumZoomScaleKey = @"maxZoom"; |
| NSString* const kSessionEntryZoomScaleKey = @"zoom"; |
| |
| // Keys used to serialize navigation properties. |
| NSString* const kSessionEntryURLKey = @"virtualUrlString"; |
| NSString* const kSessionEntryURLDeperecatedKey = @"virtualUrl"; |
| NSString* const kSessionEntryReferrerURLKey = @"referrerUrlString"; |
| NSString* const kSessionEntryReferrerURLDeprecatedKey = @"referrer"; |
| NSString* const kSessionEntryReferrerPolicyKey = @"referrerPolicy"; |
| NSString* const kSessionEntryTimestampKey = @"timestamp"; |
| NSString* const kSessionEntryTitleKey = @"title"; |
| NSString* const kSessionEntryPOSTDataKey = @"POSTData"; |
| NSString* const kSessionEntryHTTPRequestHeadersKey = @"httpHeaders"; |
| NSString* const kSessionEntrySkipResubmitConfirmationKey = |
| @"skipResubmitDataConfirmation"; |
| NSString* const kSessionEntryUseDesktopUserAgentKey = @"useDesktopUserAgent"; |
| } |
| |
| @interface CRWSessionEntry () { |
| // The original URL of the page. In cases where a redirect occurred, |url_| |
| // will contain the final post-redirect URL, and |originalUrl_| will contain |
| // the pre-redirect URL. This field is not persisted to disk. |
| GURL _originalUrl; |
| |
| // The NavigationItemImpl corresponding to this CRWSessionEntry. |
| // TODO(stuartmorgan): Move ownership to NavigationManagerImpl. |
| std::unique_ptr<web::NavigationItemImpl> _navigationItem; |
| } |
| // Redefine originalUrl to be read-write. |
| @property(nonatomic, readwrite) const GURL& originalUrl; |
| |
| // Converts a serialized NSDictionary to a web::PageDisplayState. |
| + (web::PageDisplayState)pageDisplayStateFromDictionary: |
| (NSDictionary*)dictionary; |
| // Serializes a web::PageDisplayState to an NSDictionary. |
| + (NSDictionary*)dictionaryFromPageDisplayState: |
| (const web::PageDisplayState&)displayState; |
| // Returns a readable description of |displayState|. |
| + (NSString*)descriptionForPageDisplayState: |
| (const web::PageDisplayState&)displayState; |
| @end |
| |
| @implementation CRWSessionEntry |
| |
| @synthesize originalUrl = _originalUrl; |
| |
| - (instancetype)initWithNavigationItem: |
| (std::unique_ptr<web::NavigationItem>)item { |
| self = [super init]; |
| if (self) { |
| _navigationItem.reset( |
| static_cast<web::NavigationItemImpl*>(item.release())); |
| self.originalUrl = _navigationItem->GetURL(); |
| } |
| return self; |
| } |
| |
| - (instancetype)initWithCoder:(NSCoder*)aDecoder { |
| self = [super init]; |
| if (self) { |
| _navigationItem.reset(new web::NavigationItemImpl()); |
| |
| // Desktop chrome only persists virtualUrl_ and uses it to feed the url |
| // when creating a NavigationEntry. |
| GURL url; |
| if ([aDecoder containsValueForKey:web::kSessionEntryURLKey]) { |
| url = GURL( |
| web::nscoder_util::DecodeString(aDecoder, web::kSessionEntryURLKey)); |
| } else { |
| // Backward compatibility. |
| url = net::GURLWithNSURL( |
| [aDecoder decodeObjectForKey:web::kSessionEntryURLDeperecatedKey]); |
| } |
| _navigationItem->SetURL(url); |
| self.originalUrl = url; |
| |
| if ([aDecoder containsValueForKey:web::kSessionEntryReferrerURLKey]) { |
| const std::string referrerString(web::nscoder_util::DecodeString( |
| aDecoder, web::kSessionEntryReferrerURLKey)); |
| web::ReferrerPolicy referrerPolicy = static_cast<web::ReferrerPolicy>( |
| [aDecoder decodeIntForKey:web::kSessionEntryReferrerPolicyKey]); |
| _navigationItem->SetReferrer( |
| web::Referrer(GURL(referrerString), referrerPolicy)); |
| } else { |
| // Backward compatibility. |
| NSURL* referrer = [aDecoder |
| decodeObjectForKey:web::kSessionEntryReferrerURLDeprecatedKey]; |
| _navigationItem->SetReferrer(web::Referrer( |
| net::GURLWithNSURL(referrer), web::ReferrerPolicyDefault)); |
| } |
| |
| if ([aDecoder containsValueForKey:web::kSessionEntryTimestampKey]) { |
| int64_t us = [aDecoder decodeInt64ForKey:web::kSessionEntryTimestampKey]; |
| _navigationItem->SetTimestamp(base::Time::FromInternalValue(us)); |
| } |
| |
| NSString* title = [aDecoder decodeObjectForKey:web::kSessionEntryTitleKey]; |
| // Use a transition type of reload so that we don't incorrectly increase |
| // the typed count. This is what desktop chrome does. |
| _navigationItem->SetTitle(base::SysNSStringToUTF16(title)); |
| _navigationItem->SetTransitionType(ui::PAGE_TRANSITION_RELOAD); |
| _navigationItem->SetPageDisplayState([[self class] |
| pageDisplayStateFromDictionary: |
| [aDecoder |
| decodeObjectForKey:web::kSessionEntryPageScrollStateKey]]); |
| _navigationItem->SetShouldSkipResubmitDataConfirmation([aDecoder |
| decodeBoolForKey:web::kSessionEntrySkipResubmitConfirmationKey]); |
| _navigationItem->SetIsOverridingUserAgent( |
| [aDecoder decodeBoolForKey:web::kSessionEntryUseDesktopUserAgentKey]); |
| _navigationItem->SetPostData( |
| [aDecoder decodeObjectForKey:web::kSessionEntryPOSTDataKey]); |
| _navigationItem->AddHttpRequestHeaders( |
| [aDecoder decodeObjectForKey:web::kSessionEntryHTTPRequestHeadersKey]); |
| } |
| return self; |
| } |
| |
| - (void)encodeWithCoder:(NSCoder*)aCoder { |
| // Desktop Chrome doesn't persist |url_| or |originalUrl_|, only |
| // |virtualUrl_|. |
| web::nscoder_util::EncodeString(aCoder, web::kSessionEntryURLKey, |
| _navigationItem->GetVirtualURL().spec()); |
| web::nscoder_util::EncodeString(aCoder, web::kSessionEntryReferrerURLKey, |
| _navigationItem->GetReferrer().url.spec()); |
| [aCoder encodeInt:_navigationItem->GetReferrer().policy |
| forKey:web::kSessionEntryReferrerPolicyKey]; |
| [aCoder encodeInt64:_navigationItem->GetTimestamp().ToInternalValue() |
| forKey:web::kSessionEntryTimestampKey]; |
| |
| [aCoder encodeObject:base::SysUTF16ToNSString(_navigationItem->GetTitle()) |
| forKey:web::kSessionEntryTitleKey]; |
| [aCoder encodeObject:[[self class] dictionaryFromPageDisplayState: |
| _navigationItem->GetPageDisplayState()] |
| forKey:web::kSessionEntryPageScrollStateKey]; |
| [aCoder encodeBool:_navigationItem->ShouldSkipResubmitDataConfirmation() |
| forKey:web::kSessionEntrySkipResubmitConfirmationKey]; |
| [aCoder encodeBool:_navigationItem->IsOverridingUserAgent() |
| forKey:web::kSessionEntryUseDesktopUserAgentKey]; |
| [aCoder encodeObject:_navigationItem->GetPostData() |
| forKey:web::kSessionEntryPOSTDataKey]; |
| [aCoder encodeObject:_navigationItem->GetHttpRequestHeaders() |
| forKey:web::kSessionEntryHTTPRequestHeadersKey]; |
| } |
| |
| // TODO(ios): Shall we overwrite EqualTo:? |
| |
| - (instancetype)copyWithZone:(NSZone*)zone { |
| CRWSessionEntry* copy = [[[self class] alloc] init]; |
| copy->_navigationItem.reset( |
| new web::NavigationItemImpl(*_navigationItem.get())); |
| copy->_originalUrl = _originalUrl; |
| return copy; |
| } |
| |
| - (NSString*)description { |
| return [NSString |
| stringWithFormat: |
| @"url:%@ originalurl:%@ title:%@ transition:%d displayState:%@ " |
| @"desktopUA:%d", |
| base::SysUTF8ToNSString(_navigationItem->GetURL().spec()), |
| base::SysUTF8ToNSString(self.originalUrl.spec()), |
| base::SysUTF16ToNSString(_navigationItem->GetTitle()), |
| _navigationItem->GetTransitionType(), |
| [[self class] descriptionForPageDisplayState: |
| _navigationItem->GetPageDisplayState()], |
| _navigationItem->IsOverridingUserAgent()]; |
| } |
| |
| - (web::NavigationItem*)navigationItem { |
| return _navigationItem.get(); |
| } |
| |
| - (web::NavigationItemImpl*)navigationItemImpl { |
| return _navigationItem.get(); |
| } |
| |
| #pragma mark - Serialization helpers |
| |
| + (web::PageDisplayState)pageDisplayStateFromDictionary: |
| (NSDictionary*)dictionary { |
| NSNumber* serializedValue = nil; |
| web::PageScrollState scrollState; |
| if ((serializedValue = dictionary[web::kSessionEntryScrollOffsetXKey])) |
| scrollState.set_offset_x([serializedValue doubleValue]); |
| if ((serializedValue = dictionary[web::kSessionEntryScrollOffsetYKey])) |
| scrollState.set_offset_y([serializedValue doubleValue]); |
| web::PageZoomState zoomState; |
| if ((serializedValue = dictionary[web::kSessionEntryMinimumZoomScaleKey])) |
| zoomState.set_minimum_zoom_scale([serializedValue doubleValue]); |
| if ((serializedValue = dictionary[web::kSessionEntryMaximumZoomScaleKey])) |
| zoomState.set_maximum_zoom_scale([serializedValue doubleValue]); |
| if ((serializedValue = dictionary[web::kSessionEntryZoomScaleKey])) |
| zoomState.set_zoom_scale([serializedValue doubleValue]); |
| return web::PageDisplayState(scrollState, zoomState); |
| } |
| |
| + (NSDictionary*)dictionaryFromPageDisplayState: |
| (const web::PageDisplayState&)displayState { |
| return @{ |
| web::kSessionEntryScrollOffsetXKey : |
| @(displayState.scroll_state().offset_x()), |
| web::kSessionEntryScrollOffsetYKey : |
| @(displayState.scroll_state().offset_y()), |
| web::kSessionEntryMinimumZoomScaleKey : |
| @(displayState.zoom_state().minimum_zoom_scale()), |
| web::kSessionEntryMaximumZoomScaleKey : |
| @(displayState.zoom_state().maximum_zoom_scale()), |
| web::kSessionEntryZoomScaleKey : |
| @(displayState.zoom_state().zoom_scale()) |
| }; |
| } |
| |
| + (NSString*)descriptionForPageDisplayState: |
| (const web::PageDisplayState&)displayState { |
| NSString* const kPageScrollStateDescriptionFormat = |
| @"{ scrollOffset:(%0.2f, %0.2f), zoomScaleRange:(%0.2f, %0.2f), " |
| @"zoomScale:%0.2f }"; |
| return |
| [NSString stringWithFormat:kPageScrollStateDescriptionFormat, |
| displayState.scroll_state().offset_x(), |
| displayState.scroll_state().offset_y(), |
| displayState.zoom_state().minimum_zoom_scale(), |
| displayState.zoom_state().maximum_zoom_scale(), |
| displayState.zoom_state().zoom_scale()]; |
| } |
| |
| @end |