Skip to content

History Restoration in WKWebView (and Error Pages)

Garvan Keeley edited this page Dec 21, 2018 · 5 revisions

WKWebView has no native API to restore history in the web view.

History is restored by using injected JS, the sessionrestore.html file in the bundle.

Overview:

  • Tab history is queried from the WKWebView.backForwardList and stored on disk, along with the current position in the list.
  • On tab restore, sessionrestore.html is loaded, and arguments are passed to the page with the list of history URLs, and the current position
  • sessionrestore.html performs window.history.pushState() for each URL in the history, then sets the current position in the history using window.history.go().

Implementation details

  • WKURLScheme is used to provide single-origin URLs for manipulating window.history. The scheme and domain used is internal://local/; the same-origin-restricted JS APIs we use are satisfied by that root URL.
  • internal://local/sessionrestore?history=<list of URLs and current index> is the first URL loaded in a tab. The history param is parsed, and each item is pushed using an URL format of internal://local/sessionrestore?url=<actual URL> to populate the browser history.
  • The sessionrestore.html is loaded using WKWebView.load(request), and the load API creates an entry on the history stack for the webview. Thus, the current page has to be replaced in-place to avoid further modifying the stack. This is done with location.replace(<actual URL>) in JS.
  • Location replace also has to happen when going back/forward in the history, these urls will appear as internal://local/sessionrestore?url=<actual URL>. The WKURLScheme handler will load a placeholder page in the web view first, then replace the location to the actual URL. This appears to be the only method of redirection using WKURLScheme (and related) API.

Securing URLs

  • Not mentioned above is that internal:// URLs have a uuidkey=<a uuid> URL parameter. The UUID is generated once at app start, and internal history URLs must have a valid uuidkey param to be loaded. This ensures only the native history restoration can push these URLs.

Error Page Handling

WKWebView delegate didFailProvisionalNavigation will be called when a page fails to load. Note that the webview history will be unchanged at this point.

To provide a history entry for showing the error page in-content, WKWebView.load(request) is used to load a HTML page which will show the internal error page; different HTML is shown depending on the type of error.

URL Format

The error page urls are of the format internal://local/errorpage?<many params> with URL parameters providing the original URL and details of the error.

Loading the original page

The error page (i.e. the page with the URL internal://local/errorpage?...) acts a placeholder in the history for back/forward navigation, and for history restoration. If the original page needs to be loaded, the original URL is extracted from the error page and is loaded in-place with location.replace (any equivalent JS is acceptable that avoids doing a WKWebView.load and blowing away the forward history).

Securing error pages

To ensure only the app can request these, they are secured with the uuidkey URL param as above.

Clone this wiki locally