| // 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. |
| |
| #ifndef IOS_WEB_NAVIGATION_WK_BASED_NAVIGATION_MANAGER_IMPL_H_ |
| #define IOS_WEB_NAVIGATION_WK_BASED_NAVIGATION_MANAGER_IMPL_H_ |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/macros.h" |
| #import "ios/web/navigation/navigation_item_impl.h" |
| #import "ios/web/navigation/navigation_manager_impl.h" |
| #include "ios/web/navigation/time_smoother.h" |
| #include "ios/web/public/reload_type.h" |
| #include "ui/base/page_transition_types.h" |
| #include "url/gurl.h" |
| |
| @class WKBackForwardListItem; |
| |
| namespace base { |
| class ElapsedTimer; |
| } |
| |
| namespace web { |
| class BrowserState; |
| class NavigationItem; |
| struct Referrer; |
| class SessionStorageBuilder; |
| |
| // Name of UMA histogram to log the time spent on asynchronous session |
| // restoration. |
| extern const char kRestoreNavigationTime[]; |
| |
| // WKBackForwardList based implementation of NavigationManagerImpl. |
| // This class relies on the following WKWebView APIs, defined by the |
| // CRWWebViewNavigationProxy protocol: |
| // @property URL |
| // @property backForwardList |
| // - goToBackForwardListItem: |
| // |
| // This navigation manager uses WKBackForwardList as the ground truth for back- |
| // forward navigation history. It uses the Associated Objects runtime feature |
| // to link a NavigationItemImpl object to each WKBackForwardListItem to store |
| // additional states needed by the embedder. |
| // |
| // For all main frame navigations (both UI-initiated and renderer-initiated), |
| // the NavigationItemImpl objects are created proactively via AddPendingItem and |
| // CommitPendingItem. |
| // |
| // Non-main-frame navigations can only be initiated from the renderer. The |
| // NavigationItemImpl objects in this case are created lazily in GetItemAtIndex |
| // because the provisional load and commit events for iframe navigation are not |
| // visible via the WKNavigationDelegate interface. Consequently, pending item |
| // and previous item are only tracked for the main frame. |
| // |
| // Empty Window Open Navigation edge case: |
| // |
| // If window.open() is called with an empty URL, WKWebView does not seem to |
| // create a WKBackForwardListItem for the first about:blank navigation. Any |
| // subsequent navigation in this window will replace the about:blank entry. |
| // This is consistent with the HTML spec regarding Location-object navigation |
| // when the browser context's only Document is about:blank: |
| // https://html.spec.whatwg.org/multipage/history.html (Section 7.7.4) |
| // |
| // This navigation manager will still create a pendingNavigationItem for this |
| // "empty window open item" and allow CommitPendingItem() to be called on it. |
| // All accessors will behave identically as if the navigation history has a |
| // single normal entry. The only difference is that a subsequent call to |
| // CommitPendingItem() will *replace* the empty window open item. From this |
| // 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 { |
| public: |
| WKBasedNavigationManagerImpl(); |
| ~WKBasedNavigationManagerImpl() override; |
| |
| // NavigationManagerImpl: |
| void SetSessionController(CRWSessionController* session_controller) override; |
| void InitializeSession() override; |
| void OnNavigationItemsPruned(size_t pruned_item_count) override; |
| void OnNavigationItemChanged() override; |
| void OnNavigationItemCommitted() override; |
| void DetachFromWebView() override; |
| CRWSessionController* GetSessionController() const override; |
| void AddTransientItem(const GURL& url) override; |
| void AddPendingItem( |
| const GURL& url, |
| const web::Referrer& referrer, |
| ui::PageTransition navigation_type, |
| NavigationInitiationType initiation_type, |
| UserAgentOverrideOption user_agent_override_option) override; |
| void CommitPendingItem() override; |
| int GetIndexForOffset(int offset) const override; |
| // Returns the previous navigation item in the main frame. |
| int GetPreviousItemIndex() const override; |
| void SetPreviousItemIndex(int previous_item_index) override; |
| void AddPushStateItemIfNecessary(const GURL& url, |
| NSString* state_object, |
| ui::PageTransition transition) override; |
| bool IsRestoreSessionInProgress() const override; |
| |
| // NavigationManager: |
| BrowserState* GetBrowserState() const override; |
| WebState* GetWebState() const override; |
| NavigationItem* GetVisibleItem() const override; |
| void DiscardNonCommittedItems() override; |
| int GetItemCount() const override; |
| NavigationItem* GetItemAtIndex(size_t index) const override; |
| int GetIndexOfItem(const NavigationItem* item) const override; |
| int GetPendingItemIndex() const override; |
| bool RemoveItemAtIndex(int index) override; |
| bool CanGoBack() const override; |
| bool CanGoForward() const override; |
| bool CanGoToOffset(int offset) const override; |
| void GoBack() override; |
| void GoForward() override; |
| NavigationItemList GetBackwardItems() const override; |
| NavigationItemList GetForwardItems() const override; |
| void CopyStateFromAndPrune(const NavigationManager* source) override; |
| bool CanPruneAllButLastCommittedItem() const override; |
| void Restore(int last_committed_item_index, |
| std::vector<std::unique_ptr<NavigationItem>> items) override; |
| void AddRestoreCompletionCallback(base::OnceClosure callback) override; |
| void LoadIfNecessary() override; |
| |
| private: |
| // The SessionStorageBuilder functions require access to private variables of |
| // NavigationManagerImpl. |
| 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: |
| NavigationItemImpl* GetNavigationItemImplAtIndex(size_t index) const override; |
| NavigationItemImpl* GetLastCommittedItemInCurrentOrRestoredSession() |
| const override; |
| int GetLastCommittedItemIndexInCurrentOrRestoredSession() const override; |
| // Returns the pending navigation item in the main frame. Unlike |
| // GetPendingItem(), this method does not return null during session |
| // restoration (and returns last known pending item instead). |
| NavigationItemImpl* GetPendingItemInCurrentOrRestoredSession() const override; |
| NavigationItemImpl* GetTransientItemImpl() const override; |
| void FinishGoToIndex(int index, |
| NavigationInitiationType initiation_type, |
| bool has_user_gesture) override; |
| void FinishReload() override; |
| void FinishLoadURLWithParams() override; |
| bool IsPlaceholderUrl(const GURL& url) const override; |
| |
| // 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 |
| // case the NavigationItemImpl is stored on the WKBackForwardListItem. |
| std::unique_ptr<NavigationItemImpl> pending_item_; |
| |
| // -1 if pending_item_ represents a new navigation or there is no pending |
| // navigation. Otherwise, this is the index of the pending_item in the |
| // back-forward list. |
| int pending_item_index_; |
| |
| // Index of the previous navigation item in the main frame. If there is none, |
| // this field will have value -1. |
| int previous_item_index_; |
| |
| // Index of the last committed item in the main frame. If there is none, this |
| // field will equal to -1. |
| int last_committed_item_index_; |
| |
| // The NavigationItem that corresponds to the empty window open navigation. It |
| // has to be stored separately because it has no WKBackForwardListItem. It is |
| // not null if when CommitPendingItem() is last called, the WKBackForwardList |
| // is empty but not nil. Any subsequent call to CommitPendingItem() will reset |
| // this field to null. |
| std::unique_ptr<NavigationItemImpl> empty_window_open_item_; |
| |
| // The transient item in main frame. |
| std::unique_ptr<NavigationItemImpl> transient_item_; |
| |
| // Time smoother for navigation item timestamps. See comment in |
| // navigation_controller_impl.h. |
| // NOTE: This is mutable because GetNavigationItemImplAtIndex() needs to call |
| // TimeSmoother::GetSmoothedTime() with a const 'this'. Since NavigationItems |
| // have to be lazily created on read, this is the only workaround. |
| mutable TimeSmoother time_smoother_; |
| |
| WKWebViewCache web_view_cache_; |
| |
| // Whether this navigation manager is in the process of restoring session |
| // history into WKWebView. It is set in Restore() and unset in the first |
| // OnNavigationItemCommitted() callback. |
| bool is_restore_session_in_progress_ = false; |
| |
| // Non null during the session restoration. Created when session restoration |
| // is started and reset when the restoration is finished. Used to log UMA |
| // histogram that measures session restoration time. |
| std::unique_ptr<base::ElapsedTimer> restoration_timer_; |
| |
| // The active navigation entry in the restored session. GetVisibleItem() |
| // returns this item when |is_restore_session_in_progress_| is true so that |
| // clients of this navigation manager gets sane values for visible title and |
| // URL. |
| std::unique_ptr<NavigationItem> restored_visible_item_; |
| |
| // Non-empty only during the session restoration. The callbacks are |
| // registered in AddRestoreCompletionCallback() and are executed in the first |
| // OnNavigationItemCommitted() callback. |
| std::vector<base::OnceClosure> restore_session_completion_callbacks_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WKBasedNavigationManagerImpl); |
| }; |
| |
| } // namespace web |
| |
| #endif // IOS_WEB_NAVIGATION_WK_BASED_NAVIGATION_MANAGER_IMPL_H_ |