Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit f1343c2

Browse files
committed
Implementation of loadHtmlString for WKWebView.
Adds native implementation for the `loadHtmlString` method channel call to the webview_flutter_wkwebview package.
1 parent c1dc83a commit f1343c2

File tree

6 files changed

+201
-39
lines changed

6 files changed

+201
-39
lines changed

packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 46;
6+
objectVersion = 50;
77
objects = {
88

99
/* Begin PBXBuildFile section */
@@ -274,7 +274,7 @@
274274
isa = PBXProject;
275275
attributes = {
276276
DefaultBuildSystemTypeForWorkspace = Original;
277-
LastUpgradeCheck = 1030;
277+
LastUpgradeCheck = 1300;
278278
ORGANIZATIONNAME = "The Flutter Authors";
279279
TargetAttributes = {
280280
68BDCAE823C3F7CB00D9C032 = {

packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1030"
3+
LastUpgradeVersion = "1300"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

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

Lines changed: 139 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,15 @@ - (void)testLoadFileSucceeds {
112112
allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]);
113113
}
114114

115-
- (void)testLoadFileFailsWithNilPath {
116-
XCTestExpectation *resultExpectation =
117-
[self expectationWithDescription:@"Should return failed result over the method channel."];
115+
- (void)testLoadFileFailsWithInvalidPath {
116+
NSArray *resultExpectations = @[
117+
[self expectationWithDescription:@"Should return failed result when argument is nil."],
118+
[self expectationWithDescription:
119+
@"Should return failed result when argument is not of type NSString*."],
120+
[self expectationWithDescription:
121+
@"Should return failed result when argument is an empty string."],
122+
];
123+
118124
FLTWebViewController *controller =
119125
[[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
120126
viewIdentifier:1
@@ -124,46 +130,64 @@ - (void)testLoadFileFailsWithNilPath {
124130
controller.webView = mockWebView;
125131
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:nil]
126132
result:^(id _Nullable result) {
127-
XCTAssertTrue([result class] == [FlutterError class]);
128-
FlutterError *errorResult = result;
129-
XCTAssertEqualObjects(errorResult.code, @"loadFile_failed");
130-
XCTAssertEqualObjects(errorResult.message, @"Failed parsing file path.");
131-
XCTAssertEqualObjects(errorResult.details, @"Argument is nil.");
132-
[resultExpectation fulfill];
133+
FlutterError *expected =
134+
[FlutterError errorWithCode:@"loadFile_failed"
135+
message:@"Failed parsing file path."
136+
details:@"Argument is nil."];
137+
[FLTWebViewTests assertFlutterError:result withExpected:expected];
138+
[resultExpectations[0] fulfill];
139+
}];
140+
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:@(10)]
141+
result:^(id _Nullable result) {
142+
FlutterError *expected =
143+
[FlutterError errorWithCode:@"loadFile_failed"
144+
message:@"Failed parsing file path."
145+
details:@"Argument is not of type NSString."];
146+
[FLTWebViewTests assertFlutterError:result withExpected:expected];
147+
[resultExpectations[1] fulfill];
148+
}];
149+
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:@""]
150+
result:^(id _Nullable result) {
151+
FlutterError *expected =
152+
[FlutterError errorWithCode:@"loadFile_failed"
153+
message:@"Failed parsing file path."
154+
details:@"Argument contains an empty string."];
155+
[FLTWebViewTests assertFlutterError:result withExpected:expected];
156+
[resultExpectations[2] fulfill];
133157
}];
134158

135-
[self waitForExpectations:@[ resultExpectation ] timeout:1.0];
159+
[self waitForExpectations:resultExpectations timeout:1.0];
136160
OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]);
137161
}
138162

