Chromium Code Reviews| Index: ios/web/navigation/wk_based_navigation_manager_impl.mm |
| diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.mm b/ios/web/navigation/wk_based_navigation_manager_impl.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b0aaba840d50a1d77aaba462dc1b99d2d6cf387f |
| --- /dev/null |
| +++ b/ios/web/navigation/wk_based_navigation_manager_impl.mm |
| @@ -0,0 +1,500 @@ |
| +// Copyright 2017 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/wk_based_navigation_manager_impl.h" |
| + |
|
Eugene But (OOO till 7-30)
2017/06/27 22:02:00
FYI: I did not look at this file, which I guess is
danyao
2017/06/28 22:11:04
Thanks. I've stripped down this file to just a ske
|
| +#include <memory> |
| + |
| +#include "base/logging.h" |
| +#include "base/memory/ptr_util.h" |
| +#import "ios/web/navigation/navigation_item_impl.h" |
| +#import "ios/web/navigation/navigation_manager_delegate.h" |
| +#import "ios/web/navigation/web_view_navigation_proxy.h" |
| +#include "ios/web/public/load_committed_details.h" |
| +#import "ios/web/public/navigation_item.h" |
| +#import "ios/web/public/web_client.h" |
| +#import "net/base/mac/url_conversions.h" |
| + |
| +#if !defined(__has_feature) || !__has_feature(objc_arc) |
| +#error "This file requires ARC support." |
| +#endif |
| + |
| +@class CRWSessionController; |
| + |
| +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; |
| + |
| + return existing_url.EqualsIgnoringRef(new_url); |
| +} |
| + |
| +web::WebViewNavigationProxy* GetWebViewOrDie( |
| + web::NavigationManagerDelegate* delegate) { |
| + DCHECK(delegate); |
| + web::WebViewNavigationProxy* proxy = delegate->GetWebViewNavigationProxy(); |
| + DCHECK(proxy); |
| + return proxy; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace web { |
| + |
| +WKBasedNavigationManagerImpl::WKBasedNavigationManagerImpl() |
| + : delegate_(nullptr), browser_state_(nullptr) {} |
| + |
| +WKBasedNavigationManagerImpl::~WKBasedNavigationManagerImpl() {} |
| + |
| +void WKBasedNavigationManagerImpl::SetDelegate( |
| + NavigationManagerDelegate* delegate) { |
| + delegate_ = delegate; |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::SetBrowserState( |
| + BrowserState* browser_state) { |
| + browser_state_ = browser_state; |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::SetSessionController( |
| + CRWSessionController* session_controller) {} |
| + |
| +void WKBasedNavigationManagerImpl::InitializeSession() {} |
| + |
| +void WKBasedNavigationManagerImpl::ReplaceSessionHistory( |
| + std::vector<std::unique_ptr<NavigationItem>> items, |
| + int current_index) { |
| + DLOG(WARNING) << "ReplaceSessionHistory needs to be implemented."; |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::OnNavigationItemsPruned( |
| + size_t pruned_item_count) { |
| + delegate_->OnNavigationItemsPruned(pruned_item_count); |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::OnNavigationItemChanged() { |
| + delegate_->OnNavigationItemChanged(); |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::OnNavigationItemCommitted() { |
| + LoadCommittedDetails details; |
| + details.item = GetLastCommittedItem(); |
| + DCHECK(details.item); |
| + details.previous_item_index = GetPreviousItemIndex(); |
| + if (details.previous_item_index >= 0) { |
| + NavigationItem* previous_item = GetItemAtIndex(details.previous_item_index); |
| + DCHECK(previous_item); |
| + details.previous_url = previous_item->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); |
| +} |
| + |
| +CRWSessionController* WKBasedNavigationManagerImpl::GetSessionController() |
| + const { |
| + return nullptr; |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::AddTransientItem(const GURL& url) { |
| + DLOG(WARNING) << "AddTransientItem needs to be implemented."; |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::AddPendingItem( |
| + const GURL& url, |
| + const web::Referrer& referrer, |
| + ui::PageTransition navigation_type, |
| + NavigationInitiationType initiation_type, |
| + UserAgentOverrideOption user_agent_override_option) { |
| + // This is a start of a new navigation. Discard all uncommitted items. |
| + DiscardNonCommittedItems(); |
| + |
| + // TODO(danyao): |
| + // - share code with CRWSessionController |itemWithURL| |
| + // - decide if shouldCreatePendingItemWithURL check applies: basically |
| + // WKBasedNavigationManager no longer has the responsibility to distinguish |
| + // between history navigation vs. new navigation. So perhaps we can always |
| + // create a pending item. |
| + GURL loaded_url(url); |
| + bool urlWasRewritten = false; |
| + if (transient_url_rewriters_) { |
| + urlWasRewritten = BrowserURLRewriter::RewriteURLWithWriters( |
| + &loaded_url, GetBrowserState(), *transient_url_rewriters_); |
| + } |
| + |
| + if (!urlWasRewritten) { |
| + BrowserURLRewriter::GetInstance()->RewriteURLIfNecessary(&loaded_url, |
| + GetBrowserState()); |
| + } |
| + |
| + if (initiation_type == NavigationInitiationType::RENDERER_INITIATED && |
| + loaded_url != url && GetWebClient()->IsAppSpecificURL(loaded_url)) { |
| + bool lastCommittedURLIsAppSpecific = |
| + GetLastCommittedItem() && |
| + GetWebClient()->IsAppSpecificURL(GetLastCommittedItem()->GetURL()); |
| + |
| + if (!lastCommittedURLIsAppSpecific) { |
| + // The URL should not be changed to app-specific URL if the load was |
| + // renderer-initiated requested by non app-specific URL. Pages with |
| + // app-specific URLs have elevated privileges and should not be allowed |
| + // to open app-specific URLs. |
| + loaded_url = url; |
| + } |
| + } |
| + |
| + pending_item_ = base::MakeUnique<NavigationItemImpl>(); |
| + pending_item_->SetOriginalRequestURL(loaded_url); |
| + pending_item_->SetURL(loaded_url); |
| + pending_item_->SetReferrer(referrer); |
| + pending_item_->SetTransitionType(navigation_type); |
| + pending_item_->SetNavigationInitiationType(initiation_type); |
| + if (GetWebClient()->IsAppSpecificURL(loaded_url)) { |
| + pending_item_->SetUserAgentType(UserAgentType::NONE); |
| + } |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::CommitPendingItem() { |
| + // Add pending item to the end of the list. Make sure that |
| + // committed_navigation_items_ are in sync with WKWebView. |
| + int last_committed_index = GetLastCommittedItemIndex(); |
| + DCHECK(last_committed_index >= -1); |
| + size_t commit_index = last_committed_index + 1; |
| + if (commit_index == committed_navigation_items_.size()) { |
| + committed_navigation_items_.push_back(std::move(pending_item_)); |
| + } else { |
| + committed_navigation_items_[commit_index] = std::move(pending_item_); |
| + committed_navigation_items_.resize(commit_index + 1); |
| + } |
| + |
| + SyncNavigationItems(); |
| + |
| + // Commiting a pending item invalidates both transient item and pending item. |
| + DiscardNonCommittedItems(); |
| +} |
| + |
| +int WKBasedNavigationManagerImpl::GetIndexForOffset(int offset) const { |
| + // Use cases: |
| + // - CRWWebController: determines which entry goDelta should land on |
| + |
| + // TODO(danyao): take transient item into account |
| + return GetLastCommittedItemIndex() + offset; |
| +} |
| + |
| +BrowserState* WKBasedNavigationManagerImpl::GetBrowserState() const { |
| + return browser_state_; |
| +} |
| + |
| +WebState* WKBasedNavigationManagerImpl::GetWebState() const { |
| + return delegate_->GetWebState(); |
| +} |
| + |
| +NavigationItem* WKBasedNavigationManagerImpl::GetVisibleItem() const { |
| + // Transient item if one exists |
| + // Pending item for new (non-history), browser-initiated navigation |
| + if (transient_item_.get()) { |
| + return transient_item_.get(); |
| + } |
| + |
| + // TODO(danyao): check pending item |
| + return GetLastCommittedItem(); |
| +} |
| + |
| +NavigationItem* WKBasedNavigationManagerImpl::GetLastCommittedItem() const { |
| + SyncNavigationItems(); |
| + int last_committed_index = GetLastCommittedItemIndex(); |
| + if (last_committed_index == -1) { |
| + return nullptr; |
| + } |
| + return GetItemAtIndex(static_cast<size_t>(last_committed_index)); |
| +} |
| + |
| +NavigationItem* WKBasedNavigationManagerImpl::GetPendingItem() const { |
| + return pending_item_.get(); |
| +} |
| + |
| +NavigationItem* WKBasedNavigationManagerImpl::GetTransientItem() const { |
| + return transient_item_.get(); |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::DiscardNonCommittedItems() { |
| + // WKWebView should handle this. |
| + pending_item_.reset(); |
| + transient_item_.reset(); |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::LoadURLWithParams( |
| + const NavigationManager::WebLoadParams&) { |
| + DLOG(WARNING) << "LoadURLWithParams needs to be implemented."; |
| +} |
| + |
| +// TODO(danyao): move this to NavigationManagerImpl to reduce duplication |
| +// between legacy and wk-based |
| +void WKBasedNavigationManagerImpl::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 WKBasedNavigationManagerImpl::GetItemCount() const { |
| + WebViewNavigationProxy* proxy = delegate_->GetWebViewNavigationProxy(); |
| + if (proxy) { |
| + NSUInteger count_current_page = proxy->GetCurrentItem() ? 1 : 0; |
| + return (int)(proxy->GetBackList().count + count_current_page + |
| + proxy->GetForwardList().count); |
| + } |
| + |
| + // If WebView has not been created, it's fair to say navigation has 0 item. |
| + return 0; |
| +} |
| + |
| +NavigationItem* WKBasedNavigationManagerImpl::GetItemAtIndex( |
| + size_t index) const { |
| + // TODO(danyao): figure out how transient item fits in. Also this may result |
| + // in dangling pointer if SyncNavigationItems are called after this. |
| + return GetNavigationItemImplAtIndex(index); |
| +} |
| + |
| +int WKBasedNavigationManagerImpl::GetIndexOfItem( |
| + const NavigationItem* item) const { |
| + DLOG(WARNING) << "GetIndexOfItem needs to be implemented."; |
| + return -1; |
| +} |
| + |
| +int WKBasedNavigationManagerImpl::GetPendingItemIndex() const { |
| + // TODO(danyao): can we define pending item as only new navigation in main |
| + // frame? |
| + return -1; |
| +} |
| + |
| +int WKBasedNavigationManagerImpl::GetLastCommittedItemIndex() const { |
| + WebViewNavigationProxy* proxy = delegate_->GetWebViewNavigationProxy(); |
| + |
| + // If web view is empty (i.e. currentItem = nil, backList = []), return -1 |
| + // If web view is not empty, return the index of the current item. |
| + if (proxy && proxy->GetCurrentItem()) { |
| + return static_cast<int>(proxy->GetBackList().count); |
| + } |
| + return -1; |
| +} |
| + |
| +bool WKBasedNavigationManagerImpl::RemoveItemAtIndex(int index) { |
| + DLOG(WARNING) << "RemoveItemAtIndex needs to be implemented."; |
| + return true; |
| +} |
| + |
| +bool WKBasedNavigationManagerImpl::CanGoBack() const { |
| + WebViewNavigationProxy* proxy = delegate_->GetWebViewNavigationProxy(); |
| + return proxy && proxy->CanGoBack(); |
| + ; |
| +} |
| + |
| +bool WKBasedNavigationManagerImpl::CanGoForward() const { |
| + WebViewNavigationProxy* proxy = delegate_->GetWebViewNavigationProxy(); |
| + return proxy && proxy->CanGoForward(); |
| + ; |
| +} |
| + |
| +bool WKBasedNavigationManagerImpl::CanGoToOffset(int offset) const { |
| + int index = GetIndexForOffset(offset); |
| + return index >= 0 && index < GetItemCount(); |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::GoBack() { |
| + // TODO(danyao): deal with the returned WKNavigation. |
| + GetWebViewOrDie(delegate_)->GoBack(); |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::GoForward() { |
| + // TODO(danyao): deal with the returned WKNavigation. |
| + GetWebViewOrDie(delegate_)->GoForward(); |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::GoToIndex(int index) { |
| + DLOG(WARNING) << "GoToIndex needs to be implemented."; |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::Reload(ReloadType reload_type, |
| + bool check_for_reposts) { |
| + DLOG(WARNING) << "Reload needs to be implemented."; |
| +} |
| + |
| +NavigationItemList WKBasedNavigationManagerImpl::GetBackwardItems() const { |
| + SyncNavigationItems(); |
| + int last_committed_index = GetLastCommittedItemIndex(); |
| + if (last_committed_index == -1) { |
| + return NavigationItemList(); |
| + } else { |
| + // TODO(danyao): this can potentially lead to dangling pointers if |
| + // SyncNavigationItems() is called while the caller still has access to this |
| + // object. |
| + NavigationItemList back_items(last_committed_index); |
| + for (size_t index = 0; index < static_cast<size_t>(last_committed_index); |
| + index++) { |
| + back_items[index] = GetItemAtIndex(index); |
| + } |
| + return back_items; |
| + } |
| +} |
| + |
| +NavigationItemList WKBasedNavigationManagerImpl::GetForwardItems() const { |
| + SyncNavigationItems(); |
| + int last_committed_index = GetLastCommittedItemIndex(); |
| + DCHECK(last_committed_index >= -1); |
| + |
| + NavigationItemList forward_items; |
| + for (size_t index = last_committed_index + 1; |
| + index < committed_navigation_items_.size(); index++) { |
| + forward_items.push_back(GetItemAtIndex(index)); |
| + } |
| + |
| + return forward_items; |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::CopyStateFromAndPrune( |
| + const NavigationManager* source) { |
| + DLOG(WARNING) << "CopyStateFromAndPrune needs to be implemented."; |
| +} |
| + |
| +bool WKBasedNavigationManagerImpl::CanPruneAllButLastCommittedItem() const { |
| + DLOG(WARNING) << "CanPruneAllButLastCommittedItem needs to be implemented."; |
| + return true; |
| +} |
| + |
| +void WKBasedNavigationManagerImpl::RemoveTransientURLRewriters() { |
| + transient_url_rewriters_.reset(); |
| +} |
| + |
| +std::unique_ptr<std::vector<BrowserURLRewriter::URLRewriter>> |
| +WKBasedNavigationManagerImpl::GetTransientURLRewriters() { |
| + return std::move(transient_url_rewriters_); |
| +}; |
| + |
| +// NavigationManagerImpl methods used by SessionStorageBuilder. |
| +NavigationItemImpl* WKBasedNavigationManagerImpl::GetNavigationItemImplAtIndex( |
| + size_t index) const { |
| + if (index < committed_navigation_items_.size()) { |
| + return committed_navigation_items_[index].get(); |
| + } |
| + return nullptr; |
| +} |
| + |
| +int WKBasedNavigationManagerImpl::GetPreviousItemIndex() const { |
| + // TODO(danyao): try not to support this method |
| + return GetIndexForOffset(-1); |
| +} |
| + |
| +bool isEquivalent(const NavigationItemImpl* navigation_item, |
| + const WKBackForwardListItem* wk_item) { |
| + DCHECK(navigation_item); |
| + DCHECK(wk_item); |
| + |
| + return navigation_item->GetURL() == net::GURLWithNSURL(wk_item.URL); |
| +} |
| + |
| +const WKBackForwardListItem* GetItemAtAbsoluteIndex( |
| + size_t index, |
| + const WebViewNavigationProxy* proxy) { |
| + size_t back_list_size = static_cast<size_t>(proxy->GetBackList().count); |
| + if (index == back_list_size) { |
| + return proxy->GetCurrentItem(); |
| + } else if (index < back_list_size) { |
| + return proxy->GetItemAtOffset(index - back_list_size); |
| + } else { |
| + return proxy->GetItemAtOffset(index - back_list_size - 1); |
| + } |
| +} |
| + |
| +// Backfill NavigationItemImpls for navigations that happened in the rendere |
| +// that did not notify the embedder. Usually these are navigation that results |
| +// from scripts or iframe navigation. |
| +// TODO(danyao): share code with CRWSessionController |itemWithURL| |
| +std::unique_ptr<NavigationItemImpl> NavigationItemFromWKItem( |
| + const WKBackForwardListItem* wk_item, |
| + const WKBackForwardListItem* prev_wk_item) { |
| + auto item = base::MakeUnique<NavigationItemImpl>(); |
| + GURL url = net::GURLWithNSURL(wk_item.URL); |
| + item->SetOriginalRequestURL(url); |
| + item->SetURL(url); |
| + |
| + if (prev_wk_item) { |
| + item->SetReferrer(Referrer(net::GURLWithNSURL(prev_wk_item.URL), |
| + ReferrerPolicy::ReferrerPolicyDefault)); |
| + } |
| + |
| + // Only renderer-initiated navigation items will end up here because if the |
| + // navigation was user-initiated, the item would have been created via |
| + // AddPendingItem. |
| + item->SetNavigationInitiationType( |
| + web::NavigationInitiationType::RENDERER_INITIATED); |
| + if (web::GetWebClient()->IsAppSpecificURL(url)) { |
| + item->SetUserAgentType(web::UserAgentType::NONE); |
| + } |
| + |
| + // Since this is a renderer-initiated navigation, our best guess is that it's |
| + // a link transition. |
| + item->SetTransitionType(ui::PageTransition::PAGE_TRANSITION_LINK); |
| + |
| + return item; |
| +} |
| + |
| +// TODO(danyao): remove const qualifier |
| +// Maintains navigation_items_ invariants: |
| +// 1) committed_navigation_items_.size() == GetItemCount() == backList.count + |
| +// forwardList.count + currentItem ? 1 : 0 |
| +// |
| +bool WKBasedNavigationManagerImpl::SyncNavigationItems() const { |
| + WebViewNavigationProxy* proxy = delegate_->GetWebViewNavigationProxy(); |
| + if (!proxy) { |
| + bool is_changed = !committed_navigation_items_.empty(); |
| + committed_navigation_items_.clear(); |
| + return is_changed; |
| + } |
| + |
| + size_t wk_history_length = proxy->GetBackList().count + |
| + (proxy->GetCurrentItem() ? 1 : 0) + |
| + proxy->GetForwardList().count; |
| + size_t num_equivalent_items = |
| + std::min(committed_navigation_items_.size(), wk_history_length); |
| + |
| + for (size_t index = 0; |
| + index < committed_navigation_items_.size() && index < wk_history_length; |
| + index++) { |
| + if (!isEquivalent(committed_navigation_items_[index].get(), |
| + GetItemAtAbsoluteIndex(index, proxy))) { |
| + num_equivalent_items = index; |
| + break; |
| + } |
| + } |
| + |
| + if (num_equivalent_items == committed_navigation_items_.size() && |
| + num_equivalent_items == wk_history_length) { |
| + return false; |
| + } |
| + |
| + // Now we need to rebuild everything beyond this point |
| + committed_navigation_items_.resize(wk_history_length); |
| + const WKBackForwardListItem* prev_wk_item = nullptr; |
| + for (size_t index = num_equivalent_items; index < wk_history_length; |
| + index++) { |
| + const WKBackForwardListItem* wk_item = GetItemAtAbsoluteIndex(index, proxy); |
| + committed_navigation_items_[index] = |
| + NavigationItemFromWKItem(wk_item, prev_wk_item); |
| + prev_wk_item = wk_item; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace web |