// Copyright 2013 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. #include "ios/web/navigation/navigation_manager_impl.h" #include "base/logging.h" #import "ios/web/navigation/crw_session_controller+private_constructors.h" #import "ios/web/navigation/crw_session_controller.h" #import "ios/web/navigation/crw_session_entry.h" #include "ios/web/navigation/navigation_item_impl.h" #include "ios/web/navigation/navigation_manager_delegate.h" #import "ios/web/navigation/navigation_manager_facade_delegate.h" #include "ios/web/public/load_committed_details.h" #include "ios/web/public/navigation_item.h" #include "ios/web/public/web_state/web_state.h" #include "ui/base/page_transition_types.h" namespace { // Checks whether or not two URL are an in-page navigation (differing only // in the fragment). bool AreURLsInPageNavigation(const GURL& existing_url, const GURL& new_url) { if (existing_url == new_url || !new_url.has_ref()) return false; url::Replacements<char> replacements; replacements.ClearRef(); return existing_url.ReplaceComponents(replacements) == new_url.ReplaceComponents(replacements); } } // anonymous namespace namespace web { NavigationManagerImpl::NavigationManagerImpl( NavigationManagerDelegate* delegate, BrowserState* browser_state) : delegate_(delegate), browser_state_(browser_state), facade_delegate_(nullptr) { DCHECK(browser_state_); } NavigationManagerImpl::~NavigationManagerImpl() { // The facade layer should be deleted before this object. DCHECK(!facade_delegate_); [session_controller_ setNavigationManager:nullptr]; } CRWSessionController* NavigationManagerImpl::GetSessionController() { return session_controller_; } void NavigationManagerImpl::SetSessionController( CRWSessionController* session_controller) { session_controller_.reset([session_controller retain]); [session_controller_ setNavigationManager:this]; } void NavigationManagerImpl::InitializeSession(NSString* window_name, NSString* opener_id, BOOL opened_by_dom, int opener_navigation_index) { SetSessionController([[[CRWSessionController alloc] initWithWindowName:window_name openerId:opener_id openedByDOM:opened_by_dom openerNavigationIndex:opener_navigation_index browserState:browser_state_] autorelease]); } void NavigationManagerImpl::ReplaceSessionHistory( ScopedVector<web::NavigationItem> items, int current_index) { SetSessionController([[[CRWSessionController alloc] initWithNavigationItems:items.Pass() currentIndex:current_index browserState:browser_state_] autorelease]); } void NavigationManagerImpl::SetFacadeDelegate( NavigationManagerFacadeDelegate* facade_delegate) { facade_delegate_ = facade_delegate; } NavigationManagerFacadeDelegate* NavigationManagerImpl::GetFacadeDelegate() const { return facade_delegate_; } void NavigationManagerImpl::OnNavigationItemChanged() { if (facade_delegate_) facade_delegate_->OnNavigationItemChanged(); } void NavigationManagerImpl::OnNavigationItemCommitted() { LoadCommittedDetails details; details.item = GetLastCommittedItem(); DCHECK(details.item); details.previous_item_index = [session_controller_ previousNavigationIndex]; if (details.previous_item_index >= 0) { DCHECK([session_controller_ previousEntry]); details.previous_url = [session_controller_ previousEntry].navigationItem->GetURL(); details.is_in_page = AreURLsInPageNavigation(details.previous_url, details.item->GetURL()); } else { details.previous_url = GURL(); details.is_in_page = NO; } delegate_->OnNavigationItemCommitted(details); if (facade_delegate_) { facade_delegate_->OnNavigationItemCommitted(details.previous_item_index, details.is_in_page); } } NavigationItem* NavigationManagerImpl::GetVisibleItem() const { CRWSessionEntry* entry = [session_controller_ visibleEntry]; return [entry navigationItem]; } NavigationItem* NavigationManagerImpl::GetPendingItem() const { return [[session_controller_ pendingEntry] navigationItem]; } void NavigationManagerImpl::DiscardNonCommittedItems() { [session_controller_ discardNonCommittedEntries]; } void NavigationManagerImpl::LoadIfNecessary() { // Nothing to do; iOS loads lazily. } NavigationItem* NavigationManagerImpl::GetTransientItem() const { return [[session_controller_ transientEntry] navigationItem]; } NavigationItem* NavigationManagerImpl::GetLastCommittedItem() const { CRWSessionEntry* entry = [session_controller_ lastCommittedEntry]; return [entry navigationItem]; } int NavigationManagerImpl::GetLastCommittedEntryIndex() const { if (![[session_controller_ entries] count]) return -1; return [session_controller_ currentNavigationIndex]; } bool NavigationManagerImpl::RemoveEntryAtIndex(int index) { if (index == GetLastCommittedEntryIndex() || index == GetPendingItemIndex()) return false; NSUInteger idx = static_cast<NSUInteger>(index); NSArray* entries = [session_controller_ entries]; if (idx >= entries.count) return false; [session_controller_ removeEntryAtIndex:index]; return true; } NavigationItem* NavigationManagerImpl::GetLastUserItem() const { CRWSessionEntry* entry = [session_controller_ lastUserEntry]; return [entry navigationItem]; } NavigationItem* NavigationManagerImpl::GetPreviousItem() const { CRWSessionEntry* entry = [session_controller_ previousEntry]; return [entry navigationItem]; } void NavigationManagerImpl::AddTransientURLRewriter( BrowserURLRewriter::URLRewriter rewriter) { DCHECK(rewriter); if (!transient_url_rewriters_) { transient_url_rewriters_.reset( new std::vector<BrowserURLRewriter::URLRewriter>()); } transient_url_rewriters_->push_back(rewriter); } int NavigationManagerImpl::GetEntryCount() const { return [[session_controller_ entries] count]; } NavigationItem* NavigationManagerImpl::GetItemAtIndex(size_t index) const { NSArray* entries = [session_controller_ entries]; return index < entries.count ? [entries[index] navigationItem] : nullptr; } int NavigationManagerImpl::GetCurrentEntryIndex() const { return [session_controller_ currentNavigationIndex]; } int NavigationManagerImpl::GetPendingItemIndex() const { if ([session_controller_ hasPendingEntry]) return GetCurrentEntryIndex(); return -1; } scoped_ptr<std::vector<BrowserURLRewriter::URLRewriter>> NavigationManagerImpl::GetTransientURLRewriters() { return transient_url_rewriters_.Pass(); } void NavigationManagerImpl::RemoveTransientURLRewriters() { transient_url_rewriters_.reset(); } void NavigationManagerImpl::LoadURL(const GURL& url, const web::Referrer& referrer, ui::PageTransition type) { WebState::OpenURLParams params(url, referrer, CURRENT_TAB, type, NO); delegate_->GetWebState()->OpenURL(params); } bool NavigationManagerImpl::CanGoBack() const { return [session_controller_ canGoBack]; } bool NavigationManagerImpl::CanGoForward() const { return [session_controller_ canGoForward]; } void NavigationManagerImpl::GoBack() { if (CanGoBack()) { [session_controller_ goBack]; // Signal the delegate to load the old page. delegate_->NavigateToPendingEntry(); } } void NavigationManagerImpl::GoForward() { if (CanGoForward()) { [session_controller_ goForward]; // Signal the delegate to load the new page. delegate_->NavigateToPendingEntry(); } } std::vector<NavigationItem*> NavigationManagerImpl::GetItems() { std::vector<NavigationItem*> items; size_t i = 0; items.resize([session_controller_ entries].count); for (CRWSessionEntry* entry in [session_controller_ entries]) { items[i++] = entry.navigationItem; } return items; } BrowserState* NavigationManagerImpl::GetBrowserState() const { return browser_state_; } WebState* NavigationManagerImpl::GetWebState() const { return delegate_->GetWebState(); } void NavigationManagerImpl::CopyState( NavigationManagerImpl* navigation_manager) { SetSessionController( [[navigation_manager->GetSessionController() copy] autorelease]); } } // namespace web