Permalink
Browse files

Add alternate WKWebView response mechanism that sets location.hash.

Counter-intuitively, this appears to be faster than using the specialized
window.webkit.messageHandlers.*.postMessage mechanism, since it avoids JSValue
deserialization on the native side (see http://openradar.appspot.com/17956460).
  • Loading branch information...
mihaip committed Aug 11, 2014
1 parent 64c1c5a commit 43a6fcf1b5e91e1cdca8e11d56eb71ab7e18c615
@@ -16,7 +16,8 @@
JavaScriptCore,
// WKWebView mechanisms
WKWebViewHandler,
WKMessageHandler,
WKLocationHash,
// Not actual full mechanisms, but just ways of measuring the native -> web function call time.
UIWebViewExecuteJs,
@@ -45,7 +46,7 @@ @interface PongUrlProtocol : NSURLProtocol
@end
@interface BenchmarkViewController () <TSWebViewDelegate, WebViewExport, WKScriptMessageHandler>
@interface BenchmarkViewController () <TSWebViewDelegate, WebViewExport, WKNavigationDelegate, WKScriptMessageHandler>
@end
@@ -87,6 +88,7 @@ -(void)loadView {
WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
[configuration.userContentController addScriptMessageHandler:self name:@"pong"];
_wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(15, CGRectGetMaxY(_uiWebView.frame) + 10, width - 30, 56) configuration:configuration];
_wkWebView.navigationDelegate = self;
[self.view addSubview:_wkWebView];
bottomWebView = _wkWebView;
}
@@ -171,7 +173,7 @@ -(void)startIteration {
// Cookie changes don't seem to trigger delegate methods on iOS 8. Since it's a slower mechanism,
// it's not work investigating.
[self endIteration:0];
} else if (mechanism == WKWebViewHandler) {
} else if (mechanism == WKMessageHandler || mechanism == WKLocationHash) {
if (_wkWebView) {
[_wkWebView evaluateJavaScript:[NSString stringWithFormat:@"ping(%d, '%qu')", mechanism, start] completionHandler:nil];
} else {
@@ -181,9 +183,13 @@ -(void)startIteration {
[_uiWebView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"ping(%d, '%qu')", mechanism, start]];
[self endIteration:mach_absolute_time() - start];
} else if (mechanism == WKWebViewExecuteJs) {
[_wkWebView evaluateJavaScript:[NSString stringWithFormat:@"ping(%d, '%qu')", mechanism, start] completionHandler:^(id result, NSError *error) {
[self endIteration:mach_absolute_time() - start];
}];
if (_wkWebView) {
[_wkWebView evaluateJavaScript:[NSString stringWithFormat:@"ping(%d, '%qu')", mechanism, start] completionHandler:^(id result, NSError *error) {
[self endIteration:mach_absolute_time() - start];
}];
} else {
[self endIteration:0];
}
} else {
[_uiWebView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"ping(%d, '%qu')", mechanism, start]];
}
@@ -229,10 +235,11 @@ -(void)showBenchmarkResults {
case CookieChange: name = @"document.cookie "; break;
case JavaScriptCore: name = @"JavaScriptCore "; break;
case WKWebViewHandler: name = @"WKWebViewHandler"; [results appendString:@"\nWKWebView\n"]; break;
case WKMessageHandler: name = @"MessageHandler "; [results appendString:@"\nWKWebView\n"]; break;
case WKLocationHash: name = @"location.hash "; break;
case UIWebViewExecuteJs: name = @"UI…ExecuteJs "; [results appendString:@"\nJS Execution\n"]; break;
case WKWebViewExecuteJs: name = @"WK…ExecuteJs "; break;
case UIWebViewExecuteJs: name = @"UIWebView "; [results appendString:@"\nJS Execution\n"]; break;
case WKWebViewExecuteJs: name = @"WKWebView "; break;
}
MechanismTiming *timing = &_mechanismTimings[i];
double averageMs = [self machTimeToMs:timing->sum]/(double)kNumIterationsPerMechanisms;
@@ -255,6 +262,7 @@ -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)r
return NO;
} else if ([request.URL.fragment hasPrefix:@"pong://"]) {
[self handlePongRequest:[request.URL.fragment substringFromIndex:7]];
return NO;
}
return YES;
}
@@ -264,6 +272,19 @@ -(void)webView:(UIWebView *)webView didCreateJavaScriptContext:(JSContext *)ctx
ctx[@"viewController"] = self;
}
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
// Even though we put the pong into the fragment, since the URL of the frame is about:blank when using
// loadHTMLString, we have to parse it out of the URL by hand, since NSURL handle fragments for about:blank.
NSString *url = navigationAction.request.URL.absoluteString;
NSRange pongRange = [url rangeOfString:@"pong://"];
if (pongRange.location != NSNotFound) {
[self handlePongRequest:[url substringFromIndex:pongRange.location + pongRange.length]];
decisionHandler(WKNavigationActionPolicyCancel);
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSNumber *start = (NSNumber *)message.body;
uint64_t end = mach_absolute_time();
@@ -7,7 +7,7 @@ var Mechanism = {
XhrAsync: 5,
CookieChange: 6,
JavaScriptCore: 7,
UIWebViewExecuteJs: 9
UIWebViewExecuteJs: 10
};
// The link does not need to be appended to the document, that avoids triggering
@@ -1,15 +1,19 @@
var Mechanism = {
WkWebViewHandler: 8,
WKWebViewExecuteJs: 10
WKMessageHandler: 8,
WKLocationHash: 9,
WKWebViewExecuteJs: 11
};
var pingCount = 0;
function ping(mechanism, startTime) {
pingCount++;
switch (mechanism) {
case Mechanism.WkWebViewHandler:
case Mechanism.WKMessageHandler:
window.webkit.messageHandlers.pong.postMessage(startTime);
break;
case Mechanism.WKLocationHash:
location.hash = "#pong://" + startTime;
break;
case Mechanism.WKWebViewExecuteJs:
return startTime;
break;

0 comments on commit 43a6fcf

Please sign in to comment.