139-
- (void)testLoadFileFailsWithNonStringPath {
163+
- (void)testLoadFileSucceedsWithBaseUrl {
164+
NSURL *baseUrl = [NSURL URLWithString:@"https://flutter.dev"];
140165
XCTestExpectation *resultExpectation =
141-
[self expectationWithDescription:@"Should return failed result over the method channel."];
166+
[self expectationWithDescription:@"Should return successful result over the method channel."];
142167
FLTWebViewController *controller =
143168
[[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
144169
viewIdentifier:1
145170
arguments:nil
146171
binaryMessenger:self.mockBinaryMessenger];
147172
FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class);
148173
controller.webView = mockWebView;
149-
[controller
150-
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:@(10)]
151-
result:^(id _Nullable result) {
152-
XCTAssertTrue([result class] == [FlutterError class]);
153-
FlutterError *errorResult = result;
154-
XCTAssertEqualObjects(errorResult.code, @"loadFile_failed");
155-
XCTAssertEqualObjects(errorResult.message, @"Failed parsing file path.");
156-
XCTAssertEqualObjects(errorResult.details, @"Argument is not of type NSString.");
157-
[resultExpectation fulfill];
158-
}];
174+
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString"
175+
arguments:@{
176+
@"html" : @"some HTML string",
177+
@"baseUrl" : @"https://flutter.dev"
178+
}]
179+
result:^(id _Nullable result) {
180+
XCTAssertNil(result);
181+
[resultExpectation fulfill];
182+
}];
159183

160-
[self waitForExpectations:@[ resultExpectation ] timeout:1.0];
161-
OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]);
184+
[self waitForExpectations:@[ resultExpectation ] timeout:30.0];
185+
OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:baseUrl]);
162186
}
163187

164-
- (void)testLoadFileFailsWithEmptyPath {
188+
- (void)testLoadFileSucceedsWithoutBaseUrl {
165189
XCTestExpectation *resultExpectation =
166-
[self expectationWithDescription:@"Should return failed result over the method channel."];
190+
[self expectationWithDescription:@"Should return successful result over the method channel."];
167191
FLTWebViewController *controller =
168192
[[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
169193
viewIdentifier:1
@@ -172,18 +196,91 @@ - (void)testLoadFileFailsWithEmptyPath {
172196
FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class);
173197
controller.webView = mockWebView;
174198
[controller
175-
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:@""]
199+
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString"
200+
arguments:@{@"html" : @"some HTML string"}]
176201
result:^(id _Nullable result) {
177-
XCTAssertTrue([result class] == [FlutterError class]);
178-
FlutterError *errorResult = result;
179-
XCTAssertEqualObjects(errorResult.code, @"loadFile_failed");
180-
XCTAssertEqualObjects(errorResult.message, @"Failed parsing file path.");
181-
XCTAssertEqualObjects(errorResult.details, @"Argument contains an empty string.");
202+
XCTAssertNil(result);
182203
[resultExpectation fulfill];
183204
}];
184205

185-
[self waitForExpectations:@[ resultExpectation ] timeout:1.0];
186-
OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]);
206+
[self waitForExpectations:@[ resultExpectation ] timeout:30.0];
207+
OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:nil]);
208+
}
209+
210+
- (void)testLoadHtmlStringFailsWithInvalidArgument {
211+
NSArray *resultExpectations = @[
212+
[self expectationWithDescription:@"Should return failed result when argument is nil."],
213+
[self expectationWithDescription:
214+
@"Should return failed result when argument is not of type NSDictionary*."],
215+
[self expectationWithDescription:@"Should return failed result when HTML argument is nil."],
216+
[self expectationWithDescription:
217+
@"Should return failed result when HTML argument is not of type NSString*."],
218+
[self expectationWithDescription:
219+
@"Should return failed result when HTML argument is an empty string."],
220+
];
221+
222+
FLTWebViewController *controller =
223+
[[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
224+
viewIdentifier:1
225+
arguments:nil
226+
binaryMessenger:self.mockBinaryMessenger];
227+
FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class);
228+
controller.webView = mockWebView;
229+
FlutterError *expected = [FlutterError
230+
errorWithCode:@"loadHtmlString_failed"
231+
message:@"Failed parsing arguments."
232+
details:@"Arguments should be a dictionary containing at least a 'html' element and "
233+
@"optionally a 'baseUrl' argument. For example: `@{ @\"html\": @\"some html "
234+
@"code\", @\"baseUrl\": @\"https://flutter.dev\" }`"];
235+
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString"
236+
arguments:nil]
237+
result:^(id _Nullable result) {
238+
[FLTWebViewTests assertFlutterError:result withExpected:expected];
239+
[resultExpectations[0] fulfill];
240+
}];
241+
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString"
242+
arguments:@""]
243+
result:^(id _Nullable result) {
244+
[FLTWebViewTests assertFlutterError:result withExpected:expected];
245+
[resultExpectations[1] fulfill];
246+
}];
247+
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString"
248+
arguments:@{}]
249+
result:^(id _Nullable result) {
250+
FlutterError *expected =
251+
[FlutterError errorWithCode:@"loadHtmlString_failed"
252+
message:@"Failed parsing HTML string argument."
253+
details:@"Argument is nil."];
254+
[FLTWebViewTests assertFlutterError:result withExpected:expected];
255+
[resultExpectations[2] fulfill];
256+
}];
257+
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString"
258+
arguments:@{
259+
@"html" : @(42),
260+
}]
261+
result:^(id _Nullable result) {
262+
FlutterError *expected =
263+
[FlutterError errorWithCode:@"loadHtmlString_failed"
264+
message:@"Failed parsing HTML string argument."
265+
details:@"Argument is not of type NSString."];
266+
[FLTWebViewTests assertFlutterError:result withExpected:expected];
267+
[resultExpectations[3] fulfill];
268+
}];
269+
[controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString"
270+
arguments:@{
271+
@"html" : @"",
272+
}]
273+
result:^(id _Nullable result) {
274+
FlutterError *expected =
275+
[FlutterError errorWithCode:@"loadHtmlString_failed"
276+
message:@"Failed parsing HTML string argument."
277+
details:@"Argument contains an empty string."];
278+
[FLTWebViewTests assertFlutterError:result withExpected:expected];
279+
[resultExpectations[4] fulfill];
280+
}];
281+
282+
[self waitForExpectations:resultExpectations timeout:1.0];
283+
OCMReject([mockWebView loadHTMLString:[OCMArg any] baseURL:[OCMArg any]]);
187284
}
188285

189286
- (void)testRunJavascriptFailsForNullString {
@@ -399,4 +496,12 @@ - (void)testRunJavascriptReturningResultReturnsErrorResultForWKError {
399496
[self waitForExpectationsWithTimeout:30.0 handler:nil];
400497
}
401498

499+
+ (void)assertFlutterError:(id)actual withExpected:(FlutterError *)expected {
500+
XCTAssertTrue([actual class] == [FlutterError class]);
501+
FlutterError *errorResult = actual;
502+
XCTAssertEqualObjects(errorResult.code, expected.code);
503+
XCTAssertEqualObjects(errorResult.message, expected.message);
504+
XCTAssertEqualObjects(errorResult.details, expected.details);
505+
}
506+
402507
@end

packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ const String kLocalFileExamplePage = '''
4040
<!DOCTYPE html>
4141
<html lang="en">
4242
<head>
43-
<title>Local demo page</title>
43+
<title>Load file or HTML string example</title>
4444
</head>
4545
<body>
4646
4747
<h1>Local demo page</h1>
48-
<p>This is a local page used to demonstrate how to load a local file using the <a href="https://pub.dev/packages/webview_flutter">Flutter webview</a> plugin.</p>
48+
<p>This is an example page used to demonstrate how to load a local file or HTML string using the <a href="https://pub.dev/packages/webview_flutter">Flutter webview</a> plugin.</p>
4949
5050
</body>
5151
</html>
@@ -139,6 +139,7 @@ enum _MenuOptions {
139139
clearCache,
140140
navigationDelegate,
141141
loadLocalFile,
142+
loadHtmlString,
142143
}
143144

144145
class _SampleMenu extends StatelessWidget {
@@ -179,6 +180,9 @@ class _SampleMenu extends StatelessWidget {
179180
case _MenuOptions.loadLocalFile:
180181
_onLoadLocalFileExample(controller.data!, context);
181182
break;
183+
case _MenuOptions.loadHtmlString:
184+
_onLoadHtmlStringExample(controller.data!, context);
185+
break;
182186
}
183187
},
184188
itemBuilder: (BuildContext context) => <PopupMenuItem<_MenuOptions>>[
@@ -211,6 +215,10 @@ class _SampleMenu extends StatelessWidget {
211215
value: _MenuOptions.navigationDelegate,
212216
child: Text('Navigation Delegate example'),
213217
),
218+
const PopupMenuItem<_MenuOptions>(
219+
value: _MenuOptions.loadHtmlString,
220+
child: Text('Load HTML string'),
221+
),
214222
const PopupMenuItem<_MenuOptions>(
215223
value: _MenuOptions.loadLocalFile,
216224
child: Text('Load local file'),
@@ -292,6 +300,11 @@ class _SampleMenu extends StatelessWidget {
292300
await controller.loadFile(pathToIndex);
293301
}
294302

303+
void _onLoadHtmlStringExample(
304+
WebViewController controller, BuildContext context) async {
305+
await controller.loadHtmlString(kLocalFileExamplePage);
306+
}
307+
295308
Widget _getCookieList(String cookies) {
296309
if (cookies == null || cookies == '""') {
297310
return Container();

packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,21 @@ class WebViewController {
319319
return _webViewPlatformController.loadFile(absoluteFilePath);
320320
}
321321

322+
/// Loads the supplied HTML string.
323+
///
324+
/// The [baseUrl] parameter is used when resolving relative URLs within the
325+
/// HTML string.
326+
Future<void> loadHtmlString(
327+
String html, {
328+
String? baseUrl,
329+
}) {
330+
assert(html != null || html.isNotEmpty);
331+
return _webViewPlatformController.loadHtmlString(
332+
html,
333+
baseUrl: baseUrl,
334+
);
335+
}
336+
322337
/// Loads the specified URL.
323338
///
324339
/// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
137137
[self onUpdateSettings:call result:result];
138138
} else if ([[call method] isEqualToString:@"loadFile"]) {
139139
[self onLoadFile:call result:result];
140+
} else if ([[call method] isEqualToString:@"loadHtmlString"]) {
141+
[self onLoadHtmlString:call result:result];
140142
} else if ([[call method] isEqualToString:@"loadUrl"]) {
141143
[self onLoadUrl:call result:result];
142144
} else if ([[call method] isEqualToString:@"canGoBack"]) {
@@ -203,6 +205,33 @@ - (void)onLoadFile:(FlutterMethodCall*)call result:(FlutterResult)result {
203205
result(nil);
204206
}
205207

208+
- (void)onLoadHtmlString:(FlutterMethodCall*)call result:(FlutterResult)result {
209+
NSDictionary* arguments = [call arguments];
210+
if (!arguments || ![arguments isKindOfClass:NSDictionary.class]) {
211+
result([FlutterError
212+
errorWithCode:@"loadHtmlString_failed"
213+
message:@"Failed parsing arguments."
214+
details:@"Arguments should be a dictionary containing at least a 'html' element and "
215+
@"optionally a 'baseUrl' argument. For example: `@{ @\"html\": @\"some html "
216+
@"code\", @\"baseUrl\": @\"https://flutter.dev\" }`"]);
217+
return;
218+
}
219+
220+
NSString* htmlString = [call arguments][@"html"];
221+
NSString* baseUrl =
222+
[call arguments][@"baseUrl"] == [NSNull null] ? nil : [call arguments][@"baseUrl"];
223+
NSString* error = nil;
224+
if (![FLTWebViewController isValidStringArgument:htmlString withErrorMessage:&error]) {
225+
result([FlutterError errorWithCode:@"loadHtmlString_failed"
226+
message:@"Failed parsing HTML string argument."
227+
details:error]);
228+
return;
229+
}
230+
231+
[_webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:baseUrl]];
232+
result(nil);
233+
}
234+
206235
- (void)onLoadUrl:(FlutterMethodCall*)call result:(FlutterResult)result {
207236
if (![self loadRequest:[call arguments]]) {
208237
result([FlutterError

0 commit comments

Comments
 (0)