Skip to content

Commit c673a9b

Browse files
bparrishMinesyutaaraki-toydium
authored andcommitted
[webview_flutter_wkwebview] Fixes bug where an NSError not an NSErrorData was returned (flutter#5973)
1 parent a1af363 commit c673a9b

File tree

6 files changed

+123
-7
lines changed

6 files changed

+123
-7
lines changed

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,4 +351,41 @@ - (void)testEvaluateJavaScript {
351351
XCTAssertEqualObjects(returnValue, @"result");
352352
XCTAssertNil(returnError);
353353
}
354+
355+
- (void)testEvaluateJavaScriptReturnsNSErrorData {
356+
FWFWebView *mockWebView = OCMClassMock([FWFWebView class]);
357+
358+
OCMStub([mockWebView
359+
evaluateJavaScript:@"runJavaScript"
360+
completionHandler:([OCMArg invokeBlockWithArgs:[NSNull null],
361+
[NSError errorWithDomain:@"errorDomain"
362+
code:0
363+
userInfo:@{
364+
NSLocalizedDescriptionKey :
365+
@"description"
366+
}],
367+
nil])]);
368+
369+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
370+
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:0];
371+
372+
FWFWebViewHostApiImpl *hostAPI =
373+
[[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager];
374+
375+
NSString __block *returnValue;
376+
FlutterError __block *returnError;
377+
[hostAPI evaluateJavaScriptForWebViewWithIdentifier:@0
378+
javaScriptString:@"runJavaScript"
379+
completion:^(id result, FlutterError *error) {
380+
returnValue = result;
381+
returnError = error;
382+
}];
383+
384+
XCTAssertNil(returnValue);
385+
FWFNSErrorData *errorData = returnError.details;
386+
XCTAssertTrue([errorData isKindOfClass:[FWFNSErrorData class]]);
387+
XCTAssertEqualObjects(errorData.code, @0);
388+
XCTAssertEqualObjects(errorData.domain, @"errorDomain");
389+
XCTAssertEqualObjects(errorData.localizedDescription, @"description");
390+
}
354391
@end

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie
141141
if (!result || [result isKindOfClass:[NSString class]] ||
142142
[result isKindOfClass:[NSNumber class]]) {
143143
returnValue = result;
144-
} else {
144+
} else if (![result isKindOfClass:[NSNull class]]) {
145145
NSString *className = NSStringFromClass([result class]);
146146
NSLog(@"Return type of evaluateJavaScript is not directly supported: %@. Returned "
147147
@"description of value.",
@@ -151,7 +151,7 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie
151151
} else {
152152
flutterError = [FlutterError errorWithCode:@"FWFEvaluateJavaScriptError"
153153
message:@"Failed evaluating JavaScript."
154-
details:error];
154+
details:FWFNSErrorDataFromNSError(error)];
155155
}
156156

157157
completion(returnValue, flutterError);

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -908,11 +908,25 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi {
908908
Future<Object?> evaluateJavaScriptForInstances(
909909
WKWebView instance,
910910
String javaScriptString,
911-
) {
912-
return evaluateJavaScript(
913-
instanceManager.getIdentifier(instance)!,
914-
javaScriptString,
915-
);
911+
) async {
912+
try {
913+
final Object? result = await evaluateJavaScript(
914+
instanceManager.getIdentifier(instance)!,
915+
javaScriptString,
916+
);
917+
return result;
918+
} on PlatformException catch (exception) {
919+
if (exception.details is! NSErrorData) {
920+
rethrow;
921+
}
922+
923+
throw PlatformException(
924+
code: exception.code,
925+
message: exception.message,
926+
stacktrace: exception.stacktrace,
927+
details: (exception.details as NSErrorData).toNSError(),
928+
);
929+
}
916930
}
917931

918932
/// Calls [setNavigationDelegate] with the ids of the provided object instances.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController {
573573
return '"<null>"';
574574
}
575575
return '(null)';
576+
} else if (value is bool) {
577+
return value ? '1' : '0';
578+
} else if (value is double && value.truncate() == value) {
579+
return value.truncate().toString();
576580
} else if (value is List) {
577581
final List<String> stringValues = <String>[];
578582
for (final Object? listValue in value) {

packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66

7+
import 'package:flutter/services.dart';
78
import 'package:flutter_test/flutter_test.dart';
89
import 'package:mockito/annotations.dart';
910
import 'package:mockito/mockito.dart';
@@ -815,6 +816,30 @@ void main() {
815816
.thenAnswer((_) => Future<String>.value('stopstop'));
816817
expect(webView.evaluateJavaScript('gogo'), completion('stopstop'));
817818
});
819+
820+
test('evaluateJavaScript returns NSError', () {
821+
when(mockPlatformHostApi.evaluateJavaScript(webViewInstanceId, 'gogo'))
822+
.thenThrow(
823+
PlatformException(
824+
code: '',
825+
details: NSErrorData(
826+
code: 0,
827+
domain: 'domain',
828+
localizedDescription: 'desc',
829+
),
830+
),
831+
);
832+
expect(
833+
webView.evaluateJavaScript('gogo'),
834+
throwsA(
835+
isA<PlatformException>().having(
836+
(PlatformException exception) => exception.details,
837+
'details',
838+
isA<NSError>(),
839+
),
840+
),
841+
);
842+
});
818843
});
819844

820845
group('WKUIDelegate', () {

packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,42 @@ void main() {
634634
);
635635
});
636636

637+
testWidgets('evaluateJavascript with bool return value',
638+
(WidgetTester tester) async {
639+
await buildWidget(tester);
640+
641+
when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer(
642+
(_) => Future<Object?>.value(true),
643+
);
644+
// The legacy implementation of webview_flutter_wkwebview would convert
645+
// objects to strings before returning them to Dart. This verifies bool
646+
// is represented the way it is in Objective-C.
647+
// `NSNumber.description` converts bool values to a 1 or 0.
648+
expect(
649+
testController.evaluateJavascript('runJavaScript'),
650+
completion('1'),
651+
);
652+
});
653+
654+
testWidgets('evaluateJavascript with double return value',
655+
(WidgetTester tester) async {
656+
await buildWidget(tester);
657+
658+
when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer(
659+
(_) => Future<Object?>.value(1.0),
660+
);
661+
// The legacy implementation of webview_flutter_wkwebview would convert
662+
// objects to strings before returning them to Dart. This verifies
663+
// double is represented the way it is in Objective-C. If a double
664+
// doesn't contain any decimal values, it gets truncated to an int.
665+
// This should be happenning because NSNumber convertes float values
666+
// with no decimals to an int when using `NSNumber.description`.
667+
expect(
668+
testController.evaluateJavascript('runJavaScript'),
669+
completion('1'),
670+
);
671+
});
672+
637673
testWidgets('evaluateJavascript with list return value',
638674
(WidgetTester tester) async {
639675
await buildWidget(tester);

0 commit comments

Comments
 (0)