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

[webview_flutter_wkwebview] Implement WKNavigationDelegate.didFinishNavigation as a proof of concept for callback methods #5199

Merged
merged 8 commits into from
Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all 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,11 @@ class NSObject {
: _api = NSObjectHostApiImpl(
binaryMessenger: binaryMessenger,
instanceManager: instanceManager,
);
) {
// Ensures FlutterApis for the Foundation library and FunctionFlutterApi are
// set up.
FoundationFlutterApis.instance.ensureSetUp();
}

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,54 @@ Iterable<NSKeyValueObservingOptionsEnumData>
});
}

/// Handles initialization of Flutter APIs for the Foundation library.
class FoundationFlutterApis {
/// Constructs a [FoundationFlutterApis].
@visibleForTesting
FoundationFlutterApis({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
}) : _binaryMessenger = binaryMessenger {
functionFlutterApi =
FunctionFlutterApiImpl(instanceManager: instanceManager);
}

static FoundationFlutterApis _instance = FoundationFlutterApis();

/// Sets the global instance containing the Flutter Apis for the Foundation library.
@visibleForTesting
static set instance(FoundationFlutterApis instance) {
_instance = instance;
}

/// Global instance containing the Flutter Apis for the Foundation library.
static FoundationFlutterApis get instance {
return _instance;
}

final BinaryMessenger? _binaryMessenger;
bool _hasBeenSetUp = false;

/// Flutter Api for disposing functions.
///
/// This FlutterApi is placed here because [FoundationFlutterApis.ensureSetUp]
/// is called inside [NSObject] and [NSObject] is the parent class of all
/// objects.
@visibleForTesting
late final FunctionFlutterApiImpl functionFlutterApi;

/// Ensures all the Flutter APIs have been set up to receive calls from native code.
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,51 @@ extension _NSUrlRequestConverter on NSUrlRequest {
}
}

/// Handles initialization of Flutter APIs for WebKit.
class WebKitFlutterApis {
/// Constructs a [WebKitFlutterApis].
@visibleForTesting
WebKitFlutterApis({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
}) : _binaryMessenger = binaryMessenger {
navigationDelegateFlutterApi = WKNavigationDelegateFlutterApiImpl(
instanceManager: instanceManager,
);
}

static WebKitFlutterApis _instance = WebKitFlutterApis();

/// Sets the global instance containing the Flutter Apis for the WebKit library.
@visibleForTesting
static set instance(WebKitFlutterApis instance) {
_instance = instance;
}

/// Global instance containing the Flutter Apis for the WebKit library.
static WebKitFlutterApis get instance {
return _instance;
}

final BinaryMessenger? _binaryMessenger;
bool _hasBeenSetUp = false;

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

/// Ensures all the Flutter APIs have been set up to receive calls from native code.
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 +427,47 @@ 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) ??
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)!;
function(instanceManager.getInstance(webViewInstanceId)!, url);
}
}

/// Host api implementation for [WKWebView].
Expand Down
Loading