| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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/navigation_manager_impl.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "base/logging.h" | |
| 12 #import "ios/web/navigation/crw_session_controller+private_constructors.h" | |
| 13 #import "ios/web/navigation/crw_session_controller.h" | |
| 14 #import "ios/web/navigation/navigation_item_impl.h" | |
| 15 #import "ios/web/navigation/navigation_item_impl_list.h" | |
| 16 #import "ios/web/navigation/navigation_manager_delegate.h" | |
| 17 #include "ios/web/public/load_committed_details.h" | |
| 18 #import "ios/web/public/navigation_item.h" | |
| 19 #include "ios/web/public/reload_type.h" | |
| 20 #import "ios/web/public/web_client.h" | |
| 21 #import "ios/web/public/web_state/web_state.h" | |
| 22 #include "ui/base/page_transition_types.h" | |
| 23 | |
| 24 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
| 25 #error "This file requires ARC support." | |
| 26 #endif | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 // Checks whether or not two URL are an in-page navigation (differing only | |
| 31 // in the fragment). | |
| 32 bool AreURLsInPageNavigation(const GURL& existing_url, const GURL& new_url) { | |
| 33 if (existing_url == new_url || !new_url.has_ref()) | |
| 34 return false; | |
| 35 | |
| 36 return existing_url.EqualsIgnoringRef(new_url); | |
| 37 } | |
| 38 | |
| 39 } // anonymous namespace | |
| 40 | |
| 41 namespace web { | |
| 42 | |
| 43 NavigationManager::WebLoadParams::WebLoadParams(const GURL& url) | |
| 44 : url(url), | |
| 45 transition_type(ui::PAGE_TRANSITION_LINK), | |
| 46 user_agent_override_option(UserAgentOverrideOption::INHERIT), | |
| 47 is_renderer_initiated(false), | |
| 48 post_data(nil) {} | |
| 49 | |
| 50 NavigationManager::WebLoadParams::~WebLoadParams() {} | |
| 51 | |
| 52 NavigationManager::WebLoadParams::WebLoadParams(const WebLoadParams& other) | |
| 53 : url(other.url), | |
| 54 referrer(other.referrer), | |
| 55 transition_type(other.transition_type), | |
| 56 user_agent_override_option(other.user_agent_override_option), | |
| 57 is_renderer_initiated(other.is_renderer_initiated), | |
| 58 extra_headers([other.extra_headers copy]), | |
| 59 post_data([other.post_data copy]) {} | |
| 60 | |
| 61 NavigationManager::WebLoadParams& NavigationManager::WebLoadParams::operator=( | |
| 62 const WebLoadParams& other) { | |
| 63 url = other.url; | |
| 64 referrer = other.referrer; | |
| 65 is_renderer_initiated = other.is_renderer_initiated; | |
| 66 transition_type = other.transition_type; | |
| 67 user_agent_override_option = other.user_agent_override_option; | |
| 68 extra_headers.reset([other.extra_headers copy]); | |
| 69 post_data.reset([other.post_data copy]); | |
| 70 | |
| 71 return *this; | |
| 72 } | |
| 73 | |
| 74 NavigationManagerImpl::NavigationManagerImpl() | |
| 75 : delegate_(nullptr), browser_state_(nullptr) {} | |
| 76 | |
| 77 NavigationManagerImpl::~NavigationManagerImpl() { | |
| 78 [session_controller_ setNavigationManager:nullptr]; | |
| 79 } | |
| 80 | |
| 81 void NavigationManagerImpl::SetDelegate(NavigationManagerDelegate* delegate) { | |
| 82 delegate_ = delegate; | |
| 83 } | |
| 84 | |
| 85 void NavigationManagerImpl::SetBrowserState(BrowserState* browser_state) { | |
| 86 browser_state_ = browser_state; | |
| 87 [session_controller_ setBrowserState:browser_state]; | |
| 88 } | |
| 89 | |
| 90 void NavigationManagerImpl::SetSessionController( | |
| 91 CRWSessionController* session_controller) { | |
| 92 session_controller_.reset(session_controller); | |
| 93 [session_controller_ setNavigationManager:this]; | |
| 94 } | |
| 95 | |
| 96 void NavigationManagerImpl::InitializeSession() { | |
| 97 SetSessionController( | |
| 98 [[CRWSessionController alloc] initWithBrowserState:browser_state_]); | |
| 99 } | |
| 100 | |
| 101 void NavigationManagerImpl::ReplaceSessionHistory( | |
| 102 std::vector<std::unique_ptr<web::NavigationItem>> items, | |
| 103 int lastCommittedItemIndex) { | |
| 104 SetSessionController([[CRWSessionController alloc] | |
| 105 initWithBrowserState:browser_state_ | |
| 106 navigationItems:std::move(items) | |
| 107 lastCommittedItemIndex:lastCommittedItemIndex]); | |
| 108 } | |
| 109 | |
| 110 void NavigationManagerImpl::OnNavigationItemsPruned(size_t pruned_item_count) { | |
| 111 delegate_->OnNavigationItemsPruned(pruned_item_count); | |
| 112 } | |
| 113 | |
| 114 void NavigationManagerImpl::OnNavigationItemChanged() { | |
| 115 delegate_->OnNavigationItemChanged(); | |
| 116 } | |
| 117 | |
| 118 void NavigationManagerImpl::OnNavigationItemCommitted() { | |
| 119 LoadCommittedDetails details; | |
| 120 details.item = GetLastCommittedItem(); | |
| 121 DCHECK(details.item); | |
| 122 details.previous_item_index = [session_controller_ previousItemIndex]; | |
| 123 if (details.previous_item_index >= 0) { | |
| 124 DCHECK([session_controller_ previousItem]); | |
| 125 details.previous_url = [session_controller_ previousItem]->GetURL(); | |
| 126 details.is_in_page = | |
| 127 AreURLsInPageNavigation(details.previous_url, details.item->GetURL()); | |
| 128 } else { | |
| 129 details.previous_url = GURL(); | |
| 130 details.is_in_page = NO; | |
| 131 } | |
| 132 | |
| 133 delegate_->OnNavigationItemCommitted(details); | |
| 134 } | |
| 135 | |
| 136 CRWSessionController* NavigationManagerImpl::GetSessionController() { | |
| 137 return session_controller_; | |
| 138 } | |
| 139 | |
| 140 void NavigationManagerImpl::AddTransientItem(const GURL& url) { | |
| 141 [session_controller_ addTransientItemWithURL:url]; | |
| 142 | |
| 143 // TODO(crbug.com/676129): Transient item is only supposed to be added for | |
| 144 // pending non-app-specific loads, but pending item can be null because of the | |
| 145 // bug. The workaround should be removed once the bug is fixed. | |
| 146 NavigationItem* item = GetPendingItem(); | |
| 147 if (!item) | |
| 148 item = GetLastCommittedNonAppSpecificItem(); | |
| 149 DCHECK(item->GetUserAgentType() != UserAgentType::NONE); | |
| 150 GetTransientItem()->SetUserAgentType(item->GetUserAgentType()); | |
| 151 } | |
| 152 | |
| 153 void NavigationManagerImpl::AddPendingItem( | |
| 154 const GURL& url, | |
| 155 const web::Referrer& referrer, | |
| 156 ui::PageTransition navigation_type, | |
| 157 NavigationInitiationType initiation_type, | |
| 158 UserAgentOverrideOption user_agent_override_option) { | |
| 159 [session_controller_ addPendingItem:url | |
| 160 referrer:referrer | |
| 161 transition:navigation_type | |
| 162 initiationType:initiation_type | |
| 163 userAgentOverrideOption:user_agent_override_option]; | |
| 164 | |
| 165 // Set the user agent type for web URLs. | |
| 166 NavigationItem* pending_item = GetPendingItem(); | |
| 167 if (!pending_item) | |
| 168 return; | |
| 169 | |
| 170 // |user_agent_override_option| must be INHERIT if |pending_item|'s | |
| 171 // UserAgentType is NONE, as requesting a desktop or mobile user agent should | |
| 172 // be disabled for app-specific URLs. | |
| 173 DCHECK(pending_item->GetUserAgentType() != UserAgentType::NONE || | |
| 174 user_agent_override_option == UserAgentOverrideOption::INHERIT); | |
| 175 | |
| 176 // Newly created pending items are created with UserAgentType::NONE for native | |
| 177 // pages or UserAgentType::MOBILE for non-native pages. If the pending item's | |
| 178 // URL is non-native, check which user agent type it should be created with | |
| 179 // based on |user_agent_override_option|. | |
| 180 DCHECK_NE(UserAgentType::DESKTOP, pending_item->GetUserAgentType()); | |
| 181 if (pending_item->GetUserAgentType() == UserAgentType::NONE) | |
| 182 return; | |
| 183 | |
| 184 switch (user_agent_override_option) { | |
| 185 case UserAgentOverrideOption::DESKTOP: | |
| 186 pending_item->SetUserAgentType(UserAgentType::DESKTOP); | |
| 187 break; | |
| 188 case UserAgentOverrideOption::MOBILE: | |
| 189 pending_item->SetUserAgentType(UserAgentType::MOBILE); | |
| 190 break; | |
| 191 case UserAgentOverrideOption::INHERIT: { | |
| 192 // Propagate the last committed non-native item's UserAgentType if there | |
| 193 // is one, otherwise keep the default value, which is mobile. | |
| 194 NavigationItem* last_non_native_item = | |
| 195 GetLastCommittedNonAppSpecificItem(); | |
| 196 DCHECK(!last_non_native_item || | |
| 197 last_non_native_item->GetUserAgentType() != UserAgentType::NONE); | |
| 198 if (last_non_native_item) { | |
| 199 pending_item->SetUserAgentType( | |
| 200 last_non_native_item->GetUserAgentType()); | |
| 201 } | |
| 202 break; | |
| 203 } | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 void NavigationManagerImpl::CommitPendingItem() { | |
| 208 [session_controller_ commitPendingItem]; | |
| 209 } | |
| 210 | |
| 211 BrowserState* NavigationManagerImpl::GetBrowserState() const { | |
| 212 return browser_state_; | |
| 213 } | |
| 214 | |
| 215 WebState* NavigationManagerImpl::GetWebState() const { | |
| 216 return delegate_->GetWebState(); | |
| 217 } | |
| 218 | |
| 219 NavigationItem* NavigationManagerImpl::GetVisibleItem() const { | |
| 220 return [session_controller_ visibleItem]; | |
| 221 } | |
| 222 | |
| 223 NavigationItem* NavigationManagerImpl::GetLastCommittedItem() const { | |
| 224 return [session_controller_ lastCommittedItem]; | |
| 225 } | |
| 226 | |
| 227 NavigationItem* NavigationManagerImpl::GetPendingItem() const { | |
| 228 return [session_controller_ pendingItem]; | |
| 229 } | |
| 230 | |
| 231 NavigationItem* NavigationManagerImpl::GetTransientItem() const { | |
| 232 return [session_controller_ transientItem]; | |
| 233 } | |
| 234 | |
| 235 void NavigationManagerImpl::DiscardNonCommittedItems() { | |
| 236 [session_controller_ discardNonCommittedItems]; | |
| 237 } | |
| 238 | |
| 239 void NavigationManagerImpl::LoadURLWithParams( | |
| 240 const NavigationManager::WebLoadParams& params) { | |
| 241 delegate_->LoadURLWithParams(params); | |
| 242 } | |
| 243 | |
| 244 void NavigationManagerImpl::AddTransientURLRewriter( | |
| 245 BrowserURLRewriter::URLRewriter rewriter) { | |
| 246 DCHECK(rewriter); | |
| 247 if (!transient_url_rewriters_) { | |
| 248 transient_url_rewriters_.reset( | |
| 249 new std::vector<BrowserURLRewriter::URLRewriter>()); | |
| 250 } | |
| 251 transient_url_rewriters_->push_back(rewriter); | |
| 252 } | |
| 253 | |
| 254 int NavigationManagerImpl::GetItemCount() const { | |
| 255 return [session_controller_ items].size(); | |
| 256 } | |
| 257 | |
| 258 NavigationItem* NavigationManagerImpl::GetItemAtIndex(size_t index) const { | |
| 259 return [session_controller_ itemAtIndex:index]; | |
| 260 } | |
| 261 | |
| 262 int NavigationManagerImpl::GetIndexOfItem( | |
| 263 const web::NavigationItem* item) const { | |
| 264 return [session_controller_ indexOfItem:item]; | |
| 265 } | |
| 266 | |
| 267 int NavigationManagerImpl::GetPendingItemIndex() const { | |
| 268 if (GetPendingItem()) { | |
| 269 if ([session_controller_ pendingItemIndex] != -1) { | |
| 270 return [session_controller_ pendingItemIndex]; | |
| 271 } | |
| 272 // TODO(crbug.com/665189): understand why last committed item index is | |
| 273 // returned here. | |
| 274 return GetLastCommittedItemIndex(); | |
| 275 } | |
| 276 return -1; | |
| 277 } | |
| 278 | |
| 279 int NavigationManagerImpl::GetLastCommittedItemIndex() const { | |
| 280 if (GetItemCount() == 0) | |
| 281 return -1; | |
| 282 return [session_controller_ lastCommittedItemIndex]; | |
| 283 } | |
| 284 | |
| 285 bool NavigationManagerImpl::RemoveItemAtIndex(int index) { | |
| 286 if (index == GetLastCommittedItemIndex() || index == GetPendingItemIndex()) | |
| 287 return false; | |
| 288 | |
| 289 if (index < 0 || index >= GetItemCount()) | |
| 290 return false; | |
| 291 | |
| 292 [session_controller_ removeItemAtIndex:index]; | |
| 293 return true; | |
| 294 } | |
| 295 | |
| 296 bool NavigationManagerImpl::CanGoBack() const { | |
| 297 return CanGoToOffset(-1); | |
| 298 } | |
| 299 | |
| 300 bool NavigationManagerImpl::CanGoForward() const { | |
| 301 return CanGoToOffset(1); | |
| 302 } | |
| 303 | |
| 304 bool NavigationManagerImpl::CanGoToOffset(int offset) const { | |
| 305 int index = GetIndexForOffset(offset); | |
| 306 return 0 <= index && index < GetItemCount(); | |
| 307 } | |
| 308 | |
| 309 void NavigationManagerImpl::GoBack() { | |
| 310 delegate_->GoToIndex(GetIndexForOffset(-1)); | |
| 311 } | |
| 312 | |
| 313 void NavigationManagerImpl::GoForward() { | |
| 314 delegate_->GoToIndex(GetIndexForOffset(1)); | |
| 315 } | |
| 316 | |
| 317 void NavigationManagerImpl::GoToIndex(int index) { | |
| 318 delegate_->GoToIndex(index); | |
| 319 } | |
| 320 | |
| 321 NavigationItemList NavigationManagerImpl::GetBackwardItems() const { | |
| 322 return [session_controller_ backwardItems]; | |
| 323 } | |
| 324 | |
| 325 NavigationItemList NavigationManagerImpl::GetForwardItems() const { | |
| 326 return [session_controller_ forwardItems]; | |
| 327 } | |
| 328 | |
| 329 void NavigationManagerImpl::Reload(ReloadType reload_type, | |
| 330 bool check_for_reposts) { | |
| 331 if (!GetTransientItem() && !GetPendingItem() && !GetLastCommittedItem()) | |
| 332 return; | |
| 333 | |
| 334 // Reload with ORIGINAL_REQUEST_URL type should reload with the original | |
| 335 // request url of the transient item, or pending item if transient doesn't | |
| 336 // exist, or last committed item if both of them don't exist. The reason is | |
| 337 // that a server side redirect may change the item's url. | |
| 338 // For example, the user visits www.chromium.org and is then redirected | |
| 339 // to m.chromium.org, when the user wants to refresh the page with a different | |
| 340 // configuration (e.g. user agent), the user would be expecting to visit | |
| 341 // www.chromium.org instead of m.chromium.org. | |
| 342 if (reload_type == web::ReloadType::ORIGINAL_REQUEST_URL) { | |
| 343 NavigationItem* reload_item = nullptr; | |
| 344 if (GetTransientItem()) | |
| 345 reload_item = GetTransientItem(); | |
| 346 else if (GetPendingItem()) | |
| 347 reload_item = GetPendingItem(); | |
| 348 else | |
| 349 reload_item = GetLastCommittedItem(); | |
| 350 DCHECK(reload_item); | |
| 351 | |
| 352 reload_item->SetURL(reload_item->GetOriginalRequestURL()); | |
| 353 } | |
| 354 | |
| 355 delegate_->Reload(); | |
| 356 } | |
| 357 | |
| 358 void NavigationManagerImpl::CopyStateFromAndPrune( | |
| 359 const NavigationManager* manager) { | |
| 360 DCHECK(manager); | |
| 361 CRWSessionController* other_session = | |
| 362 static_cast<const NavigationManagerImpl*>(manager)->session_controller_; | |
| 363 [session_controller_ copyStateFromSessionControllerAndPrune:other_session]; | |
| 364 } | |
| 365 | |
| 366 bool NavigationManagerImpl::CanPruneAllButLastCommittedItem() const { | |
| 367 return [session_controller_ canPruneAllButLastCommittedItem]; | |
| 368 } | |
| 369 | |
| 370 std::unique_ptr<std::vector<BrowserURLRewriter::URLRewriter>> | |
| 371 NavigationManagerImpl::GetTransientURLRewriters() { | |
| 372 return std::move(transient_url_rewriters_); | |
| 373 } | |
| 374 | |
| 375 void NavigationManagerImpl::RemoveTransientURLRewriters() { | |
| 376 transient_url_rewriters_.reset(); | |
| 377 } | |
| 378 | |
| 379 int NavigationManagerImpl::GetIndexForOffset(int offset) const { | |
| 380 int result = [session_controller_ pendingItemIndex] == -1 | |
| 381 ? GetLastCommittedItemIndex() | |
| 382 : static_cast<int>([session_controller_ pendingItemIndex]); | |
| 383 | |
| 384 if (offset < 0) { | |
| 385 if (GetTransientItem() && [session_controller_ pendingItemIndex] == -1) { | |
| 386 // Going back from transient item that added to the end navigation stack | |
| 387 // is a matter of discarding it as there is no need to move navigation | |
| 388 // index back. | |
| 389 offset++; | |
| 390 } | |
| 391 | |
| 392 while (offset < 0 && result > 0) { | |
| 393 // To stop the user getting 'stuck' on redirecting pages they weren't | |
| 394 // even aware existed, it is necessary to pass over pages that would | |
| 395 // immediately result in a redirect (the item *before* the redirected | |
| 396 // page). | |
| 397 while (result > 0 && IsRedirectItemAtIndex(result)) { | |
| 398 --result; | |
| 399 } | |
| 400 --result; | |
| 401 ++offset; | |
| 402 } | |
| 403 // Result may be out of bounds, so stop trying to skip redirect items and | |
| 404 // simply add the remainder. | |
| 405 result += offset; | |
| 406 if (result > GetItemCount() /* overflow */) | |
| 407 result = INT_MIN; | |
| 408 } else if (offset > 0) { | |
| 409 while (offset > 0 && result < GetItemCount()) { | |
| 410 ++result; | |
| 411 --offset; | |
| 412 // As with going back, skip over redirects. | |
| 413 while (result + 1 < GetItemCount() && IsRedirectItemAtIndex(result + 1)) { | |
| 414 ++result; | |
| 415 } | |
| 416 } | |
| 417 // Result may be out of bounds, so stop trying to skip redirect items and | |
| 418 // simply add the remainder. | |
| 419 result += offset; | |
| 420 if (result < 0 /* overflow */) | |
| 421 result = INT_MAX; | |
| 422 } | |
| 423 | |
| 424 return result; | |
| 425 } | |
| 426 | |
| 427 bool NavigationManagerImpl::IsRedirectItemAtIndex(int index) const { | |
| 428 DCHECK_GE(index, 0); | |
| 429 DCHECK_LT(index, GetItemCount()); | |
| 430 ui::PageTransition transition = GetItemAtIndex(index)->GetTransitionType(); | |
| 431 return transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK; | |
| 432 } | |
| 433 | |
| 434 NavigationItem* NavigationManagerImpl::GetLastCommittedNonAppSpecificItem() | |
| 435 const { | |
| 436 int index = GetLastCommittedItemIndex(); | |
| 437 if (index == -1) | |
| 438 return nullptr; | |
| 439 WebClient* client = GetWebClient(); | |
| 440 const ScopedNavigationItemImplList& items = [session_controller_ items]; | |
| 441 while (index >= 0) { | |
| 442 NavigationItem* item = items[index--].get(); | |
| 443 if (!client->IsAppSpecificURL(item->GetVirtualURL())) | |
| 444 return item; | |
| 445 } | |
| 446 return nullptr; | |
| 447 } | |
| 448 | |
| 449 } // namespace web | |
| OLD | NEW |