OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/web/navigation/crw_session_controller.h" | 5 #import "ios/web/navigation/crw_session_controller.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <utility> | 10 #include <utility> |
11 #include <vector> | |
12 | 11 |
13 #include "base/format_macros.h" | 12 #include "base/format_macros.h" |
14 #include "base/logging.h" | 13 #include "base/logging.h" |
15 #import "base/mac/foundation_util.h" | 14 #import "base/mac/foundation_util.h" |
16 #import "base/mac/scoped_nsobject.h" | 15 #import "base/mac/scoped_nsobject.h" |
| 16 #include "base/memory/ptr_util.h" |
17 #include "base/strings/sys_string_conversions.h" | 17 #include "base/strings/sys_string_conversions.h" |
18 #include "ios/web/history_state_util.h" | 18 #include "ios/web/history_state_util.h" |
19 #import "ios/web/navigation/crw_session_certificate_policy_manager.h" | 19 #import "ios/web/navigation/crw_session_certificate_policy_manager.h" |
20 #import "ios/web/navigation/crw_session_controller+private_constructors.h" | 20 #import "ios/web/navigation/crw_session_controller+private_constructors.h" |
21 #import "ios/web/navigation/crw_session_entry.h" | |
22 #import "ios/web/navigation/navigation_item_impl.h" | 21 #import "ios/web/navigation/navigation_item_impl.h" |
23 #include "ios/web/navigation/navigation_manager_facade_delegate.h" | 22 #include "ios/web/navigation/navigation_manager_facade_delegate.h" |
24 #import "ios/web/navigation/navigation_manager_impl.h" | 23 #import "ios/web/navigation/navigation_manager_impl.h" |
25 #include "ios/web/navigation/time_smoother.h" | 24 #include "ios/web/navigation/time_smoother.h" |
26 #include "ios/web/public/browser_state.h" | 25 #include "ios/web/public/browser_state.h" |
27 #include "ios/web/public/browser_url_rewriter.h" | 26 #include "ios/web/public/browser_url_rewriter.h" |
28 #include "ios/web/public/referrer.h" | 27 #include "ios/web/public/referrer.h" |
29 #include "ios/web/public/ssl_status.h" | 28 #include "ios/web/public/ssl_status.h" |
30 #import "ios/web/public/web_client.h" | 29 #import "ios/web/public/web_client.h" |
31 | 30 |
32 #if !defined(__has_feature) || !__has_feature(objc_arc) | 31 #if !defined(__has_feature) || !__has_feature(objc_arc) |
33 #error "This file requires ARC support." | 32 #error "This file requires ARC support." |
34 #endif | 33 #endif |
35 | 34 |
36 @interface CRWSessionController () { | 35 @interface CRWSessionController () { |
37 // Weak pointer back to the owning NavigationManager. This is to facilitate | 36 // Weak pointer back to the owning NavigationManager. This is to facilitate |
38 // the incremental merging of the two classes. | 37 // the incremental merging of the two classes. |
39 web::NavigationManagerImpl* _navigationManager; | 38 web::NavigationManagerImpl* _navigationManager; |
40 | 39 |
41 // Identifies the index of the current navigation in the CRWSessionEntry | 40 // Identifies the index of the current navigation in the NavigationItem |
42 // array. | 41 // array. |
43 NSInteger _currentNavigationIndex; | 42 NSInteger _currentNavigationIndex; |
44 // Identifies the index of the previous navigation in the CRWSessionEntry | 43 // Identifies the index of the previous navigation in the NavigationItem |
45 // array. | 44 // array. |
46 NSInteger _previousNavigationIndex; | 45 NSInteger _previousNavigationIndex; |
47 // Ordered array of |CRWSessionEntry| objects, one for each site in session | |
48 // history. End of the list is the most recent load. | |
49 NSMutableArray* _entries; | |
50 | |
51 // An entry we haven't gotten a response for yet. This will be discarded | |
52 // when we navigate again. It's used only so we know what the currently | |
53 // displayed tab is. It backs the property of the same name and should only | |
54 // be set through its setter. | |
55 base::scoped_nsobject<CRWSessionEntry> _pendingEntry; | |
56 | |
57 // The transient entry, if any. A transient entry is discarded on any | |
58 // navigation, and is used for representing interstitials that need to be | |
59 // represented in the session. It backs the property of the same name and | |
60 // should only be set through its setter. | |
61 base::scoped_nsobject<CRWSessionEntry> _transientEntry; | |
62 | 46 |
63 // Stores the certificate policies decided by the user. | 47 // Stores the certificate policies decided by the user. |
64 CRWSessionCertificatePolicyManager* _sessionCertificatePolicyManager; | 48 CRWSessionCertificatePolicyManager* _sessionCertificatePolicyManager; |
65 | 49 |
66 // The browser state associated with this CRWSessionController; | 50 // The browser state associated with this CRWSessionController; |
67 web::BrowserState* _browserState; // weak | 51 web::BrowserState* _browserState; // weak |
68 | 52 |
69 // Time smoother for navigation entry timestamps; see comment in | 53 // Time smoother for navigation item timestamps; see comment in |
70 // navigation_controller_impl.h | 54 // navigation_controller_impl.h |
71 web::TimeSmoother _timeSmoother; | 55 web::TimeSmoother _timeSmoother; |
| 56 |
| 57 // Backing objects for properties of the same name. |
| 58 web::ScopedNavigationItemImplList _items; |
| 59 // |_pendingItem| only contains a NavigationItem for non-history navigations. |
| 60 // For back/forward navigations within session history, _pendingItemIndex will |
| 61 // be equal to -1, and self.pendingItem will return an item contained within |
| 62 // |_items|. |
| 63 std::unique_ptr<web::NavigationItemImpl> _pendingItem; |
| 64 std::unique_ptr<web::NavigationItemImpl> _transientItem; |
72 } | 65 } |
73 | 66 |
74 // Redefine as readwrite. | 67 // Redefine as readwrite. |
75 @property(nonatomic, readwrite, assign) NSInteger currentNavigationIndex; | 68 @property(nonatomic, readwrite, assign) NSInteger currentNavigationIndex; |
76 | 69 |
77 // TODO(rohitrao): These properties must be redefined readwrite to work around a | 70 // TODO(rohitrao): These properties must be redefined readwrite to work around a |
78 // clang bug. crbug.com/228650 | 71 // clang bug. crbug.com/228650 |
79 @property(nonatomic, readwrite, strong) NSArray* entries; | |
80 @property(nonatomic, readwrite, strong) | 72 @property(nonatomic, readwrite, strong) |
81 CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager; | 73 CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager; |
82 | 74 |
83 // Expose setters for serialization properties. These are exposed in a category | 75 // Expose setters for serialization properties. These are exposed in a category |
84 // in SessionStorageBuilder, and will be removed as ownership of | 76 // in SessionStorageBuilder, and will be removed as ownership of |
85 // their backing ivars moves to NavigationManagerImpl. | 77 // their backing ivars moves to NavigationManagerImpl. |
86 @property(nonatomic, readwrite, getter=isOpenedByDOM) BOOL openedByDOM; | 78 @property(nonatomic, readwrite, getter=isOpenedByDOM) BOOL openedByDOM; |
87 @property(nonatomic, readwrite, assign) NSInteger previousNavigationIndex; | 79 @property(nonatomic, readwrite, assign) NSInteger previousNavigationIndex; |
88 | 80 |
89 // Removes all entries after currentNavigationIndex_. | 81 // Removes all items after currentNavigationIndex_. |
90 - (void)clearForwardItems; | 82 - (void)clearForwardItems; |
91 // Discards the transient entry, if any. | 83 // Discards the transient item, if any. |
92 - (void)discardTransientItem; | 84 - (void)discardTransientItem; |
93 // Create a new autoreleased session entry. | 85 // Creates a NavigationItemImpl with the specified properties. |
94 - (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url | 86 - (std::unique_ptr<web::NavigationItemImpl>) |
95 referrer:(const web::Referrer&)referrer | 87 itemWithURL:(const GURL&)url |
96 transition:(ui::PageTransition)transition | 88 referrer:(const web::Referrer&)referrer |
97 initiationType: | 89 transition:(ui::PageTransition)transition |
98 (web::NavigationInitiationType)initiationType; | 90 initiationType:(web::NavigationInitiationType)initiationType; |
99 // Returns YES if the PageTransition for the underlying navigationItem at | 91 // Returns YES if the PageTransition for the underlying navigationItem at |
100 // |index| in |entries_| has ui::PAGE_TRANSITION_IS_REDIRECT_MASK. | 92 // |index| in |items| has ui::PAGE_TRANSITION_IS_REDIRECT_MASK. |
101 - (BOOL)isRedirectTransitionForItemAtIndex:(NSInteger)index; | 93 - (BOOL)isRedirectTransitionForItemAtIndex:(size_t)index; |
102 // Returns a NavigationItemList containing the NavigationItems from |entries|. | 94 |
103 - (web::NavigationItemList)itemListForEntryList:(NSArray*)entries; | |
104 @end | 95 @end |
105 | 96 |
106 @implementation CRWSessionController | 97 @implementation CRWSessionController |
107 | 98 |
108 @synthesize currentNavigationIndex = _currentNavigationIndex; | 99 @synthesize currentNavigationIndex = _currentNavigationIndex; |
109 @synthesize previousNavigationIndex = _previousNavigationIndex; | 100 @synthesize previousNavigationIndex = _previousNavigationIndex; |
110 @synthesize pendingItemIndex = _pendingItemIndex; | 101 @synthesize pendingItemIndex = _pendingItemIndex; |
111 @synthesize entries = _entries; | |
112 @synthesize openedByDOM = _openedByDOM; | 102 @synthesize openedByDOM = _openedByDOM; |
113 @synthesize sessionCertificatePolicyManager = _sessionCertificatePolicyManager; | 103 @synthesize sessionCertificatePolicyManager = _sessionCertificatePolicyManager; |
114 | 104 |
115 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState | 105 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState |
116 openedByDOM:(BOOL)openedByDOM { | 106 openedByDOM:(BOOL)openedByDOM { |
117 self = [super init]; | 107 self = [super init]; |
118 if (self) { | 108 if (self) { |
119 _openedByDOM = openedByDOM; | 109 _openedByDOM = openedByDOM; |
120 _browserState = browserState; | 110 _browserState = browserState; |
121 _entries = [NSMutableArray array]; | |
122 _currentNavigationIndex = -1; | 111 _currentNavigationIndex = -1; |
123 _previousNavigationIndex = -1; | 112 _previousNavigationIndex = -1; |
124 _pendingItemIndex = -1; | 113 _pendingItemIndex = -1; |
125 _sessionCertificatePolicyManager = | 114 _sessionCertificatePolicyManager = |
126 [[CRWSessionCertificatePolicyManager alloc] init]; | 115 [[CRWSessionCertificatePolicyManager alloc] init]; |
127 } | 116 } |
128 return self; | 117 return self; |
129 } | 118 } |
130 | 119 |
131 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState | 120 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState |
132 navigationItems:(web::ScopedNavigationItemList)items | 121 navigationItems:(web::ScopedNavigationItemList)items |
133 currentIndex:(NSUInteger)currentIndex { | 122 currentIndex:(NSUInteger)currentIndex { |
134 self = [super init]; | 123 self = [super init]; |
135 if (self) { | 124 if (self) { |
136 _browserState = browserState; | 125 _browserState = browserState; |
137 | 126 _items = web::CreateScopedNavigationItemImplList(std::move(items)); |
138 // Create entries array from list of navigations. | 127 _currentNavigationIndex = |
139 _entries = [[NSMutableArray alloc] initWithCapacity:items.size()]; | 128 std::min(static_cast<NSInteger>(currentIndex), |
140 for (auto& item : items) { | 129 static_cast<NSInteger>(_items.size()) - 1); |
141 base::scoped_nsobject<CRWSessionEntry> entry( | |
142 [[CRWSessionEntry alloc] initWithNavigationItem:std::move(item)]); | |
143 [_entries addObject:entry]; | |
144 } | |
145 self.currentNavigationIndex = currentIndex; | |
146 // Prior to M34, 0 was used as "no index" instead of -1; adjust for that. | |
147 if (![_entries count]) | |
148 self.currentNavigationIndex = -1; | |
149 if (_currentNavigationIndex >= static_cast<NSInteger>(items.size())) { | |
150 self.currentNavigationIndex = static_cast<NSInteger>(items.size()) - 1; | |
151 } | |
152 _previousNavigationIndex = -1; | 130 _previousNavigationIndex = -1; |
153 _pendingItemIndex = -1; | 131 _pendingItemIndex = -1; |
154 _sessionCertificatePolicyManager = | 132 _sessionCertificatePolicyManager = |
155 [[CRWSessionCertificatePolicyManager alloc] init]; | 133 [[CRWSessionCertificatePolicyManager alloc] init]; |
156 } | 134 } |
157 return self; | 135 return self; |
158 } | 136 } |
159 | 137 |
160 - (id)copyWithZone:(NSZone*)zone { | 138 #pragma mark - Accessors |
161 CRWSessionController* copy = [[[self class] alloc] init]; | |
162 copy->_openedByDOM = _openedByDOM; | |
163 copy->_currentNavigationIndex = _currentNavigationIndex; | |
164 copy->_previousNavigationIndex = _previousNavigationIndex; | |
165 copy->_pendingItemIndex = _pendingItemIndex; | |
166 copy->_entries = | |
167 [[NSMutableArray alloc] initWithArray:_entries copyItems:YES]; | |
168 copy->_sessionCertificatePolicyManager = | |
169 [_sessionCertificatePolicyManager copy]; | |
170 return copy; | |
171 } | |
172 | 139 |
173 - (void)setCurrentNavigationIndex:(NSInteger)currentNavigationIndex { | 140 - (void)setCurrentNavigationIndex:(NSInteger)currentNavigationIndex { |
174 if (_currentNavigationIndex != currentNavigationIndex) { | 141 if (_currentNavigationIndex != currentNavigationIndex) { |
175 _currentNavigationIndex = currentNavigationIndex; | 142 _currentNavigationIndex = currentNavigationIndex; |
176 if (_navigationManager) | 143 if (_navigationManager) |
177 _navigationManager->RemoveTransientURLRewriters(); | 144 _navigationManager->RemoveTransientURLRewriters(); |
178 } | 145 } |
179 } | 146 } |
180 | 147 |
181 - (void)setPendingItemIndex:(NSInteger)index { | 148 - (void)setPendingItemIndex:(NSInteger)pendingItemIndex { |
182 DCHECK_GE(index, -1); | 149 DCHECK_GE(pendingItemIndex, -1); |
183 DCHECK_LT(index, static_cast<NSInteger>(_entries.count)); | 150 DCHECK_LT(pendingItemIndex, static_cast<NSInteger>(self.items.size())); |
184 _pendingItemIndex = index; | 151 _pendingItemIndex = pendingItemIndex; |
185 CRWSessionEntry* entry = index != -1 ? _entries[index] : nil; | 152 DCHECK(_pendingItemIndex == -1 || self.pendingItem); |
186 _pendingEntry.reset(entry); | |
187 DCHECK(_pendingItemIndex == -1 || _pendingEntry); | |
188 } | 153 } |
189 | 154 |
| 155 - (const web::ScopedNavigationItemImplList&)items { |
| 156 return _items; |
| 157 } |
| 158 |
| 159 - (web::NavigationItemImpl*)currentItem { |
| 160 if (self.transientItem) |
| 161 return self.transientItem; |
| 162 if (self.pendingItem) |
| 163 return self.pendingItem; |
| 164 return self.lastCommittedItem; |
| 165 } |
| 166 |
| 167 - (web::NavigationItemImpl*)visibleItem { |
| 168 if (self.transientItem) |
| 169 return self.transientItem; |
| 170 // Only return the |pendingItem| for new (non-history), browser-initiated |
| 171 // navigations in order to prevent URL spoof attacks. |
| 172 web::NavigationItemImpl* pendingItem = self.pendingItem; |
| 173 if (pendingItem) { |
| 174 bool isUserInitiated = pendingItem->NavigationInitiationType() == |
| 175 web::NavigationInitiationType::USER_INITIATED; |
| 176 bool safeToShowPending = isUserInitiated && _pendingItemIndex == -1; |
| 177 |
| 178 if (safeToShowPending) |
| 179 return pendingItem; |
| 180 } |
| 181 return self.lastCommittedItem; |
| 182 } |
| 183 |
| 184 - (web::NavigationItemImpl*)pendingItem { |
| 185 if (self.pendingItemIndex == -1) |
| 186 return _pendingItem.get(); |
| 187 return self.items[self.pendingItemIndex].get(); |
| 188 } |
| 189 |
| 190 - (web::NavigationItemImpl*)transientItem { |
| 191 return _transientItem.get(); |
| 192 } |
| 193 |
| 194 - (web::NavigationItemImpl*)lastCommittedItem { |
| 195 NSInteger index = self.currentNavigationIndex; |
| 196 return index == -1 ? nullptr : self.items[index].get(); |
| 197 } |
| 198 |
| 199 - (web::NavigationItemImpl*)previousItem { |
| 200 NSInteger index = self.previousNavigationIndex; |
| 201 return index == -1 || self.items.empty() ? nullptr : self.items[index].get(); |
| 202 } |
| 203 |
| 204 - (web::NavigationItemImpl*)lastUserItem { |
| 205 if (self.items.empty()) |
| 206 return nil; |
| 207 |
| 208 NSInteger index = self.currentNavigationIndex; |
| 209 // This will return the first NavigationItem if all other items are |
| 210 // redirects, regardless of the transition state of the first item. |
| 211 while (index > 0 && [self isRedirectTransitionForItemAtIndex:index]) |
| 212 --index; |
| 213 |
| 214 return self.items[index].get(); |
| 215 } |
| 216 |
| 217 - (web::NavigationItemList)backwardItems { |
| 218 web::NavigationItemList items; |
| 219 for (size_t index = _currentNavigationIndex; index > 0; --index) { |
| 220 if (![self isRedirectTransitionForItemAtIndex:index]) |
| 221 items.push_back(self.items[index - 1].get()); |
| 222 } |
| 223 return items; |
| 224 } |
| 225 |
| 226 - (web::NavigationItemList)forwardItems { |
| 227 web::NavigationItemList items; |
| 228 NSUInteger lastNonRedirectedIndex = _currentNavigationIndex + 1; |
| 229 while (lastNonRedirectedIndex < self.items.size()) { |
| 230 web::NavigationItem* item = self.items[lastNonRedirectedIndex].get(); |
| 231 if (!ui::PageTransitionIsRedirect(item->GetTransitionType())) |
| 232 items.push_back(item); |
| 233 ++lastNonRedirectedIndex; |
| 234 } |
| 235 return items; |
| 236 } |
| 237 |
| 238 #pragma mark - NSObject |
| 239 |
| 240 - (NSString*)description { |
| 241 // Create description for |items|. |
| 242 NSMutableString* itemsDescription = [NSMutableString stringWithString:@"[\n"]; |
| 243 #ifndef NDEBUG |
| 244 for (const auto& item : self.items) |
| 245 [itemsDescription appendFormat:@"%@\n", item->GetDescription()]; |
| 246 #endif |
| 247 [itemsDescription appendString:@"]"]; |
| 248 // Create description for |pendingItem| and |transientItem|. |
| 249 NSString* pendingItemDescription = @"(null)"; |
| 250 NSString* transientItemDescription = @"(null)"; |
| 251 #ifndef NDEBUG |
| 252 if (self.pendingItem) |
| 253 pendingItemDescription = self.pendingItem->GetDescription(); |
| 254 if (self.transientItem) |
| 255 transientItemDescription = self.transientItem->GetDescription(); |
| 256 #else |
| 257 if (self.pendingItem) { |
| 258 pendingItemDescription = |
| 259 [NSString stringWithFormat:@"%p", self.pendingItem]; |
| 260 } |
| 261 if (self.transientItem) { |
| 262 transientItemDescription = |
| 263 [NSString stringWithFormat:@"%p", self.transientItem]; |
| 264 } |
| 265 #endif |
| 266 return [NSString stringWithFormat:@"current index: %" PRIdNS |
| 267 @"\nprevious index: %" PRIdNS |
| 268 @"\npending" |
| 269 @" index: %" PRIdNS |
| 270 @"\n%@\npending: %@\ntransient: %@\n", |
| 271 _currentNavigationIndex, |
| 272 _previousNavigationIndex, _pendingItemIndex, |
| 273 itemsDescription, pendingItemDescription, |
| 274 transientItemDescription]; |
| 275 } |
| 276 |
| 277 #pragma mark - NSCopying |
| 278 |
| 279 - (id)copyWithZone:(NSZone*)zone { |
| 280 CRWSessionController* copy = [[[self class] alloc] init]; |
| 281 copy->_openedByDOM = _openedByDOM; |
| 282 copy->_currentNavigationIndex = _currentNavigationIndex; |
| 283 copy->_previousNavigationIndex = _previousNavigationIndex; |
| 284 copy->_pendingItemIndex = _pendingItemIndex; |
| 285 copy->_sessionCertificatePolicyManager = |
| 286 [_sessionCertificatePolicyManager copy]; |
| 287 web::ScopedNavigationItemImplList itemCopies(self.items.size()); |
| 288 for (size_t index = 0; index < self.items.size(); ++index) { |
| 289 itemCopies[index] = |
| 290 base::MakeUnique<web::NavigationItemImpl>(*self.items[index]); |
| 291 } |
| 292 std::swap(copy->_items, itemCopies); |
| 293 return copy; |
| 294 } |
| 295 |
| 296 #pragma mark - Public |
| 297 |
190 - (void)setNavigationManager:(web::NavigationManagerImpl*)navigationManager { | 298 - (void)setNavigationManager:(web::NavigationManagerImpl*)navigationManager { |
191 _navigationManager = navigationManager; | 299 _navigationManager = navigationManager; |
192 if (_navigationManager) { | 300 if (_navigationManager) { |
193 // _browserState will be nullptr if CRWSessionController has been | 301 // _browserState will be nullptr if CRWSessionController has been |
194 // initialized with -initWithCoder: method. Take _browserState from | 302 // initialized with -initWithCoder: method. Take _browserState from |
195 // NavigationManagerImpl if that's the case. | 303 // NavigationManagerImpl if that's the case. |
196 if (!_browserState) { | 304 if (!_browserState) { |
197 _browserState = _navigationManager->GetBrowserState(); | 305 _browserState = _navigationManager->GetBrowserState(); |
198 } | 306 } |
199 DCHECK_EQ(_browserState, _navigationManager->GetBrowserState()); | 307 DCHECK_EQ(_browserState, _navigationManager->GetBrowserState()); |
200 } | 308 } |
201 } | 309 } |
202 | 310 |
203 - (void)setBrowserState:(web::BrowserState*)browserState { | 311 - (void)setBrowserState:(web::BrowserState*)browserState { |
204 _browserState = browserState; | 312 _browserState = browserState; |
205 DCHECK(!_navigationManager || | 313 DCHECK(!_navigationManager || |
206 _navigationManager->GetBrowserState() == _browserState); | 314 _navigationManager->GetBrowserState() == _browserState); |
207 } | 315 } |
208 | 316 |
209 - (NSString*)description { | |
210 return [NSString stringWithFormat:@"current index: %" PRIdNS | |
211 @"\nprevious index: %" PRIdNS | |
212 @"\npending index: %" PRIdNS | |
213 @"\n%@\npending: %@\ntransient: %@\n", | |
214 _currentNavigationIndex, | |
215 _previousNavigationIndex, _pendingItemIndex, | |
216 _entries, _pendingEntry.get(), | |
217 _transientEntry.get()]; | |
218 } | |
219 | |
220 - (web::NavigationItemList)items { | |
221 return [self itemListForEntryList:self.entries]; | |
222 } | |
223 | |
224 - (NSUInteger)itemCount { | |
225 return self.entries.count; | |
226 } | |
227 | |
228 - (web::NavigationItemImpl*)currentItem { | |
229 return self.currentEntry.navigationItemImpl; | |
230 } | |
231 | |
232 - (web::NavigationItemImpl*)visibleItem { | |
233 return self.visibleEntry.navigationItemImpl; | |
234 } | |
235 | |
236 - (web::NavigationItemImpl*)pendingItem { | |
237 return self.pendingEntry.navigationItemImpl; | |
238 } | |
239 | |
240 - (web::NavigationItemImpl*)transientItem { | |
241 return self.transientEntry.navigationItemImpl; | |
242 } | |
243 | |
244 - (web::NavigationItemImpl*)lastCommittedItem { | |
245 return self.lastCommittedEntry.navigationItemImpl; | |
246 } | |
247 | |
248 - (web::NavigationItemImpl*)previousItem { | |
249 return self.previousEntry.navigationItemImpl; | |
250 } | |
251 | |
252 - (web::NavigationItemImpl*)lastUserItem { | |
253 return self.lastUserEntry.navigationItemImpl; | |
254 } | |
255 | |
256 - (web::NavigationItemList)backwardItems { | |
257 return [self itemListForEntryList:self.backwardEntries]; | |
258 } | |
259 | |
260 - (web::NavigationItemList)forwardItems { | |
261 return [self itemListForEntryList:self.forwardEntries]; | |
262 } | |
263 | |
264 // Returns the current entry in the session list, or the pending entry if there | |
265 // is a navigation in progress. | |
266 - (CRWSessionEntry*)currentEntry { | |
267 if (_transientEntry) | |
268 return _transientEntry.get(); | |
269 if (_pendingEntry) | |
270 return _pendingEntry.get(); | |
271 return [self lastCommittedEntry]; | |
272 } | |
273 | |
274 // See NavigationController::GetVisibleEntry for the motivation for this | |
275 // distinction. | |
276 - (CRWSessionEntry*)visibleEntry { | |
277 if (_transientEntry) | |
278 return _transientEntry.get(); | |
279 // Only return the pending_entry for new (non-history), browser-initiated | |
280 // navigations in order to prevent URL spoof attacks. | |
281 web::NavigationItemImpl* pendingItem = [_pendingEntry navigationItemImpl]; | |
282 | |
283 if (pendingItem) { | |
284 bool isUserInitiated = pendingItem->NavigationInitiationType() == | |
285 web::NavigationInitiationType::USER_INITIATED; | |
286 bool safeToShowPending = isUserInitiated && _pendingItemIndex == -1; | |
287 | |
288 if (safeToShowPending) | |
289 return _pendingEntry.get(); | |
290 } | |
291 | |
292 return [self lastCommittedEntry]; | |
293 } | |
294 | |
295 - (CRWSessionEntry*)pendingEntry { | |
296 return _pendingEntry.get(); | |
297 } | |
298 | |
299 - (CRWSessionEntry*)transientEntry { | |
300 return _transientEntry.get(); | |
301 } | |
302 | |
303 - (CRWSessionEntry*)lastCommittedEntry { | |
304 if (_currentNavigationIndex == -1) | |
305 return nil; | |
306 return [_entries objectAtIndex:_currentNavigationIndex]; | |
307 } | |
308 | |
309 // Returns the previous entry in the session list, or nil if there isn't any. | |
310 - (CRWSessionEntry*)previousEntry { | |
311 if ((_previousNavigationIndex < 0) || (![_entries count])) | |
312 return nil; | |
313 return [_entries objectAtIndex:_previousNavigationIndex]; | |
314 } | |
315 | |
316 - (void)addPendingItem:(const GURL&)url | 317 - (void)addPendingItem:(const GURL&)url |
317 referrer:(const web::Referrer&)ref | 318 referrer:(const web::Referrer&)ref |
318 transition:(ui::PageTransition)trans | 319 transition:(ui::PageTransition)trans |
319 initiationType:(web::NavigationInitiationType)initiationType { | 320 initiationType:(web::NavigationInitiationType)initiationType { |
320 [self discardTransientItem]; | 321 [self discardTransientItem]; |
321 _pendingItemIndex = -1; | 322 self.pendingItemIndex = -1; |
322 | 323 |
323 // Don't create a new entry if it's already the same as the current entry, | 324 // Don't create a new item if it's already the same as the current item, |
324 // allowing this routine to be called multiple times in a row without issue. | 325 // allowing this routine to be called multiple times in a row without issue. |
325 // Note: CRWSessionController currently has the responsibility to distinguish | 326 // Note: CRWSessionController currently has the responsibility to distinguish |
326 // between new navigations and history stack navigation, hence the inclusion | 327 // between new navigations and history stack navigation, hence the inclusion |
327 // of specific transiton type logic here, in order to make it reliable with | 328 // of specific transiton type logic here, in order to make it reliable with |
328 // real-world observed behavior. | 329 // real-world observed behavior. |
329 // TODO(crbug.com/676129): Fix the way changes are detected/reported elsewhere | 330 // TODO(crbug.com/676129): Fix the way changes are detected/reported elsewhere |
330 // in the web layer so that this hack can be removed. | 331 // in the web layer so that this hack can be removed. |
331 // Remove the workaround code from -presentSafeBrowsingWarningForResource:. | 332 // Remove the workaround code from -presentSafeBrowsingWarningForResource:. |
332 CRWSessionEntry* currentEntry = self.currentEntry; | 333 web::NavigationItemImpl* currentItem = self.currentItem; |
333 if (currentEntry) { | 334 if (currentItem) { |
334 web::NavigationItem* item = [currentEntry navigationItem]; | 335 BOOL hasSameURL = currentItem->GetURL() == url; |
335 | |
336 BOOL hasSameURL = item->GetURL() == url; | |
337 BOOL isPendingTransitionFormSubmit = | 336 BOOL isPendingTransitionFormSubmit = |
338 PageTransitionCoreTypeIs(trans, ui::PAGE_TRANSITION_FORM_SUBMIT); | 337 PageTransitionCoreTypeIs(trans, ui::PAGE_TRANSITION_FORM_SUBMIT); |
339 BOOL isCurrentTransitionFormSubmit = PageTransitionCoreTypeIs( | 338 BOOL isCurrentTransitionFormSubmit = PageTransitionCoreTypeIs( |
340 item->GetTransitionType(), ui::PAGE_TRANSITION_FORM_SUBMIT); | 339 currentItem->GetTransitionType(), ui::PAGE_TRANSITION_FORM_SUBMIT); |
341 BOOL shouldCreatePendingItem = | 340 BOOL shouldCreatePendingItem = |
342 !hasSameURL || | 341 !hasSameURL || |
343 (isPendingTransitionFormSubmit && !isCurrentTransitionFormSubmit); | 342 (isPendingTransitionFormSubmit && !isCurrentTransitionFormSubmit); |
344 | 343 |
345 if (!shouldCreatePendingItem) { | 344 if (!shouldCreatePendingItem) { |
346 // Send the notification anyway, to preserve old behavior. It's unknown | 345 // Send the notification anyway, to preserve old behavior. It's unknown |
347 // whether anything currently relies on this, but since both this whole | 346 // whether anything currently relies on this, but since both this whole |
348 // hack and the content facade will both be going away, it's not worth | 347 // hack and the content facade will both be going away, it's not worth |
349 // trying to unwind. | 348 // trying to unwind. |
350 if (_navigationManager && _navigationManager->GetFacadeDelegate()) { | 349 if (_navigationManager && _navigationManager->GetFacadeDelegate()) |
351 _navigationManager->GetFacadeDelegate()->OnNavigationItemPending(); | 350 _navigationManager->GetFacadeDelegate()->OnNavigationItemPending(); |
352 } | |
353 return; | 351 return; |
354 } | 352 } |
355 } | 353 } |
356 | 354 |
357 _pendingEntry.reset([self sessionEntryWithURL:url | 355 _pendingItem = [self itemWithURL:url |
358 referrer:ref | 356 referrer:ref |
359 transition:trans | 357 transition:trans |
360 initiationType:initiationType]); | 358 initiationType:initiationType]; |
361 | 359 |
362 if (_navigationManager && _navigationManager->GetFacadeDelegate()) { | 360 if (_navigationManager && _navigationManager->GetFacadeDelegate()) |
363 _navigationManager->GetFacadeDelegate()->OnNavigationItemPending(); | 361 _navigationManager->GetFacadeDelegate()->OnNavigationItemPending(); |
364 } | |
365 } | 362 } |
366 | 363 |
367 - (void)updatePendingItem:(const GURL&)url { | 364 - (void)updatePendingItem:(const GURL&)url { |
368 // If there is no pending entry, navigation is probably happening within the | 365 // If there is no pending item, navigation is probably happening within the |
369 // session history. Don't modify the entry list. | 366 // session history. Don't modify the item list. |
370 if (!_pendingEntry) | 367 web::NavigationItemImpl* item = self.pendingItem; |
| 368 if (!item) |
371 return; | 369 return; |
372 | 370 |
373 web::NavigationItemImpl* item = [_pendingEntry navigationItemImpl]; | |
374 if (url != item->GetURL()) { | 371 if (url != item->GetURL()) { |
375 // Assume a redirection, and discard any transient entry. | 372 // Assume a redirection, and discard any transient item. |
376 // TODO(stuartmorgan): Once the current safe browsing code is gone, | 373 // TODO(stuartmorgan): Once the current safe browsing code is gone, |
377 // consider making this a DCHECK that there's no transient entry. | 374 // consider making this a DCHECK that there's no transient item. |
378 [self discardTransientItem]; | 375 [self discardTransientItem]; |
379 | 376 |
380 item->SetURL(url); | 377 item->SetURL(url); |
381 item->SetVirtualURL(url); | 378 item->SetVirtualURL(url); |
382 // Redirects (3xx response code), or client side navigation must change | 379 // Redirects (3xx response code), or client side navigation must change |
383 // POST requests to GETs. | 380 // POST requests to GETs. |
384 item->SetPostData(nil); | 381 item->SetPostData(nil); |
385 item->ResetHttpRequestHeaders(); | 382 item->ResetHttpRequestHeaders(); |
386 } | 383 } |
387 | 384 |
388 // This should probably not be sent if the URLs matched, but that's what was | 385 // This should probably not be sent if the URLs matched, but that's what was |
389 // done before, so preserve behavior in case something relies on it. | 386 // done before, so preserve behavior in case something relies on it. |
390 if (_navigationManager && _navigationManager->GetFacadeDelegate()) { | 387 if (_navigationManager && _navigationManager->GetFacadeDelegate()) |
391 _navigationManager->GetFacadeDelegate()->OnNavigationItemPending(); | 388 _navigationManager->GetFacadeDelegate()->OnNavigationItemPending(); |
392 } | |
393 } | 389 } |
394 | 390 |
395 - (void)clearForwardItems { | 391 - (void)clearForwardItems { |
396 DCHECK_EQ(_pendingItemIndex, -1); | 392 DCHECK_EQ(self.pendingItemIndex, -1); |
397 [self discardTransientItem]; | 393 [self discardTransientItem]; |
398 | 394 |
399 NSInteger forwardItemStartIndex = _currentNavigationIndex + 1; | 395 NSInteger forwardItemStartIndex = _currentNavigationIndex + 1; |
400 DCHECK(forwardItemStartIndex >= 0); | 396 DCHECK(forwardItemStartIndex >= 0); |
401 | 397 |
402 if (forwardItemStartIndex >= static_cast<NSInteger>([_entries count])) | 398 size_t itemCount = self.items.size(); |
| 399 if (forwardItemStartIndex >= static_cast<NSInteger>(itemCount)) |
403 return; | 400 return; |
404 | 401 |
405 NSRange remove = NSMakeRange(forwardItemStartIndex, | |
406 [_entries count] - forwardItemStartIndex); | |
407 // Store removed items in temporary NSArray so they can be deallocated after | |
408 // their facades. | |
409 base::scoped_nsobject<NSArray> removedItems( | |
410 [_entries subarrayWithRange:remove]); | |
411 [_entries removeObjectsInRange:remove]; | |
412 if (_previousNavigationIndex >= forwardItemStartIndex) | 402 if (_previousNavigationIndex >= forwardItemStartIndex) |
413 _previousNavigationIndex = -1; | 403 _previousNavigationIndex = -1; |
| 404 |
| 405 // Remove the NavigationItems and notify the NavigationManater |
| 406 _items.erase(_items.begin() + forwardItemStartIndex, _items.end()); |
414 if (_navigationManager) { | 407 if (_navigationManager) { |
415 _navigationManager->OnNavigationItemsPruned(remove.length); | 408 _navigationManager->OnNavigationItemsPruned(itemCount - |
| 409 forwardItemStartIndex); |
416 } | 410 } |
417 } | 411 } |
418 | 412 |
419 - (void)commitPendingItem { | 413 - (void)commitPendingItem { |
420 if (_pendingEntry) { | 414 if (self.pendingItem) { |
421 NSInteger newNavigationIndex = _pendingItemIndex; | 415 // Once an item is committed it's not renderer-initiated any more. (Matches |
422 if (_pendingItemIndex == -1) { | 416 // the implementation in NavigationController.) |
| 417 self.pendingItem->ResetForCommit(); |
| 418 |
| 419 NSInteger newNavigationIndex = self.pendingItemIndex; |
| 420 if (newNavigationIndex == -1) { |
423 [self clearForwardItems]; | 421 [self clearForwardItems]; |
424 // Add the new entry at the end. | 422 // Add the new item at the end. |
425 [_entries addObject:_pendingEntry]; | 423 _items.push_back(std::move(_pendingItem)); |
426 newNavigationIndex = [_entries count] - 1; | 424 newNavigationIndex = self.items.size() - 1; |
427 } | 425 } |
428 _previousNavigationIndex = _currentNavigationIndex; | 426 _previousNavigationIndex = _currentNavigationIndex; |
429 self.currentNavigationIndex = newNavigationIndex; | 427 self.currentNavigationIndex = newNavigationIndex; |
430 // Once an entry is committed it's not renderer-initiated any more. (Matches | 428 self.pendingItemIndex = -1; |
431 // the implementation in NavigationController.) | 429 DCHECK(!_pendingItem); |
432 [_pendingEntry navigationItemImpl]->ResetForCommit(); | |
433 _pendingEntry.reset(); | |
434 _pendingItemIndex = -1; | |
435 } | 430 } |
436 | 431 |
437 CRWSessionEntry* currentEntry = self.currentEntry; | 432 web::NavigationItem* item = self.currentItem; |
438 web::NavigationItem* item = currentEntry.navigationItem; | |
439 // Update the navigation timestamp now that it's actually happened. | 433 // Update the navigation timestamp now that it's actually happened. |
440 if (item) | 434 if (item) |
441 item->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now())); | 435 item->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now())); |
442 | 436 |
443 if (_navigationManager && item) | 437 if (_navigationManager && item) |
444 _navigationManager->OnNavigationItemCommitted(); | 438 _navigationManager->OnNavigationItemCommitted(); |
445 DCHECK_EQ(_pendingItemIndex, -1); | 439 DCHECK_EQ(self.pendingItemIndex, -1); |
446 } | 440 } |
447 | 441 |
448 - (void)addTransientItemWithURL:(const GURL&)URL { | 442 - (void)addTransientItemWithURL:(const GURL&)URL { |
449 _transientEntry.reset([self | 443 _transientItem = |
450 sessionEntryWithURL:URL | 444 [self itemWithURL:URL |
451 referrer:web::Referrer() | 445 referrer:web::Referrer() |
452 transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT | 446 transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT |
453 initiationType:web::NavigationInitiationType::USER_INITIATED]); | 447 initiationType:web::NavigationInitiationType::USER_INITIATED]; |
454 | 448 _transientItem->SetTimestamp( |
455 web::NavigationItem* navigationItem = [_transientEntry navigationItem]; | |
456 DCHECK(navigationItem); | |
457 navigationItem->SetTimestamp( | |
458 _timeSmoother.GetSmoothedTime(base::Time::Now())); | 449 _timeSmoother.GetSmoothedTime(base::Time::Now())); |
459 } | 450 } |
460 | 451 |
461 - (void)pushNewItemWithURL:(const GURL&)URL | 452 - (void)pushNewItemWithURL:(const GURL&)URL |
462 stateObject:(NSString*)stateObject | 453 stateObject:(NSString*)stateObject |
463 transition:(ui::PageTransition)transition { | 454 transition:(ui::PageTransition)transition { |
464 DCHECK(![self pendingEntry]); | 455 DCHECK(!self.pendingItem); |
465 DCHECK([self currentEntry]); | 456 DCHECK(self.currentItem); |
466 | 457 |
467 web::NavigationItem* lastCommittedItem = | 458 web::NavigationItem* lastCommittedItem = self.lastCommittedItem; |
468 self.lastCommittedEntry.navigationItem; | |
469 CHECK(web::history_state_util::IsHistoryStateChangeValid( | 459 CHECK(web::history_state_util::IsHistoryStateChangeValid( |
470 lastCommittedItem->GetURL(), URL)); | 460 lastCommittedItem->GetURL(), URL)); |
471 | 461 |
472 web::Referrer referrer(lastCommittedItem->GetURL(), | 462 web::Referrer referrer(lastCommittedItem->GetURL(), |
473 web::ReferrerPolicyDefault); | 463 web::ReferrerPolicyDefault); |
474 base::scoped_nsobject<CRWSessionEntry> pushedEntry([self | 464 std::unique_ptr<web::NavigationItemImpl> pushedItem = |
475 sessionEntryWithURL:URL | 465 [self itemWithURL:URL |
476 referrer:referrer | 466 referrer:referrer |
477 transition:transition | 467 transition:transition |
478 initiationType:web::NavigationInitiationType::USER_INITIATED]); | 468 initiationType:web::NavigationInitiationType::USER_INITIATED]; |
479 | |
480 web::NavigationItemImpl* pushedItem = [pushedEntry navigationItemImpl]; | |
481 pushedItem->SetUserAgentType(lastCommittedItem->GetUserAgentType()); | 469 pushedItem->SetUserAgentType(lastCommittedItem->GetUserAgentType()); |
482 pushedItem->SetSerializedStateObject(stateObject); | 470 pushedItem->SetSerializedStateObject(stateObject); |
483 pushedItem->SetIsCreatedFromPushState(true); | 471 pushedItem->SetIsCreatedFromPushState(true); |
484 pushedItem->GetSSL() = lastCommittedItem->GetSSL(); | 472 pushedItem->GetSSL() = lastCommittedItem->GetSSL(); |
485 pushedItem->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now())); | 473 pushedItem->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now())); |
486 | 474 |
487 [self clearForwardItems]; | 475 [self clearForwardItems]; |
488 // Add the new entry at the end. | 476 // Add the new item at the end. |
489 [_entries addObject:pushedEntry]; | 477 _items.push_back(std::move(pushedItem)); |
490 _previousNavigationIndex = _currentNavigationIndex; | 478 _previousNavigationIndex = _currentNavigationIndex; |
491 self.currentNavigationIndex = [_entries count] - 1; | 479 self.currentNavigationIndex = self.items.size() - 1; |
492 | 480 |
493 if (_navigationManager) | 481 if (_navigationManager) |
494 _navigationManager->OnNavigationItemCommitted(); | 482 _navigationManager->OnNavigationItemCommitted(); |
495 } | 483 } |
496 | 484 |
497 - (void)updateCurrentItemWithURL:(const GURL&)url | 485 - (void)updateCurrentItemWithURL:(const GURL&)url |
498 stateObject:(NSString*)stateObject { | 486 stateObject:(NSString*)stateObject { |
499 DCHECK(!_transientEntry); | 487 DCHECK(!self.transientItem); |
500 CRWSessionEntry* currentEntry = self.currentEntry; | 488 web::NavigationItemImpl* currentItem = self.currentItem; |
501 web::NavigationItemImpl* currentItem = self.currentEntry.navigationItemImpl; | |
502 currentItem->SetURL(url); | 489 currentItem->SetURL(url); |
503 currentItem->SetSerializedStateObject(stateObject); | 490 currentItem->SetSerializedStateObject(stateObject); |
504 currentItem->SetHasStateBeenReplaced(true); | 491 currentItem->SetHasStateBeenReplaced(true); |
505 currentItem->SetPostData(nil); | 492 currentItem->SetPostData(nil); |
506 currentEntry.navigationItem->SetURL(url); | 493 // If the change is to a committed item, notify interested parties. |
507 // If the change is to a committed entry, notify interested parties. | 494 if (currentItem != self.pendingItem && _navigationManager) |
508 if (currentEntry != self.pendingEntry && _navigationManager) | |
509 _navigationManager->OnNavigationItemChanged(); | 495 _navigationManager->OnNavigationItemChanged(); |
510 } | 496 } |
511 | 497 |
512 - (void)discardNonCommittedItems { | 498 - (void)discardNonCommittedItems { |
513 [self discardTransientItem]; | 499 [self discardTransientItem]; |
514 _pendingEntry.reset(); | 500 _pendingItem.reset(); |
515 _pendingItemIndex = -1; | 501 self.pendingItemIndex = -1; |
516 } | 502 } |
517 | 503 |
518 - (void)discardTransientItem { | 504 - (void)discardTransientItem { |
519 // Keep the entry alive temporarily. There are flows that get the current | 505 _transientItem.reset(); |
520 // entry, do some navigation operation, and then try to use that old current | |
521 // entry; since navigations clear the transient entry, these flows might | |
522 // crash. (This should be removable once more session management is handled | |
523 // within this class and/or NavigationManager). | |
524 _transientEntry.reset(); | |
525 } | 506 } |
526 | 507 |
527 - (void)insertStateFromSessionController:(CRWSessionController*)sourceSession { | 508 - (void)insertStateFromSessionController:(CRWSessionController*)sourceSession { |
528 DCHECK(sourceSession); | 509 DCHECK(sourceSession); |
529 // The other session may not have any entries, in which case there is nothing | 510 |
530 // to insert. The other session's currentNavigationEntry will be bogus | 511 // The other session may not have any items, in which case there is nothing |
531 // in such cases, so ignore it and return early. | 512 // to insert. The other session's currentItem will be bogus in such cases, so |
532 NSArray* sourceEntries = sourceSession.entries; | 513 // ignore it and return early. |
533 if (!sourceEntries.count) | 514 web::ScopedNavigationItemImplList& sourceItems = sourceSession->_items; |
| 515 if (sourceItems.empty()) |
534 return; | 516 return; |
535 | 517 |
536 // Cycle through the entries from the other session and insert them before any | 518 // Cycle through the items from the other session and insert them before any |
537 // entries from this session. Do not copy anything that comes after the other | 519 // items from this session. Do not copy anything that comes after the other |
538 // session's current entry. | 520 // session's current item. |
539 NSInteger lastIndexToCopy = sourceSession.currentNavigationIndex; | 521 NSInteger lastIndexToCopy = sourceSession.currentNavigationIndex; |
540 for (NSInteger i = 0; i <= lastIndexToCopy; ++i) { | 522 for (NSInteger i = 0; i <= lastIndexToCopy; ++i) { |
541 [_entries insertObject:sourceEntries[i] atIndex:i]; | 523 std::unique_ptr<web::NavigationItemImpl> sourceItemCopy = |
| 524 base::MakeUnique<web::NavigationItemImpl>(*sourceItems[i]); |
| 525 _items.insert(_items.begin() + i, std::move(sourceItemCopy)); |
542 } | 526 } |
543 | 527 |
| 528 // Update state to reflect inserted NavigationItems. |
544 _previousNavigationIndex = -1; | 529 _previousNavigationIndex = -1; |
545 _currentNavigationIndex += lastIndexToCopy + 1; | 530 _currentNavigationIndex += lastIndexToCopy + 1; |
546 if (_pendingItemIndex != -1) | 531 if (self.pendingItemIndex != -1) |
547 _pendingItemIndex += lastIndexToCopy + 1; | 532 self.pendingItemIndex += lastIndexToCopy + 1; |
548 | 533 |
549 DCHECK_LT(static_cast<NSUInteger>(_currentNavigationIndex), _entries.count); | 534 DCHECK_LT(static_cast<NSUInteger>(_currentNavigationIndex), |
550 DCHECK(_pendingItemIndex == -1 || _pendingEntry); | 535 self.items.size()); |
| 536 DCHECK(self.pendingItemIndex == -1 || self.pendingItem); |
551 } | 537 } |
552 | 538 |
553 - (void)goToItemAtIndex:(NSInteger)index { | 539 - (void)goToItemAtIndex:(NSInteger)index { |
554 if (index < 0 || static_cast<NSUInteger>(index) >= _entries.count) | 540 if (index < 0 || static_cast<NSUInteger>(index) >= self.items.size()) |
555 return; | 541 return; |
556 | 542 |
557 if (index < _currentNavigationIndex) { | 543 if (index < _currentNavigationIndex) { |
558 // Going back. | 544 // Going back. |
559 [self discardNonCommittedItems]; | 545 [self discardNonCommittedItems]; |
560 } else if (_currentNavigationIndex < index) { | 546 } else if (_currentNavigationIndex < index) { |
561 // Going forward. | 547 // Going forward. |
562 [self discardTransientItem]; | 548 [self discardTransientItem]; |
563 } else { | 549 } else { |
564 // |delta| is 0, no need to change current navigation index. | 550 // |delta| is 0, no need to change current navigation index. |
565 return; | 551 return; |
566 } | 552 } |
567 | 553 |
568 _previousNavigationIndex = _currentNavigationIndex; | 554 _previousNavigationIndex = _currentNavigationIndex; |
569 _currentNavigationIndex = index; | 555 _currentNavigationIndex = index; |
570 } | 556 } |
571 | 557 |
572 - (void)removeItemAtIndex:(NSInteger)index { | 558 - (void)removeItemAtIndex:(NSInteger)index { |
573 DCHECK(index < static_cast<NSInteger>([_entries count])); | 559 DCHECK(index < static_cast<NSInteger>(self.items.size())); |
574 DCHECK(index != _currentNavigationIndex); | 560 DCHECK(index != _currentNavigationIndex); |
575 DCHECK(index >= 0); | 561 DCHECK(index >= 0); |
576 | 562 |
577 [self discardNonCommittedItems]; | 563 [self discardNonCommittedItems]; |
578 | 564 |
579 [_entries removeObjectAtIndex:index]; | 565 _items.erase(_items.begin() + index); |
580 if (_currentNavigationIndex > index) | 566 if (_currentNavigationIndex > index) |
581 _currentNavigationIndex--; | 567 _currentNavigationIndex--; |
582 if (_previousNavigationIndex >= index) | 568 if (_previousNavigationIndex >= index) |
583 _previousNavigationIndex--; | 569 _previousNavigationIndex--; |
584 } | 570 } |
585 | 571 |
586 - (NSArray*)backwardEntries { | |
587 NSMutableArray* entries = [NSMutableArray array]; | |
588 for (NSInteger index = _currentNavigationIndex; index > 0; --index) { | |
589 if (![self isRedirectTransitionForItemAtIndex:index]) | |
590 [entries addObject:_entries[index - 1]]; | |
591 } | |
592 return entries; | |
593 } | |
594 | |
595 - (NSArray*)forwardEntries { | |
596 NSMutableArray* entries = [NSMutableArray array]; | |
597 NSUInteger lastNonRedirectedIndex = _currentNavigationIndex + 1; | |
598 while (lastNonRedirectedIndex < [_entries count]) { | |
599 CRWSessionEntry* entry = [_entries objectAtIndex:lastNonRedirectedIndex]; | |
600 if (!ui::PageTransitionIsRedirect( | |
601 entry.navigationItem->GetTransitionType())) { | |
602 [entries addObject:entry]; | |
603 } | |
604 ++lastNonRedirectedIndex; | |
605 } | |
606 return entries; | |
607 } | |
608 | |
609 - (BOOL)isSameDocumentNavigationBetweenItem:(web::NavigationItem*)firstItem | 572 - (BOOL)isSameDocumentNavigationBetweenItem:(web::NavigationItem*)firstItem |
610 andItem:(web::NavigationItem*)secondItem { | 573 andItem:(web::NavigationItem*)secondItem { |
611 if (!firstItem || !secondItem || firstItem == secondItem) | 574 if (!firstItem || !secondItem || firstItem == secondItem) |
612 return NO; | 575 return NO; |
613 NSUInteger firstIndex = [self indexOfItem:firstItem]; | 576 NSUInteger firstIndex = [self indexOfItem:firstItem]; |
614 NSUInteger secondIndex = [self indexOfItem:secondItem]; | 577 NSUInteger secondIndex = [self indexOfItem:secondItem]; |
615 if (firstIndex == NSNotFound || secondIndex == NSNotFound) | 578 if (firstIndex == NSNotFound || secondIndex == NSNotFound) |
616 return NO; | 579 return NO; |
617 NSUInteger startIndex = firstIndex < secondIndex ? firstIndex : secondIndex; | 580 NSUInteger startIndex = firstIndex < secondIndex ? firstIndex : secondIndex; |
618 NSUInteger endIndex = firstIndex < secondIndex ? secondIndex : firstIndex; | 581 NSUInteger endIndex = firstIndex < secondIndex ? secondIndex : firstIndex; |
619 | 582 |
620 for (NSUInteger i = startIndex + 1; i <= endIndex; i++) { | 583 for (NSUInteger i = startIndex + 1; i <= endIndex; i++) { |
621 web::NavigationItemImpl* item = [_entries[i] navigationItemImpl]; | 584 web::NavigationItemImpl* item = self.items[i].get(); |
622 // Every entry in the sequence has to be created from a hash change or | 585 // Every item in the sequence has to be created from a hash change or |
623 // pushState() call. | 586 // pushState() call. |
624 if (!item->IsCreatedFromPushState() && !item->IsCreatedFromHashChange()) | 587 if (!item->IsCreatedFromPushState() && !item->IsCreatedFromHashChange()) |
625 return NO; | 588 return NO; |
626 // Every entry in the sequence has to have a URL that could have been | 589 // Every item in the sequence has to have a URL that could have been |
627 // created from a pushState() call. | 590 // created from a pushState() call. |
628 if (!web::history_state_util::IsHistoryStateChangeValid(firstItem->GetURL(), | 591 if (!web::history_state_util::IsHistoryStateChangeValid(firstItem->GetURL(), |
629 item->GetURL())) | 592 item->GetURL())) |
630 return NO; | 593 return NO; |
631 } | 594 } |
632 return YES; | 595 return YES; |
633 } | 596 } |
634 | 597 |
635 - (CRWSessionEntry*)lastUserEntry { | |
636 if (![_entries count]) | |
637 return nil; | |
638 | |
639 NSInteger index = _currentNavigationIndex; | |
640 // This will return the first session entry if all other entries are | |
641 // redirects, regardless of the transition state of the first entry. | |
642 while (index > 0 && [self isRedirectTransitionForItemAtIndex:index]) { | |
643 --index; | |
644 } | |
645 return [_entries objectAtIndex:index]; | |
646 } | |
647 | |
648 - (NSInteger)indexOfItem:(const web::NavigationItem*)item { | 598 - (NSInteger)indexOfItem:(const web::NavigationItem*)item { |
649 for (NSUInteger index = 0; index < self.entries.count; ++index) { | 599 DCHECK(item); |
650 if ([self.entries[index] navigationItem] == item) | 600 for (size_t index = 0; index < self.items.size(); ++index) { |
651 return static_cast<NSInteger>(index); | 601 if (self.items[index].get() == item) |
| 602 return index; |
652 } | 603 } |
653 return NSNotFound; | 604 return NSNotFound; |
654 } | 605 } |
655 | 606 |
656 - (web::NavigationItemImpl*)itemAtIndex:(NSInteger)index { | 607 - (web::NavigationItemImpl*)itemAtIndex:(NSInteger)index { |
657 if (index < 0 || self.entries.count <= static_cast<NSUInteger>(index)) | 608 if (index < 0 || self.items.size() <= static_cast<NSUInteger>(index)) |
658 return nullptr; | 609 return nullptr; |
659 return static_cast<web::NavigationItemImpl*>( | 610 return self.items[index].get(); |
660 [self.entries[index] navigationItem]); | |
661 } | 611 } |
662 | 612 |
663 #pragma mark - | 613 #pragma mark - |
664 #pragma mark Private methods | 614 #pragma mark Private methods |
665 | 615 |
666 - (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url | 616 - (std::unique_ptr<web::NavigationItemImpl>) |
667 referrer:(const web::Referrer&)referrer | 617 itemWithURL:(const GURL&)url |
668 transition:(ui::PageTransition)transition | 618 referrer:(const web::Referrer&)referrer |
669 initiationType: | 619 transition:(ui::PageTransition)transition |
670 (web::NavigationInitiationType)initiationType { | 620 initiationType:(web::NavigationInitiationType)initiationType { |
671 GURL loaded_url(url); | 621 GURL loaded_url(url); |
672 BOOL urlWasRewritten = NO; | 622 BOOL urlWasRewritten = NO; |
673 if (_navigationManager) { | 623 if (_navigationManager) { |
674 std::unique_ptr<std::vector<web::BrowserURLRewriter::URLRewriter>> | 624 std::unique_ptr<std::vector<web::BrowserURLRewriter::URLRewriter>> |
675 transientRewriters = _navigationManager->GetTransientURLRewriters(); | 625 transientRewriters = _navigationManager->GetTransientURLRewriters(); |
676 if (transientRewriters) { | 626 if (transientRewriters) { |
677 urlWasRewritten = web::BrowserURLRewriter::RewriteURLWithWriters( | 627 urlWasRewritten = web::BrowserURLRewriter::RewriteURLWithWriters( |
678 &loaded_url, _browserState, *transientRewriters.get()); | 628 &loaded_url, _browserState, *transientRewriters.get()); |
679 } | 629 } |
680 } | 630 } |
681 if (!urlWasRewritten) { | 631 if (!urlWasRewritten) { |
682 web::BrowserURLRewriter::GetInstance()->RewriteURLIfNecessary( | 632 web::BrowserURLRewriter::GetInstance()->RewriteURLIfNecessary( |
683 &loaded_url, _browserState); | 633 &loaded_url, _browserState); |
684 } | 634 } |
685 | 635 |
686 std::unique_ptr<web::NavigationItemImpl> item(new web::NavigationItemImpl()); | 636 std::unique_ptr<web::NavigationItemImpl> item(new web::NavigationItemImpl()); |
687 item->SetOriginalRequestURL(loaded_url); | 637 item->SetOriginalRequestURL(loaded_url); |
688 item->SetURL(loaded_url); | 638 item->SetURL(loaded_url); |
689 item->SetReferrer(referrer); | 639 item->SetReferrer(referrer); |
690 item->SetTransitionType(transition); | 640 item->SetTransitionType(transition); |
691 item->SetNavigationInitiationType(initiationType); | 641 item->SetNavigationInitiationType(initiationType); |
692 if (web::GetWebClient()->IsAppSpecificURL(loaded_url)) | 642 if (web::GetWebClient()->IsAppSpecificURL(loaded_url)) |
693 item->SetUserAgentType(web::UserAgentType::NONE); | 643 item->SetUserAgentType(web::UserAgentType::NONE); |
694 return [[CRWSessionEntry alloc] initWithNavigationItem:std::move(item)]; | 644 return item; |
695 } | 645 } |
696 | 646 |
697 - (BOOL)isRedirectTransitionForItemAtIndex:(NSInteger)index { | 647 - (BOOL)isRedirectTransitionForItemAtIndex:(size_t)index { |
698 ui::PageTransition transition = | 648 DCHECK_LT(index, self.items.size()); |
699 [_entries[index] navigationItem]->GetTransitionType(); | 649 ui::PageTransition transition = self.items[index]->GetTransitionType(); |
700 return (transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) ? YES : NO; | 650 return (transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) ? YES : NO; |
701 } | 651 } |
702 | 652 |
703 - (web::NavigationItemList)itemListForEntryList:(NSArray*)entries { | |
704 web::NavigationItemList list(entries.count); | |
705 for (size_t index = 0; index < entries.count; ++index) | |
706 list[index] = [entries[index] navigationItem]; | |
707 return list; | |
708 } | |
709 | |
710 @end | 653 @end |
OLD | NEW |