diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 979b0f797968..913b0a44b7ea 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.9.0 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +* Replaces platform implementation with WebKit API built with pigeon. ## 2.8.1 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index 4ed8769ea518..1efee8f844ef 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,8 +8,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */; }; - 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; }; @@ -32,7 +30,6 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; D7587C3652F6906210B3AE88 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 17781D9462A1AEA7C99F8E45 /* libPods-RunnerTests.a */; }; DAF0E91266956134538CC667 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 572FFC2B2BA326B420B22679 /* libPods-Runner.a */; }; - E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */; }; F7151F77266057800028CB91 /* FLTWebViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F76266057800028CB91 /* FLTWebViewUITests.m */; }; /* End PBXBuildFile section */ @@ -74,10 +71,8 @@ 39B2BDAA45DC06EAB8A6C4E7 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 572FFC2B2BA326B420B22679 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWKNavigationDelegateTests.m; sourceTree = ""; }; 68BDCAE923C3F7CB00D9C032 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 68BDCAED23C3F7CB00D9C032 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewTests.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -104,7 +99,6 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B89AA31A64040E4A2F1E0CAF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCookieManagerTests.m; sourceTree = ""; }; F7151F74266057800028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F7151F76266057800028CB91 /* FLTWebViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewUITests.m; sourceTree = ""; }; F7151F78266057800028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -150,10 +144,7 @@ 68BDCAEA23C3F7CB00D9C032 /* RunnerTests */ = { isa = PBXGroup; children = ( - 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */, - 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */, 68BDCAED23C3F7CB00D9C032 /* Info.plist */, - E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */, 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */, 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */, 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */, @@ -471,16 +462,13 @@ 8FB79B672820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m in Sources */, 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */, 8FB79B73282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m in Sources */, - 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, 8FB79B7928209D1300C101D3 /* FWFUserContentControllerHostApiTests.m in Sources */, 8FB79B6B28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m in Sources */, 8FB79B8F2820BAB300C101D3 /* FWFScrollViewHostApiTests.m in Sources */, 8FB79B912820BAC700C101D3 /* FWFUIViewHostApiTests.m in Sources */, - 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */, 8FB79B6D2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m in Sources */, 8FB79B832820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m in Sources */, - E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, 8FB79B6928204E8700C101D3 /* FWFPreferencesHostApiTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m deleted file mode 100644 index 837c0d892b64..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2013 The Flutter 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 Flutter; -@import XCTest; -@import webview_flutter_wkwebview; -@import webview_flutter_wkwebview.Test; - -// OCMock library doesn't generate a valid modulemap. -#import - -@interface FLTCookieManagerTests : XCTestCase - -@end - -@implementation FLTCookieManagerTests - -- (void)setUp { - [super setUp]; -} - -- (void)testSetCookieForResultSetsCookieAndReturnsResultOnIOS11 { - if (@available(iOS 11.0, *)) { - // Setup - XCTestExpectation *resultExpectation = [self - expectationWithDescription:@"Should return success result when setting cookie completes."]; - [FLTCookieManager.instance setHttpCookieStore:OCMClassMock(WKHTTPCookieStore.class)]; - NSDictionary *arguments = @{ - @"name" : @"foo", - @"value" : @"bar", - @"domain" : @"flutter.dev", - @"path" : @"/", - }; - NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : arguments[@"name"], - NSHTTPCookieValue : arguments[@"value"], - NSHTTPCookieDomain : arguments[@"domain"], - NSHTTPCookiePath : arguments[@"path"], - }]; - [OCMStub([FLTCookieManager.instance.httpCookieStore setCookie:[OCMArg isEqual:cookie] - completionHandler:[OCMArg any]]) - andDo:^(NSInvocation *invocation) { - void (^setCookieCompletionHandler)(void); - [invocation getArgument:&setCookieCompletionHandler atIndex:3]; - setCookieCompletionHandler(); - }]; - // Run - [[FLTCookieManager instance] - setCookieForResult:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - } - arguments:arguments]; - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; - } -} - -- (void)testSetCookieForDataSetsCookieOnIOS11 { - if (@available(iOS 11.0, *)) { - // Setup - WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock(WKHTTPCookieStore.class); - [FLTCookieManager.instance setHttpCookieStore:mockHttpCookieStore]; - NSDictionary *cookieData = @{ - @"name" : @"foo", - @"value" : @"bar", - @"domain" : @"flutter.dev", - @"path" : @"/", - }; - // Run - [[FLTCookieManager instance] setCookieForData:cookieData]; - // Verify - NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : cookieData[@"name"], - NSHTTPCookieValue : cookieData[@"value"], - NSHTTPCookieDomain : cookieData[@"domain"], - NSHTTPCookiePath : cookieData[@"path"], - }]; - OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie] - completionHandler:[OCMArg any]]); - } -} - -- (void)testSetCookiesForDataSetsCookiesOnIOS11 { - if (@available(iOS 11.0, *)) { - // Setup - WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock(WKHTTPCookieStore.class); - [FLTCookieManager.instance setHttpCookieStore:mockHttpCookieStore]; - NSArray *cookieDatas = @[ - @{ - @"name" : @"foo1", - @"value" : @"bar1", - @"domain" : @"flutter.dev", - @"path" : @"/", - }, - @{ - @"name" : @"foo2", - @"value" : @"bar2", - @"domain" : @"flutter2.dev", - @"path" : @"/2", - } - ]; - // Run - [[FLTCookieManager instance] setCookiesForData:cookieDatas]; - // Verify - NSHTTPCookie *cookie1 = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : cookieDatas[0][@"name"], - NSHTTPCookieValue : cookieDatas[0][@"value"], - NSHTTPCookieDomain : cookieDatas[0][@"domain"], - NSHTTPCookiePath : cookieDatas[0][@"path"], - }]; - - OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie1] - completionHandler:[OCMArg any]]); - NSHTTPCookie *cookie2 = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : cookieDatas[1][@"name"], - NSHTTPCookieValue : cookieDatas[1][@"value"], - NSHTTPCookieDomain : cookieDatas[1][@"domain"], - NSHTTPCookiePath : cookieDatas[1][@"path"], - }]; - OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie2] - completionHandler:[OCMArg any]]); - } -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m deleted file mode 100644 index d39a9f203d70..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2013 The Flutter 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 Flutter; -@import XCTest; -@import webview_flutter_wkwebview; -@import webview_flutter_wkwebview.Test; - -// OCMock library doesn't generate a valid modulemap. -#import - -@interface FLTWKNavigationDelegateTests : XCTestCase - -@property(strong, nonatomic) FlutterMethodChannel *mockMethodChannel; -@property(strong, nonatomic) FLTWKNavigationDelegate *navigationDelegate; -@property(strong, nonatomic) WKNavigation *navigation; - -@end - -@implementation FLTWKNavigationDelegateTests - -NSString *const zoomDisablingJavascript = - @"var meta = document.createElement('meta');" - @"meta.name = 'viewport';" - @"meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - @"user-scalable=no';" - @"var head = document.getElementsByTagName('head')[0];head.appendChild(meta);"; - -- (void)setUp { - self.mockMethodChannel = OCMClassMock(FlutterMethodChannel.class); - self.navigationDelegate = - [[FLTWKNavigationDelegate alloc] initWithChannel:self.mockMethodChannel]; -} - -- (void)testWebViewWebContentProcessDidTerminateCallsRecourseErrorChannel { - if (@available(iOS 9.0, *)) { - // `webViewWebContentProcessDidTerminate` is only available on iOS 9.0 and above. - WKWebView *webview = OCMClassMock(WKWebView.class); - [self.navigationDelegate webViewWebContentProcessDidTerminate:webview]; - OCMVerify([self.mockMethodChannel - invokeMethod:@"onWebResourceError" - arguments:[OCMArg checkWithBlock:^BOOL(NSDictionary *args) { - XCTAssertEqualObjects(args[@"errorType"], @"webContentProcessTerminated"); - return true; - }]]); - } -} - -- (void)testWebViewWebEvaluateJavaScriptSourceIsCorrectWhenShouldEnableZoomIsFalse { - WKWebView *webview = OCMClassMock(WKWebView.class); - WKNavigation *navigation = OCMClassMock(WKNavigation.class); - NSURL *testUrl = [[NSURL alloc] initWithString:@"www.example.com"]; - OCMStub([webview URL]).andReturn(testUrl); - - self.navigationDelegate.shouldEnableZoom = false; - [self.navigationDelegate webView:webview didFinishNavigation:navigation]; - OCMVerify([webview evaluateJavaScript:zoomDisablingJavascript completionHandler:nil]); -} - -- (void)testWebViewWebEvaluateJavaScriptShouldNotBeCalledWhenShouldEnableZoomIsTrue { - WKWebView *webview = OCMClassMock(WKWebView.class); - WKNavigation *navigation = OCMClassMock(WKNavigation.class); - NSURL *testUrl = [[NSURL alloc] initWithString:@"www.example.com"]; - OCMStub([webview URL]).andReturn(testUrl); - - self.navigationDelegate.shouldEnableZoom = true; - - OCMReject([webview evaluateJavaScript:zoomDisablingJavascript completionHandler:nil]); - [self.navigationDelegate webView:webview didFinishNavigation:navigation]; -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m deleted file mode 100644 index 976b9c46579a..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m +++ /dev/null @@ -1,833 +0,0 @@ -// Copyright 2013 The Flutter 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 Flutter; -@import XCTest; -@import webview_flutter_wkwebview; -@import webview_flutter_wkwebview.Test; - -// OCMock library doesn't generate a valid modulemap. -#import - -static bool feq(CGFloat a, CGFloat b) { return fabs(b - a) < FLT_EPSILON; } - -@interface FLTWebViewTests : XCTestCase - -@property(strong, nonatomic) NSObject *mockBinaryMessenger; - -@property(strong, nonatomic) FLTCookieManager *mockCookieManager; - -@end - -@implementation FLTWebViewTests - -- (void)setUp { - [super setUp]; - self.mockBinaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); - self.mockCookieManager = OCMClassMock(FLTCookieManager.class); -} - -- (void)testCanInitFLTWebViewController { - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTAssertNotNil(controller); -} - -- (void)testCanInitFLTWebViewFactory { - FLTWebViewFactory *factory = [[FLTWebViewFactory alloc] initWithMessenger:self.mockBinaryMessenger - cookieManager:self.mockCookieManager]; - XCTAssertNotNil(factory); -} - -- (void)webViewContentInsetBehaviorShouldBeNeverOnIOS11 { - if (@available(iOS 11, *)) { - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - UIView *view = controller.view; - XCTAssertTrue([view isKindOfClass:WKWebView.class]); - WKWebView *webView = (WKWebView *)view; - XCTAssertEqual(webView.scrollView.contentInsetAdjustmentBehavior, - UIScrollViewContentInsetAdjustmentNever); - } -} - -- (void)testWebViewScrollIndicatorAticautomaticallyAdjustsScrollIndicatorInsetsShouldbeNoOnIOS13 { - if (@available(iOS 13, *)) { - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - UIView *view = controller.view; - XCTAssertTrue([view isKindOfClass:WKWebView.class]); - WKWebView *webView = (WKWebView *)view; - XCTAssertFalse(webView.scrollView.automaticallyAdjustsScrollIndicatorInsets); - } -} - -- (void)testContentInsetsSumAlwaysZeroAfterSetFrame { - FLTWKWebView *webView = [[FLTWKWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 400)]; - webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 300, 0); - XCTAssertFalse(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); - webView.frame = CGRectMake(0, 0, 300, 200); - XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); - XCTAssertTrue(CGRectEqualToRect(webView.frame, CGRectMake(0, 0, 300, 200))); - - if (@available(iOS 11, *)) { - // After iOS 11, we need to make sure the contentInset compensates the adjustedContentInset. - UIScrollView *partialMockScrollView = OCMPartialMock(webView.scrollView); - UIEdgeInsets insetToAdjust = UIEdgeInsetsMake(0, 0, 300, 0); - OCMStub(partialMockScrollView.adjustedContentInset).andReturn(insetToAdjust); - XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); - webView.frame = CGRectMake(0, 0, 300, 100); - XCTAssertTrue(feq(webView.scrollView.contentInset.bottom, -insetToAdjust.bottom)); - XCTAssertTrue(CGRectEqualToRect(webView.frame, CGRectMake(0, 0, 300, 100))); - } -} - -- (void)testLoadFileSucceeds { - NSString *testFilePath = @"/assets/file.html"; - NSURL *url = [NSURL fileURLWithPath:testFilePath isDirectory:NO]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" - arguments:testFilePath] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:30.0]; - OCMVerify([mockWebView loadFileURL:url - allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]); -} - -- (void)testLoadFileFailsWithInvalidPath { - NSArray *resultExpectations = @[ - [self expectationWithDescription:@"Should return failed result when argument is nil."], - [self expectationWithDescription: - @"Should return failed result when argument is not of type NSString*."], - [self expectationWithDescription: - @"Should return failed result when argument is an empty string."], - ]; - - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:nil] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:@"Argument is nil."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[0] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:@(10)] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:@"Argument is not of type NSString."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[1] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:@""] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:@"Argument contains an empty string."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[2] fulfill]; - }]; - - [self waitForExpectations:resultExpectations timeout:1.0]; - OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); -} - -- (void)testLoadFlutterAssetSucceeds { - NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); - NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; - NSURL *url = [NSURL URLWithString:[@"file:///" stringByAppendingString:filePath]]; - [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] - withExtension:@"html"]) andReturn:(url)]; - - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:@"assets/file.html"] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; - OCMVerify([mockWebView loadFileURL:url - allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]); -} - -- (void)testLoadFlutterAssetFailsWithInvalidKey { - NSArray *resultExpectations = @[ - [self expectationWithDescription:@"Should return failed result when argument is nil."], - [self expectationWithDescription: - @"Should return failed result when argument is not of type NSString*."], - [self expectationWithDescription: - @"Should return failed result when argument is an empty string."], - ]; - - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:nil] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Supplied asset key is not valid." - details:@"Argument is nil."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[0] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:@(10)] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Supplied asset key is not valid." - details:@"Argument is not of type NSString."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[1] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:@""] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Supplied asset key is not valid." - details:@"Argument contains an empty string."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[2] fulfill]; - }]; - - [self waitForExpectations:resultExpectations timeout:1.0]; - OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); -} - -- (void)testLoadFlutterAssetFailsWithParsingError { - NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); - NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; - [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] - withExtension:@"html"]) andReturn:(nil)]; - - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return failed result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:@"assets/file.html"] - result:^(id _Nullable result) { - FlutterError *expected = [FlutterError - errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Failed parsing file path for supplied key." - details:[NSString - stringWithFormat: - @"Failed to convert path '%@' into NSURL for key '%@'.", - filePath, @"assets/file.html"]]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; - OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); -} - -- (void)testLoadHtmlStringSucceedsWithBaseUrl { - NSURL *baseUrl = [NSURL URLWithString:@"https://flutter.dev"]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{ - @"html" : @"some HTML string", - @"baseUrl" : @"https://flutter.dev" - }] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:30.0]; - OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:baseUrl]); -} - -- (void)testLoadHtmlStringSucceedsWithoutBaseUrl { - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{@"html" : @"some HTML string"}] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:30.0]; - OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:nil]); -} - -- (void)testLoadHtmlStringFailsWithInvalidArgument { - NSArray *resultExpectations = @[ - [self expectationWithDescription:@"Should return failed result when argument is nil."], - [self expectationWithDescription: - @"Should return failed result when argument is not of type NSDictionary*."], - [self expectationWithDescription:@"Should return failed result when HTML argument is nil."], - [self expectationWithDescription: - @"Should return failed result when HTML argument is not of type NSString*."], - [self expectationWithDescription: - @"Should return failed result when HTML argument is an empty string."], - ]; - - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - FlutterError *expected = [FlutterError - errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing arguments." - details:@"Arguments should be a dictionary containing at least a 'html' element and " - @"optionally a 'baseUrl' argument. For example: `@{ @\"html\": @\"some html " - @"code\", @\"baseUrl\": @\"https://flutter.dev\" }`"]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:nil] - result:^(id _Nullable result) { - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[0] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@""] - result:^(id _Nullable result) { - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[1] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{}] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing HTML string argument." - details:@"Argument is nil."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[2] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{ - @"html" : @(42), - }] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing HTML string argument." - details:@"Argument is not of type NSString."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[3] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{ - @"html" : @"", - }] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing HTML string argument." - details:@"Argument contains an empty string."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[4] fulfill]; - }]; - - [self waitForExpectations:resultExpectations timeout:1.0]; - OCMReject([mockWebView loadHTMLString:[OCMArg any] baseURL:[OCMArg any]]); -} - -- (void)testRunJavascriptFailsForNullString { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result over the method channel."]; - - // Run - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript" - arguments:nil] - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptRunsStringWithSuccessResult { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(@"RESULT", nil); - }]; - controller.webView = mockView; - - // Run - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturnsErrorResultForWKError { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result over the method channel."]; - NSError *testError = - [NSError errorWithDomain:@"" - // Any error code but WKErrorJavascriptResultTypeIsUnsupported - code:WKErrorJavaScriptResultTypeIsUnsupported + 1 - userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(nil, testError); - }]; - controller.webView = mockView; - - // Run - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturnsSuccessForWKErrorJavascriptResultTypeIsUnsupported { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return nil result over the method channel."]; - NSError *testError = [NSError errorWithDomain:@"" - code:WKErrorJavaScriptResultTypeIsUnsupported - userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(nil, testError); - }]; - controller.webView = mockView; - - // Run - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturningResultFailsForNullString { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result over the method channel."]; - - // Run - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult" - arguments:nil] - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturningResultRunsStringWithSuccessResult { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(@"RESULT", nil); - }]; - controller.webView = mockView; - - // Run - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertTrue([@"RESULT" isEqualToString:result]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturningResultReturnsErrorResultForWKError { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result over the method channel."]; - NSError *testError = [NSError errorWithDomain:@"" - code:5 - userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(nil, testError); - }]; - controller.webView = mockView; - - // Run - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -+ (void)assertFlutterError:(id)actual withExpected:(FlutterError *)expected { - XCTAssertTrue([actual class] == [FlutterError class]); - FlutterError *errorResult = actual; - XCTAssertEqualObjects(errorResult.code, expected.code); - XCTAssertEqualObjects(errorResult.message, expected.message); - XCTAssertEqualObjects(errorResult.details, expected.details); -} - -- (void)testBuildNSURLRequestReturnsNilForNonDictionaryValue { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : @"Non Dictionary Value"}]; - - // Verify - XCTAssertNil(request); -} - -- (void)testBuildNSURLRequestReturnsNilForMissingURI { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : @{}}]; - - // Verify - XCTAssertNil(request); -} - -- (void)testBuildNSURLRequestReturnsNilForInvalidURI { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSDictionary *requestData = @{@"uri" : @"invalid uri"}; - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : requestData}]; - - // Verify - XCTAssertNil(request); -} - -- (void)testBuildNSURLRequestBuildsNSMutableURLRequestWithOptionalParameters { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSDictionary *requestData = @{ - @"uri" : @"https://flutter.dev", - @"method" : @"POST", - @"headers" : @{@"Foo" : @"Bar"}, - @"body" : [FlutterStandardTypedData - typedDataWithBytes:[@"Test Data" dataUsingEncoding:NSUTF8StringEncoding]], - }; - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : requestData}]; - - // Verify - XCTAssertNotNil(request); - XCTAssertEqualObjects(request.URL.absoluteString, @"https://flutter.dev"); - XCTAssertEqualObjects(request.HTTPMethod, @"POST"); - XCTAssertEqualObjects(request.allHTTPHeaderFields, @{@"Foo" : @"Bar"}); - XCTAssertEqualObjects(request.HTTPBody, [@"Test Data" dataUsingEncoding:NSUTF8StringEncoding]); -} - -- (void)testBuildNSURLRequestBuildsNSMutableURLRequestWithoutOptionalParameters { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSDictionary *requestData = @{ - @"uri" : @"https://flutter.dev", - }; - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : requestData}]; - - // Verify - XCTAssertNotNil(request); - XCTAssertEqualObjects(request.URL.absoluteString, @"https://flutter.dev"); - XCTAssertEqualObjects(request.HTTPMethod, @"GET"); - XCTAssertNil(request.allHTTPHeaderFields); - XCTAssertNil(request.HTTPBody); -} - -- (void)testOnLoadUrlReturnsErrorResultForInvalidRequest { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result when request cannot be built"]; - - // Run - FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"loadUrl" - arguments:@{}]; - [controller onLoadUrl:methodCall - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testOnLoadUrlLoadsRequestWithSuccessResult { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return nil"]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockView; - - // Run - FlutterMethodCall *methodCall = - [FlutterMethodCall methodCallWithMethodName:@"loadUrl" - arguments:@{@"url" : @"https://flutter.dev/"}]; - [controller onLoadUrl:methodCall - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - // Verify - OCMVerify([mockView loadRequest:[OCMArg any]]); - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testOnLoadRequestReturnsErrorResultForInvalidRequest { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result when request cannot be built"]; - - // Run - FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"loadRequest" - arguments:@{}]; - [controller onLoadRequest:methodCall - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testOnLoadRequestLoadsRequestWithSuccessResult { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return nil"]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockView; - - // Run - FlutterMethodCall *methodCall = [FlutterMethodCall - methodCallWithMethodName:@"loadRequest" - arguments:@{@"request" : @{@"uri" : @"https://flutter.dev/"}}]; - [controller onLoadRequest:methodCall - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - // Verify - OCMVerify([mockView loadRequest:[OCMArg any]]); - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testCreateWithFrameShouldSetCookiesOnIOS11 { - if (@available(iOS 11, *)) { - // Setup - FLTWebViewFactory *factory = - [[FLTWebViewFactory alloc] initWithMessenger:self.mockBinaryMessenger - cookieManager:self.mockCookieManager]; - NSArray *cookies = - @[ @{@"name" : @"foo", @"value" : @"bar", @"domain" : @"flutter.dev", @"path" : @"/"} ]; - // Run - [factory createWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:@{@"cookies" : cookies}]; - // Verify - OCMVerify([_mockCookieManager setCookiesForData:cookies]); - } -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h deleted file mode 100644 index b18e58b57cb3..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Flutter 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 -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTCookieManager : NSObject - -+ (FLTCookieManager *)instance; - -- (void)setCookiesForData:(NSArray *)cookies; - -- (void)setCookieForData:(NSDictionary *)cookie; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m deleted file mode 100644 index 39976d11153e..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2013 The Flutter 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 "FLTCookieManager.h" -#import "FLTCookieManager_Test.h" - -@implementation FLTCookieManager { - WKHTTPCookieStore *_httpCookieStore API_AVAILABLE(macos(10.13), ios(11.0)); -} - -+ (FLTCookieManager *)instance { - static FLTCookieManager *instance = nil; - if (instance == nil) { - instance = [[FLTCookieManager alloc] init]; - } - return instance; -} - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/cookie_manager" - binaryMessenger:[registrar messenger]]; - [registrar addMethodCallDelegate:[self instance] channel:channel]; -} - -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([[call method] isEqualToString:@"clearCookies"]) { - [self clearCookies:result]; - } else if ([[call method] isEqualToString:@"setCookie"]) { - [self setCookieForResult:result arguments:[call arguments]]; - } else { - result(FlutterMethodNotImplemented); - } -} - -- (void)clearCookies:(FlutterResult)result { - if (@available(iOS 9.0, *)) { - NSSet *websiteDataTypes = [NSSet setWithObject:WKWebsiteDataTypeCookies]; - WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; - - void (^deleteAndNotify)(NSArray *) = - ^(NSArray *cookies) { - BOOL hasCookies = cookies.count > 0; - [dataStore removeDataOfTypes:websiteDataTypes - forDataRecords:cookies - completionHandler:^{ - result(@(hasCookies)); - }]; - }; - - [dataStore fetchDataRecordsOfTypes:websiteDataTypes completionHandler:deleteAndNotify]; - } else { - // support for iOS8 tracked in https://github.com/flutter/flutter/issues/27624. - NSLog(@"Clearing cookies is not supported for Flutter WebViews prior to iOS 9."); - } -} - -- (void)setCookiesForData:(NSArray *)cookies { - for (id cookie in cookies) { - [self setCookieForData:cookie]; - } -} - -- (void)setCookieForData:(NSDictionary *)cookieData { - if (@available(iOS 11.0, *)) { - if (!_httpCookieStore) { - _httpCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; - } - NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : cookieData[@"name"], - NSHTTPCookieValue : cookieData[@"value"], - NSHTTPCookieDomain : cookieData[@"domain"], - NSHTTPCookiePath : cookieData[@"path"], - }]; - [_httpCookieStore setCookie:cookie - completionHandler:^{ - }]; - } else { - NSLog(@"Setting cookies is not supported for Flutter WebViews prior to iOS 11."); - } -} - -- (void)setCookieForResult:(FlutterResult)result arguments:(NSDictionary *)arguments { - if (@available(iOS 11.0, *)) { - if (!_httpCookieStore) { - _httpCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; - } - NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : arguments[@"name"], - NSHTTPCookieValue : arguments[@"value"], - NSHTTPCookieDomain : arguments[@"domain"], - NSHTTPCookiePath : arguments[@"path"], - }]; - [_httpCookieStore setCookie:cookie - completionHandler:^{ - result(nil); - }]; - } else { - NSLog(@"Setting cookies is not supported for Flutter WebViews prior to iOS 11."); - result(nil); - } -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h deleted file mode 100644 index fecec4932570..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This header is available in the Test module. Import via "@import webview_flutter_wkwebview.Test;" - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTCookieManager () - -@property(nonatomic, strong) - WKHTTPCookieStore *httpCookieStore API_AVAILABLE(macos(10.13), ios(11.0)); - -- (void)setCookieForResult:(FlutterResult)result arguments:(NSDictionary *)arguments; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.h deleted file mode 100644 index 6531931c4cf4..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2013 The Flutter 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 -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTWKNavigationDelegate : NSObject - -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel; - -/** - * Whether to delegate navigation decisions over the method channel. - */ -@property(nonatomic, assign) BOOL hasDartNavigationDelegate; - -/** - * Whether to allow zoom functionality on the WebView. - */ -@property(nonatomic, assign) BOOL shouldEnableZoom; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m deleted file mode 100644 index 125d3cabdcf1..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2013 The Flutter 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 "FLTWKNavigationDelegate.h" - -@implementation FLTWKNavigationDelegate { - FlutterMethodChannel *_methodChannel; -} - -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel { - self = [super init]; - if (self) { - _methodChannel = channel; - } - return self; -} - -#pragma mark - WKNavigationDelegate conformance - -- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { - [_methodChannel invokeMethod:@"onPageStarted" arguments:@{@"url" : webView.URL.absoluteString}]; -} - -- (void)webView:(WKWebView *)webView - decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction - decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - if (!self.hasDartNavigationDelegate) { - decisionHandler(WKNavigationActionPolicyAllow); - return; - } - NSDictionary *arguments = @{ - @"url" : navigationAction.request.URL.absoluteString, - @"isForMainFrame" : @(navigationAction.targetFrame.isMainFrame) - }; - [_methodChannel invokeMethod:@"navigationRequest" - arguments:arguments - result:^(id _Nullable result) { - if ([result isKindOfClass:[FlutterError class]]) { - NSLog(@"navigationRequest has unexpectedly completed with an error, " - @"allowing navigation."); - decisionHandler(WKNavigationActionPolicyAllow); - return; - } - if (result == FlutterMethodNotImplemented) { - NSLog(@"navigationRequest was unexepectedly not implemented: %@, " - @"allowing navigation.", - result); - decisionHandler(WKNavigationActionPolicyAllow); - return; - } - if (![result isKindOfClass:[NSNumber class]]) { - NSLog(@"navigationRequest unexpectedly returned a non boolean value: " - @"%@, allowing navigation.", - result); - decisionHandler(WKNavigationActionPolicyAllow); - return; - } - NSNumber *typedResult = result; - decisionHandler([typedResult boolValue] ? WKNavigationActionPolicyAllow - : WKNavigationActionPolicyCancel); - }]; -} - -- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { - if (!self.shouldEnableZoom) { - NSString *source = - @"var meta = document.createElement('meta');" - @"meta.name = 'viewport';" - @"meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - @"user-scalable=no';" - @"var head = document.getElementsByTagName('head')[0];head.appendChild(meta);"; - - [webView evaluateJavaScript:source completionHandler:nil]; - } - - [_methodChannel invokeMethod:@"onPageFinished" arguments:@{@"url" : webView.URL.absoluteString}]; -} - -+ (id)errorCodeToString:(NSUInteger)code { - switch (code) { - case WKErrorUnknown: - return @"unknown"; - case WKErrorWebContentProcessTerminated: - return @"webContentProcessTerminated"; - case WKErrorWebViewInvalidated: - return @"webViewInvalidated"; - case WKErrorJavaScriptExceptionOccurred: - return @"javaScriptExceptionOccurred"; - case WKErrorJavaScriptResultTypeIsUnsupported: - return @"javaScriptResultTypeIsUnsupported"; - } - - return [NSNull null]; -} - -- (void)onWebResourceError:(NSError *)error { - [_methodChannel invokeMethod:@"onWebResourceError" - arguments:@{ - @"errorCode" : @(error.code), - @"domain" : error.domain, - @"description" : error.description, - @"errorType" : [FLTWKNavigationDelegate errorCodeToString:error.code], - }]; -} - -- (void)webView:(WKWebView *)webView - didFailNavigation:(WKNavigation *)navigation - withError:(NSError *)error { - [self onWebResourceError:error]; -} - -- (void)webView:(WKWebView *)webView - didFailProvisionalNavigation:(WKNavigation *)navigation - withError:(NSError *)error { - [self onWebResourceError:error]; -} - -- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView { - NSError *contentProcessTerminatedError = - [[NSError alloc] initWithDomain:WKErrorDomain - code:WKErrorWebContentProcessTerminated - userInfo:nil]; - [self onWebResourceError:contentProcessTerminatedError]; -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.h deleted file mode 100644 index 96af4ef6c578..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2013 The Flutter 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 -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTWKProgressionDelegate : NSObject - -- (instancetype)initWithWebView:(WKWebView *)webView channel:(FlutterMethodChannel *)channel; - -- (void)stopObservingProgress:(WKWebView *)webView; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.m deleted file mode 100644 index 8e7af4649aa0..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.m +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013 The Flutter 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 "FLTWKProgressionDelegate.h" - -NSString *const FLTWKEstimatedProgressKeyPath = @"estimatedProgress"; - -@implementation FLTWKProgressionDelegate { - FlutterMethodChannel *_methodChannel; -} - -- (instancetype)initWithWebView:(WKWebView *)webView channel:(FlutterMethodChannel *)channel { - self = [super init]; - if (self) { - _methodChannel = channel; - [webView addObserver:self - forKeyPath:FLTWKEstimatedProgressKeyPath - options:NSKeyValueObservingOptionNew - context:nil]; - } - return self; -} - -- (void)stopObservingProgress:(WKWebView *)webView { - [webView removeObserver:self forKeyPath:FLTWKEstimatedProgressKeyPath]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context { - if ([keyPath isEqualToString:FLTWKEstimatedProgressKeyPath]) { - NSNumber *newValue = - change[NSKeyValueChangeNewKey] ?: 0; // newValue is anywhere between 0.0 and 1.0 - int newValueAsInt = [newValue floatValue] * 100; // Anywhere between 0 and 100 - [_methodChannel invokeMethod:@"onProgress" arguments:@{@"progress" : @(newValueAsInt)}]; - } -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m index a63d6a60b114..5795018b2043 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m @@ -3,18 +3,112 @@ // found in the LICENSE file. #import "FLTWebViewFlutterPlugin.h" -#import "FLTCookieManager.h" +#import "FWFGeneratedWebKitApis.h" +#import "FWFHTTPCookieStoreHostApi.h" +#import "FWFInstanceManager.h" +#import "FWFNavigationDelegateHostApi.h" +#import "FWFObjectHostApi.h" +#import "FWFPreferencesHostApi.h" +#import "FWFScriptMessageHandlerHostApi.h" +#import "FWFScrollViewHostApi.h" +#import "FWFUIDelegateHostApi.h" +#import "FWFUIViewHostApi.h" +#import "FWFUserContentControllerHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" #import "FWFWebViewHostApi.h" -#import "FlutterWebView.h" +#import "FWFWebsiteDataStoreHostApi.h" + +@interface FWFWebViewFactory : NSObject +@property(nonatomic, weak) FWFInstanceManager *instanceManager; + +- (instancetype)initWithManager:(FWFInstanceManager *)manager; +@end + +@implementation FWFWebViewFactory +- (instancetype)initWithManager:(FWFInstanceManager *)manager { + self = [self init]; + if (self) { + _instanceManager = manager; + } + return self; +} + +- (NSObject *)createArgsCodec { + return [FlutterStandardMessageCodec sharedInstance]; +} + +- (NSObject *)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + NSNumber *identifier = (NSNumber *)args; + FWFWebView *webView = + (FWFWebView *)[self.instanceManager instanceForIdentifier:identifier.longValue]; + webView.frame = frame; + return webView; +} + +@end @implementation FLTWebViewFlutterPlugin + (void)registerWithRegistrar:(NSObject *)registrar { - [FLTCookieManager registerWithRegistrar:registrar]; - FLTWebViewFactory *webviewFactory = - [[FLTWebViewFactory alloc] initWithMessenger:registrar.messenger - cookieManager:[FLTCookieManager instance]]; + FWFInstanceManager *instanceManager = + [[FWFInstanceManager alloc] initWithDeallocCallback:^(long identifier) { + FWFObjectFlutterApiImpl *objectApi = [[FWFObjectFlutterApiImpl alloc] + initWithBinaryMessenger:registrar.messenger + instanceManager:[[FWFInstanceManager alloc] init]]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [objectApi disposeObjectWithIdentifier:@(identifier) + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; + }); + }]; + FWFWKHttpCookieStoreHostApiSetup( + registrar.messenger, + [[FWFHTTPCookieStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKNavigationDelegateHostApiSetup( + registrar.messenger, + [[FWFNavigationDelegateHostApiImpl alloc] initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + FWFNSObjectHostApiSetup(registrar.messenger, + [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKPreferencesHostApiSetup(registrar.messenger, [[FWFPreferencesHostApiImpl alloc] + initWithInstanceManager:instanceManager]); + FWFWKScriptMessageHandlerHostApiSetup( + registrar.messenger, + [[FWFScriptMessageHandlerHostApiImpl alloc] initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + FWFUIScrollViewHostApiSetup(registrar.messenger, [[FWFScrollViewHostApiImpl alloc] + initWithInstanceManager:instanceManager]); + FWFWKUIDelegateHostApiSetup(registrar.messenger, [[FWFUIDelegateHostApiImpl alloc] + initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + FWFUIViewHostApiSetup(registrar.messenger, + [[FWFUIViewHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKUserContentControllerHostApiSetup( + registrar.messenger, + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKWebsiteDataStoreHostApiSetup( + registrar.messenger, + [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKWebViewConfigurationHostApiSetup( + registrar.messenger, + [[FWFWebViewConfigurationHostApiImpl alloc] initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + FWFWKWebViewHostApiSetup(registrar.messenger, [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + + FWFWebViewFactory *webviewFactory = [[FWFWebViewFactory alloc] initWithManager:instanceManager]; [registrar registerViewFactory:webviewFactory withId:@"plugins.flutter.io/webview"]; + + // InstanceManager is published so that a strong reference is maintained. + [registrar publish:instanceManager]; } +- (void)detachFromEngineForRegistrar:(NSObject *)registrar { + [registrar publish:[NSNull null]]; +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h deleted file mode 100644 index 69a15fc063c8..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2013 The Flutter 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 -#import - -#import "FLTCookieManager.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * The WkWebView used for the plugin. - * - * This class overrides some methods in `WKWebView` to serve the needs for the plugin. - */ -@interface FLTWKWebView : WKWebView -@end - -@interface FLTWebViewController : NSObject - -@property(nonatomic) FLTWKWebView *webView; - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject *)messenger; - -- (UIView *)view; - -- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result; - -@end - -@interface FLTWebViewFactory : NSObject -- (instancetype)initWithMessenger:(NSObject *)messenger - cookieManager:(FLTCookieManager *)cookieManager; -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m deleted file mode 100644 index 5bb81fce89db..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ /dev/null @@ -1,699 +0,0 @@ -// Copyright 2013 The Flutter 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 "FlutterWebView.h" -#import "FLTWKNavigationDelegate.h" -#import "FLTWKProgressionDelegate.h" -#import "FlutterWebView_Test.h" -#import "JavaScriptChannelHandler.h" - -@implementation FLTWebViewFactory { - NSObject *_messenger; - FLTCookieManager *_cookieManager; -} - -- (instancetype)initWithMessenger:(NSObject *)messenger - cookieManager:(FLTCookieManager *)cookieManager { - self = [super init]; - if (self) { - _messenger = messenger; - _cookieManager = cookieManager; - } - return self; -} - -- (NSObject *)createArgsCodec { - return [FlutterStandardMessageCodec sharedInstance]; -} - -- (NSObject *)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - if (@available(iOS 11.0, *)) { - [_cookieManager setCookiesForData:args[@"cookies"]]; - } - - FLTWebViewController *webviewController = [[FLTWebViewController alloc] initWithFrame:frame - viewIdentifier:viewId - arguments:args - binaryMessenger:_messenger]; - return webviewController; -} - -@end - -@implementation FLTWKWebView - -- (void)setFrame:(CGRect)frame { - [super setFrame:frame]; - self.scrollView.contentInset = UIEdgeInsetsZero; - // We don't want the contentInsets to be adjusted by iOS, flutter should always take control of - // webview's contentInsets. - // self.scrollView.contentInset = UIEdgeInsetsZero; - if (@available(iOS 11, *)) { - // Above iOS 11, adjust contentInset to compensate the adjustedContentInset so the sum will - // always be 0. - if (UIEdgeInsetsEqualToEdgeInsets(self.scrollView.adjustedContentInset, UIEdgeInsetsZero)) { - return; - } - UIEdgeInsets insetToAdjust = self.scrollView.adjustedContentInset; - self.scrollView.contentInset = UIEdgeInsetsMake(-insetToAdjust.top, -insetToAdjust.left, - -insetToAdjust.bottom, -insetToAdjust.right); - } -} - -@end - -@implementation FLTWebViewController { - FLTWKWebView *_webView; - int64_t _viewId; - FlutterMethodChannel *_channel; - NSString *_currentUrl; - // The set of registered JavaScript channel names. - NSMutableSet *_javaScriptChannelNames; - FLTWKNavigationDelegate *_navigationDelegate; - FLTWKProgressionDelegate *_progressionDelegate; -} - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject *)messenger { - if (self = [super init]) { - _viewId = viewId; - - NSString *channelName = [NSString stringWithFormat:@"plugins.flutter.io/webview_%lld", viewId]; - _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger]; - _javaScriptChannelNames = [[NSMutableSet alloc] init]; - - WKUserContentController *userContentController = [[WKUserContentController alloc] init]; - if ([args[@"javascriptChannelNames"] isKindOfClass:[NSArray class]]) { - NSArray *javaScriptChannelNames = args[@"javascriptChannelNames"]; - [_javaScriptChannelNames addObjectsFromArray:javaScriptChannelNames]; - [self registerJavaScriptChannels:_javaScriptChannelNames controller:userContentController]; - } - - NSDictionary *settings = args[@"settings"]; - - WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; - [self applyConfigurationSettings:settings toConfiguration:configuration]; - configuration.userContentController = userContentController; - [self updateAutoMediaPlaybackPolicy:args[@"autoMediaPlaybackPolicy"] - inConfiguration:configuration]; - - _webView = [[FLTWKWebView alloc] initWithFrame:frame configuration:configuration]; - - // Background color - NSNumber *backgroundColorNSNumber = args[@"backgroundColor"]; - if ([backgroundColorNSNumber isKindOfClass:[NSNumber class]]) { - int backgroundColorInt = [backgroundColorNSNumber intValue]; - UIColor *backgroundColor = [UIColor colorWithRed:(backgroundColorInt >> 16 & 0xff) / 255.0 - green:(backgroundColorInt >> 8 & 0xff) / 255.0 - blue:(backgroundColorInt & 0xff) / 255.0 - alpha:(backgroundColorInt >> 24 & 0xff) / 255.0]; - _webView.opaque = NO; - _webView.backgroundColor = UIColor.clearColor; - _webView.scrollView.backgroundColor = backgroundColor; - } - - _navigationDelegate = [[FLTWKNavigationDelegate alloc] initWithChannel:_channel]; - _webView.UIDelegate = self; - _webView.navigationDelegate = _navigationDelegate; - __weak __typeof__(self) weakSelf = self; - [_channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { - [weakSelf onMethodCall:call result:result]; - }]; - - if (@available(iOS 11.0, *)) { - _webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; - if (@available(iOS 13.0, *)) { - _webView.scrollView.automaticallyAdjustsScrollIndicatorInsets = NO; - } - } - - [self applySettings:settings]; - // TODO(amirh): return an error if apply settings failed once it's possible to do so. - // https://github.com/flutter/flutter/issues/36228 - - NSString *initialUrl = args[@"initialUrl"]; - if ([initialUrl isKindOfClass:[NSString class]]) { - NSURL *url = [NSURL URLWithString:initialUrl]; - if (url) { - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [_webView loadRequest:request]; - } - } - } - return self; -} - -- (void)dealloc { - if (_progressionDelegate != nil) { - [_progressionDelegate stopObservingProgress:_webView]; - } -} - -- (UIView *)view { - return _webView; -} - -- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([[call method] isEqualToString:@"updateSettings"]) { - [self onUpdateSettings:call result:result]; - } else if ([[call method] isEqualToString:@"loadFile"]) { - [self onLoadFile:call result:result]; - } else if ([[call method] isEqualToString:@"loadFlutterAsset"]) { - [self onLoadFlutterAsset:call result:result]; - } else if ([[call method] isEqualToString:@"loadHtmlString"]) { - [self onLoadHtmlString:call result:result]; - } else if ([[call method] isEqualToString:@"loadUrl"]) { - [self onLoadUrl:call result:result]; - } else if ([[call method] isEqualToString:@"loadRequest"]) { - [self onLoadRequest:call result:result]; - } else if ([[call method] isEqualToString:@"canGoBack"]) { - [self onCanGoBack:call result:result]; - } else if ([[call method] isEqualToString:@"canGoForward"]) { - [self onCanGoForward:call result:result]; - } else if ([[call method] isEqualToString:@"goBack"]) { - [self onGoBack:call result:result]; - } else if ([[call method] isEqualToString:@"goForward"]) { - [self onGoForward:call result:result]; - } else if ([[call method] isEqualToString:@"reload"]) { - [self onReload:call result:result]; - } else if ([[call method] isEqualToString:@"currentUrl"]) { - [self onCurrentUrl:call result:result]; - } else if ([[call method] isEqualToString:@"evaluateJavascript"]) { - [self onEvaluateJavaScript:call result:result]; - } else if ([[call method] isEqualToString:@"runJavascript"]) { - [self onRunJavaScript:call result:result sendReturnValue:NO]; - } else if ([[call method] isEqualToString:@"runJavascriptReturningResult"]) { - [self onRunJavaScript:call result:result sendReturnValue:YES]; - } else if ([[call method] isEqualToString:@"addJavascriptChannels"]) { - [self onAddJavaScriptChannels:call result:result]; - } else if ([[call method] isEqualToString:@"removeJavascriptChannels"]) { - [self onRemoveJavaScriptChannels:call result:result]; - } else if ([[call method] isEqualToString:@"clearCache"]) { - [self clearCache:result]; - } else if ([[call method] isEqualToString:@"getTitle"]) { - [self onGetTitle:result]; - } else if ([[call method] isEqualToString:@"scrollTo"]) { - [self onScrollTo:call result:result]; - } else if ([[call method] isEqualToString:@"scrollBy"]) { - [self onScrollBy:call result:result]; - } else if ([[call method] isEqualToString:@"getScrollX"]) { - [self getScrollX:call result:result]; - } else if ([[call method] isEqualToString:@"getScrollY"]) { - [self getScrollY:call result:result]; - } else { - result(FlutterMethodNotImplemented); - } -} - -- (void)onUpdateSettings:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *error = [self applySettings:[call arguments]]; - if (error == nil) { - result(nil); - return; - } - result([FlutterError errorWithCode:@"updateSettings_failed" message:error details:nil]); -} - -- (void)onLoadFile:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *error = nil; - if (![FLTWebViewController isValidStringArgument:[call arguments] withErrorMessage:&error]) { - result([FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:error]); - return; - } - - NSURL *url = [NSURL fileURLWithPath:[call arguments] isDirectory:NO]; - - if (!url) { - NSString *errorDetails = [NSString stringWithFormat:@"Initializing NSURL with the supplied " - @"'%@' path resulted in a nil value.", - [call arguments]]; - result([FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:errorDetails]); - return; - } - - NSURL *baseUrl = [url URLByDeletingLastPathComponent]; - - [_webView loadFileURL:url allowingReadAccessToURL:baseUrl]; - result(nil); -} - -- (void)onLoadFlutterAsset:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *error = nil; - if (![FLTWebViewController isValidStringArgument:[call arguments] withErrorMessage:&error]) { - result([FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Supplied asset key is not valid." - details:error]); - return; - } - - NSString *assetKey = [call arguments]; - NSString *assetFilePath = [FlutterDartProject lookupKeyForAsset:assetKey]; - NSURL *url = [[NSBundle mainBundle] URLForResource:[assetFilePath stringByDeletingPathExtension] - withExtension:assetFilePath.pathExtension]; - - if (!url) { - result([FlutterError - errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Failed parsing file path for supplied key." - details:[NSString - stringWithFormat:@"Failed to convert path '%@' into NSURL for key '%@'.", - assetFilePath, assetKey]]); - return; - } - - [_webView loadFileURL:url allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]; - result(nil); -} - -- (void)onLoadHtmlString:(FlutterMethodCall *)call result:(FlutterResult)result { - NSDictionary *arguments = [call arguments]; - if (![arguments isKindOfClass:NSDictionary.class]) { - result([FlutterError - errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing arguments." - details:@"Arguments should be a dictionary containing at least a 'html' element and " - @"optionally a 'baseUrl' argument. For example: `@{ @\"html\": @\"some html " - @"code\", @\"baseUrl\": @\"https://flutter.dev\" }`"]); - return; - } - - NSString *htmlString = [call arguments][@"html"]; - NSString *baseUrl = - [call arguments][@"baseUrl"] == [NSNull null] ? nil : [call arguments][@"baseUrl"]; - NSString *error = nil; - if (![FLTWebViewController isValidStringArgument:htmlString withErrorMessage:&error]) { - result([FlutterError errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing HTML string argument." - details:error]); - return; - } - - [_webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:baseUrl]]; - result(nil); -} - -- (void)onLoadUrl:(FlutterMethodCall *)call result:(FlutterResult)result { - NSMutableDictionary *requestData = [[NSMutableDictionary alloc] init]; - if (call.arguments[@"url"]) { - requestData[@"uri"] = call.arguments[@"url"]; - } - if (call.arguments[@"headers"]) { - requestData[@"headers"] = call.arguments[@"headers"]; - } - NSURLRequest *request = [self buildNSURLRequest:@{@"request" : requestData}]; - if (!request) { - result([FlutterError - errorWithCode:@"loadUrl_failed" - message:@"Failed parsing the URL" - details:[NSString stringWithFormat:@"Request was: '%@'", [call arguments]]]); - } else { - [_webView loadRequest:request]; - result(nil); - } -} - -- (void)onLoadRequest:(FlutterMethodCall *)call result:(FlutterResult)result { - NSURLRequest *request = [self buildNSURLRequest:[call arguments]]; - if (!request) { - result([FlutterError - errorWithCode:@"loadRequest_failed" - message:@"Failed parsing the URL" - details:[NSString stringWithFormat:@"Request was: '%@'", [call arguments]]]); - } else { - [_webView loadRequest:request]; - result(nil); - } -} - -- (void)onCanGoBack:(FlutterMethodCall *)call result:(FlutterResult)result { - BOOL canGoBack = [_webView canGoBack]; - result(@(canGoBack)); -} - -- (void)onCanGoForward:(FlutterMethodCall *)call result:(FlutterResult)result { - BOOL canGoForward = [_webView canGoForward]; - result(@(canGoForward)); -} - -- (void)onGoBack:(FlutterMethodCall *)call result:(FlutterResult)result { - [_webView goBack]; - result(nil); -} - -- (void)onGoForward:(FlutterMethodCall *)call result:(FlutterResult)result { - [_webView goForward]; - result(nil); -} - -- (void)onReload:(FlutterMethodCall *)call result:(FlutterResult)result { - [_webView reload]; - result(nil); -} - -- (void)onCurrentUrl:(FlutterMethodCall *)call result:(FlutterResult)result { - _currentUrl = [[_webView URL] absoluteString]; - result(_currentUrl); -} - -- (void)onEvaluateJavaScript:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *jsString = [call arguments]; - if (!jsString) { - result([FlutterError errorWithCode:@"evaluateJavaScript_failed" - message:@"JavaScript String cannot be null" - details:nil]); - return; - } - [_webView evaluateJavaScript:jsString - completionHandler:^(_Nullable id evaluateResult, NSError *_Nullable error) { - if (error) { - result([FlutterError - errorWithCode:@"evaluateJavaScript_failed" - message:@"Failed evaluating JavaScript" - details:[NSString stringWithFormat:@"JavaScript string was: '%@'\n%@", - jsString, error]]); - } else { - result([NSString stringWithFormat:@"%@", evaluateResult]); - } - }]; -} - -- (void)onRunJavaScript:(FlutterMethodCall *)call - result:(FlutterResult)result - sendReturnValue:(BOOL)sendReturnValue { - NSString *jsString = [call arguments]; - if (!jsString) { - result([FlutterError errorWithCode:@"runJavascript_failed" - message:@"JavaScript String cannot be null" - details:nil]); - return; - } - [_webView - evaluateJavaScript:jsString - completionHandler:^(_Nullable id evaluateResult, NSError *_Nullable error) { - if (error) { - // WebKit will throw an error (WKErrorJavaScriptResultTypeIsUnsupported) when the - // type of the evaluated value is unsupported. This also goes for - // `null` and `undefined` on iOS 14+, for example when running a void function. - // For ease of use this specific error is ignored when no return value is expected. - BOOL sendError = - sendReturnValue || error.code != WKErrorJavaScriptResultTypeIsUnsupported; - result(sendError - ? [FlutterError - errorWithCode:(sendReturnValue ? @"runJavascriptReturningResult_failed" - : @"runJavascript_failed") - message:@"Failed running JavaScript" - details:[NSString - stringWithFormat:@"JavaScript string was: '%@'\n%@", - jsString, error]] - : nil); - return; - } - result(sendReturnValue ? [NSString stringWithFormat:@"%@", evaluateResult] : nil); - }]; -} - -- (void)onAddJavaScriptChannels:(FlutterMethodCall *)call result:(FlutterResult)result { - NSArray *channelNames = [call arguments]; - NSSet *channelNamesSet = [[NSSet alloc] initWithArray:channelNames]; - [_javaScriptChannelNames addObjectsFromArray:channelNames]; - [self registerJavaScriptChannels:channelNamesSet - controller:_webView.configuration.userContentController]; - result(nil); -} - -- (void)onRemoveJavaScriptChannels:(FlutterMethodCall *)call result:(FlutterResult)result { - // WkWebView does not support removing a single user script, so instead we remove all - // user scripts, all message handlers. And re-register channels that shouldn't be removed. - [_webView.configuration.userContentController removeAllUserScripts]; - for (NSString *channelName in _javaScriptChannelNames) { - [_webView.configuration.userContentController removeScriptMessageHandlerForName:channelName]; - } - - NSArray *channelNamesToRemove = [call arguments]; - for (NSString *channelName in channelNamesToRemove) { - [_javaScriptChannelNames removeObject:channelName]; - } - - [self registerJavaScriptChannels:_javaScriptChannelNames - controller:_webView.configuration.userContentController]; - result(nil); -} - -- (void)clearCache:(FlutterResult)result { - NSSet *cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; - WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; - NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; - [dataStore removeDataOfTypes:cacheDataTypes - modifiedSince:dateFrom - completionHandler:^{ - result(nil); - }]; -} - -- (void)onGetTitle:(FlutterResult)result { - NSString *title = _webView.title; - result(title); -} - -- (void)onScrollTo:(FlutterMethodCall *)call result:(FlutterResult)result { - NSDictionary *arguments = [call arguments]; - int x = [arguments[@"x"] intValue]; - int y = [arguments[@"y"] intValue]; - - _webView.scrollView.contentOffset = CGPointMake(x, y); - result(nil); -} - -- (void)onScrollBy:(FlutterMethodCall *)call result:(FlutterResult)result { - CGPoint contentOffset = _webView.scrollView.contentOffset; - - NSDictionary *arguments = [call arguments]; - int x = [arguments[@"x"] intValue] + contentOffset.x; - int y = [arguments[@"y"] intValue] + contentOffset.y; - - _webView.scrollView.contentOffset = CGPointMake(x, y); - result(nil); -} - -- (void)getScrollX:(FlutterMethodCall *)call result:(FlutterResult)result { - int offsetX = _webView.scrollView.contentOffset.x; - result(@(offsetX)); -} - -- (void)getScrollY:(FlutterMethodCall *)call result:(FlutterResult)result { - int offsetY = _webView.scrollView.contentOffset.y; - result(@(offsetY)); -} - -// Returns nil when successful, or an error message when one or more keys are unknown. -- (NSString *)applySettings:(NSDictionary *)settings { - NSMutableArray *unknownKeys = [[NSMutableArray alloc] init]; - for (NSString *key in settings) { - if ([key isEqualToString:@"jsMode"]) { - NSNumber *mode = settings[key]; - [self updateJsMode:mode]; - } else if ([key isEqualToString:@"hasNavigationDelegate"]) { - NSNumber *hasDartNavigationDelegate = settings[key]; - _navigationDelegate.hasDartNavigationDelegate = [hasDartNavigationDelegate boolValue]; - } else if ([key isEqualToString:@"hasProgressTracking"]) { - NSNumber *hasProgressTrackingValue = settings[key]; - bool hasProgressTracking = [hasProgressTrackingValue boolValue]; - if (hasProgressTracking) { - _progressionDelegate = [[FLTWKProgressionDelegate alloc] initWithWebView:_webView - channel:_channel]; - } - } else if ([key isEqualToString:@"debuggingEnabled"]) { - // no-op debugging is always enabled on iOS. - } else if ([key isEqualToString:@"gestureNavigationEnabled"]) { - NSNumber *allowsBackForwardNavigationGestures = settings[key]; - _webView.allowsBackForwardNavigationGestures = - [allowsBackForwardNavigationGestures boolValue]; - } else if ([key isEqualToString:@"userAgent"]) { - NSString *userAgent = settings[key]; - [self updateUserAgent:[userAgent isEqual:[NSNull null]] ? nil : userAgent]; - } else if ([key isEqualToString:@"zoomEnabled"]) { - NSNumber *zoomEnabled = settings[key]; - _navigationDelegate.shouldEnableZoom = [zoomEnabled boolValue]; - } else { - [unknownKeys addObject:key]; - } - } - if ([unknownKeys count] == 0) { - return nil; - } - return [NSString stringWithFormat:@"webview_flutter: unknown setting keys: {%@}", - [unknownKeys componentsJoinedByString:@", "]]; -} - -- (void)applyConfigurationSettings:(NSDictionary *)settings - toConfiguration:(WKWebViewConfiguration *)configuration { - NSAssert(configuration != _webView.configuration, - @"configuration needs to be updated before webView.configuration."); - for (NSString *key in settings) { - if ([key isEqualToString:@"allowsInlineMediaPlayback"]) { - NSNumber *allowsInlineMediaPlayback = settings[key]; - configuration.allowsInlineMediaPlayback = [allowsInlineMediaPlayback boolValue]; - } - } -} - -- (void)updateJsMode:(NSNumber *)mode { - WKPreferences *preferences = [[_webView configuration] preferences]; - switch ([mode integerValue]) { - case 0: // disabled - [preferences setJavaScriptEnabled:NO]; - break; - case 1: // unrestricted - [preferences setJavaScriptEnabled:YES]; - break; - default: - NSLog(@"webview_flutter: unknown JavaScript mode: %@", mode); - } -} - -- (void)updateAutoMediaPlaybackPolicy:(NSNumber *)policy - inConfiguration:(WKWebViewConfiguration *)configuration { - switch ([policy integerValue]) { - case 0: // require_user_action_for_all_media_types - if (@available(iOS 10.0, *)) { - configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - configuration.requiresUserActionForMediaPlayback = true; -#pragma clang diagnostic pop - } - break; - case 1: // always_allow - if (@available(iOS 10.0, *)) { - configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - configuration.requiresUserActionForMediaPlayback = false; -#pragma clang diagnostic pop - } - break; - default: - NSLog(@"webview_flutter: unknown auto media playback policy: %@", policy); - } -} - -/** - * Parses the method call arguments and converts them to an NSURLRequest object. - * - * @param arguments the method call arguments. - * - * @return NSURLRequest object. - */ -- (NSURLRequest *)buildNSURLRequest:(NSDictionary *)arguments { - id requestParameters = arguments[@"request"]; - if (![requestParameters isKindOfClass:[NSDictionary class]]) { - return nil; - } - - NSString *urlString = requestParameters[@"uri"]; - if (!urlString) { - return nil; - } - - NSURL *url = [NSURL URLWithString:urlString]; - if (!url) { - return nil; - } - - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - - NSString *httpMethod = requestParameters[@"method"]; - if (httpMethod) { - [request setHTTPMethod:httpMethod]; - } - - id httpBody = requestParameters[@"body"]; - if ([httpBody isKindOfClass:[FlutterStandardTypedData class]]) { - [request setHTTPBody:[httpBody data]]; - } - - id headers = requestParameters[@"headers"]; - if ([headers isKindOfClass:[NSDictionary class]]) { - [request setAllHTTPHeaderFields:headers]; - } - - return request; -} - -- (void)registerJavaScriptChannels:(NSSet *)channelNames - controller:(WKUserContentController *)userContentController { - for (NSString *channelName in channelNames) { - FLTJavaScriptChannel *channel = - [[FLTJavaScriptChannel alloc] initWithMethodChannel:_channel - javaScriptChannelName:channelName]; - [userContentController addScriptMessageHandler:channel name:channelName]; - NSString *wrapperSource = [NSString - stringWithFormat:@"window.%@ = webkit.messageHandlers.%@;", channelName, channelName]; - WKUserScript *wrapperScript = - [[WKUserScript alloc] initWithSource:wrapperSource - injectionTime:WKUserScriptInjectionTimeAtDocumentStart - forMainFrameOnly:NO]; - [userContentController addUserScript:wrapperScript]; - } -} - -- (void)updateUserAgent:(NSString *)userAgent { - [_webView setCustomUserAgent:userAgent]; -} - -/** - * Validates if the given `argument` is a non-null, non-empty string. - * - * @param argument The argument that should be validated. - * @param errorDetails An optional NSString variable which will contain a detailed error message in - * case the supplied argument is not valid. - * @return `YES` if the given `argument` is a valid non-null, non-empty string; otherwise `NO`. - */ -+ (BOOL)isValidStringArgument:(id)argument withErrorMessage:(NSString **)errorDetails { - if (!argument) { - if (errorDetails) { - *errorDetails = @"Argument is nil."; - } - return NO; - } - if (![argument isKindOfClass:NSString.class]) { - if (errorDetails) { - *errorDetails = @"Argument is not of type NSString."; - } - return NO; - } - if (![argument length]) { - if (errorDetails) { - *errorDetails = @"Argument contains an empty string."; - } - return NO; - } - - return YES; -} - -#pragma mark WKUIDelegate - -- (WKWebView *)webView:(WKWebView *)webView - createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration - forNavigationAction:(WKNavigationAction *)navigationAction - windowFeatures:(WKWindowFeatures *)windowFeatures { - if (!navigationAction.targetFrame.isMainFrame) { - [webView loadRequest:navigationAction.request]; - } - - return nil; -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap index 639d89498d00..1b7eaf646ee9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap @@ -5,8 +5,6 @@ framework module webview_flutter_wkwebview { module * { export * } explicit module Test { - header "FlutterWebView_Test.h" - header "FLTCookieManager_Test.h" header "FWFInstanceManager_Test.h" } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h deleted file mode 100644 index a84954e17c97..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This header is available in the Test module. Import via "@import webview_flutter_wkwebview.Test;" - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTWebViewController () - -- (NSURLRequest *)buildNSURLRequest:(NSDictionary *)arguments; - -- (void)onLoadUrl:(FlutterMethodCall *)call result:(FlutterResult)result; - -- (void)onLoadRequest:(FlutterMethodCall *)call result:(FlutterResult)result; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h deleted file mode 100644 index f442d7af8d87..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Flutter 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 -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTJavaScriptChannel : NSObject - -- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel - javaScriptChannelName:(NSString *)javaScriptChannelName; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m deleted file mode 100644 index 1aed25f1b7d9..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 The Flutter 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 "JavaScriptChannelHandler.h" - -@implementation FLTJavaScriptChannel { - FlutterMethodChannel *_methodChannel; - NSString *_javaScriptChannelName; -} - -- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel - javaScriptChannelName:(NSString *)javaScriptChannelName { - self = [super init]; - NSAssert(methodChannel != nil, @"methodChannel must not be null."); - NSAssert(javaScriptChannelName != nil, @"javaScriptChannelName must not be null."); - if (self) { - _methodChannel = methodChannel; - _javaScriptChannelName = javaScriptChannelName; - } - return self; -} - -- (void)userContentController:(WKUserContentController *)userContentController - didReceiveScriptMessage:(WKScriptMessage *)message { - NSAssert(_methodChannel != nil, @"Can't send a message to an unitialized JavaScript channel."); - NSAssert(_javaScriptChannelName != nil, - @"Can't send a message to an unitialized JavaScript channel."); - NSDictionary *arguments = @{ - @"channel" : _javaScriptChannelName, - @"message" : [NSString stringWithFormat:@"%@", message.body] - }; - [_methodChannel invokeMethod:@"javascriptChannelMessage" arguments:arguments]; -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h index 0aa409e4b31f..dbcd876d15c9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h @@ -3,9 +3,6 @@ // found in the LICENSE file. #import -#import -#import -#import #import #import #import @@ -22,5 +19,3 @@ #import #import #import -#import -#import diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart deleted file mode 100644 index 46322319ae89..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2013 The Flutter 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 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; -import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; - -/// Handles all cookie operations for the WebView platform. -class WebKitCookieManager extends WebViewCookieManagerPlatform { - /// Constructs a [WebKitCookieManager]. - WebKitCookieManager({WKWebsiteDataStore? websiteDataStore}) - : websiteDataStore = - websiteDataStore ?? WKWebsiteDataStore.defaultDataStore; - - /// Manages stored data for [WKWebView]s. - final WKWebsiteDataStore websiteDataStore; - - @override - Future clearCookies() async { - return websiteDataStore.removeDataOfTypes( - {WKWebsiteDataType.cookies}, - DateTime.fromMillisecondsSinceEpoch(0), - ); - } - - @override - Future setCookie(WebViewCookie cookie) { - if (!_isValidPath(cookie.path)) { - throw ArgumentError( - 'The path property for the provided cookie was not given a legal value.'); - } - - return websiteDataStore.httpCookieStore.setCookie( - NSHttpCookie.withProperties( - { - NSHttpCookiePropertyKey.name: cookie.name, - NSHttpCookiePropertyKey.value: cookie.value, - NSHttpCookiePropertyKey.domain: cookie.domain, - NSHttpCookiePropertyKey.path: cookie.path, - }, - ), - ); - } - - bool _isValidPath(String path) { - // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 - return !path.codeUnits.any( - (int char) { - return (char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E); - }, - ); - } -} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 852f9caa0c49..327210983ae2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -340,10 +340,6 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future evaluateJavascript(String javascript) async { final Object? result = await webView.evaluateJavaScript(javascript); - // The legacy implementation of webview_flutter_wkwebview would convert - // objects to strings before returning them to Dart. This method attempts - // to converts Dart objects to Strings the way it is done in Objective-C - // to avoid breaking users expecting the same String format. return _asObjectiveCString(result); } @@ -373,7 +369,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { 'Use `runJavascript` when expecting a null return value.', ); } - return result.toString(); + return _asObjectiveCString(result); } @override @@ -603,6 +599,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ); } + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This method attempts + // to converts Dart objects to Strings the way it is done in Objective-C + // to avoid breaking users expecting the same String format. + // TODO(bparrishMines): Remove this method with the next breaking change. + // See https://github.com/flutter/flutter/issues/107491 String _asObjectiveCString(Object? value, {bool inContainer = false}) { if (value == null) { // An NSNull inside an NSArray or NSDictionary is represented as a String diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart index cd00d9a7d14b..f046ea4378b8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart @@ -9,6 +9,9 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; + +import 'foundation/foundation.dart'; /// Builds an iOS webview. /// @@ -25,22 +28,24 @@ class CupertinoWebView implements WebViewPlatform { WebViewPlatformCreatedCallback? onWebViewPlatformCreated, Set>? gestureRecognizers, }) { - return UiKitView( - viewType: 'plugins.flutter.io/webview', - onPlatformViewCreated: (int id) { - if (onWebViewPlatformCreated == null) { - return; - } - onWebViewPlatformCreated(MethodChannelWebViewPlatform( - id, - webViewPlatformCallbacksHandler, - javascriptChannelRegistry, - )); + return WebKitWebViewWidget( + creationParams: creationParams, + callbacksHandler: webViewPlatformCallbacksHandler, + javascriptChannelRegistry: javascriptChannelRegistry, + onBuildWidget: (WebKitWebViewPlatformController controller) { + return UiKitView( + viewType: 'plugins.flutter.io/webview', + onPlatformViewCreated: (int id) { + if (onWebViewPlatformCreated != null) { + onWebViewPlatformCreated(controller); + } + }, + gestureRecognizers: gestureRecognizers, + creationParams: + NSObject.globalInstanceManager.getIdentifier(controller.webView), + creationParamsCodec: const StandardMessageCodec(), + ); }, - gestureRecognizers: gestureRecognizers, - creationParams: - MethodChannelWebViewPlatform.creationParamsToMap(creationParams), - creationParamsCodec: const StandardMessageCodec(), ); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart index d460ce02ab68..59c9f580db74 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart @@ -3,11 +3,26 @@ // found in the LICENSE file. import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; -/// Handles all cookie operations for the current platform. +/// Handles all cookie operations for the WebView platform. class WKWebViewCookieManager extends WebViewCookieManagerPlatform { + /// Constructs a [WKWebViewCookieManager]. + WKWebViewCookieManager({WKWebsiteDataStore? websiteDataStore}) + : websiteDataStore = + websiteDataStore ?? WKWebsiteDataStore.defaultDataStore; + + /// Manages stored data for [WKWebView]s. + final WKWebsiteDataStore websiteDataStore; + @override - Future clearCookies() => MethodChannelWebViewPlatform.clearCookies(); + Future clearCookies() async { + return websiteDataStore.removeDataOfTypes( + {WKWebsiteDataType.cookies}, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } @override Future setCookie(WebViewCookie cookie) { @@ -15,16 +30,25 @@ class WKWebViewCookieManager extends WebViewCookieManagerPlatform { throw ArgumentError( 'The path property for the provided cookie was not given a legal value.'); } - return MethodChannelWebViewPlatform.setCookie(cookie); + + return websiteDataStore.httpCookieStore.setCookie( + NSHttpCookie.withProperties( + { + NSHttpCookiePropertyKey.name: cookie.name, + NSHttpCookiePropertyKey.value: cookie.value, + NSHttpCookiePropertyKey.domain: cookie.domain, + NSHttpCookiePropertyKey.path: cookie.path, + }, + ), + ); } bool _isValidPath(String path) { // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 - for (final int char in path.codeUnits) { - if ((char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E)) { - return false; - } - } - return true; + return !path.codeUnits.any( + (int char) { + return (char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E); + }, + ); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index c69d0d51b622..cd92b8625105 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.1 +version: 2.9.0 environment: sdk: ">=2.17.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart index 6baff12eda17..73d8c8f33a11 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -8,7 +8,7 @@ import 'package:mockito/mockito.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; -import 'package:webview_flutter_wkwebview/src/web_kit_cookie_manager.dart'; +import 'package:webview_flutter_wkwebview/src/wkwebview_cookie_manager.dart'; import 'web_kit_cookie_manager_test.mocks.dart'; @@ -23,7 +23,7 @@ void main() { late MockWKWebsiteDataStore mockWebsiteDataStore; late MockWKHttpCookieStore mockWKHttpCookieStore; - late WebKitCookieManager cookieManager; + late WKWebViewCookieManager cookieManager; setUp(() { mockWebsiteDataStore = MockWKWebsiteDataStore(); @@ -32,7 +32,7 @@ void main() { .thenReturn(mockWKHttpCookieStore); cookieManager = - WebKitCookieManager(websiteDataStore: mockWebsiteDataStore); + WKWebViewCookieManager(websiteDataStore: mockWebsiteDataStore); }); test('clearCookies', () async { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index b5f7b1a486dd..c6d90d04e35e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -745,6 +745,23 @@ void main() { ); }); + testWidgets('runJavascriptReturningResult with bool return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(false), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies bool + // is represented the way it is in Objective-C. + // `NSNumber.description` converts bool values to a 1 or 0. + expect( + testController.runJavascriptReturningResult('runJavaScript'), + completion('0'), + ); + }); + testWidgets('runJavascript', (WidgetTester tester) async { await buildWidget(tester); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart deleted file mode 100644 index 54b1921583b9..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 The Flutter 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 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'package:webview_flutter_wkwebview/src/wkwebview_cookie_manager.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - const MethodChannel cookieChannel = - MethodChannel('plugins.flutter.io/cookie_manager'); - final List log = []; - - cookieChannel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - - if (methodCall.method == 'clearCookies') { - return true; - } - - // Return null explicitly instead of relying on the implicit null - // returned by the method channel if no return statement is specified. - return null; - }); - - tearDown(() { - log.clear(); - }); - - test('clearCookies should call `clearCookies` on the method channel', - () async { - await WKWebViewCookieManager().clearCookies(); - expect( - log, - [ - isMethodCall( - 'clearCookies', - arguments: null, - ), - ], - ); - }); - - test('setCookie should call `setCookie` on the method channel', () async { - await WKWebViewCookieManager().setCookie( - const WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev'), - ); - expect( - log, - [ - isMethodCall( - 'setCookie', - arguments: { - 'name': 'foo', - 'value': 'bar', - 'domain': 'flutter.dev', - 'path': '/', - }, - ), - ], - ); - }); -}