Commit 0efc969a authored by Danyao Wang's avatar Danyao Wang Committed by Commit Bot

[Nav Experiment] Add DetachFromWebView to WKBasedNavigationManagerImpl.

CRWWebController sometimes needs to remove WKWebView (e.g. crbug/770914)
and this obliterates the session history when WKBasedNavigationManager
is used. This CL adds the ability for WKBasedNavigationManager to
temporarily be detached from the web view.

Major parts to this CL:
1. NavigationManagerImpl::DetachFromWebView() is a new API that
   CRWWebController uses to notify the navigation manager before
   removing the web view.
2. WKBasedNavigationManager implements this API with the help of a new
   nested class WKWebViewShim. It knows how to cache the items when
   detaching from a web view and service getter calls in detached mode.
3. Added overrides for FinishReload(), FinishLoadURLWithParams() and
   LoadIfNecessary() in WKBasedNavigationManager so it can switch back
   to attached mode when a new navigation starts by restoring the cached
   navigation items.

Also cleaned up unneeded APIs from CRWWebViewNavigationProxy.

Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: I2b1796731964f021df8c0489d3a6ef56ae390e04
Bug: 815248
Reviewed-on: https://chromium-review.googlesource.com/999054
Commit-Queue: Danyao Wang <danyao@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549880}
parent 0736da95
...@@ -83,6 +83,9 @@ class NavigationManagerImpl : public NavigationManager { ...@@ -83,6 +83,9 @@ class NavigationManagerImpl : public NavigationManager {
virtual void OnNavigationItemChanged() = 0; virtual void OnNavigationItemChanged() = 0;
virtual void OnNavigationItemCommitted() = 0; virtual void OnNavigationItemCommitted() = 0;
// Prepares for the deletion of WKWebView such as caching necessary data.
virtual void DetachFromWebView();
// Temporary accessors and content/ class pass-throughs. // Temporary accessors and content/ class pass-throughs.
// TODO(stuartmorgan): Re-evaluate this list once the refactorings have // TODO(stuartmorgan): Re-evaluate this list once the refactorings have
// settled down. // settled down.
...@@ -171,7 +174,7 @@ class NavigationManagerImpl : public NavigationManager { ...@@ -171,7 +174,7 @@ class NavigationManagerImpl : public NavigationManager {
void GoToIndex(int index) final; void GoToIndex(int index) final;
void Reload(ReloadType reload_type, bool check_for_reposts) final; void Reload(ReloadType reload_type, bool check_for_reposts) final;
void ReloadWithUserAgentType(UserAgentType user_agent_type) final; void ReloadWithUserAgentType(UserAgentType user_agent_type) final;
void LoadIfNecessary() final; void LoadIfNecessary() override;
// Implementation for corresponding NavigationManager getters. // Implementation for corresponding NavigationManager getters.
virtual NavigationItemImpl* GetPendingItemImpl() const = 0; virtual NavigationItemImpl* GetPendingItemImpl() const = 0;
...@@ -222,6 +225,8 @@ class NavigationManagerImpl : public NavigationManager { ...@@ -222,6 +225,8 @@ class NavigationManagerImpl : public NavigationManager {
// Subclass specific implementation to update session state. // Subclass specific implementation to update session state.
virtual void FinishGoToIndex(int index, NavigationInitiationType type) = 0; virtual void FinishGoToIndex(int index, NavigationInitiationType type) = 0;
virtual void FinishReload();
virtual void FinishLoadURLWithParams();
// Returns true if the subclass uses placeholder URLs and this is such a URL. // Returns true if the subclass uses placeholder URLs and this is such a URL.
virtual bool IsPlaceholderUrl(const GURL& url) const; virtual bool IsPlaceholderUrl(const GURL& url) const;
......
...@@ -130,6 +130,8 @@ void NavigationManagerImpl::SetBrowserState(BrowserState* browser_state) { ...@@ -130,6 +130,8 @@ void NavigationManagerImpl::SetBrowserState(BrowserState* browser_state) {
browser_state_ = browser_state; browser_state_ = browser_state;
} }
void NavigationManagerImpl::DetachFromWebView() {}
void NavigationManagerImpl::RemoveTransientURLRewriters() { void NavigationManagerImpl::RemoveTransientURLRewriters() {
transient_url_rewriters_.clear(); transient_url_rewriters_.clear();
} }
...@@ -286,7 +288,7 @@ void NavigationManagerImpl::LoadURLWithParams( ...@@ -286,7 +288,7 @@ void NavigationManagerImpl::LoadURLWithParams(
added_item->SetShouldSkipRepostFormConfirmation(true); added_item->SetShouldSkipRepostFormConfirmation(true);
} }
delegate_->LoadCurrentItem(); FinishLoadURLWithParams();
} }
void NavigationManagerImpl::AddTransientURLRewriter( void NavigationManagerImpl::AddTransientURLRewriter(
...@@ -321,7 +323,7 @@ void NavigationManagerImpl::Reload(ReloadType reload_type, ...@@ -321,7 +323,7 @@ void NavigationManagerImpl::Reload(ReloadType reload_type,
reload_item->SetURL(reload_item->GetOriginalRequestURL()); reload_item->SetURL(reload_item->GetOriginalRequestURL());
} }
delegate_->Reload(); FinishReload();
} }
void NavigationManagerImpl::ReloadWithUserAgentType( void NavigationManagerImpl::ReloadWithUserAgentType(
...@@ -438,6 +440,14 @@ NavigationItem* NavigationManagerImpl::GetLastCommittedNonAppSpecificItem() ...@@ -438,6 +440,14 @@ NavigationItem* NavigationManagerImpl::GetLastCommittedNonAppSpecificItem()
return nullptr; return nullptr;
} }
void NavigationManagerImpl::FinishReload() {
delegate_->Reload();
}
void NavigationManagerImpl::FinishLoadURLWithParams() {
delegate_->LoadCurrentItem();
}
bool NavigationManagerImpl::IsPlaceholderUrl(const GURL& url) const { bool NavigationManagerImpl::IsPlaceholderUrl(const GURL& url) const {
return false; // Default implementation doesn't use placeholder URLs return false; // Default implementation doesn't use placeholder URLs
} }
......
...@@ -31,10 +31,6 @@ class SessionStorageBuilder; ...@@ -31,10 +31,6 @@ class SessionStorageBuilder;
// CRWWebViewNavigationProxy protocol: // CRWWebViewNavigationProxy protocol:
// @property URL // @property URL
// @property backForwardList // @property backForwardList
// @property canGoBack
// @property canGoForward
// - goBack
// - goForward
// - goToBackForwardListItem: // - goToBackForwardListItem:
// //
// This navigation manager uses WKBackForwardList as the ground truth for back- // This navigation manager uses WKBackForwardList as the ground truth for back-
...@@ -67,6 +63,18 @@ class SessionStorageBuilder; ...@@ -67,6 +63,18 @@ class SessionStorageBuilder;
// single normal entry. The only difference is that a subsequent call to // single normal entry. The only difference is that a subsequent call to
// CommitPendingItem() will *replace* the empty window open item. From this // CommitPendingItem() will *replace* the empty window open item. From this
// point onward, it is as if the empty window open item never occurred. // point onward, it is as if the empty window open item never occurred.
//
// Detach from web view edge case:
//
// As long as this navigation manager is alive, the navigation manager
// delegate should not delete its WKWebView. However, legacy use cases exist
// (e.g. https://crbug/770914). As a workaround, before deleting the
// WKWebView, the delegate must call
// NavigationManagerImpl::DetachFromWebView() to cache the current session
// history. This puts the navigation manager in a detached state. While in
// this state, all getters are serviced using the cached session history.
// Mutation methods are not allowed. The navigation manager returns to the
// attached state when a new navigation starts.
class WKBasedNavigationManagerImpl : public NavigationManagerImpl { class WKBasedNavigationManagerImpl : public NavigationManagerImpl {
public: public:
WKBasedNavigationManagerImpl(); WKBasedNavigationManagerImpl();
...@@ -78,6 +86,7 @@ class WKBasedNavigationManagerImpl : public NavigationManagerImpl { ...@@ -78,6 +86,7 @@ class WKBasedNavigationManagerImpl : public NavigationManagerImpl {
void OnNavigationItemsPruned(size_t pruned_item_count) override; void OnNavigationItemsPruned(size_t pruned_item_count) override;
void OnNavigationItemChanged() override; void OnNavigationItemChanged() override;
void OnNavigationItemCommitted() override; void OnNavigationItemCommitted() override;
void DetachFromWebView() override;
CRWSessionController* GetSessionController() const override; CRWSessionController* GetSessionController() const override;
void AddTransientItem(const GURL& url) override; void AddTransientItem(const GURL& url) override;
void AddPendingItem( void AddPendingItem(
...@@ -117,12 +126,68 @@ class WKBasedNavigationManagerImpl : public NavigationManagerImpl { ...@@ -117,12 +126,68 @@ class WKBasedNavigationManagerImpl : public NavigationManagerImpl {
bool CanPruneAllButLastCommittedItem() const override; bool CanPruneAllButLastCommittedItem() const override;
void Restore(int last_committed_item_index, void Restore(int last_committed_item_index,
std::vector<std::unique_ptr<NavigationItem>> items) override; std::vector<std::unique_ptr<NavigationItem>> items) override;
void LoadIfNecessary() override;
private: private:
// The SessionStorageBuilder functions require access to private variables of // The SessionStorageBuilder functions require access to private variables of
// NavigationManagerImpl. // NavigationManagerImpl.
friend SessionStorageBuilder; friend SessionStorageBuilder;
// Access shim for NavigationItems associated with the WKBackForwardList. It
// is responsible for caching NavigationItems when the navigation manager
// detaches from its web view.
class WKWebViewCache {
public:
explicit WKWebViewCache(WKBasedNavigationManagerImpl* navigation_manager);
~WKWebViewCache();
// Returns true if the navigation manager is attached to a WKWebView.
bool IsAttachedToWebView() const;
// Caches NavigationItems from the WKWebView in |this| and changes state to
// detached.
void DetachFromWebView();
// Clears the cached NavigationItems and resets state to attached. Callers
// that wish to restore the cached navigation items into the new web view
// must call ReleaseCachedItems() first.
void ResetToAttached();
// Returns ownership of the cached NavigationItems. This is convenient for
// restoring session history when reattaching to a new web view.
std::vector<std::unique_ptr<NavigationItem>> ReleaseCachedItems();
// Returns the number of items in the back-forward history.
size_t GetBackForwardListItemCount() const;
// Returns the absolute index of WKBackForwardList's |currentItem| or -1 if
// |currentItem| is nil. If navigation manager is in detached mode, returns
// the cached value of this property captured at the last call of
// DetachFromWebView().
int GetCurrentItemIndex() const;
// Returns the NavigationItem associated with the WKBackForwardListItem at
// |index|. If |create_if_missing| is true and the WKBackForwardListItem
// does not have an associated NavigationItem, creates a new one and returns
// it to the caller.
NavigationItemImpl* GetNavigationItemImplAtIndex(
size_t index,
bool create_if_missing) const;
// Returns the WKBackForwardListItem at |index|. Must only be called when
// IsAttachedToWebView() is true.
WKBackForwardListItem* GetWKItemAtIndex(size_t index) const;
private:
WKBasedNavigationManagerImpl* navigation_manager_;
bool attached_to_web_view_;
std::vector<std::unique_ptr<NavigationItemImpl>> cached_items_;
int cached_current_item_index_;
DISALLOW_COPY_AND_ASSIGN(WKWebViewCache);
};
// NavigationManagerImpl: // NavigationManagerImpl:
NavigationItemImpl* GetNavigationItemImplAtIndex(size_t index) const override; NavigationItemImpl* GetNavigationItemImplAtIndex(size_t index) const override;
NavigationItemImpl* GetLastCommittedItemImpl() const override; NavigationItemImpl* GetLastCommittedItemImpl() const override;
...@@ -131,18 +196,10 @@ class WKBasedNavigationManagerImpl : public NavigationManagerImpl { ...@@ -131,18 +196,10 @@ class WKBasedNavigationManagerImpl : public NavigationManagerImpl {
NavigationItemImpl* GetTransientItemImpl() const override; NavigationItemImpl* GetTransientItemImpl() const override;
void FinishGoToIndex(int index, void FinishGoToIndex(int index,
NavigationInitiationType initiation_type) override; NavigationInitiationType initiation_type) override;
void FinishReload() override;
void FinishLoadURLWithParams() override;
bool IsPlaceholderUrl(const GURL& url) const override; bool IsPlaceholderUrl(const GURL& url) const override;
// Returns the absolute index of WKBackForwardList's |currentItem|. Returns -1
// if |currentItem| is nil.
int GetWKCurrentItemIndex() const;
// Returns the WKNavigationItem object at the absolute index, where index = 0
// corresponds to the oldest entry in the back-forward history, and
// index = GetItemCount() - 1 corresponds to the newest entry in the back-
// forward history. Returns nil if |index| is outside [0, GetItemCount()).
WKBackForwardListItem* GetWKItemAtIndex(int index) const;
// The pending main frame navigation item. This is nullptr if there is no // The pending main frame navigation item. This is nullptr if there is no
// pending item or if the pending item is a back-forward navigation, in which // pending item or if the pending item is a back-forward navigation, in which
// case the NavigationItemImpl is stored on the WKBackForwardListItem. // case the NavigationItemImpl is stored on the WKBackForwardListItem.
...@@ -178,6 +235,8 @@ class WKBasedNavigationManagerImpl : public NavigationManagerImpl { ...@@ -178,6 +235,8 @@ class WKBasedNavigationManagerImpl : public NavigationManagerImpl {
// have to be lazily created on read, this is the only workaround. // have to be lazily created on read, this is the only workaround.
mutable TimeSmoother time_smoother_; mutable TimeSmoother time_smoother_;
WKWebViewCache web_view_cache_;
DISALLOW_COPY_AND_ASSIGN(WKBasedNavigationManagerImpl); DISALLOW_COPY_AND_ASSIGN(WKBasedNavigationManagerImpl);
}; };
......
...@@ -53,7 +53,8 @@ namespace web { ...@@ -53,7 +53,8 @@ namespace web {
WKBasedNavigationManagerImpl::WKBasedNavigationManagerImpl() WKBasedNavigationManagerImpl::WKBasedNavigationManagerImpl()
: pending_item_index_(-1), : pending_item_index_(-1),
previous_item_index_(-1), previous_item_index_(-1),
last_committed_item_index_(-1) {} last_committed_item_index_(-1),
web_view_cache_(this) {}
WKBasedNavigationManagerImpl::~WKBasedNavigationManagerImpl() = default; WKBasedNavigationManagerImpl::~WKBasedNavigationManagerImpl() = default;
...@@ -71,6 +72,10 @@ void WKBasedNavigationManagerImpl::OnNavigationItemChanged() { ...@@ -71,6 +72,10 @@ void WKBasedNavigationManagerImpl::OnNavigationItemChanged() {
delegate_->OnNavigationItemChanged(); delegate_->OnNavigationItemChanged();
} }
void WKBasedNavigationManagerImpl::DetachFromWebView() {
web_view_cache_.DetachFromWebView();
}
void WKBasedNavigationManagerImpl::OnNavigationItemCommitted() { void WKBasedNavigationManagerImpl::OnNavigationItemCommitted() {
LoadCommittedDetails details; LoadCommittedDetails details;
details.item = GetLastCommittedItem(); details.item = GetLastCommittedItem();
...@@ -91,6 +96,7 @@ CRWSessionController* WKBasedNavigationManagerImpl::GetSessionController() ...@@ -91,6 +96,7 @@ CRWSessionController* WKBasedNavigationManagerImpl::GetSessionController()
} }
void WKBasedNavigationManagerImpl::AddTransientItem(const GURL& url) { void WKBasedNavigationManagerImpl::AddTransientItem(const GURL& url) {
DCHECK(web_view_cache_.IsAttachedToWebView());
NavigationItem* last_committed_item = GetLastCommittedItem(); NavigationItem* last_committed_item = GetLastCommittedItem();
transient_item_ = CreateNavigationItemWithRewriters( transient_item_ = CreateNavigationItemWithRewriters(
url, Referrer(), ui::PAGE_TRANSITION_CLIENT_REDIRECT, url, Referrer(), ui::PAGE_TRANSITION_CLIENT_REDIRECT,
...@@ -115,6 +121,7 @@ void WKBasedNavigationManagerImpl::AddPendingItem( ...@@ -115,6 +121,7 @@ void WKBasedNavigationManagerImpl::AddPendingItem(
UserAgentOverrideOption user_agent_override_option) { UserAgentOverrideOption user_agent_override_option) {
DiscardNonCommittedItems(); DiscardNonCommittedItems();
pending_item_index_ = -1;
NavigationItem* last_committed_item = GetLastCommittedItem(); NavigationItem* last_committed_item = GetLastCommittedItem();
pending_item_ = CreateNavigationItemWithRewriters( pending_item_ = CreateNavigationItemWithRewriters(
url, referrer, navigation_type, initiation_type, url, referrer, navigation_type, initiation_type,
...@@ -125,6 +132,11 @@ void WKBasedNavigationManagerImpl::AddPendingItem( ...@@ -125,6 +132,11 @@ void WKBasedNavigationManagerImpl::AddPendingItem(
GetLastCommittedNonAppSpecificItem(), GetLastCommittedNonAppSpecificItem(),
pending_item_.get()); pending_item_.get());
// No need to detect renderer-initiated back/forward navigation in detached
// mode because there is no renderer.
if (!web_view_cache_.IsAttachedToWebView())
return;
// AddPendingItem is called no later than |didCommitNavigation|. The only time // AddPendingItem is called no later than |didCommitNavigation|. The only time
// when all three of WKWebView's URL, the pending URL and WKBackForwardList's // when all three of WKWebView's URL, the pending URL and WKBackForwardList's
// current item URL are identical before |didCommitNavigation| is when the // current item URL are identical before |didCommitNavigation| is when the
...@@ -142,7 +154,7 @@ void WKBasedNavigationManagerImpl::AddPendingItem( ...@@ -142,7 +154,7 @@ void WKBasedNavigationManagerImpl::AddPendingItem(
if (proxy.backForwardList.currentItem && if (proxy.backForwardList.currentItem &&
current_item_url == net::GURLWithNSURL(proxy.URL) && current_item_url == net::GURLWithNSURL(proxy.URL) &&
current_item_is_pending_item) { current_item_is_pending_item) {
pending_item_index_ = GetWKCurrentItemIndex(); pending_item_index_ = web_view_cache_.GetCurrentItemIndex();
// If |currentItem| is not already associated with a NavigationItemImpl, // If |currentItem| is not already associated with a NavigationItemImpl,
// associate the newly created item with it. Otherwise, discard the new item // associate the newly created item with it. Otherwise, discard the new item
...@@ -153,12 +165,12 @@ void WKBasedNavigationManagerImpl::AddPendingItem( ...@@ -153,12 +165,12 @@ void WKBasedNavigationManagerImpl::AddPendingItem(
SetNavigationItemInWKItem(current_wk_item, std::move(pending_item_)); SetNavigationItemInWKItem(current_wk_item, std::move(pending_item_));
} }
pending_item_.reset(); pending_item_.reset();
} else {
pending_item_index_ = -1;
} }
} }
void WKBasedNavigationManagerImpl::CommitPendingItem() { void WKBasedNavigationManagerImpl::CommitPendingItem() {
DCHECK(web_view_cache_.IsAttachedToWebView());
// CommitPendingItem may be called multiple times. Do nothing if there is no // CommitPendingItem may be called multiple times. Do nothing if there is no
// pending item. // pending item.
if (pending_item_index_ == -1 && !pending_item_) if (pending_item_index_ == -1 && !pending_item_)
...@@ -204,14 +216,15 @@ void WKBasedNavigationManagerImpl::CommitPendingItem() { ...@@ -204,14 +216,15 @@ void WKBasedNavigationManagerImpl::CommitPendingItem() {
// If the newly committed item is the empty window open item, fake an index of // If the newly committed item is the empty window open item, fake an index of
// 0 because WKBackForwardList is empty at this point. // 0 because WKBackForwardList is empty at this point.
last_committed_item_index_ = last_committed_item_index_ =
empty_window_open_item_ ? 0 : GetWKCurrentItemIndex(); empty_window_open_item_ ? 0 : web_view_cache_.GetCurrentItemIndex();
OnNavigationItemCommitted(); OnNavigationItemCommitted();
} }
int WKBasedNavigationManagerImpl::GetIndexForOffset(int offset) const { int WKBasedNavigationManagerImpl::GetIndexForOffset(int offset) const {
int current_item_index = pending_item_index_; int current_item_index = pending_item_index_;
if (pending_item_index_ == -1) { if (pending_item_index_ == -1) {
current_item_index = empty_window_open_item_ ? 0 : GetWKCurrentItemIndex(); current_item_index =
empty_window_open_item_ ? 0 : web_view_cache_.GetCurrentItemIndex();
} }
if (offset < 0 && GetTransientItem() && pending_item_index_ == -1) { if (offset < 0 && GetTransientItem() && pending_item_index_ == -1) {
...@@ -229,6 +242,7 @@ int WKBasedNavigationManagerImpl::GetPreviousItemIndex() const { ...@@ -229,6 +242,7 @@ int WKBasedNavigationManagerImpl::GetPreviousItemIndex() const {
void WKBasedNavigationManagerImpl::SetPreviousItemIndex( void WKBasedNavigationManagerImpl::SetPreviousItemIndex(
int previous_item_index) { int previous_item_index) {
DCHECK(web_view_cache_.IsAttachedToWebView());
previous_item_index_ = previous_item_index; previous_item_index_ = previous_item_index;
} }
...@@ -278,16 +292,7 @@ int WKBasedNavigationManagerImpl::GetItemCount() const { ...@@ -278,16 +292,7 @@ int WKBasedNavigationManagerImpl::GetItemCount() const {
return 1; return 1;
} }
id<CRWWebViewNavigationProxy> proxy = delegate_->GetWebViewNavigationProxy(); return web_view_cache_.GetBackForwardListItemCount();
if (proxy) {
int count_current_page = proxy.backForwardList.currentItem ? 1 : 0;
return static_cast<int>(proxy.backForwardList.backList.count) +
count_current_page +
static_cast<int>(proxy.backForwardList.forwardList.count);
}
// If WebView has not been created, it's fair to say navigation has 0 item.
return 0;
} }
NavigationItem* WKBasedNavigationManagerImpl::GetItemAtIndex( NavigationItem* WKBasedNavigationManagerImpl::GetItemAtIndex(
...@@ -301,10 +306,11 @@ int WKBasedNavigationManagerImpl::GetIndexOfItem( ...@@ -301,10 +306,11 @@ int WKBasedNavigationManagerImpl::GetIndexOfItem(
return 0; return 0;
} }
for (int index = 0; index < GetItemCount(); index++) { for (size_t index = 0; index < web_view_cache_.GetBackForwardListItemCount();
if (GetNavigationItemFromWKItem(GetWKItemAtIndex(index)) == item) { index++) {
if (web_view_cache_.GetNavigationItemImplAtIndex(
index, false /* create_if_missing */) == item)
return index; return index;
}
} }
return -1; return -1;
} }
...@@ -333,7 +339,7 @@ int WKBasedNavigationManagerImpl::GetLastCommittedItemIndex() const { ...@@ -333,7 +339,7 @@ int WKBasedNavigationManagerImpl::GetLastCommittedItemIndex() const {
if (pending_item_index_ >= 0 || empty_window_open_item_) { if (pending_item_index_ >= 0 || empty_window_open_item_) {
return last_committed_item_index_; return last_committed_item_index_;
} }
return GetWKCurrentItemIndex(); return web_view_cache_.GetCurrentItemIndex();
} }
bool WKBasedNavigationManagerImpl::RemoveItemAtIndex(int index) { bool WKBasedNavigationManagerImpl::RemoveItemAtIndex(int index) {
...@@ -373,21 +379,22 @@ NavigationItemList WKBasedNavigationManagerImpl::GetBackwardItems() const { ...@@ -373,21 +379,22 @@ NavigationItemList WKBasedNavigationManagerImpl::GetBackwardItems() const {
// If the current navigation item is a transient item (e.g. SSL // If the current navigation item is a transient item (e.g. SSL
// interstitial), the last committed item should also be considered part of // interstitial), the last committed item should also be considered part of
// the backward history. // the backward history.
int wk_current_item_index = GetWKCurrentItemIndex(); int current_back_forward_item_index = web_view_cache_.GetCurrentItemIndex();
if (GetTransientItem() && wk_current_item_index >= 0) { if (GetTransientItem() && current_back_forward_item_index >= 0) {
items.push_back(GetItemAtIndex(wk_current_item_index)); items.push_back(GetItemAtIndex(current_back_forward_item_index));
} }
for (int index = wk_current_item_index - 1; index >= 0; index--) { for (int index = current_back_forward_item_index - 1; index >= 0; index--) {
items.push_back(GetItemAtIndex(index)); items.push_back(GetItemAtIndex(index));
} }
return items; return items;
} }
NavigationItemList WKBasedNavigationManagerImpl::GetForwardItems() const { NavigationItemList WKBasedNavigationManagerImpl::GetForwardItems() const {
NavigationItemList items; NavigationItemList items;
for (int index = GetWKCurrentItemIndex() + 1; index < GetItemCount(); for (int index = web_view_cache_.GetCurrentItemIndex() + 1;
index++) { index < GetItemCount(); index++) {
items.push_back(GetItemAtIndex(index)); items.push_back(GetItemAtIndex(index));
} }
return items; return items;
...@@ -411,6 +418,9 @@ void WKBasedNavigationManagerImpl::Restore( ...@@ -411,6 +418,9 @@ void WKBasedNavigationManagerImpl::Restore(
if (items.empty()) if (items.empty())
return; return;
if (!web_view_cache_.IsAttachedToWebView())
web_view_cache_.ResetToAttached();
DiscardNonCommittedItems(); DiscardNonCommittedItems();
if (GetItemCount() > 0) { if (GetItemCount() > 0) {
delegate_->RemoveWebView(); delegate_->RemoveWebView();
...@@ -442,6 +452,17 @@ void WKBasedNavigationManagerImpl::Restore( ...@@ -442,6 +452,17 @@ void WKBasedNavigationManagerImpl::Restore(
GetPendingItemImpl()->SetVirtualURL(items[0]->GetVirtualURL()); GetPendingItemImpl()->SetVirtualURL(items[0]->GetVirtualURL());
} }
void WKBasedNavigationManagerImpl::LoadIfNecessary() {
if (!web_view_cache_.IsAttachedToWebView()) {
// Loading from detached mode is equivalent to restoring cached history.
Restore(web_view_cache_.GetCurrentItemIndex(),
web_view_cache_.ReleaseCachedItems());
DCHECK(web_view_cache_.IsAttachedToWebView());
} else {
delegate_->LoadIfNecessary();
}
}
NavigationItemImpl* WKBasedNavigationManagerImpl::GetNavigationItemImplAtIndex( NavigationItemImpl* WKBasedNavigationManagerImpl::GetNavigationItemImplAtIndex(
size_t index) const { size_t index) const {
if (empty_window_open_item_) { if (empty_window_open_item_) {
...@@ -454,47 +475,8 @@ NavigationItemImpl* WKBasedNavigationManagerImpl::GetNavigationItemImplAtIndex( ...@@ -454,47 +475,8 @@ NavigationItemImpl* WKBasedNavigationManagerImpl::GetNavigationItemImplAtIndex(
return index == 0 ? empty_window_open_item_.get() : nullptr; return index == 0 ? empty_window_open_item_.get() : nullptr;
} }
WKBackForwardListItem* wk_item = GetWKItemAtIndex(index); return web_view_cache_.GetNavigationItemImplAtIndex(
NavigationItemImpl* item = GetNavigationItemFromWKItem(wk_item); index, true /* create_if_missing */);
if (!wk_item || item) {
return item;
}
// TODO(crbug.com/734150): Add a stat counter to track rebuilding frequency.
WKBackForwardListItem* prev_wk_item =
index == 0 ? nil : GetWKItemAtIndex(index - 1);
std::unique_ptr<web::NavigationItemImpl> new_item =
CreateNavigationItemWithRewriters(
net::GURLWithNSURL(wk_item.URL),
(prev_wk_item ? web::Referrer(net::GURLWithNSURL(prev_wk_item.URL),
web::ReferrerPolicyAlways)
: web::Referrer()),
ui::PageTransition::PAGE_TRANSITION_LINK,
NavigationInitiationType::RENDERER_INITIATED,
// Not using GetLastCommittedItem()->GetURL() in case the last
// committed item in the WKWebView hasn't been linked to a
// NavigationItem and this method is called in that code path to avoid
// an infinite cycle.
net::GURLWithNSURL(prev_wk_item.URL),
nullptr /* use default rewriters only */);
new_item->SetTimestamp(time_smoother_.GetSmoothedTime(base::Time::Now()));
const GURL& url = new_item->GetURL();
// If this navigation item has a restore_session.html URL, then it was created
// to restore session history and will redirect to the target URL encoded in
// the query parameter automatically. Set virtual URL to the target URL so the
// internal restore_session.html is not exposed in the UI and to URL-sensing
// components outside of //ios/web layer.
if (wk_navigation_util::IsRestoreSessionUrl(url)) {
GURL virtual_url;
bool success = wk_navigation_util::ExtractTargetURL(url, &virtual_url);
DCHECK(success);
if (success)
new_item->SetVirtualURL(virtual_url);
}
SetNavigationItemInWKItem(wk_item, std::move(new_item));
return GetNavigationItemFromWKItem(wk_item);
} }
NavigationItemImpl* WKBasedNavigationManagerImpl::GetLastCommittedItemImpl() NavigationItemImpl* WKBasedNavigationManagerImpl::GetLastCommittedItemImpl()
...@@ -521,11 +503,19 @@ NavigationItemImpl* WKBasedNavigationManagerImpl::GetTransientItemImpl() const { ...@@ -521,11 +503,19 @@ NavigationItemImpl* WKBasedNavigationManagerImpl::GetTransientItemImpl() const {
void WKBasedNavigationManagerImpl::FinishGoToIndex( void WKBasedNavigationManagerImpl::FinishGoToIndex(
int index, int index,
NavigationInitiationType type) { NavigationInitiationType type) {
if (!web_view_cache_.IsAttachedToWebView()) {
// GoToIndex from detached mode is equivalent to restoring history with
// |last_committed_item_index| updated to |index|.
Restore(index, web_view_cache_.ReleaseCachedItems());
DCHECK(web_view_cache_.IsAttachedToWebView());
return;
}
DiscardNonCommittedItems(); DiscardNonCommittedItems();
NavigationItem* item = GetItemAtIndex(index); NavigationItem* item = GetItemAtIndex(index);
item->SetTransitionType(ui::PageTransitionFromInt( item->SetTransitionType(ui::PageTransitionFromInt(
item->GetTransitionType() | ui::PAGE_TRANSITION_FORWARD_BACK)); item->GetTransitionType() | ui::PAGE_TRANSITION_FORWARD_BACK));
WKBackForwardListItem* wk_item = GetWKItemAtIndex(index); WKBackForwardListItem* wk_item = web_view_cache_.GetWKItemAtIndex(index);
if (wk_item) { if (wk_item) {
[delegate_->GetWebViewNavigationProxy() goToBackForwardListItem:wk_item]; [delegate_->GetWebViewNavigationProxy() goToBackForwardListItem:wk_item];
} else { } else {
...@@ -536,29 +526,182 @@ void WKBasedNavigationManagerImpl::FinishGoToIndex( ...@@ -536,29 +526,182 @@ void WKBasedNavigationManagerImpl::FinishGoToIndex(
} }
} }
void WKBasedNavigationManagerImpl::FinishReload() {
if (!web_view_cache_.IsAttachedToWebView()) {
// Reload from detached mode is equivalent to restoring history unchanged.
Restore(web_view_cache_.GetCurrentItemIndex(),
web_view_cache_.ReleaseCachedItems());
DCHECK(web_view_cache_.IsAttachedToWebView());
return;
}
delegate_->Reload();
}
void WKBasedNavigationManagerImpl::FinishLoadURLWithParams() {
if (!web_view_cache_.IsAttachedToWebView()) {
DCHECK_EQ(pending_item_index_, -1);
if (pending_item_ && web_view_cache_.GetBackForwardListItemCount() > 0) {
// Loading a pending item from detached state is equivalent to replacing
// all forward history after the cached current item with the new pending
// item.
std::vector<std::unique_ptr<NavigationItem>> cached_items =
web_view_cache_.ReleaseCachedItems();
int next_item_index = web_view_cache_.GetCurrentItemIndex() + 1;
DCHECK_GT(next_item_index, 0);
cached_items.resize(next_item_index + 1);
cached_items[next_item_index].reset(pending_item_.release());
Restore(next_item_index, std::move(cached_items));
DCHECK(web_view_cache_.IsAttachedToWebView());
return;
}
web_view_cache_.ResetToAttached();
}
delegate_->LoadCurrentItem();
}
bool WKBasedNavigationManagerImpl::IsPlaceholderUrl(const GURL& url) const { bool WKBasedNavigationManagerImpl::IsPlaceholderUrl(const GURL& url) const {
return wk_navigation_util::IsPlaceholderUrl(url); return wk_navigation_util::IsPlaceholderUrl(url);
} }
int WKBasedNavigationManagerImpl::GetWKCurrentItemIndex() const { WKBasedNavigationManagerImpl::WKWebViewCache::WKWebViewCache(
id<CRWWebViewNavigationProxy> proxy = delegate_->GetWebViewNavigationProxy(); WKBasedNavigationManagerImpl* navigation_manager)
: navigation_manager_(navigation_manager), attached_to_web_view_(true) {}
WKBasedNavigationManagerImpl::WKWebViewCache::~WKWebViewCache() = default;
bool WKBasedNavigationManagerImpl::WKWebViewCache::IsAttachedToWebView() const {
return attached_to_web_view_;
}
void WKBasedNavigationManagerImpl::WKWebViewCache::DetachFromWebView() {
if (IsAttachedToWebView()) {
cached_current_item_index_ = GetCurrentItemIndex();
cached_items_.resize(GetBackForwardListItemCount());
for (size_t index = 0; index < GetBackForwardListItemCount(); index++) {
cached_items_[index].reset(new NavigationItemImpl(
*GetNavigationItemImplAtIndex(index, true /* create_if_missing */)));
}
}
attached_to_web_view_ = false;
}
void WKBasedNavigationManagerImpl::WKWebViewCache::ResetToAttached() {
cached_items_.clear();
cached_current_item_index_ = -1;
attached_to_web_view_ = true;
}
std::vector<std::unique_ptr<NavigationItem>>
WKBasedNavigationManagerImpl::WKWebViewCache::ReleaseCachedItems() {
DCHECK(!IsAttachedToWebView());
std::vector<std::unique_ptr<NavigationItem>> result(cached_items_.size());
for (size_t index = 0; index < cached_items_.size(); index++) {
result[index].reset(cached_items_[index].release());
}
cached_items_.clear();
return result;
}
size_t
WKBasedNavigationManagerImpl::WKWebViewCache::GetBackForwardListItemCount()
const {
if (!IsAttachedToWebView())
return cached_items_.size();
id<CRWWebViewNavigationProxy> proxy =
navigation_manager_->delegate_->GetWebViewNavigationProxy();
if (proxy) {
size_t count_current_page = proxy.backForwardList.currentItem ? 1 : 0;
return proxy.backForwardList.backList.count + count_current_page +
proxy.backForwardList.forwardList.count;
}
// If WebView has not been created, it's fair to say navigation has 0 item.
return 0;
}
int WKBasedNavigationManagerImpl::WKWebViewCache::GetCurrentItemIndex() const {
if (!IsAttachedToWebView())
return cached_current_item_index_;
id<CRWWebViewNavigationProxy> proxy =
navigation_manager_->delegate_->GetWebViewNavigationProxy();
if (proxy.backForwardList.currentItem) { if (proxy.backForwardList.currentItem) {
return static_cast<int>(proxy.backForwardList.backList.count); return static_cast<int>(proxy.backForwardList.backList.count);
} }
return -1; return -1;
} }
WKBackForwardListItem* WKBasedNavigationManagerImpl::GetWKItemAtIndex( NavigationItemImpl*
int index) const { WKBasedNavigationManagerImpl::WKWebViewCache::GetNavigationItemImplAtIndex(
if (index < 0 || index >= GetItemCount()) { size_t index,
bool create_if_missing) const {
if (index >= GetBackForwardListItemCount())
return nullptr;
if (!IsAttachedToWebView())
return cached_items_[index].get();
WKBackForwardListItem* wk_item = GetWKItemAtIndex(index);
NavigationItemImpl* item = GetNavigationItemFromWKItem(wk_item);
if (!wk_item || item || !create_if_missing) {
return item;
}
// TODO(crbug.com/734150): Add a stat counter to track rebuilding frequency.
WKBackForwardListItem* prev_wk_item =
index == 0 ? nil : GetWKItemAtIndex(index - 1);
std::unique_ptr<web::NavigationItemImpl> new_item =
navigation_manager_->CreateNavigationItemWithRewriters(
net::GURLWithNSURL(wk_item.URL),
(prev_wk_item ? web::Referrer(net::GURLWithNSURL(prev_wk_item.URL),
web::ReferrerPolicyAlways)
: web::Referrer()),
ui::PageTransition::PAGE_TRANSITION_LINK,
NavigationInitiationType::RENDERER_INITIATED,
// Not using GetLastCommittedItem()->GetURL() in case the last
// committed item in the WKWebView hasn't been linked to a
// NavigationItem and this method is called in that code path to avoid
// an infinite cycle.
net::GURLWithNSURL(prev_wk_item.URL),
nullptr /* use default rewriters only */);
new_item->SetTimestamp(
navigation_manager_->time_smoother_.GetSmoothedTime(base::Time::Now()));
const GURL& url = new_item->GetURL();
// If this navigation item has a restore_session.html URL, then it was created
// to restore session history and will redirect to the target URL encoded in
// the query parameter automatically. Set virtual URL to the target URL so the
// internal restore_session.html is not exposed in the UI and to URL-sensing
// components outside of //ios/web layer.
if (wk_navigation_util::IsRestoreSessionUrl(url)) {
GURL virtual_url;
bool success = wk_navigation_util::ExtractTargetURL(url, &virtual_url);
DCHECK(success);
if (success)
new_item->SetVirtualURL(virtual_url);
}
SetNavigationItemInWKItem(wk_item, std::move(new_item));
return GetNavigationItemFromWKItem(wk_item);
}
WKBackForwardListItem*
WKBasedNavigationManagerImpl::WKWebViewCache::GetWKItemAtIndex(
size_t index) const {
DCHECK(IsAttachedToWebView());
if (index >= GetBackForwardListItemCount()) {
return nil; return nil;
} }
// Convert the index to an offset relative to backForwardList.currentItem ( // Convert the index to an offset relative to backForwardList.currentItem (
// which is also the last committed item), then use WKBackForwardList API to // which is also the last committed item), then use WKBackForwardList API to
// retrieve the item. // retrieve the item.
int offset = index - GetWKCurrentItemIndex(); int offset = static_cast<int>(index) - GetCurrentItemIndex();
id<CRWWebViewNavigationProxy> proxy = delegate_->GetWebViewNavigationProxy(); id<CRWWebViewNavigationProxy> proxy =
navigation_manager_->delegate_->GetWebViewNavigationProxy();
return [proxy.backForwardList itemAtIndex:offset]; return [proxy.backForwardList itemAtIndex:offset];
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#import "ios/web/navigation/wk_navigation_util.h" #import "ios/web/navigation/wk_navigation_util.h"
#include "ios/web/public/load_committed_details.h" #include "ios/web/public/load_committed_details.h"
#include "ios/web/public/navigation_item.h" #include "ios/web/public/navigation_item.h"
#include "ios/web/public/reload_type.h"
#include "ios/web/public/test/fakes/test_browser_state.h" #include "ios/web/public/test/fakes/test_browser_state.h"
#import "ios/web/public/web_client.h" #import "ios/web/public/web_client.h"
#import "ios/web/test/fakes/crw_fake_back_forward_list.h" #import "ios/web/test/fakes/crw_fake_back_forward_list.h"
...@@ -788,4 +789,148 @@ TEST_F(WKBasedNavigationManagerTest, EmptyWindowOpenNavigation) { ...@@ -788,4 +789,148 @@ TEST_F(WKBasedNavigationManagerTest, EmptyWindowOpenNavigation) {
manager_->GoToIndex(0); manager_->GoToIndex(0);
} }
// Test fixture for detach from web view mode for WKBasedNavigationManagerImpl.
class WKBasedNavigationManagerDetachedModeTest
: public WKBasedNavigationManagerTest {
protected:
void SetUp() override {
// Sets up each test case with a session history of 3 items. The middle item
// is the current item.
url0_ = GURL("http://www.0.com");
url1_ = GURL("http://www.1.com");
url2_ = GURL("http://www.2.com");
[mock_wk_list_ setCurrentURL:@"http://www.1.com"
backListURLs:@[ @"http://www.0.com" ]
forwardListURLs:@[ @"http://www.2.com" ]];
ASSERT_EQ(url0_, manager_->GetItemAtIndex(0)->GetURL());
ASSERT_EQ(url1_, manager_->GetItemAtIndex(1)->GetURL());
ASSERT_EQ(url2_, manager_->GetItemAtIndex(2)->GetURL());
}
// Returns the value of the "?session" URL param from |url|.
static std::string ExtractRestoredSession(const GURL& url) {
std::string session_json;
net::GetValueForKeyInQuery(
url, wk_navigation_util::kRestoreSessionSessionQueryKey, &session_json);
return session_json;
}
GURL url0_;
GURL url1_;
GURL url2_;
};
// Tests that all getters return the expected value in detached mode.
TEST_F(WKBasedNavigationManagerDetachedModeTest, CachedSessionHistory) {
manager_->DetachFromWebView();
delegate_.RemoveWebView();
EXPECT_EQ(url1_, manager_->GetVisibleItem()->GetURL());
EXPECT_EQ(3, manager_->GetItemCount());
EXPECT_EQ(url0_, manager_->GetItemAtIndex(0)->GetURL());
EXPECT_EQ(url1_, manager_->GetItemAtIndex(1)->GetURL());
EXPECT_EQ(url2_, manager_->GetItemAtIndex(2)->GetURL());
EXPECT_EQ(0, manager_->GetIndexOfItem(manager_->GetItemAtIndex(0)));
EXPECT_EQ(1, manager_->GetIndexOfItem(manager_->GetItemAtIndex(1)));
EXPECT_EQ(2, manager_->GetIndexOfItem(manager_->GetItemAtIndex(2)));
EXPECT_EQ(-1, manager_->GetPendingItemIndex());
EXPECT_EQ(nullptr, manager_->GetPendingItem());
EXPECT_EQ(1, manager_->GetLastCommittedItemIndex());
EXPECT_EQ(url1_, manager_->GetLastCommittedItem()->GetURL());
EXPECT_TRUE(manager_->CanGoBack());
EXPECT_TRUE(manager_->CanGoForward());
EXPECT_TRUE(manager_->CanGoToOffset(0));
EXPECT_TRUE(manager_->CanGoToOffset(-1));
EXPECT_TRUE(manager_->CanGoToOffset(1));
EXPECT_EQ(0, manager_->GetIndexForOffset(-1));
EXPECT_EQ(1, manager_->GetIndexForOffset(0));
EXPECT_EQ(2, manager_->GetIndexForOffset(1));
NavigationItemList backward_items = manager_->GetBackwardItems();
EXPECT_EQ(1UL, backward_items.size());
EXPECT_EQ(url0_, backward_items[0]->GetURL());
NavigationItemList forward_items = manager_->GetForwardItems();
EXPECT_EQ(1UL, forward_items.size());
EXPECT_EQ(url2_, forward_items[0]->GetURL());
}
// Tests that detaching from an empty WKWebView works.
TEST_F(WKBasedNavigationManagerDetachedModeTest, NothingToCache) {
delegate_.RemoveWebView();
manager_->DetachFromWebView();
EXPECT_EQ(0, manager_->GetItemCount());
EXPECT_EQ(nullptr, manager_->GetVisibleItem());
EXPECT_EQ(nullptr, manager_->GetItemAtIndex(0));
EXPECT_EQ(nullptr, manager_->GetPendingItem());
EXPECT_EQ(-1, manager_->GetLastCommittedItemIndex());
manager_->Reload(web::ReloadType::NORMAL, false /* check_for_repost */);
EXPECT_EQ(nullptr, manager_->GetPendingItem());
}
// Tests that Reload from detached mode restores cached history.
TEST_F(WKBasedNavigationManagerDetachedModeTest, Reload) {
manager_->DetachFromWebView();
delegate_.RemoveWebView();
manager_->Reload(web::ReloadType::NORMAL, false /* check_for_repost */);
EXPECT_EQ(
"{\"offset\":-1,\"titles\":[\"\",\"\",\"\"],\"urls\":[\"http://www.0.com/"
"\",\"http://www.1.com/\",\"http://www.2.com/\"]}",
ExtractRestoredSession(manager_->GetPendingItem()->GetURL()));
EXPECT_EQ(url0_, manager_->GetPendingItem()->GetVirtualURL());
}
// Tests that GoToIndex from detached mode restores cached history with updated
// current item offset.
TEST_F(WKBasedNavigationManagerDetachedModeTest, GoToIndex) {
manager_->DetachFromWebView();
delegate_.RemoveWebView();
manager_->GoToIndex(0);
EXPECT_EQ(
"{\"offset\":-2,\"titles\":[\"\",\"\",\"\"],\"urls\":[\"http://www.0.com/"
"\",\"http://www.1.com/\",\"http://www.2.com/\"]}",
ExtractRestoredSession(manager_->GetPendingItem()->GetURL()));
EXPECT_EQ(url0_, manager_->GetPendingItem()->GetVirtualURL());
}
// Tests that LoadIfNecessary from detached mode restores cached history.
TEST_F(WKBasedNavigationManagerDetachedModeTest, LoadIfNecessary) {
manager_->DetachFromWebView();
delegate_.RemoveWebView();
manager_->LoadIfNecessary();
EXPECT_EQ(
"{\"offset\":-1,\"titles\":[\"\",\"\",\"\"],\"urls\":[\"http://www.0.com/"
"\",\"http://www.1.com/\",\"http://www.2.com/\"]}",
ExtractRestoredSession(manager_->GetPendingItem()->GetURL()));
EXPECT_EQ(url0_, manager_->GetPendingItem()->GetVirtualURL());
}
// Tests that LoadURLWithParams from detached mode restores backward history and
// adds the new item at the end.
TEST_F(WKBasedNavigationManagerDetachedModeTest, LoadURLWithParams) {
manager_->DetachFromWebView();
delegate_.RemoveWebView();
NavigationManager::WebLoadParams params(GURL("http://www.3.com"));
manager_->LoadURLWithParams(params);
EXPECT_EQ(
"{\"offset\":0,\"titles\":[\"\",\"\",\"\"],\"urls\":[\"http://www.0.com/"
"\",\"http://www.1.com/\",\"http://www.3.com/\"]}",
ExtractRestoredSession(manager_->GetPendingItem()->GetURL()));
EXPECT_EQ(url0_, manager_->GetPendingItem()->GetVirtualURL());
}
} // namespace web } // namespace web
...@@ -3909,6 +3909,7 @@ registerLoadRequestForURL:(const GURL&)requestURL ...@@ -3909,6 +3909,7 @@ registerLoadRequestForURL:(const GURL&)requestURL
return; return;
_webStateImpl->CancelDialogs(); _webStateImpl->CancelDialogs();
self.navigationManagerImpl->DetachFromWebView();
[self abortLoad]; [self abortLoad];
[_webView removeFromSuperview]; [_webView removeFromSuperview];
......
...@@ -14,11 +14,7 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -14,11 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
@property(nullable, nonatomic, readonly, copy) NSURL* URL; @property(nullable, nonatomic, readonly, copy) NSURL* URL;
@property(nonatomic, readonly, strong) WKBackForwardList* backForwardList; @property(nonatomic, readonly, strong) WKBackForwardList* backForwardList;
@property(nonatomic, readonly) BOOL canGoBack;
@property(nonatomic, readonly) BOOL canGoForward;
- (nullable WKNavigation*)goBack;
- (nullable WKNavigation*)goForward;
- (nullable WKNavigation*)goToBackForwardListItem:(WKBackForwardListItem*)item; - (nullable WKNavigation*)goToBackForwardListItem:(WKBackForwardListItem*)item;
@end @end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment