Skip to content

Commit 96c8e2d

Browse files
authored
[webview_flutter_wkwebview] Adds the isInspectable to WebKitWebViewController. (#3984)
Fixes: flutter/flutter#126899 Starting from iOS version 16.4, it is now possible to change the value of the isInspectable property in WKWebView. https://developer.apple.com/documentation/webkit/wkwebview/4111163-isinspectable Since iOS 16.4, the Web Inspector feature is disabled by default. Therefore, to use Web Inspector, need to change the value of isInspectable. Here is a blog post from WebKit that discusses this https://webkit.org/blog/13936/enabling-the-inspection-of-web-content-in-apps/ Apple Developer Forums thread (iOS 16.4 webview can not debug in safari web inspector) https://developer.apple.com/forums/thread/727049
1 parent 05efbb2 commit 96c8e2d

File tree

17 files changed

+233
-2
lines changed

17 files changed

+233
-2
lines changed

packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 3.6.0
2+
3+
* Adds support to enable debugging of web contents on the latest versions of WebKit. See
4+
`WebKitWebViewController.setInspectable`.
5+
16
## 3.5.0
27

38
* Adds support to limit navigation to pages within the app’s domain. See

packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,4 +464,20 @@ - (void)testContentInsetsSumAlwaysZeroAfterSetFrame {
464464
XCTAssertTrue(feq(webView.scrollView.contentInset.bottom, -insetToAdjust.bottom));
465465
XCTAssertTrue(CGRectEqualToRect(webView.frame, CGRectMake(0, 0, 300, 100)));
466466
}
467+
468+
- (void)testSetInspectable API_AVAILABLE(ios(16.4), macos(13.3), tvos(16.4)) {
469+
FWFWebView *mockWebView = OCMClassMock([FWFWebView class]);
470+
471+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
472+
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:0];
473+
474+
FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc]
475+
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
476+
instanceManager:instanceManager];
477+
478+
FlutterError *error;
479+
[hostAPI setInspectableForWebViewWithIdentifier:@0 inspectable:@YES error:&error];
480+
OCMVerify([mockWebView setInspectable:YES]);
481+
XCTAssertNil(error);
482+
}
467483
@end

packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,9 @@ NSObject<FlutterMessageCodec> *FWFWKWebViewHostApiGetCodec(void);
751751
javaScriptString:(NSString *)javaScriptString
752752
completion:(void (^)(id _Nullable,
753753
FlutterError *_Nullable))completion;
754+
- (void)setInspectableForWebViewWithIdentifier:(NSNumber *)identifier
755+
inspectable:(NSNumber *)inspectable
756+
error:(FlutterError *_Nullable *_Nonnull)error;
754757
@end
755758

756759
extern void FWFWKWebViewHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,

packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,6 +2481,31 @@ void FWFWKWebViewHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
24812481
[channel setMessageHandler:nil];
24822482
}
24832483
}
2484+
{
2485+
FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
2486+
initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.setInspectable"
2487+
binaryMessenger:binaryMessenger
2488+
codec:FWFWKWebViewHostApiGetCodec()];
2489+
if (api) {
2490+
NSCAssert([api respondsToSelector:@selector(setInspectableForWebViewWithIdentifier:
2491+
inspectable:error:)],
2492+
@"FWFWKWebViewHostApi api (%@) doesn't respond to "
2493+
@"@selector(setInspectableForWebViewWithIdentifier:inspectable:error:)",
2494+
api);
2495+
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
2496+
NSArray *args = message;
2497+
NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0);
2498+
NSNumber *arg_inspectable = GetNullableObjectAtIndex(args, 1);
2499+
FlutterError *error;
2500+
[api setInspectableForWebViewWithIdentifier:arg_identifier
2501+
inspectable:arg_inspectable
2502+
error:&error];
2503+
callback(wrapResult(nil, error));
2504+
}];
2505+
} else {
2506+
[channel setMessageHandler:nil];
2507+
}
2508+
}
24842509
}
24852510
NSObject<FlutterMessageCodec> *FWFWKUIDelegateHostApiGetCodec(void) {
24862511
static FlutterStandardMessageCodec *sSharedObject = nil;

packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,18 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie
197197
}];
198198
}
199199

