From 6b4c8fc5df9f3a1613bb8e1f22bd0c573c7686b8 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 15 Jun 2022 07:43:11 -0700 Subject: [PATCH] [webview_flutter_wkwebview] Instantiate a `WKWebViewConfiguration` in `WKUIDelegate.onCreateWebView` (#5971) --- .../RunnerTests/FWFUIDelegateHostApiTests.m | 10 +- .../ios/Classes/FWFGeneratedWebKitApis.h | 10 +- .../ios/Classes/FWFGeneratedWebKitApis.m | 124 +++++++++++++++++- .../ios/Classes/FWFUIDelegateHostApi.h | 3 + .../ios/Classes/FWFUIDelegateHostApi.m | 17 ++- .../Classes/FWFWebViewConfigurationHostApi.h | 12 ++ .../Classes/FWFWebViewConfigurationHostApi.m | 23 ++++ .../lib/src/common/web_kit.pigeon.dart | 35 ++++- .../lib/src/web_kit/web_kit_api_impls.dart | 42 +++++- .../pigeons/web_kit.dart | 9 ++ .../test/src/common/test_web_kit.pigeon.dart | 2 +- .../src/foundation/foundation_test.mocks.dart | 2 +- .../test/src/ui_kit/ui_kit_test.mocks.dart | 2 +- .../test/src/web_kit/web_kit_test.dart | 14 ++ .../test/src/web_kit/web_kit_test.mocks.dart | 2 +- .../web_kit_cookie_manager_test.mocks.dart | 2 +- .../web_kit_webview_widget_test.mocks.dart | 2 +- 17 files changed, 293 insertions(+), 18 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m index 17cb4367b3a..eec7a2b5a6d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m @@ -68,7 +68,13 @@ - (void)testOnCreateWebViewForDelegateWithIdentifier { [instanceManager addDartCreatedInstance:mockWebView withIdentifier:1]; WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; - [instanceManager addDartCreatedInstance:configuration withIdentifier:2]; + id mockConfigurationFlutterApi = OCMPartialMock(mockFlutterAPI.webViewConfigurationFlutterApi); + NSNumber *__block configurationIdentifier; + OCMStub([mockConfigurationFlutterApi createWithIdentifier:[OCMArg checkWithBlock:^BOOL(id value) { + configurationIdentifier = value; + return YES; + }] + completion:OCMOCK_ANY]); WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]); OCMStub([mockNavigationAction request]) @@ -85,7 +91,7 @@ - (void)testOnCreateWebViewForDelegateWithIdentifier { OCMVerify([mockFlutterAPI onCreateWebViewForDelegateWithIdentifier:@0 webViewIdentifier:@1 - configurationIdentifier:@2 + configurationIdentifier:configurationIdentifier navigationAction:[OCMArg isKindOfClass:[FWFWKNavigationActionData class]] completion:OCMOCK_ANY]); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h index ebd29f5caca..b0856ae1103 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v3.1.4), do not edit directly. +// Autogenerated from Pigeon (v3.1.5), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -297,6 +297,14 @@ extern void FWFWKWebViewConfigurationHostApiSetup( id binaryMessenger, NSObject *_Nullable api); +/// The codec used by FWFWKWebViewConfigurationFlutterApi. +NSObject *FWFWKWebViewConfigurationFlutterApiGetCodec(void); + +@interface FWFWKWebViewConfigurationFlutterApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)createWithIdentifier:(NSNumber *)identifier + completion:(void (^)(NSError *_Nullable))completion; +@end /// The codec used by FWFWKUserContentControllerHostApi. NSObject *FWFWKUserContentControllerHostApiGetCodec(void); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m index 592fa87ac9d..96c59987e84 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v3.1.4), do not edit directly. +// Autogenerated from Pigeon (v3.1.5), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "FWFGeneratedWebKitApis.h" #import @@ -35,58 +35,72 @@ static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { @interface FWFNSKeyValueObservingOptionsEnumData () + (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSKeyValueObservingOptionsEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSKeyValueChangeKeyEnumData () + (FWFNSKeyValueChangeKeyEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSKeyValueChangeKeyEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKUserScriptInjectionTimeEnumData () + (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKUserScriptInjectionTimeEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKAudiovisualMediaTypeEnumData () + (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKAudiovisualMediaTypeEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKWebsiteDataTypeEnumData () + (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKWebsiteDataTypeEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKNavigationActionPolicyEnumData () + (FWFWKNavigationActionPolicyEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKNavigationActionPolicyEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSHttpCookiePropertyKeyEnumData () + (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSHttpCookiePropertyKeyEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSUrlRequestData () + (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSUrlRequestData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKUserScriptData () + (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKUserScriptData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKNavigationActionData () + (FWFWKNavigationActionData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKNavigationActionData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKFrameInfoData () + (FWFWKFrameInfoData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKFrameInfoData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSErrorData () + (FWFNSErrorData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSErrorData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKScriptMessageData () + (FWFWKScriptMessageData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKScriptMessageData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSHttpCookieData () + (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSHttpCookieData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @@ -103,6 +117,9 @@ + (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFNSKeyValueObservingOptionsEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSKeyValueObservingOptionsEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -121,6 +138,9 @@ + (FWFNSKeyValueChangeKeyEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFNSKeyValueChangeKeyEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSKeyValueChangeKeyEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -141,6 +161,9 @@ + (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFWKUserScriptInjectionTimeEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKUserScriptInjectionTimeEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -161,6 +184,9 @@ + (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFWKAudiovisualMediaTypeEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKAudiovisualMediaTypeEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -179,6 +205,9 @@ + (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFWKWebsiteDataTypeEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKWebsiteDataTypeEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -199,6 +228,9 @@ + (FWFWKNavigationActionPolicyEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFWKNavigationActionPolicyEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKNavigationActionPolicyEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -219,6 +251,9 @@ + (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFNSHttpCookiePropertyKeyEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSHttpCookiePropertyKeyEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -248,6 +283,9 @@ + (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict { NSAssert(pigeonResult.allHttpHeaderFields != nil, @""); return pigeonResult; } ++ (nullable FWFNSUrlRequestData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSUrlRequestData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"url" : (self.url ?: [NSNull null]), @@ -272,12 +310,15 @@ + (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict { FWFWKUserScriptData *pigeonResult = [[FWFWKUserScriptData alloc] init]; pigeonResult.source = GetNullableObject(dict, @"source"); NSAssert(pigeonResult.source != nil, @""); - pigeonResult.injectionTime = - [FWFWKUserScriptInjectionTimeEnumData fromMap:GetNullableObject(dict, @"injectionTime")]; + pigeonResult.injectionTime = [FWFWKUserScriptInjectionTimeEnumData + nullableFromMap:GetNullableObject(dict, @"injectionTime")]; pigeonResult.isMainFrameOnly = GetNullableObject(dict, @"isMainFrameOnly"); NSAssert(pigeonResult.isMainFrameOnly != nil, @""); return pigeonResult; } ++ (nullable FWFWKUserScriptData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKUserScriptData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"source" : (self.source ?: [NSNull null]), @@ -297,12 +338,16 @@ + (instancetype)makeWithRequest:(FWFNSUrlRequestData *)request } + (FWFWKNavigationActionData *)fromMap:(NSDictionary *)dict { FWFWKNavigationActionData *pigeonResult = [[FWFWKNavigationActionData alloc] init]; - pigeonResult.request = [FWFNSUrlRequestData fromMap:GetNullableObject(dict, @"request")]; + pigeonResult.request = [FWFNSUrlRequestData nullableFromMap:GetNullableObject(dict, @"request")]; NSAssert(pigeonResult.request != nil, @""); - pigeonResult.targetFrame = [FWFWKFrameInfoData fromMap:GetNullableObject(dict, @"targetFrame")]; + pigeonResult.targetFrame = + [FWFWKFrameInfoData nullableFromMap:GetNullableObject(dict, @"targetFrame")]; NSAssert(pigeonResult.targetFrame != nil, @""); return pigeonResult; } ++ (nullable FWFWKNavigationActionData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKNavigationActionData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"request" : (self.request ? [self.request toMap] : [NSNull null]), @@ -323,6 +368,9 @@ + (FWFWKFrameInfoData *)fromMap:(NSDictionary *)dict { NSAssert(pigeonResult.isMainFrame != nil, @""); return pigeonResult; } ++ (nullable FWFWKFrameInfoData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKFrameInfoData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"isMainFrame" : (self.isMainFrame ?: [NSNull null]), @@ -350,6 +398,9 @@ + (FWFNSErrorData *)fromMap:(NSDictionary *)dict { NSAssert(pigeonResult.localizedDescription != nil, @""); return pigeonResult; } ++ (nullable FWFNSErrorData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSErrorData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"code" : (self.code ?: [NSNull null]), @@ -373,6 +424,9 @@ + (FWFWKScriptMessageData *)fromMap:(NSDictionary *)dict { pigeonResult.body = GetNullableObject(dict, @"body"); return pigeonResult; } ++ (nullable FWFWKScriptMessageData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKScriptMessageData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"name" : (self.name ?: [NSNull null]), @@ -397,6 +451,9 @@ + (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict { NSAssert(pigeonResult.propertyValues != nil, @""); return pigeonResult; } ++ (nullable FWFNSHttpCookieData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSHttpCookieData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"propertyKeys" : (self.propertyKeys ?: [NSNull null]), @@ -899,6 +956,63 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess } } } +@interface FWFWKWebViewConfigurationFlutterApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKWebViewConfigurationFlutterApiCodecReader +@end + +@interface FWFWKWebViewConfigurationFlutterApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKWebViewConfigurationFlutterApiCodecWriter +@end + +@interface FWFWKWebViewConfigurationFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKWebViewConfigurationFlutterApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKWebViewConfigurationFlutterApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKWebViewConfigurationFlutterApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKWebViewConfigurationFlutterApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKWebViewConfigurationFlutterApiCodecReaderWriter *readerWriter = + [[FWFWKWebViewConfigurationFlutterApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +@interface FWFWKWebViewConfigurationFlutterApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@end + +@implementation FWFWKWebViewConfigurationFlutterApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + self = [super init]; + if (self) { + _binaryMessenger = binaryMessenger; + } + return self; +} +- (void)createWithIdentifier:(NSNumber *)arg_identifier + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.WKWebViewConfigurationFlutterApi.create" + binaryMessenger:self.binaryMessenger + codec:FWFWKWebViewConfigurationFlutterApiGetCodec()]; + [channel sendMessage:@[ arg_identifier ?: [NSNull null] ] + reply:^(id reply) { + completion(nil); + }]; +} +@end @interface FWFWKUserContentControllerHostApiCodecReader : FlutterStandardReader @end @implementation FWFWKUserContentControllerHostApiCodecReader diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h index 65c148e6b0b..1bb65914e09 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h @@ -8,6 +8,7 @@ #import "FWFGeneratedWebKitApis.h" #import "FWFInstanceManager.h" #import "FWFObjectHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" NS_ASSUME_NONNULL_BEGIN @@ -17,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN * Handles making callbacks to Dart for a WKUIDelegate. */ @interface FWFUIDelegateFlutterApiImpl : FWFWKUIDelegateFlutterApi +@property(readonly, nonatomic) + FWFWebViewConfigurationFlutterApiImpl *webViewConfigurationFlutterApi; - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m index 4ec8b583b1f..36c4b2feefe 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m @@ -4,10 +4,11 @@ #import "FWFUIDelegateHostApi.h" #import "FWFDataConverters.h" -#import "FWFWebViewConfigurationHostApi.h" @interface FWFUIDelegateFlutterApiImpl () -// This reference must be weak to prevent a circular reference with the objects it stores. +// BinaryMessenger and InstanceManager must be weak to prevent a circular reference +// with the objects it stores. +@property(nonatomic, weak) id binaryMessenger; @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @@ -16,7 +17,11 @@ - (instancetype)initWithBinaryMessenger:(id)binaryMessen instanceManager:(FWFInstanceManager *)instanceManager { self = [self initWithBinaryMessenger:binaryMessenger]; if (self) { + _binaryMessenger = binaryMessenger; _instanceManager = instanceManager; + _webViewConfigurationFlutterApi = + [[FWFWebViewConfigurationFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; } return self; } @@ -30,10 +35,18 @@ - (void)onCreateWebViewForDelegate:(FWFUIDelegate *)instance configuration:(WKWebViewConfiguration *)configuration navigationAction:(WKNavigationAction *)navigationAction completion:(void (^)(NSError *_Nullable))completion { + if (![self.instanceManager containsInstance:configuration]) { + [self.webViewConfigurationFlutterApi createWithConfiguration:configuration + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; + } + NSNumber *configurationIdentifier = @([self.instanceManager identifierWithStrongReferenceForInstance:configuration]); FWFWKNavigationActionData *navigationActionData = FWFWKNavigationActionDataFromNavigationAction(navigationAction); + [self onCreateWebViewForDelegateWithIdentifier:@([self identifierForDelegate:instance]) webViewIdentifier: @([self.instanceManager diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h index 4c01d935410..e2279b48c40 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h @@ -10,6 +10,18 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Flutter api implementation for WKWebViewConfiguration. + * + * Handles making callbacks to Dart for a WKWebViewConfiguration. + */ +@interface FWFWebViewConfigurationFlutterApiImpl : FWFWKWebViewConfigurationFlutterApi +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; +- (void)createWithConfiguration:(WKWebViewConfiguration *)configuration + completion:(void (^)(NSError *_Nullable))completion; +@end + /** * Host api implementation for WKWebViewConfiguration. * diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m index 05ca38e2a47..2b7d1be86d3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m @@ -6,6 +6,29 @@ #import "FWFDataConverters.h" #import "FWFWebViewConfigurationHostApi.h" +@interface FWFWebViewConfigurationFlutterApiImpl () +// BinaryMessenger and InstanceManager must be weak to prevent a circular reference +// with the objects it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; +@end + +@implementation FWFWebViewConfigurationFlutterApiImpl +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self initWithBinaryMessenger:binaryMessenger]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (void)createWithConfiguration:(WKWebViewConfiguration *)configuration + completion:(void (^)(NSError *_Nullable))completion { + long identifier = [self.instanceManager addHostCreatedInstance:configuration]; + [self createWithIdentifier:@(identifier) completion:completion]; +} +@end + @interface FWFWebViewConfigurationHostApiImpl () @property(nonatomic) FWFInstanceManager *instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index c1971f09d06..1640c61211c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v3.1.4), do not edit directly. +// Autogenerated from Pigeon (v3.1.5), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -868,6 +868,39 @@ class WKWebViewConfigurationHostApi { } } +class _WKWebViewConfigurationFlutterApiCodec extends StandardMessageCodec { + const _WKWebViewConfigurationFlutterApiCodec(); +} + +abstract class WKWebViewConfigurationFlutterApi { + static const MessageCodec codec = + _WKWebViewConfigurationFlutterApiCodec(); + + void create(int identifier); + static void setup(WKWebViewConfigurationFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationFlutterApi.create was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} + class _WKUserContentControllerHostApiCodec extends StandardMessageCodec { const _WKUserContentControllerHostApiCodec(); @override diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index 4eb173238e7..d0423b44df8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -239,9 +239,14 @@ class WebKitFlutterApis { instanceManager: instanceManager, ), scriptMessageHandler = WKScriptMessageHandlerFlutterApiImpl( - instanceManager: instanceManager), + instanceManager: instanceManager, + ), uiDelegate = WKUIDelegateFlutterApiImpl( instanceManager: instanceManager, + ), + webViewConfiguration = WKWebViewConfigurationFlutterApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, ); static WebKitFlutterApis _instance = WebKitFlutterApis(); @@ -272,6 +277,10 @@ class WebKitFlutterApis { @visibleForTesting final WKUIDelegateFlutterApiImpl uiDelegate; + /// Flutter Api for [WKWebViewConfiguration]. + @visibleForTesting + final WKWebViewConfigurationFlutterApiImpl webViewConfiguration; + /// Ensures all the Flutter APIs have been set up to receive calls from native code. void ensureSetUp() { if (!_hasBeenSetUp) { @@ -576,6 +585,37 @@ class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { } } +/// Flutter api implementation for [WKWebViewConfiguration]. +@immutable +class WKWebViewConfigurationFlutterApiImpl + extends WKWebViewConfigurationFlutterApi { + /// Constructs a [WKWebViewConfigurationFlutterApiImpl]. + WKWebViewConfigurationFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier) { + instanceManager.addHostCreatedInstance( + WKWebViewConfiguration.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + identifier, + ); + } +} + /// Host api implementation for [WKUIDelegate]. class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { /// Constructs a [WKUIDelegateHostApiImpl]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index e46dafa01a6..dfd27163357 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -312,6 +312,15 @@ abstract class WKWebViewConfigurationHostApi { ); } +/// Handles callbacks from an WKWebViewConfiguration instance. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc. +@FlutterApi() +abstract class WKWebViewConfigurationFlutterApi { + @ObjCSelector('createWithIdentifier:') + void create(int identifier); +} + /// Mirror of WKUserContentController. /// /// See https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 5bf50e2b985..a9e5c8bb1db 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v3.1.4), do not edit directly. +// Autogenerated from Pigeon (v3.1.5), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart index e328a292fcb..62a51e17bc7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. // Do not manually edit this file. import 'package:mockito/mockito.dart' as _i1; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart index 58939f5b882..a382ecff677 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index 1d6c1f0e67a..4000e0d718d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -70,6 +70,20 @@ void main() { TestWKWebViewConfigurationHostApi.setup(null); }); + test('WKWebViewConfigurationFlutterApi.create', () { + final WebKitFlutterApis flutterApis = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + flutterApis.webViewConfiguration.create(2); + + expect(instanceManager.containsIdentifier(2), isTrue); + expect( + instanceManager.getInstanceWithWeakReference(2), + isA(), + ); + }); + test('createFromWebViewConfiguration', () { verify(mockPlatformHostApi.createFromWebViewConfiguration( instanceManager.getIdentifier(websiteDataStore), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart index 39ba08f3aa9..18f30d43495 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i3; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index 8289831fc4e..b0c63b66306 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index c8ed4c856dc..6161bd04443 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i6;