Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 'instance_manager.dart';
import 'web_kit.pigeon.dart';

/// Flutter api to dispose functions.
class FunctionFlutterApiImpl extends FunctionFlutterApi {
/// Constructs a [FunctionFlutterApiImpl].
FunctionFlutterApiImpl({InstanceManager? instanceManager}) {
this.instanceManager = instanceManager ?? InstanceManager.instance;
}

/// Maintains instances stored to communicate with native language objects.
late final InstanceManager instanceManager;

@override
void dispose(int instanceId) {
final Function? function = instanceManager.getInstance(instanceId);
if (function != null) {
instanceManager.removeInstance(function);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,75 @@ class WKNavigationDelegateHostApi {
return;
}
}

Future<void> setDidFinishNavigation(
int arg_instanceId, int? arg_functionInstanceId) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation',
codec,
binaryMessenger: _binaryMessenger);
final Map<Object?, Object?>? replyMap =
await channel.send(<Object?>[arg_instanceId, arg_functionInstanceId])
as Map<Object?, Object?>?;
if (replyMap == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyMap['error'] != null) {
final Map<Object?, Object?> error =
(replyMap['error'] as Map<Object?, Object?>?)!;
throw PlatformException(
code: (error['code'] as String?)!,
message: error['message'] as String?,
details: error['details'],
);
} else {
return;
}
}
}

class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec {
const _WKNavigationDelegateFlutterApiCodec();
}

abstract class WKNavigationDelegateFlutterApi {
static const MessageCodec<Object?> codec =
_WKNavigationDelegateFlutterApiCodec();

void didFinishNavigation(
int functionInstanceId, int webViewInstanceId, String? url);
static void setup(WKNavigationDelegateFlutterApi? api,
{BinaryMessenger? binaryMessenger}) {
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
channel.setMessageHandler(null);
} else {
channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_functionInstanceId = (args[0] as int?);
assert(arg_functionInstanceId != null,
'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.');
final int? arg_webViewInstanceId = (args[1] as int?);
assert(arg_webViewInstanceId != null,
'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.');
final String? arg_url = (args[2] as String?);
assert(arg_url != null,
'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null String.');
api.didFinishNavigation(
arg_functionInstanceId!, arg_webViewInstanceId!, arg_url!);
return;
});
}
}
}
}

class _NSObjectHostApiCodec extends StandardMessageCodec {
Expand Down Expand Up @@ -1137,6 +1206,38 @@ class NSObjectHostApi {
}
}

class _FunctionFlutterApiCodec extends StandardMessageCodec {
const _FunctionFlutterApiCodec();
}

abstract class FunctionFlutterApi {
static const MessageCodec<Object?> codec = _FunctionFlutterApiCodec();

void dispose(int instanceId);
static void setup(FunctionFlutterApi? api,
{BinaryMessenger? binaryMessenger}) {
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.FunctionFlutterApi.dispose', codec,
binaryMessenger: binaryMessenger);
if (api == null) {
channel.setMessageHandler(null);
} else {
channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_instanceId = (args[0] as int?);
assert(arg_instanceId != null,
'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose was null, expected non-null int.');
api.dispose(arg_instanceId!);
return;
});
}
}
}
}

class _WKWebViewHostApiCodec extends StandardMessageCodec {
const _WKWebViewHostApiCodec();
@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ class NSObject {
: _api = NSObjectHostApiImpl(
binaryMessenger: binaryMessenger,
instanceManager: instanceManager,
);
) {
FoundationFlutterApis.instance.ensureSetUp();
Comment thread
bparrishMines marked this conversation as resolved.
}

final NSObjectHostApiImpl _api;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

import '../common/function_flutter_api_impls.dart';
import '../common/instance_manager.dart';
import '../common/web_kit.pigeon.dart';
import 'foundation.dart';
Expand Down Expand Up @@ -35,6 +37,46 @@ Iterable<NSKeyValueObservingOptionsEnumData>
});
}

/// Handles initialization of Flutter APIs for the Foundation library.
class FoundationFlutterApis {
/// Constructs a [FoundationFlutterApis].
///
/// This should only be changed for testing purposes.
@visibleForTesting
FoundationFlutterApis({
this.binaryMessenger,
InstanceManager? instanceManager,
}) {
functionFlutterApi =
FunctionFlutterApiImpl(instanceManager: instanceManager);
}

/// Mutable instance containing all Flutter Apis for the Foundation library.
///
/// This should only be changed for testing purposes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to enforce this with a private variable, a public getter, and a @visibleForTesting setter?

static FoundationFlutterApis instance = FoundationFlutterApis();

/// Sends binary data across the Flutter platform barrier.
final BinaryMessenger? binaryMessenger;

bool _hasBeenSetUp = false;

/// Flutter Api for disposing functions.
@visibleForTesting
late final FunctionFlutterApiImpl functionFlutterApi;
Comment thread
bparrishMines marked this conversation as resolved.
Comment thread
stuartmorgan-g marked this conversation as resolved.

/// Ensures all the Flutter APIs have been setup to receive calls from native code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: set up

void ensureSetUp() {
if (!_hasBeenSetUp) {
FunctionFlutterApi.setup(
functionFlutterApi,
binaryMessenger: binaryMessenger,
);
_hasBeenSetUp = true;
}
}
}