200+
- (void)setInspectableForWebViewWithIdentifier:(NSNumber *)identifier
201+
inspectable:(NSNumber *)inspectable
202+
error:(FlutterError *_Nullable *_Nonnull)error {
203+
if (@available(macOS 13.3, iOS 16.4, tvOS 16.4, *)) {
204+
[[self webViewForIdentifier:identifier] setInspectable:inspectable.boolValue];
205+
} else {
206+
*error = [FlutterError errorWithCode:@"FWFUnsupportedVersionError"
207+
message:@"setInspectable is only supported on versions 16.4+."
208+
details:nil];
209+
}
210+
}
211+
200212
- (void)goBackForWebViewWithIdentifier:(nonnull NSNumber *)identifier
201213
error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
202214
[[self webViewForIdentifier:identifier] goBack];

packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,7 @@ class ObjectOrIdentifier {
675675

676676
class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec {
677677
const _WKWebsiteDataStoreHostApiCodec();
678+
678679
@override
679680
void writeValue(WriteBuffer buffer, Object? value) {
680681
if (value is WKWebsiteDataTypeEnumData) {
@@ -961,6 +962,7 @@ class UIScrollViewHostApi {
961962

962963
class _WKWebViewConfigurationHostApiCodec extends StandardMessageCodec {
963964
const _WKWebViewConfigurationHostApiCodec();
965+
964966
@override
965967
void writeValue(WriteBuffer buffer, Object? value) {
966968
if (value is WKAudiovisualMediaTypeEnumData) {
@@ -1150,6 +1152,7 @@ abstract class WKWebViewConfigurationFlutterApi {
11501152

11511153
class _WKUserContentControllerHostApiCodec extends StandardMessageCodec {
11521154
const _WKUserContentControllerHostApiCodec();
1155+
11531156
@override
11541157
void writeValue(WriteBuffer buffer, Object? value) {
11551158
if (value is WKUserScriptData) {
@@ -1435,6 +1438,7 @@ class WKScriptMessageHandlerHostApi {
14351438

14361439
class _WKScriptMessageHandlerFlutterApiCodec extends StandardMessageCodec {
14371440
const _WKScriptMessageHandlerFlutterApiCodec();
1441+
14381442
@override
14391443
void writeValue(WriteBuffer buffer, Object? value) {
14401444
if (value is WKScriptMessageData) {
@@ -1537,6 +1541,7 @@ class WKNavigationDelegateHostApi {
15371541

15381542
class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec {
15391543
const _WKNavigationDelegateFlutterApiCodec();
1544+
15401545
@override
15411546
void writeValue(WriteBuffer buffer, Object? value) {
15421547
if (value is NSErrorData) {
@@ -1768,6 +1773,7 @@ abstract class WKNavigationDelegateFlutterApi {
17681773

17691774
class _NSObjectHostApiCodec extends StandardMessageCodec {
17701775
const _NSObjectHostApiCodec();
1776+
17711777
@override
17721778
void writeValue(WriteBuffer buffer, Object? value) {
17731779
if (value is NSKeyValueObservingOptionsEnumData) {
@@ -1881,6 +1887,7 @@ class NSObjectHostApi {
18811887

18821888
class _NSObjectFlutterApiCodec extends StandardMessageCodec {
18831889
const _NSObjectFlutterApiCodec();
1890+
18841891
@override
18851892
void writeValue(WriteBuffer buffer, Object? value) {
18861893
if (value is NSKeyValueChangeKeyEnumData) {
@@ -1982,6 +1989,7 @@ abstract class NSObjectFlutterApi {
19821989

19831990
class _WKWebViewHostApiCodec extends StandardMessageCodec {
19841991
const _WKWebViewHostApiCodec();
1992+
19851993
@override
19861994
void writeValue(WriteBuffer buffer, Object? value) {
19871995
if (value is NSErrorData) {
@@ -2527,6 +2535,28 @@ class WKWebViewHostApi {
25272535
return replyList[0];
25282536
}
25292537
}
2538+
2539+
Future<void> setInspectable(int arg_identifier, bool arg_inspectable) async {
2540+
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
2541+
'dev.flutter.pigeon.WKWebViewHostApi.setInspectable', codec,
2542+
binaryMessenger: _binaryMessenger);
2543+
final List<Object?>? replyList = await channel
2544+
.send(<Object?>[arg_identifier, arg_inspectable]) as List<Object?>?;
2545+
if (replyList == null) {
2546+
throw PlatformException(
2547+
code: 'channel-error',
2548+
message: 'Unable to establish connection on channel.',
2549+
);
2550+
} else if (replyList.length > 1) {
2551+
throw PlatformException(
2552+
code: replyList[0]! as String,
2553+
message: replyList[1] as String?,
2554+
details: replyList[2],
2555+
);
2556+
} else {
2557+
return;
2558+
}
2559+
}
25302560
}
25312561

25322562
/// Mirror of WKUIDelegate.
@@ -2567,6 +2597,7 @@ class WKUIDelegateHostApi {
25672597

25682598
class _WKUIDelegateFlutterApiCodec extends StandardMessageCodec {
25692599
const _WKUIDelegateFlutterApiCodec();
2600+
25702601
@override
25712602
void writeValue(WriteBuffer buffer, Object? value) {
25722603
if (value is NSUrlRequestData) {
@@ -2703,6 +2734,7 @@ abstract class WKUIDelegateFlutterApi {
27032734

27042735
class _WKHttpCookieStoreHostApiCodec extends StandardMessageCodec {
27052736
const _WKHttpCookieStoreHostApiCodec();
2737+
27062738
@override
27072739
void writeValue(WriteBuffer buffer, Object? value) {
27082740
if (value is NSHttpCookieData) {

packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,24 @@ class WKWebView extends UIView {
11071107
);
11081108
}
11091109

1110+
/// Enables debugging of web contents (HTML / CSS / JavaScript) in the
1111+
/// underlying WebView.
1112+
///
1113+
/// This flag can be enabled in order to facilitate debugging of web layouts
1114+
/// and JavaScript code running inside WebViews. Please refer to [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc).
1115+
/// documentation for the debugging guide.
1116+
///
1117+
/// Starting from macOS version 13.3, iOS version 16.4, and tvOS version 16.4,
1118+
/// the default value is set to false.
1119+
///
1120+
/// Defaults to true in previous versions.
1121+
Future<void> setInspectable(bool inspectable) {
1122+
return _webViewApi.setInspectableForInstances(
1123+
this,
1124+
inspectable,
1125+
);
1126+
}
1127+
11101128
@override
11111129
WKWebView copy() {
11121130
return WKWebView.detached(

packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,17 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi {
10671067
}
10681068
}
10691069

1070+
/// Calls [setInspectable] with the ids of the provided object instances.
1071+
Future<void> setInspectableForInstances(
1072+
WKWebView instance,
1073+
bool inspectable,
1074+
) async {
1075+
return setInspectable(
1076+
instanceManager.getIdentifier(instance)!,
1077+
inspectable,
1078+
);
1079+
}
1080+
10701081
/// Calls [setNavigationDelegate] with the ids of the provided object instances.
10711082
Future<void> setNavigationDelegateForInstances(
10721083
WKWebView instance,

packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,18 @@ class WebKitWebViewController extends PlatformWebViewController {
544544
) async {
545545
_onPermissionRequestCallback = onPermissionRequest;
546546
}
547+
548+
/// Whether to enable tools for debugging the current WKWebView content.
549+
///
550+
/// It needs to be activated in each WKWebView where you want to enable it.
551+
///
552+
/// Starting from macOS version 13.3, iOS version 16.4, and tvOS version 16.4,
553+
/// the default value is set to false.
554+
///
555+
/// Defaults to true in previous versions.
556+
Future<void> setInspectable(bool inspectable) {
557+
return _webView.setInspectable(inspectable);
558+
}
547559
}
548560

549561
/// An implementation of [JavaScriptChannelParams] with the WebKit api.

packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,9 @@ abstract class WKWebViewHostApi {
694694
@ObjCSelector('evaluateJavaScriptForWebViewWithIdentifier:javaScriptString:')
695695
@async
696696
Object? evaluateJavaScript(int identifier, String javaScriptString);
697+
698+
@ObjCSelector('setInspectableForWebViewWithIdentifier:inspectable:')
699+
void setInspectable(int identifier, bool inspectable);
697700
}
698701

699702
/// Mirror of WKUIDelegate.

0 commit comments

Comments
 (0)