Index: ios/web/web_state/ui/crw_wk_web_view_crash_detector.mm |
diff --git a/ios/web/web_state/ui/crw_wk_web_view_crash_detector.mm b/ios/web/web_state/ui/crw_wk_web_view_crash_detector.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..50f19239bbc864b22f01252b8e3523c3b48a87b3 |
--- /dev/null |
+++ b/ios/web/web_state/ui/crw_wk_web_view_crash_detector.mm |
@@ -0,0 +1,99 @@ |
+// Copyright 2014 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. |
+ |
+#import "ios/web/web_state/ui/crw_wk_web_view_crash_detector.h" |
+ |
+#import "base/ios/weak_nsobject.h" |
+#include "base/mac/scoped_block.h" |
+#import "base/mac/scoped_nsobject.h" |
+#import "base/strings/sys_string_conversions.h" |
+ |
+namespace { |
+ |
+// WKWebView property key path that is changed when web process is terminated. |
+// The value of this property is not important, but its change may indicate |
+// web process termination. |
+NSString* const kCrashIndicatorKeyPath = @"scrollView.backgroundColor"; |
+ |
+// Returns |YES| if given |error| means that WKWebView process was terminated. |
+BOOL IsWebViewTerminationError(NSError* error) { |
+ DLOG_IF(WARNING, ![error.domain isEqualToString:WKErrorDomain]) << |
+ "Unexpected WKWebView error domain: " << |
+ base::SysNSStringToUTF8(error.domain); |
+ // WKErrorUnknown is returned from unusable WKWebView so assume it's crashed. |
+ return ((error.code == WKErrorWebContentProcessTerminated || |
+ error.code == WKErrorUnknown) && |
+ [error.domain isEqualToString:WKErrorDomain]); |
+} |
+ |
+} // namespace |
+ |
+@interface CRWWKWebViewCrashDetector() |
+ |
+// Checks WKWebView health by evaluating a simple JavaScript. If evaluation |
+// fails with Termination Error then crash is reported. |
+- (void)checkHealth; |
+ |
+// Calls _crashHandler. |
+- (void)reportCrash; |
+ |
+@end |
+ |
+@implementation CRWWKWebViewCrashDetector { |
+ base::scoped_nsobject<WKWebView> _webView; |
+ base::mac::ScopedBlock<ProceduralBlock> _crashHandler; |
+} |
+ |
+- (instancetype)init { |
+ NOTREACHED(); |
+ return nil; |
+} |
+ |
+- (instancetype)initWithWebView:(WKWebView*)webView |
+ crashHandler:(ProceduralBlock)handler { |
+ DCHECK(webView); |
+ DCHECK(handler); |
+ |
+ if (self = [super init]) { |
+ _webView.reset([webView retain]); |
+ _crashHandler.reset([handler copy]); |
+ |
+ // Once Web Process is terminated -[WKWebView _processDidExit] is called. |
+ // That private method does different things inside including the change |
+ // of scroll view's background color. We use that color change as an |
+ // indication that WKWebView process may have crashed. |
+ [_webView addObserver:self |
+ forKeyPath:kCrashIndicatorKeyPath |
+ options:0 |
+ context:nil]; |
+ } |
+ return self; |
+} |
+ |
+- (void)dealloc { |
+ [_webView removeObserver:self forKeyPath:kCrashIndicatorKeyPath]; |
+ [super dealloc]; |
+} |
+ |
+- (void)observeValueForKeyPath:(NSString*)keyPath |
+ ofObject:(id)object |
+ change:(NSDictionary*)change |
+ context:(void*)context { |
+ DCHECK([keyPath isEqualToString:kCrashIndicatorKeyPath]); |
+ [self checkHealth]; |
+} |
+ |
+- (void)checkHealth { |
+ base::WeakNSObject<CRWWKWebViewCrashDetector> weakSelf(self); |
+ [_webView evaluateJavaScript:@"null" completionHandler:^(id, NSError* error) { |
+ if (error && IsWebViewTerminationError(error)) |
+ [weakSelf reportCrash]; |
+ }]; |
+} |
+ |
+- (void)reportCrash { |
+ _crashHandler.get()(); |
+} |
+ |
+@end |