/// Host api implementation for [NSObject].
class NSObjectHostApiImpl extends NSObjectHostApi {
/// Constructs an [NSObjectHostApiImpl].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,26 +515,28 @@ class WKUIDelegate {
/// coordinate changes in your web view’s main frame.
///
/// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc).
class WKNavigationDelegate {
class WKNavigationDelegate extends NSObject {
/// Constructs a [WKNavigationDelegate].
WKNavigationDelegate({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
}) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl(
}) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl(
binaryMessenger: binaryMessenger,
instanceManager: instanceManager,
),
super(
binaryMessenger: binaryMessenger,
instanceManager: instanceManager,
) {
WebKitFlutterApis.instance.ensureSetUp();
_navigationDelegateApi.createForInstances(this);
}

final WKNavigationDelegateHostApiImpl _navigationDelegateApi;

/// Called when navigation from the main frame has started.
Future<void> setDidStartProvisionalNavigation(
void Function(
WKWebView webView,
String? url,
)?
void Function(WKWebView webView, String? url)?
didStartProvisionalNavigation,
) {
throw UnimplementedError();
Expand All @@ -544,7 +546,10 @@ class WKNavigationDelegate {
Future<void> setDidFinishNavigation(
void Function(WKWebView webView, String? url)? didFinishNavigation,
) {
throw UnimplementedError();
return _navigationDelegateApi.setDidFinishNavigationFromInstance(
this,
didFinishNavigation,
);
}

/// Called when permission is needed to navigate to new content.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

import '../common/instance_manager.dart';
Expand Down Expand Up @@ -106,6 +107,44 @@ extension _NSUrlRequestConverter on NSUrlRequest {
}
}

/// Handles initialization of Flutter APIs for WebKit.
class WebKitFlutterApis {
/// Constructs a [WebKitFlutterApis].
///
/// This should only be changed for testing purposes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't make sense here, since the constructor doesn't change anything.

@visibleForTesting
WebKitFlutterApis({this.binaryMessenger, InstanceManager? instanceManager}) {
navigationDelegateFlutterApi = WKNavigationDelegateFlutterApiImpl(
instanceManager: instanceManager,
);
}

/// Mutable instance containing all Flutter Apis for WebKit.
///
/// This should only be changed for testing purposes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same note here as in the other file; this can be a getter+annotated setter

static WebKitFlutterApis instance = WebKitFlutterApis();

/// Sends binary data across the Flutter platform barrier.
final BinaryMessenger? binaryMessenger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want this to be public?


bool _hasBeenSetUp = false;

/// Flutter Api for [WKNavigationDelegate].
@visibleForTesting
late final WKNavigationDelegateFlutterApiImpl navigationDelegateFlutterApi;

/// Ensures all the Flutter APIs have been setup to receive calls from native code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set up

void ensureSetUp() {
if (!_hasBeenSetUp) {
WKNavigationDelegateFlutterApi.setup(
navigationDelegateFlutterApi,
binaryMessenger: binaryMessenger,
);
_hasBeenSetUp = true;
}
}
}

/// Host api implementation for [WKWebSiteDataStore].
class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi {
/// Constructs a [WebsiteDataStoreHostApiImpl].
Expand Down Expand Up @@ -381,6 +420,53 @@ class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi {
await create(instanceId);
}
}

/// Calls [setDidFinishNavigation] with the ids of the provided object instances.
Future<void> setDidFinishNavigationFromInstance(
WKNavigationDelegate instance,
void Function(WKWebView, String?)? didFinishNavigation,
) {
int? functionInstanceId;
if (didFinishNavigation != null) {
functionInstanceId = instanceManager.getInstanceId(didFinishNavigation);
functionInstanceId ??=

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why = then ??= rather than just functionInstanceId = ... ?? ...;?

instanceManager.tryAddInstance(didFinishNavigation);
}
return setDidFinishNavigation(
instanceManager.getInstanceId(instance)!,
functionInstanceId,
);
}
}

/// Flutter api implementation for [WKNavigationDelegate].
class WKNavigationDelegateFlutterApiImpl
extends WKNavigationDelegateFlutterApi {
/// Constructs a [WKNavigationDelegateFlutterApiImpl].
WKNavigationDelegateFlutterApiImpl({InstanceManager? instanceManager}) {
this.instanceManager = instanceManager ?? InstanceManager.instance;
}

/// Maintains instances stored to communicate with native language objects.
late final InstanceManager instanceManager;

@override
void didFinishNavigation(
int functionInstanceId,
int webViewInstanceId,
String? url,
) {
final void Function(
WKWebView webView,
String? url,
) function =
instanceManager.getInstance(functionInstanceId)! as void Function(
WKWebView webView,
String? url,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems worth typedefing this instead of repeating the signature twice.

@bparrishMines bparrishMines Apr 13, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually realized that InstanceManager.getInstance uses generics to make this casting easier. So, I didn't actually need the as ....

);

function(instanceManager.getInstance(webViewInstanceId)!, url);
}
}

/// Host api implementation for [WKWebView].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,20 @@ abstract class WKScriptMessageHandlerHostApi {
@HostApi(dartHostTestHandler: 'TestWKNavigationDelegateHostApi')
abstract class WKNavigationDelegateHostApi {
void create(int instanceId);

void setDidFinishNavigation(int instanceId, int? functionInstanceId);
}

/// Mirror of WKNavigationDelegate.
///
/// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc.
@FlutterApi()
abstract class WKNavigationDelegateFlutterApi {
void didFinishNavigation(
int functionInstanceId,
int webViewInstanceId,
String? url,
);
}

/// Mirror of NSObject.
Expand All @@ -300,6 +314,12 @@ abstract class NSObjectHostApi {
void removeObserver(int instanceId, int observerInstanceId, String keyPath);
}

/// Disposes references to functions.
@FlutterApi()
abstract class FunctionFlutterApi {
void dispose(int instanceId);
}

/// Mirror of WKWebView.
///
/// See https://developer.apple.com/documentation/webkit/wkwebview?language=objc.
Expand Down
Loading