Commit d3b760ac authored by Justin Cohen's avatar Justin Cohen Committed by Commit Bot

ios: Use webkit.postMessage to flush IPC during session restore.

Session restore uses history.pushState to inject URLs and titles into
each WKWebView session.  For large numbers of pages, this can generate
a very large number of IPC calls. iOS 12.2 introduced IPC throttling
that can delay the async pushState calls before the sync history.go
call. This was worked around via a check to see when all the push state
calls are correctly processed.

iOS14 introduced an optimization which breaks the above mentioned
workaround.  Instead, this change uses our own IPC call to native code
(which will be enqueued to the end of the IPC queue) to finalize session
restore.

The effect is faster restore for the average case, but slightly slower
restore for the edge case of a large numbers of pages.

Bug: 1127521, 1126863
Change-Id: Ibb70008afef312897e8165e06f16ed5c03808b38
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2407332
Commit-Queue: Justin Cohen <justincohen@chromium.org>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Reviewed-by: default avatarMike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarAli Juma <ajuma@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806721}
parent 05372606
...@@ -90,24 +90,34 @@ ...@@ -90,24 +90,34 @@
getRestoreURL(sessionHistoryObject.urls[i] || "about:blank")); getRestoreURL(sessionHistoryObject.urls[i] || "about:blank"));
} }
// iOS12.2 added a throttling mechanism where previous pushStates may // iOS12.2 added a throttling mechanism where the previous pushStates
// not immediately be available. Set a 10ms interval delay until // may not immediately be available. Instead of directly calling
// history.length reaches sessionHistory.length. // finishRestore(currentItemOffset) here, call it via a round trip
// native call to flush out the pushState IPC backlog.
var currentItemOffset = parseInt(sessionHistoryObject.offset); var currentItemOffset = parseInt(sessionHistoryObject.offset);
var goWhenReady = setInterval(() => { var offset = {'offset': currentItemOffset}
if (history.length == sessionHistoryObject.urls.length) { window.webkit.messageHandlers['session_restore'].postMessage(offset);
history.go(currentItemOffset); } catch (e) {
window.clearInterval(goWhenReady); handleError(e.name + ": " + e.message + " raw session history: " + sessionHistory);
}
}
// Queue a reload to redirect to the target URL after history.go /**
// is processed. * Finishes restoration by history.go-ing to |offset| and reloading the
setTimeout(() => { * page.
location.reload(); * @param {int} offset Either zero, or the number of pages (negative) to
}); * go backwards.
} */
}, 10); function _crFinishSessionRestoration(offset) {
try {
history.go(offset);
// Queue a reload to redirect to the target URL after history.go
// is processed.
setTimeout(() => {
location.reload();
});
} catch (e) { } catch (e) {
handleError(e.name + ": " + e.message + " raw session history: " + sessionHistory); handleError(e.name + ": " + e.message + " offset: " + offset);
} }
} }
......
...@@ -86,6 +86,9 @@ NSString* const kIsMainFrame = @"isMainFrame"; ...@@ -86,6 +86,9 @@ NSString* const kIsMainFrame = @"isMainFrame";
// URL scheme for messages sent from javascript for asynchronous processing. // URL scheme for messages sent from javascript for asynchronous processing.
NSString* const kScriptMessageName = @"crwebinvoke"; NSString* const kScriptMessageName = @"crwebinvoke";
// URL scheme for session restore.
NSString* const kSessionRestoreScriptMessageName = @"session_restore";
} // namespace } // namespace
@interface CRWWebController () <CRWWKNavigationHandlerDelegate, @interface CRWWebController () <CRWWKNavigationHandlerDelegate,
...@@ -443,6 +446,15 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*); ...@@ -443,6 +446,15 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
name:kScriptMessageName name:kScriptMessageName
webView:_webView]; webView:_webView];
// TODO(crbug.com/1127521) Consider consolidating session restore script
// logic into a different place.
[messageRouter
setScriptMessageHandler:^(WKScriptMessage* message) {
[weakSelf didReceiveSessionRestoreScriptMessage:message];
}
name:kSessionRestoreScriptMessageName
webView:_webView];
_webView.allowsBackForwardNavigationGestures = _webView.allowsBackForwardNavigationGestures =
_allowsBackForwardNavigationGestures; _allowsBackForwardNavigationGestures;
} }
...@@ -1105,6 +1117,22 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*); ...@@ -1105,6 +1117,22 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
} }
} }
// TODO(crbug.com/1127521) Consider consolidating session restore script
// logic into a different place.
- (void)didReceiveSessionRestoreScriptMessage:(WKScriptMessage*)message {
if ([message.name isEqualToString:kSessionRestoreScriptMessageName] &&
[message.body[@"offset"] isKindOfClass:[NSNumber class]]) {
NSString* method =
[NSString stringWithFormat:@"_crFinishSessionRestoration('%@')",
message.body[@"offset"]];
// Don't use |_jsInjector| -executeJavaScript here, as it relies on
// |windowID| being injected before window.onload starts.
web::ExecuteJavaScript(self.webView, method, nil);
} else {
DLOG(WARNING) << "Invalid session restore JS message name.";
}
}
- (BOOL)respondToWKScriptMessage:(WKScriptMessage*)scriptMessage { - (BOOL)respondToWKScriptMessage:(WKScriptMessage*)scriptMessage {
if (![scriptMessage.name isEqualToString:kScriptMessageName]) { if (![scriptMessage.name isEqualToString:kScriptMessageName]) {
return NO; return NO;
......
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