blob: 23d50b6f5dc888c4e7c991227f8fd14e899b0003 [file] [log] [blame]
// 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_controller.h"
#include <stddef.h>
#include <algorithm>
#include <utility>
#include <vector>
#include "base/format_macros.h"
#include "base/logging.h"
#import "base/mac/foundation_util.h"
#import "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "ios/web/history_state_util.h"
#import "ios/web/navigation/crw_session_certificate_policy_manager.h"
#import "ios/web/navigation/crw_session_controller+private_constructors.h"
#import "ios/web/navigation/crw_session_entry.h"
#import "ios/web/navigation/navigation_item_impl.h"
#include "ios/web/navigation/navigation_manager_facade_delegate.h"
#import "ios/web/navigation/navigation_manager_impl.h"
#include "ios/web/navigation/time_smoother.h"
#include "ios/web/public/browser_state.h"
#include "ios/web/public/browser_url_rewriter.h"
#include "ios/web/public/referrer.h"
#include "ios/web/public/ssl_status.h"
#import "ios/web/public/web_client.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface CRWSessionController () {
// Weak pointer back to the owning NavigationManager. This is to facilitate
// the incremental merging of the two classes.
web::NavigationManagerImpl* _navigationManager;
// Identifies the index of the current navigation in the CRWSessionEntry
// array.
NSInteger _currentNavigationIndex;
// Identifies the index of the previous navigation in the CRWSessionEntry
// array.
NSInteger _previousNavigationIndex;
// Ordered array of |CRWSessionEntry| objects, one for each site in session
// history. End of the list is the most recent load.
NSMutableArray* _entries;
// An entry we haven't gotten a response for yet. This will be discarded
// when we navigate again. It's used only so we know what the currently
// displayed tab is. It backs the property of the same name and should only
// be set through its setter.
base::scoped_nsobject<CRWSessionEntry> _pendingEntry;
// The transient entry, if any. A transient entry is discarded on any
// navigation, and is used for representing interstitials that need to be
// represented in the session. It backs the property of the same name and
// should only be set through its setter.
base::scoped_nsobject<CRWSessionEntry> _transientEntry;
// Stores the certificate policies decided by the user.
CRWSessionCertificatePolicyManager* _sessionCertificatePolicyManager;
// The browser state associated with this CRWSessionController;
web::BrowserState* _browserState; // weak
// Time smoother for navigation entry timestamps; see comment in
// navigation_controller_impl.h
web::TimeSmoother _timeSmoother;
}
// Redefine as readwrite.
@property(nonatomic, readwrite, assign) NSInteger currentNavigationIndex;
// TODO(rohitrao): These properties must be redefined readwrite to work around a
// clang bug. crbug.com/228650
@property(nonatomic, readwrite, strong) NSArray* entries;
@property(nonatomic, readwrite, strong)
CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
// Expose setters for serialization properties. These are exposed in a category
// in SessionStorageBuilder, and will be removed as ownership of
// their backing ivars moves to NavigationManagerImpl.
@property(nonatomic, readwrite, getter=isOpenedByDOM) BOOL openedByDOM;
@property(nonatomic, readwrite, assign) NSInteger previousNavigationIndex;
// Removes all entries after currentNavigationIndex_.
- (void)clearForwardItems;
// Discards the transient entry, if any.
- (void)discardTransientItem;
// Create a new autoreleased session entry.
- (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url
referrer:(const web::Referrer&)referrer
transition:(ui::PageTransition)transition
initiationType:
(web::NavigationInitiationType)initiationType;
// Returns YES if the PageTransition for the underlying navigationItem at
// |index| in |entries_| has ui::PAGE_TRANSITION_IS_REDIRECT_MASK.
- (BOOL)isRedirectTransitionForItemAtIndex:(NSInteger)index;
// Returns a NavigationItemList containing the NavigationItems from |entries|.
- (web::NavigationItemList)itemListForEntryList:(NSArray*)entries;
@end
@implementation CRWSessionController
@synthesize currentNavigationIndex = _currentNavigationIndex;
@synthesize previousNavigationIndex = _previousNavigationIndex;
@synthesize pendingItemIndex = _pendingItemIndex;
@synthesize entries = _entries;
@synthesize openedByDOM = _openedByDOM;
@synthesize sessionCertificatePolicyManager = _sessionCertificatePolicyManager;
- (instancetype)initWithBrowserState:(web::BrowserState*)browserState
openedByDOM:(BOOL)openedByDOM {
self = [super init];
if (self) {
_openedByDOM = openedByDOM;
_browserState = browserState;
_entries = [NSMutableArray array];
_currentNavigationIndex = -1;
_previousNavigationIndex = -1;
_pendingItemIndex = -1;
_sessionCertificatePolicyManager =
[[CRWSessionCertificatePolicyManager alloc] init];
}
return self;
}
- (instancetype)initWithBrowserState:(web::BrowserState*)browserState
navigationItems:(web::ScopedNavigationItemList)items
currentIndex:(NSUInteger)currentIndex {
self = [super init];
if (self) {
_browserState = browserState;
// Create entries array from list of navigations.
_entries = [[NSMutableArray alloc] initWithCapacity:items.size()];
for (auto& item : items) {
base::scoped_nsobject<CRWSessionEntry> entry(
[[CRWSessionEntry alloc] initWithNavigationItem:std::move(item)]);
[_entries addObject:entry];
}
self.currentNavigationIndex = currentIndex;
// Prior to M34, 0 was used as "no index" instead of -1; adjust for that.
if (![_entries count])
self.currentNavigationIndex = -1;
if (_currentNavigationIndex >= static_cast<NSInteger>(items.size())) {
self.currentNavigationIndex = static_cast<NSInteger>(items.size()) - 1;
}
_previousNavigationIndex = -1;
_pendingItemIndex = -1;
_sessionCertificatePolicyManager =
[[CRWSessionCertificatePolicyManager alloc] init];
}
return self;
}
- (id)copyWithZone:(NSZone*)zone {
CRWSessionController* copy = [[[self class] alloc] init];
copy->_openedByDOM = _openedByDOM;
copy->_currentNavigationIndex = _currentNavigationIndex;
copy->_previousNavigationIndex = _previousNavigationIndex;
copy->_pendingItemIndex = _pendingItemIndex;
copy->_entries =
[[NSMutableArray alloc] initWithArray:_entries copyItems:YES];
copy->_sessionCertificatePolicyManager =
[_sessionCertificatePolicyManager copy];
return copy;
}
- (void)setCurrentNavigationIndex:(NSInteger)currentNavigationIndex {
if (_currentNavigationIndex != currentNavigationIndex) {
_currentNavigationIndex = currentNavigationIndex;
if (_navigationManager)
_navigationManager->RemoveTransientURLRewriters();
}
}
- (void)setPendingItemIndex:(NSInteger)index {
DCHECK_GE(index, -1);
DCHECK_LT(index, static_cast<NSInteger>(_entries.count));
_pendingItemIndex = index;
CRWSessionEntry* entry = index != -1 ? _entries[index] : nil;
_pendingEntry.reset(entry);
DCHECK(_pendingItemIndex == -1 || _pendingEntry);
}
- (void)setNavigationManager:(web::NavigationManagerImpl*)navigationManager {
_navigationManager = navigationManager;
if (_navigationManager) {
// _browserState will be nullptr if CRWSessionController has been
// initialized with -initWithCoder: method. Take _browserState from
// NavigationManagerImpl if that's the case.
if (!_browserState) {
_browserState = _navigationManager->GetBrowserState();
}
DCHECK_EQ(_browserState, _navigationManager->GetBrowserState());
}
}
- (void)setBrowserState:(web::BrowserState*)browserState {
_browserState = browserState;
DCHECK(!_navigationManager ||
_navigationManager->GetBrowserState() == _browserState);
}
- (NSString*)description {
return [NSString stringWithFormat:@"current index: %" PRIdNS
@"\nprevious index: %" PRIdNS
@"\npending index: %" PRIdNS
@"\n%@\npending: %@\ntransient: %@\n",
_currentNavigationIndex,
_previousNavigationIndex, _pendingItemIndex,
_entries, _pendingEntry.get(),
_transientEntry.get()];
}
- (web::NavigationItemList)items {
return [self itemListForEntryList:self.entries];
}
- (NSUInteger)itemCount {
return self.entries.count;
}
- (web::NavigationItemImpl*)currentItem {
return self.currentEntry.navigationItemImpl;
}
- (web::NavigationItemImpl*)visibleItem {
return self.visibleEntry.navigationItemImpl;
}
- (web::NavigationItemImpl*)pendingItem {
return self.pendingEntry.navigationItemImpl;
}
- (web::NavigationItemImpl*)transientItem {
return self.transientEntry.navigationItemImpl;
}
- (web::NavigationItemImpl*)lastCommittedItem {
return self.lastCommittedEntry.navigationItemImpl;
}
- (web::NavigationItemImpl*)previousItem {
return self.previousEntry.navigationItemImpl;
}
- (web::NavigationItemImpl*)lastUserItem {
return self.lastUserEntry.navigationItemImpl;
}
- (web::NavigationItemList)backwardItems {
return [self itemListForEntryList:self.backwardEntries];
}
- (web::NavigationItemList)forwardItems {
return [self itemListForEntryList:self.forwardEntries];
}
// Returns the current entry in the session list, or the pending entry if there
// is a navigation in progress.
- (CRWSessionEntry*)currentEntry {
if (_transientEntry)
return _transientEntry.get();
if (_pendingEntry)
return _pendingEntry.get();
return [self lastCommittedEntry];
}
// See NavigationController::GetVisibleEntry for the motivation for this
// distinction.
- (CRWSessionEntry*)visibleEntry {
if (_transientEntry)
return _transientEntry.get();
// Only return the pending_entry for new (non-history), browser-initiated
// navigations in order to prevent URL spoof attacks.
web::NavigationItemImpl* pendingItem = [_pendingEntry navigationItemImpl];
if (pendingItem) {
bool isUserInitiated = pendingItem->NavigationInitiationType() ==
web::NavigationInitiationType::USER_INITIATED;
bool safeToShowPending = isUserInitiated && _pendingItemIndex == -1;
if (safeToShowPending)
return _pendingEntry.get();
}
return [self lastCommittedEntry];
}
- (CRWSessionEntry*)pendingEntry {
return _pendingEntry.get();
}
- (CRWSessionEntry*)transientEntry {
return _transientEntry.get();
}
- (CRWSessionEntry*)lastCommittedEntry {
if (_currentNavigationIndex == -1)
return nil;
return [_entries objectAtIndex:_currentNavigationIndex];
}
// Returns the previous entry in the session list, or nil if there isn't any.
- (CRWSessionEntry*)previousEntry {
if ((_previousNavigationIndex < 0) || (![_entries count]))
return nil;
return [_entries objectAtIndex:_previousNavigationIndex];
}
- (void)addPendingItem:(const GURL&)url
referrer:(const web::Referrer&)ref
transition:(ui::PageTransition)trans
initiationType:(web::NavigationInitiationType)initiationType {
[self discardTransientItem];
_pendingItemIndex = -1;
// Don't create a new entry if it's already the same as the current entry,
// allowing this routine to be called multiple times in a row without issue.
// Note: CRWSessionController currently has the responsibility to distinguish
// between new navigations and history stack navigation, hence the inclusion
// of specific transiton type logic here, in order to make it reliable with
// real-world observed behavior.
// TODO(crbug.com/676129): Fix the way changes are detected/reported elsewhere
// in the web layer so that this hack can be removed.
// Remove the workaround code from -presentSafeBrowsingWarningForResource:.
CRWSessionEntry* currentEntry = self.currentEntry;
if (currentEntry) {
web::NavigationItem* item = [currentEntry navigationItem];
BOOL hasSameURL = item->GetURL() == url;
BOOL isPendingTransitionFormSubmit =
PageTransitionCoreTypeIs(trans, ui::PAGE_TRANSITION_FORM_SUBMIT);
BOOL isCurrentTransitionFormSubmit = PageTransitionCoreTypeIs(
item->GetTransitionType(), ui::PAGE_TRANSITION_FORM_SUBMIT);
BOOL shouldCreatePendingItem =
!hasSameURL ||
(isPendingTransitionFormSubmit && !isCurrentTransitionFormSubmit);
if (!shouldCreatePendingItem) {
// Send the notification anyway, to preserve old behavior. It's unknown
// whether anything currently relies on this, but since both this whole
// hack and the content facade will both be going away, it's not worth
// trying to unwind.
if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
_navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
}
return;
}
}
_pendingEntry.reset([self sessionEntryWithURL:url
referrer:ref
transition:trans
initiationType:initiationType]);
if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
_navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
}
}
- (void)updatePendingItem:(const GURL&)url {
// If there is no pending entry, navigation is probably happening within the
// session history. Don't modify the entry list.
if (!_pendingEntry)
return;
web::NavigationItemImpl* item = [_pendingEntry navigationItemImpl];
if (url != item->GetURL()) {
// Assume a redirection, and discard any transient entry.
// TODO(stuartmorgan): Once the current safe browsing code is gone,
// consider making this a DCHECK that there's no transient entry.
[self discardTransientItem];
item->SetURL(url);
item->SetVirtualURL(url);
// Redirects (3xx response code), or client side navigation must change
// POST requests to GETs.
item->SetPostData(nil);
item->ResetHttpRequestHeaders();
}
// This should probably not be sent if the URLs matched, but that's what was
// done before, so preserve behavior in case something relies on it.
if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
_navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
}
}
- (void)clearForwardItems {
DCHECK_EQ(_pendingItemIndex, -1);
[self discardTransientItem];
NSInteger forwardItemStartIndex = _currentNavigationIndex + 1;
DCHECK(forwardItemStartIndex >= 0);
if (forwardItemStartIndex >= static_cast<NSInteger>([_entries count]))
return;
NSRange remove = NSMakeRange(forwardItemStartIndex,
[_entries count] - forwardItemStartIndex);
// Store removed items in temporary NSArray so they can be deallocated after
// their facades.
base::scoped_nsobject<NSArray> removedItems(
[_entries subarrayWithRange:remove]);
[_entries removeObjectsInRange:remove];
if (_previousNavigationIndex >= forwardItemStartIndex)
_previousNavigationIndex = -1;
if (_navigationManager) {
_navigationManager->OnNavigationItemsPruned(remove.length);
}
}
- (void)commitPendingItem {
if (_pendingEntry) {
NSInteger newNavigationIndex = _pendingItemIndex;
if (_pendingItemIndex == -1) {
[self clearForwardItems];
// Add the new entry at the end.
[_entries addObject:_pendingEntry];
newNavigationIndex = [_entries count] - 1;
}
_previousNavigationIndex = _currentNavigationIndex;
self.currentNavigationIndex = newNavigationIndex;
// Once an entry is committed it's not renderer-initiated any more. (Matches
// the implementation in NavigationController.)
[_pendingEntry navigationItemImpl]->ResetForCommit();
_pendingEntry.reset();
_pendingItemIndex = -1;
}
CRWSessionEntry* currentEntry = self.currentEntry;
web::NavigationItem* item = currentEntry.navigationItem;
// Update the navigation timestamp now that it's actually happened.
if (item)
item->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now()));
if (_navigationManager && item)
_navigationManager->OnNavigationItemCommitted();
DCHECK_EQ(_pendingItemIndex, -1);
}
- (void)addTransientItemWithURL:(const GURL&)URL {
_transientEntry.reset([self
sessionEntryWithURL:URL
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
initiationType:web::NavigationInitiationType::USER_INITIATED]);
web::NavigationItem* navigationItem = [_transientEntry navigationItem];
DCHECK(navigationItem);
navigationItem->SetTimestamp(
_timeSmoother.GetSmoothedTime(base::Time::Now()));
}
- (void)pushNewItemWithURL:(const GURL&)URL
stateObject:(NSString*)stateObject
transition:(ui::PageTransition)transition {
DCHECK(![self pendingEntry]);
DCHECK([self currentEntry]);
web::NavigationItem* lastCommittedItem =
self.lastCommittedEntry.navigationItem;
CHECK(web::history_state_util::IsHistoryStateChangeValid(
lastCommittedItem->GetURL(), URL));
web::Referrer referrer(lastCommittedItem->GetURL(),
web::ReferrerPolicyDefault);
base::scoped_nsobject<CRWSessionEntry> pushedEntry([self
sessionEntryWithURL:URL
referrer:referrer
transition:transition
initiationType:web::NavigationInitiationType::USER_INITIATED]);
web::NavigationItemImpl* pushedItem = [pushedEntry navigationItemImpl];
pushedItem->SetUserAgentType(lastCommittedItem->GetUserAgentType());
pushedItem->SetSerializedStateObject(stateObject);
pushedItem->SetIsCreatedFromPushState(true);
pushedItem->GetSSL() = lastCommittedItem->GetSSL();
[self clearForwardItems];
// Add the new entry at the end.
[_entries addObject:pushedEntry];
_previousNavigationIndex = _currentNavigationIndex;
self.currentNavigationIndex = [_entries count] - 1;
if (_navigationManager)
_navigationManager->OnNavigationItemCommitted();
}
- (void)updateCurrentItemWithURL:(const GURL&)url
stateObject:(NSString*)stateObject {
DCHECK(!_transientEntry);
CRWSessionEntry* currentEntry = self.currentEntry;
web::NavigationItemImpl* currentItem = self.currentEntry.navigationItemImpl;
currentItem->SetURL(url);
currentItem->SetSerializedStateObject(stateObject);
currentItem->SetHasStateBeenReplaced(true);
currentItem->SetPostData(nil);
currentEntry.navigationItem->SetURL(url);
// If the change is to a committed entry, notify interested parties.
if (currentEntry != self.pendingEntry && _navigationManager)
_navigationManager->OnNavigationItemChanged();
}
- (void)discardNonCommittedItems {
[self discardTransientItem];
_pendingEntry.reset();
_pendingItemIndex = -1;
}
- (void)discardTransientItem {
// Keep the entry alive temporarily. There are flows that get the current
// entry, do some navigation operation, and then try to use that old current
// entry; since navigations clear the transient entry, these flows might
// crash. (This should be removable once more session management is handled
// within this class and/or NavigationManager).
_transientEntry.reset();
}
- (void)insertStateFromSessionController:(CRWSessionController*)sourceSession {
DCHECK(sourceSession);
// The other session may not have any entries, in which case there is nothing
// to insert. The other session's currentNavigationEntry will be bogus
// in such cases, so ignore it and return early.
NSArray* sourceEntries = sourceSession.entries;
if (!sourceEntries.count)
return;
// Cycle through the entries from the other session and insert them before any
// entries from this session. Do not copy anything that comes after the other
// session's current entry.
NSInteger lastIndexToCopy = sourceSession.currentNavigationIndex;
for (NSInteger i = 0; i <= lastIndexToCopy; ++i) {
[_entries insertObject:sourceEntries[i] atIndex:i];
}
_previousNavigationIndex = -1;
_currentNavigationIndex += lastIndexToCopy + 1;
if (_pendingItemIndex != -1)
_pendingItemIndex += lastIndexToCopy + 1;
DCHECK_LT(static_cast<NSUInteger>(_currentNavigationIndex), _entries.count);
DCHECK(_pendingItemIndex == -1 || _pendingEntry);
}
- (void)goToItemAtIndex:(NSInteger)index {
if (index < 0 || static_cast<NSUInteger>(index) >= _entries.count)
return;
if (index < _currentNavigationIndex) {
// Going back.
[self discardNonCommittedItems];
} else if (_currentNavigationIndex < index) {
// Going forward.
[self discardTransientItem];
} else {
// |delta| is 0, no need to change current navigation index.
return;
}
_previousNavigationIndex = _currentNavigationIndex;
_currentNavigationIndex = index;
}
- (void)removeItemAtIndex:(NSInteger)index {
DCHECK(index < static_cast<NSInteger>([_entries count]));
DCHECK(index != _currentNavigationIndex);
DCHECK(index >= 0);
[self discardNonCommittedItems];
[_entries removeObjectAtIndex:index];
if (_currentNavigationIndex > index)
_currentNavigationIndex--;
if (_previousNavigationIndex >= index)
_previousNavigationIndex--;
}
- (NSArray*)backwardEntries {
NSMutableArray* entries = [NSMutableArray array];
for (NSInteger index = _currentNavigationIndex; index > 0; --index) {
if (![self isRedirectTransitionForItemAtIndex:index])
[entries addObject:_entries[index - 1]];
}
return entries;
}
- (NSArray*)forwardEntries {
NSMutableArray* entries = [NSMutableArray array];
NSUInteger lastNonRedirectedIndex = _currentNavigationIndex + 1;
while (lastNonRedirectedIndex < [_entries count]) {
CRWSessionEntry* entry = [_entries objectAtIndex:lastNonRedirectedIndex];
if (!ui::PageTransitionIsRedirect(
entry.navigationItem->GetTransitionType())) {
[entries addObject:entry];
}
++lastNonRedirectedIndex;
}
return entries;
}
- (BOOL)isSameDocumentNavigationBetweenItem:(web::NavigationItem*)firstItem
andItem:(web::NavigationItem*)secondItem {
if (!firstItem || !secondItem || firstItem == secondItem)
return NO;
NSUInteger firstIndex = [self indexOfItem:firstItem];
NSUInteger secondIndex = [self indexOfItem:secondItem];
if (firstIndex == NSNotFound || secondIndex == NSNotFound)
return NO;
NSUInteger startIndex = firstIndex < secondIndex ? firstIndex : secondIndex;
NSUInteger endIndex = firstIndex < secondIndex ? secondIndex : firstIndex;
for (NSUInteger i = startIndex + 1; i <= endIndex; i++) {
web::NavigationItemImpl* item = [_entries[i] navigationItemImpl];
// Every entry in the sequence has to be created from a hash change or
// pushState() call.
if (!item->IsCreatedFromPushState() && !item->IsCreatedFromHashChange())
return NO;
// Every entry in the sequence has to have a URL that could have been
// created from a pushState() call.
if (!web::history_state_util::IsHistoryStateChangeValid(firstItem->GetURL(),
item->GetURL()))
return NO;
}
return YES;
}
- (CRWSessionEntry*)lastUserEntry {
if (![_entries count])
return nil;
NSInteger index = _currentNavigationIndex;
// This will return the first session entry if all other entries are
// redirects, regardless of the transition state of the first entry.
while (index > 0 && [self isRedirectTransitionForItemAtIndex:index]) {
--index;
}
return [_entries objectAtIndex:index];
}
- (NSInteger)indexOfItem:(const web::NavigationItem*)item {
for (NSUInteger index = 0; index < self.entries.count; ++index) {
if ([self.entries[index] navigationItem] == item)
return static_cast<NSInteger>(index);
}
return NSNotFound;
}
- (web::NavigationItemImpl*)itemAtIndex:(NSInteger)index {
if (index < 0 || self.entries.count <= static_cast<NSUInteger>(index))
return nullptr;
return static_cast<web::NavigationItemImpl*>(
[self.entries[index] navigationItem]);
}
#pragma mark -
#pragma mark Private methods
- (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url
referrer:(const web::Referrer&)referrer
transition:(ui::PageTransition)transition
initiationType:
(web::NavigationInitiationType)initiationType {
GURL loaded_url(url);
BOOL urlWasRewritten = NO;
if (_navigationManager) {
std::unique_ptr<std::vector<web::BrowserURLRewriter::URLRewriter>>
transientRewriters = _navigationManager->GetTransientURLRewriters();
if (transientRewriters) {
urlWasRewritten = web::BrowserURLRewriter::RewriteURLWithWriters(
&loaded_url, _browserState, *transientRewriters.get());
}
}
if (!urlWasRewritten) {
web::BrowserURLRewriter::GetInstance()->RewriteURLIfNecessary(
&loaded_url, _browserState);
}
std::unique_ptr<web::NavigationItemImpl> item(new web::NavigationItemImpl());
item->SetOriginalRequestURL(loaded_url);
item->SetURL(loaded_url);
item->SetReferrer(referrer);
item->SetTransitionType(transition);
item->SetNavigationInitiationType(initiationType);
if (web::GetWebClient()->IsAppSpecificURL(loaded_url))
item->SetUserAgentType(web::UserAgentType::NONE);
return [[CRWSessionEntry alloc] initWithNavigationItem:std::move(item)];
}
- (BOOL)isRedirectTransitionForItemAtIndex:(NSInteger)index {
ui::PageTransition transition =
[_entries[index] navigationItem]->GetTransitionType();
return (transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) ? YES : NO;
}
- (web::NavigationItemList)itemListForEntryList:(NSArray*)entries {
web::NavigationItemList list(entries.count);
for (size_t index = 0; index < entries.count; ++index)
list[index] = [entries[index] navigationItem];
return list;
}
@end