1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_IMPL_H_
6 #define IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_IMPL_H_
7 
8 #include <stddef.h>
9 
10 #include <memory>
11 #include <vector>
12 
13 #include "base/callback.h"
14 #include "base/macros.h"
15 #import "ios/web/navigation/navigation_item_impl.h"
16 #import "ios/web/public/deprecated/navigation_item_list.h"
17 #import "ios/web/public/navigation/navigation_manager.h"
18 #include "ios/web/public/navigation/reload_type.h"
19 #include "ui/base/page_transition_types.h"
20 #include "url/gurl.h"
21 
22 namespace web {
23 class BrowserState;
24 class NavigationItem;
25 class NavigationManagerDelegate;
26 class SessionStorageBuilder;
27 
28 // Name of UMA histogram to log the number of items Navigation Manager was
29 // requested to restore. 100 is logged when the number of navigation items is
30 // greater than 100. This is just a requested count and actual number of
31 // restored items can be smaller.
32 extern const char kRestoreNavigationItemCount[];
33 
34 // Defines the ways how a pending navigation can be initiated.
35 enum class NavigationInitiationType {
36   // Navigation initiation type is only valid for pending navigations, use NONE
37   // if a navigation is already committed.
38   NONE = 0,
39 
40   // Navigation was initiated by the browser by calling NavigationManager
41   // methods. Examples of methods which cause browser-initiated navigations
42   // include:
43   //  * NavigationManager::Reload()
44   //  * NavigationManager::GoBack()
45   //  * NavigationManager::GoForward()
46   BROWSER_INITIATED,
47 
48   // Navigation was initiated by renderer. Examples of renderer-initiated
49   // navigations include:
50   //  * <a> link click
51   //  * changing window.location.href
52   //  * redirect via the <meta http-equiv="refresh"> tag
53   //  * using window.history.pushState
54   RENDERER_INITIATED,
55 };
56 
57 // Implementation of NavigationManager.
58 // Generally mirrors upstream's NavigationController.
59 class NavigationManagerImpl : public NavigationManager {
60  public:
61   NavigationManagerImpl();
62   ~NavigationManagerImpl() override;
63 
64   // Returns the most recent Committed Item that is not the result of a client
65   // or server-side redirect from the given Navigation Manager. Returns nullptr
66   // if there's an error condition on the input |nav_manager|, such as nullptr
67   // or no non-redirect items.
68   static NavigationItem* GetLastCommittedNonRedirectedItem(
69       const NavigationManager* nav_manager);
70 
71   // Setters for NavigationManagerDelegate and BrowserState.
72   virtual void SetDelegate(NavigationManagerDelegate* delegate);
73   virtual void SetBrowserState(BrowserState* browser_state);
74 
75   // Initializes a new session history.
76   virtual void InitializeSession() = 0;
77 
78   // Helper functions for notifying WebStateObservers of changes.
79   // TODO(stuartmorgan): Make these private once the logic triggering them moves
80   // into this layer.
81   virtual void OnNavigationItemCommitted() = 0;
82 
83   // Called when a navigation has started.
84   virtual void OnNavigationStarted(const GURL& url) = 0;
85 
86   // Prepares for the deletion of WKWebView such as caching necessary data.
87   virtual void DetachFromWebView();
88 
89   // Adds a transient item with the given URL. A transient item will be
90   // discarded on any navigation.
91   virtual void AddTransientItem(const GURL& url) = 0;
92 
93   // Adds a new item with the given url, referrer, navigation type, initiation
94   // type and user agent override option, making it the pending item. If pending
95   // item is the same as the current item, this does nothing. |referrer| may be
96   // nil if there isn't one. The item starts out as pending, and will be lost
97   // unless |-commitPendingItem| is called.
98   virtual void AddPendingItem(const GURL& url,
99                               const web::Referrer& referrer,
100                               ui::PageTransition navigation_type,
101                               NavigationInitiationType initiation_type) = 0;
102 
103   // Commits the pending item, if any.
104   // TODO(crbug.com/936933): Remove this method.
105   virtual void CommitPendingItem() = 0;
106 
107   // Commits given pending |item| stored outside of navigation manager
108   // (normally in NavigationContext). It is possible to have additional pending
109   // items owned by navigation manager and/or outside of navigation manager.
110   virtual void CommitPendingItem(std::unique_ptr<NavigationItemImpl> item) = 0;
111 
112   // Removes pending item, so it can be stored in NavigationContext.
113   // Pending item is stored in this object when NavigationContext object does
114   // not yet exist (e.g. when navigation was just requested, or when navigation
115   // has aborted).
116   virtual std::unique_ptr<NavigationItemImpl> ReleasePendingItem() = 0;
117 
118   // Allows transferring pending item from NavigationContext to this object.
119   // Pending item can be moved from NavigationContext to this object when
120   // navigation is aborted, but pending item should be retained.
121   virtual void SetPendingItem(
122       std::unique_ptr<web::NavigationItemImpl> item) = 0;
123 
124   // Returns the navigation index that differs from the current item (or pending
125   // item if it exists) by the specified |offset|, skipping redirect navigation
126   // items. The index returned is not guaranteed to be valid.
127   // TODO(crbug.com/661316): Make this method private once navigation code is
128   // moved from CRWWebController to NavigationManagerImpl.
129   virtual int GetIndexForOffset(int offset) const = 0;
130 
131   // Updates navigation history (if applicable) after pushState.
132   // TODO(crbug.com/783382): This is a legacy method to maintain backward
133   // compatibility for PageLoad stat. Remove this method once PageLoad no longer
134   // depend on WebStateObserver::DidStartLoading.
135   virtual void AddPushStateItemIfNecessary(const GURL& url,
136                                            NSString* state_object,
137                                            ui::PageTransition transition) = 0;
138 
139   // Sets the index of the pending navigation item. -1 means no navigation or a
140   // new navigation.
141   virtual void SetPendingItemIndex(int index) = 0;
142 
143   // Applies the workaround for crbug.com/887497.
144   virtual void ApplyWKWebViewForwardHistoryClobberWorkaround();
145 
146   // Set ShouldSkipSerialization to true for the next pending item, provided it
147   // matches |url|.  Applies the workaround for crbug.com/997182
148   virtual void SetWKWebViewNextPendingUrlNotSerializable(const GURL& url);
149 
150   // Returns true if specific URL is blocked from session restore.
151   virtual bool ShouldBlockUrlDuringRestore(const GURL& url) = 0;
152 
153   // Resets the transient url rewriter list.
154   void RemoveTransientURLRewriters();
155 
156   // Updates the URL of the yet to be committed pending item. Useful for page
157   // redirects. Does nothing if there is no pending item.
158   void UpdatePendingItemUrl(const GURL& url) const;
159 
160   // The current NavigationItem. During a pending navigation, returns the
161   // NavigationItem for that navigation. If a transient NavigationItem exists,
162   // this NavigationItem will be returned.
163   // TODO(crbug.com/661316): Make this private once all navigation code is moved
164   // out of CRWWebController.
165   NavigationItemImpl* GetCurrentItemImpl() const;
166 
167   // Returns the last committed NavigationItem, which may be null if there
168   // are no committed entries or session restoration is in-progress.
169   NavigationItemImpl* GetLastCommittedItemImpl() const;
170 
171   // Updates the pending or last committed navigation item after replaceState.
172   // TODO(crbug.com/783382): This is a legacy method to maintain backward
173   // compatibility for PageLoad stat. Remove this method once PageLoad no longer
174   // depend on WebStateObserver::DidStartLoading.
175   void UpdateCurrentItemForReplaceState(const GURL& url,
176                                         NSString* state_object);
177 
178   // Same as GoToIndex(int), but allows renderer-initiated navigations and
179   // specifying whether or not the navigation is caused by the user gesture.
180   void GoToIndex(int index,
181                  NavigationInitiationType initiation_type,
182                  bool has_user_gesture);
183 
184   // NavigationManager:
185   NavigationItem* GetLastCommittedItem() const final;
186   int GetLastCommittedItemIndex() const final;
187   NavigationItem* GetPendingItem() const final;
188   NavigationItem* GetTransientItem() const final;
189   void LoadURLWithParams(const NavigationManager::WebLoadParams&) override;
190   void AddTransientURLRewriter(BrowserURLRewriter::URLRewriter rewriter) final;
191   void GoToIndex(int index) final;
192   void Reload(ReloadType reload_type, bool check_for_reposts) final;
193   void ReloadWithUserAgentType(UserAgentType user_agent_type) final;
194   void LoadIfNecessary() override;
195   void AddRestoreCompletionCallback(base::OnceClosure callback) override;
196 
197   // Implementation for corresponding NavigationManager getters.
198   virtual NavigationItemImpl* GetPendingItemInCurrentOrRestoredSession()
199       const = 0;
200   virtual NavigationItemImpl* GetTransientItemImpl() const = 0;
201   // Unlike GetLastCommittedItem(), this method does not return null during
202   // session restoration (and returns last known committed item instead).
203   virtual NavigationItemImpl* GetLastCommittedItemInCurrentOrRestoredSession()
204       const = 0;
205   // Unlike GetLastCommittedItemIndex(), this method does not return -1 during
206   // session restoration (and returns last known committed item index instead).
207   virtual int GetLastCommittedItemIndexInCurrentOrRestoredSession() const = 0;
208 
209   // Identical to GetItemAtIndex() but returns the underlying NavigationItemImpl
210   // instead of the public NavigationItem interface.
211   virtual NavigationItemImpl* GetNavigationItemImplAtIndex(
212       size_t index) const = 0;
213 
214  protected:
215   // The SessionStorageBuilder functions require access to private variables of
216   // NavigationManagerImpl.
217   friend SessionStorageBuilder;
218 
219   // TODO(crbug.com/738020): Remove legacy code and merge
220   // WKBasedNavigationManager into this class after the navigation experiment.
221 
222   // Must be called by subclasses before restoring |item_count| navigation
223   // items.
224   void WillRestore(size_t item_count);
225 
226   // Some app-specific URLs need to be rewritten to about: scheme.
227   void RewriteItemURLIfNecessary(NavigationItem* item) const;
228 
229   // Creates a NavigationItem using the given properties, where |previous_url|
230   // is the URL of the navigation just prior to the current one. If
231   // |url_rewriters| is not nullptr, apply them before applying the permanent
232   // URL rewriters from BrowserState.
233   // TODO(crbug.com/738020): Make this private when WKBasedNavigationManagerImpl
234   // is merged into this class.
235   std::unique_ptr<NavigationItemImpl> CreateNavigationItemWithRewriters(
236       const GURL& url,
237       const Referrer& referrer,
238       ui::PageTransition transition,
239       NavigationInitiationType initiation_type,
240       const GURL& previous_url,
241       const std::vector<BrowserURLRewriter::URLRewriter>* url_rewriters) const;
242 
243   // Returns the most recent NavigationItem with an URL that generates an HTTP
244   // request.
245   NavigationItem* GetLastCommittedItemWithUserAgentType() const;
246 
247   // Subclass specific implementation to update session state.
248   virtual void FinishGoToIndex(int index,
249                                NavigationInitiationType type,
250                                bool has_user_gesture) = 0;
251   virtual void FinishReload();
252   virtual void FinishLoadURLWithParams(
253       NavigationInitiationType initiation_type);
254 
255   // Returns true if the subclass uses placeholder URLs and this is such a URL.
256   virtual bool IsPlaceholderUrl(const GURL& url) const;
257 
258   // The primary delegate for this manager.
259   NavigationManagerDelegate* delegate_;
260 
261   // The BrowserState that is associated with this instance.
262   BrowserState* browser_state_;
263 
264   // List of transient url rewriters added by |AddTransientURLRewriter()|.
265   std::vector<BrowserURLRewriter::URLRewriter> transient_url_rewriters_;
266 };
267 
268 }  // namespace web
269 
270 #endif  // IOS_WEB_NAVIGATION_NAVIGATION_MANAGER_IMPL_H_
271