Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(707)

Unified Diff: ios/web/navigation/wk_based_navigation_manager_impl.mm

Issue 2957163002: [Navigation Experiment] Add WKBasedNavigationManagerImpl. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698