[webview_flutter_android] Adds Android implementation to override console log (#4702)

Adds the Android implementation for registering a JavaScript console callback. This will allow developers to receive JavaScript console messages in a Dart callback.

This PR contains the `webview_flutter_android` specific changes from PR #4541.

Related issue: flutter/flutter#32908

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
This commit is contained in:
Maurits van Beusekom
2023-09-29 21:39:07 +02:00
committed by GitHub
parent 95b9959463
commit eac45de20d
23 changed files with 1064 additions and 11 deletions

View File

@ -1,3 +1,7 @@
## 3.11.0
* Adds support to register a callback to receive JavaScript console messages. See `AndroidWebViewController.onConsoleMessage`.
## 3.10.1
* Bumps androidx.annotation:annotation from 1.5.0 to 1.7.0.

View File

@ -94,6 +94,62 @@ public class GeneratedAndroidWebView {
}
}
/**
* Indicates the type of message logged to the console.
*
* <p>See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel.
*/
public enum ConsoleMessageLevel {
/**
* Indicates a message is logged for debugging.
*
* <p>See
* https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#DEBUG.
*/
DEBUG(0),
/**
* Indicates a message is provided as an error.
*
* <p>See
* https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#ERROR.
*/
ERROR(1),
/**
* Indicates a message is provided as a basic log message.
*
* <p>See
* https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#LOG.
*/
LOG(2),
/**
* Indicates a message is provided as a tip.
*
* <p>See
* https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#TIP.
*/
TIP(3),
/**
* Indicates a message is provided as a warning.
*
* <p>See
* https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#WARNING.
*/
WARNING(4),
/**
* Indicates a message with an unknown level.
*
* <p>This does not represent an actual value provided by the platform and only indicates a
* value was provided that isn't currently supported.
*/
UNKNOWN(5);
final int index;
private ConsoleMessageLevel(final int index) {
this.index = index;
}
}
/** Generated class from Pigeon that represents data sent in messages. */
public static final class WebResourceRequestData {
private @NonNull String url;
@ -409,6 +465,136 @@ public class GeneratedAndroidWebView {
}
}
/**
* Represents a JavaScript console message from WebCore.
*
* <p>See https://developer.android.com/reference/android/webkit/ConsoleMessage
*
* <p>Generated class from Pigeon that represents data sent in messages.
*/
public static final class ConsoleMessage {
private @NonNull Long lineNumber;
public @NonNull Long getLineNumber() {
return lineNumber;
}
public void setLineNumber(@NonNull Long setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"lineNumber\" is null.");
}
this.lineNumber = setterArg;
}
private @NonNull String message;
public @NonNull String getMessage() {
return message;
}
public void setMessage(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"message\" is null.");
}
this.message = setterArg;
}
private @NonNull ConsoleMessageLevel level;
public @NonNull ConsoleMessageLevel getLevel() {
return level;
}
public void setLevel(@NonNull ConsoleMessageLevel setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"level\" is null.");
}
this.level = setterArg;
}
private @NonNull String sourceId;
public @NonNull String getSourceId() {
return sourceId;
}
public void setSourceId(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"sourceId\" is null.");
}
this.sourceId = setterArg;
}
/** Constructor is non-public to enforce null safety; use Builder. */
ConsoleMessage() {}
public static final class Builder {
private @Nullable Long lineNumber;
public @NonNull Builder setLineNumber(@NonNull Long setterArg) {
this.lineNumber = setterArg;
return this;
}
private @Nullable String message;
public @NonNull Builder setMessage(@NonNull String setterArg) {
this.message = setterArg;
return this;
}
private @Nullable ConsoleMessageLevel level;
public @NonNull Builder setLevel(@NonNull ConsoleMessageLevel setterArg) {
this.level = setterArg;
return this;
}
private @Nullable String sourceId;
public @NonNull Builder setSourceId(@NonNull String setterArg) {
this.sourceId = setterArg;
return this;
}
public @NonNull ConsoleMessage build() {
ConsoleMessage pigeonReturn = new ConsoleMessage();
pigeonReturn.setLineNumber(lineNumber);
pigeonReturn.setMessage(message);
pigeonReturn.setLevel(level);
pigeonReturn.setSourceId(sourceId);
return pigeonReturn;
}
}
@NonNull
ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(4);
toListResult.add(lineNumber);
toListResult.add(message);
toListResult.add(level == null ? null : level.index);
toListResult.add(sourceId);
return toListResult;
}
static @NonNull ConsoleMessage fromList(@NonNull ArrayList<Object> list) {
ConsoleMessage pigeonResult = new ConsoleMessage();
Object lineNumber = list.get(0);
pigeonResult.setLineNumber(
(lineNumber == null)
? null
: ((lineNumber instanceof Integer) ? (Integer) lineNumber : (Long) lineNumber));
Object message = list.get(1);
pigeonResult.setMessage((String) message);
Object level = list.get(2);
pigeonResult.setLevel(ConsoleMessageLevel.values()[(int) level]);
Object sourceId = list.get(3);
pigeonResult.setSourceId((String) sourceId);
return pigeonResult;
}
}
public interface Result<T> {
@SuppressWarnings("UnknownNullness")
void success(T result);
@ -2401,6 +2587,9 @@ public class GeneratedAndroidWebView {
void setSynchronousReturnValueForOnShowFileChooser(
@NonNull Long instanceId, @NonNull Boolean value);
void setSynchronousReturnValueForOnConsoleMessage(
@NonNull Long instanceId, @NonNull Boolean value);
/** The codec used by WebChromeClientHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
@ -2463,6 +2652,33 @@ public class GeneratedAndroidWebView {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnConsoleMessage",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number instanceIdArg = (Number) args.get(0);
Boolean valueArg = (Boolean) args.get(1);
try {
api.setSynchronousReturnValueForOnConsoleMessage(
(instanceIdArg == null) ? null : instanceIdArg.longValue(), valueArg);
wrapped.add(0, null);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
@ -2536,6 +2752,34 @@ public class GeneratedAndroidWebView {
}
}
}
private static class WebChromeClientFlutterApiCodec extends StandardMessageCodec {
public static final WebChromeClientFlutterApiCodec INSTANCE =
new WebChromeClientFlutterApiCodec();
private WebChromeClientFlutterApiCodec() {}
@Override
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
switch (type) {
case (byte) 128:
return ConsoleMessage.fromList((ArrayList<Object>) readValue(buffer));
default:
return super.readValueOfType(type, buffer);
}
}
@Override
protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
if (value instanceof ConsoleMessage) {
stream.write(128);
writeValue(stream, ((ConsoleMessage) value).toList());
} else {
super.writeValue(stream, value);
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
public static class WebChromeClientFlutterApi {
private final @NonNull BinaryMessenger binaryMessenger;
@ -2551,7 +2795,7 @@ public class GeneratedAndroidWebView {
}
/** The codec used by WebChromeClientFlutterApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
return WebChromeClientFlutterApiCodec.INSTANCE;
}
public void onProgressChanged(
@ -2656,6 +2900,20 @@ public class GeneratedAndroidWebView {
new ArrayList<Object>(Collections.singletonList(identifierArg)),
channelReply -> callback.reply(null));
}
/** Callback to Dart function `WebChromeClient.onConsoleMessage`. */
public void onConsoleMessage(
@NonNull Long instanceIdArg,
@NonNull ConsoleMessage messageArg,
@NonNull Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebChromeClientFlutterApi.onConsoleMessage",
getCodec());
channel.send(
new ArrayList<Object>(Arrays.asList(instanceIdArg, messageArg)),
channelReply -> callback.reply(null));
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface WebStorageHostApi {

View File

@ -6,6 +6,7 @@ package io.flutter.plugins.webviewflutter;
import android.os.Build;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;
@ -27,6 +28,24 @@ public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
private final InstanceManager instanceManager;
private final WebViewFlutterApiImpl webViewFlutterApi;
private static GeneratedAndroidWebView.ConsoleMessageLevel toConsoleMessageLevel(
ConsoleMessage.MessageLevel level) {
switch (level) {
case TIP:
return GeneratedAndroidWebView.ConsoleMessageLevel.TIP;
case LOG:
return GeneratedAndroidWebView.ConsoleMessageLevel.LOG;
case WARNING:
return GeneratedAndroidWebView.ConsoleMessageLevel.WARNING;
case ERROR:
return GeneratedAndroidWebView.ConsoleMessageLevel.ERROR;
case DEBUG:
return GeneratedAndroidWebView.ConsoleMessageLevel.DEBUG;
}
return GeneratedAndroidWebView.ConsoleMessageLevel.UNKNOWN;
}
/**
* Creates a Flutter api that sends messages to Dart.
*
@ -149,6 +168,25 @@ public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
callback);
}
/**
* Sends a message to Dart to call `WebChromeClient.onConsoleMessage` on the Dart object
* representing `instance`.
*/
public void onConsoleMessage(
@NonNull WebChromeClient instance,
@NonNull ConsoleMessage message,
@NonNull Reply<Void> callback) {
super.onConsoleMessage(
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(instance)),
new GeneratedAndroidWebView.ConsoleMessage.Builder()
.setLineNumber((long) message.lineNumber())
.setMessage(message.message())
.setLevel(toConsoleMessageLevel(message.messageLevel()))
.setSourceId(message.sourceId())
.build(),
callback);
}
private long getIdentifierForClient(WebChromeClient webChromeClient) {
final Long identifier = instanceManager.getIdentifierForStrongReference(webChromeClient);
if (identifier == null) {

View File

@ -9,6 +9,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Message;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
@ -40,6 +41,7 @@ public class WebChromeClientHostApiImpl implements WebChromeClientHostApi {
public static class WebChromeClientImpl extends SecureWebChromeClient {
private final WebChromeClientFlutterApiImpl flutterApi;
private boolean returnValueForOnShowFileChooser = false;
private boolean returnValueForOnConsoleMessage = false;
/**
* Creates a {@link WebChromeClient} that passes arguments of callbacks methods to Dart.
@ -107,10 +109,21 @@ public class WebChromeClientHostApiImpl implements WebChromeClientHostApi {
flutterApi.onPermissionRequest(this, request, reply -> {});
}
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
flutterApi.onConsoleMessage(this, consoleMessage, reply -> {});
return returnValueForOnConsoleMessage;
}
/** Sets return value for {@link #onShowFileChooser}. */
public void setReturnValueForOnShowFileChooser(boolean value) {
returnValueForOnShowFileChooser = value;
}
/** Sets return value for {@link #onConsoleMessage}. */
public void setReturnValueForOnConsoleMessage(boolean value) {
returnValueForOnConsoleMessage = value;
}
}
/**
@ -246,4 +259,12 @@ public class WebChromeClientHostApiImpl implements WebChromeClientHostApi {
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setReturnValueForOnShowFileChooser(value);
}
@Override
public void setSynchronousReturnValueForOnConsoleMessage(
@NonNull Long instanceId, @NonNull Boolean value) {
final WebChromeClientImpl webChromeClient =
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setReturnValueForOnConsoleMessage(value);
}
}

View File

@ -16,6 +16,7 @@ import static org.mockito.Mockito.when;
import android.net.Uri;
import android.os.Message;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;
@ -162,4 +163,17 @@ public class WebChromeClientTest {
webChromeClient.onGeolocationPermissionsHidePrompt();
verify(mockFlutterApi).onGeolocationPermissionsHidePrompt(eq(webChromeClient), any());
}
@Test
public void onConsoleMessage() {
webChromeClient.onConsoleMessage(
new ConsoleMessage("message", "sourceId", 23, ConsoleMessage.MessageLevel.ERROR));
verify(mockFlutterApi).onConsoleMessage(eq(webChromeClient), any(), any());
}
@Test
public void setReturnValueForOnConsoleMessage() {
webChromeClient.setReturnValueForOnConsoleMessage(true);
assertTrue(webChromeClient.onConsoleMessage(null));
}
}

View File

@ -1310,6 +1310,47 @@ Future<void> main() async {
);
},
);
group('Logging', () {
testWidgets('can receive console log messages',
(WidgetTester tester) async {
const String testPage = '''
<!DOCTYPE html>
<html>
<head>
<title>WebResourceError test</title>
</head>
<body onload="console.debug('Debug message')">
<p>Test page</p>
</body>
</html>
''';
final Completer<String> debugMessageReceived = Completer<String>();
final PlatformWebViewController controller = PlatformWebViewController(
const PlatformWebViewControllerCreationParams(),
);
unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));
await controller.setOnConsoleMessage((JavaScriptConsoleMessage message) {
debugMessageReceived
.complete('${message.level.name}:${message.message}');
});
await controller.loadHtmlString(testPage);
await tester.pumpWidget(Builder(
builder: (BuildContext context) {
return PlatformWebViewWidget(
PlatformWebViewWidgetCreationParams(controller: controller),
).build(context);
},
));
await expectLater(
debugMessageReceived.future, completion('debug:Debug message'));
});
});
}
/// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests.

View File

@ -73,6 +73,40 @@ const String kTransparentBackgroundPage = '''
</html>
''';
const String kLogExamplePage = '''
<!DOCTYPE html>
<html lang="en">
<head>
<title>Load file or HTML string example</title>
</head>
<body onload="console.log('Logging that the page is loading.')">
<h1>Local demo page</h1>
<p>
This page is used to test the forwarding of console logs to Dart.
</p>
<style>
.btn-group button {
padding: 24px; 24px;
display: block;
width: 25%;
margin: 5px 0px 0px 0px;
}
</style>
<div class="btn-group">
<button onclick="console.error('This is an error message.')">Error</button>
<button onclick="console.warn('This is a warning message.')">Warning</button>
<button onclick="console.info('This is a info message.')">Info</button>
<button onclick="console.debug('This is a debug message.')">Debug</button>
<button onclick="console.log('This is a log message.')">Log</button>
</div>
</body>
</html>
''';
class WebViewExample extends StatefulWidget {
const WebViewExample({super.key, this.cookieManager});
@ -202,6 +236,7 @@ enum MenuOptions {
transparentBackground,
setCookie,
videoExample,
logExample,
}
class SampleMenu extends StatelessWidget {
@ -265,6 +300,9 @@ class SampleMenu extends StatelessWidget {
case MenuOptions.videoExample:
_onVideoExample(context);
break;
case MenuOptions.logExample:
_onLogExample();
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
@ -321,6 +359,10 @@ class SampleMenu extends StatelessWidget {
value: MenuOptions.transparentBackground,
child: Text('Transparent background example'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.logExample,
child: Text('Log example'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.videoExample,
child: Text('Video example'),
@ -497,6 +539,16 @@ class SampleMenu extends StatelessWidget {
return indexFile.path;
}
Future<void> _onLogExample() {
webViewController
.setOnConsoleMessage((JavaScriptConsoleMessage consoleMessage) {
debugPrint(
'== JS == ${consoleMessage.level.name}: ${consoleMessage.message}');
});
return webViewController.loadHtmlString(kLogExamplePage);
}
}
class NavigationControls extends StatelessWidget {

View File

@ -17,7 +17,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
webview_flutter_platform_interface: ^2.4.0
webview_flutter_platform_interface: ^2.6.0
dev_dependencies:
espresso: ^0.2.0

View File

@ -44,6 +44,9 @@ class AndroidWebViewProxy {
onGeolocationPermissionsShowPrompt,
void Function(android_webview.WebChromeClient instance)?
onGeolocationPermissionsHidePrompt,
void Function(android_webview.WebChromeClient instance,
android_webview.ConsoleMessage message)?
onConsoleMessage,
void Function(
android_webview.WebChromeClient instance,
android_webview.View view,

View File

@ -12,7 +12,8 @@ import 'android_webview.g.dart';
import 'android_webview_api_impls.dart';
import 'instance_manager.dart';
export 'android_webview_api_impls.dart' show FileChooserMode;
export 'android_webview_api_impls.dart'
show ConsoleMessage, ConsoleMessageLevel, FileChooserMode;
/// Root of the Java class hierarchy.
///
@ -1047,6 +1048,7 @@ class WebChromeClient extends JavaObject {
this.onGeolocationPermissionsHidePrompt,
this.onShowCustomView,
this.onHideCustomView,
this.onConsoleMessage,
@visibleForTesting super.binaryMessenger,
@visibleForTesting super.instanceManager,
}) : super.detached() {
@ -1068,6 +1070,7 @@ class WebChromeClient extends JavaObject {
this.onGeolocationPermissionsHidePrompt,
this.onShowCustomView,
this.onHideCustomView,
this.onConsoleMessage,
super.binaryMessenger,
super.instanceManager,
}) : super.detached();
@ -1121,6 +1124,10 @@ class WebChromeClient extends JavaObject {
/// mode.
final HideCustomViewCallback? onHideCustomView;
/// Report a JavaScript console message to the host application.
final void Function(WebChromeClient instance, ConsoleMessage message)?
onConsoleMessage;
/// Sets the required synchronous return value for the Java method,
/// `WebChromeClient.onShowFileChooser(...)`.
///
@ -1150,6 +1157,33 @@ class WebChromeClient extends JavaObject {
);
}
/// Sets the required synchronous return value for the Java method,
/// `WebChromeClient.onShowFileChooser(...)`.
///
/// The Java method, `WebChromeClient.onConsoleMessage(...)`, requires
/// a boolean to be returned and this method sets the returned value for all
/// calls to the Java method.
///
/// Setting this to true indicates that the client is handling all console
/// messages.
///
/// Requires [onConsoleMessage] to be nonnull.
///
/// Defaults to false.
Future<void> setSynchronousReturnValueForOnConsoleMessage(
bool value,
) {
if (value && onConsoleMessage == null) {
throw StateError(
'Setting this to true requires `onConsoleMessage` to be nonnull.',
);
}
return api.setSynchronousReturnValueForOnConsoleMessageFromInstance(
this,
value,
);
}
@override
WebChromeClient copy() {
return WebChromeClient.detached(
@ -1160,6 +1194,7 @@ class WebChromeClient extends JavaObject {
onGeolocationPermissionsHidePrompt: onGeolocationPermissionsHidePrompt,
onShowCustomView: onShowCustomView,
onHideCustomView: onHideCustomView,
onConsoleMessage: onConsoleMessage,
binaryMessenger: _api.binaryMessenger,
instanceManager: _api.instanceManager,
);

View File

@ -32,6 +32,42 @@ enum FileChooserMode {
save,
}
/// Indicates the type of message logged to the console.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel.
enum ConsoleMessageLevel {
/// Indicates a message is logged for debugging.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#DEBUG.
debug,
/// Indicates a message is provided as an error.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#ERROR.
error,
/// Indicates a message is provided as a basic log message.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#LOG.
log,
/// Indicates a message is provided as a tip.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#TIP.
tip,
/// Indicates a message is provided as a warning.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#WARNING.
warning,
/// Indicates a message with an unknown level.
///
/// This does not represent an actual value provided by the platform and only
/// indicates a value was provided that isn't currently supported.
unknown,
}
class WebResourceRequestData {
WebResourceRequestData({
required this.url,
@ -131,6 +167,45 @@ class WebViewPoint {
}
}
/// Represents a JavaScript console message from WebCore.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage
class ConsoleMessage {
ConsoleMessage({
required this.lineNumber,
required this.message,
required this.level,
required this.sourceId,
});
int lineNumber;
String message;
ConsoleMessageLevel level;
String sourceId;
Object encode() {
return <Object?>[
lineNumber,
message,
level.index,
sourceId,
];
}
static ConsoleMessage decode(Object result) {
result as List<Object?>;
return ConsoleMessage(
lineNumber: result[0]! as int,
message: result[1]! as String,
level: ConsoleMessageLevel.values[result[2]! as int],
sourceId: result[3]! as String,
);
}
}
/// Host API for managing the native `InstanceManager`.
class InstanceManagerHostApi {
/// Constructor for [InstanceManagerHostApi]. The [binaryMessenger] named argument is
@ -1943,6 +2018,30 @@ class WebChromeClientHostApi {
return;
}
}
Future<void> setSynchronousReturnValueForOnConsoleMessage(
int arg_instanceId, bool arg_value) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnConsoleMessage',
codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel
.send(<Object?>[arg_instanceId, arg_value]) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else {
return;
}
}
}
class FlutterAssetManagerHostApi {
@ -2012,8 +2111,31 @@ class FlutterAssetManagerHostApi {
}
}
class _WebChromeClientFlutterApiCodec extends StandardMessageCodec {
const _WebChromeClientFlutterApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is ConsoleMessage) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
return ConsoleMessage.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
}
}
abstract class WebChromeClientFlutterApi {
static const MessageCodec<Object?> codec = StandardMessageCodec();
static const MessageCodec<Object?> codec = _WebChromeClientFlutterApiCodec();
void onProgressChanged(int instanceId, int webViewInstanceId, int progress);
@ -2037,6 +2159,9 @@ abstract class WebChromeClientFlutterApi {
/// Callback to Dart function `WebChromeClient.onGeolocationPermissionsHidePrompt`.
void onGeolocationPermissionsHidePrompt(int identifier);
/// Callback to Dart function `WebChromeClient.onConsoleMessage`.
void onConsoleMessage(int instanceId, ConsoleMessage message);
static void setup(WebChromeClientFlutterApi? api,
{BinaryMessenger? binaryMessenger}) {
{
@ -2210,6 +2335,29 @@ abstract class WebChromeClientFlutterApi {
});
}
}
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.webview_flutter_android.WebChromeClientFlutterApi.onConsoleMessage',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
channel.setMessageHandler(null);
} else {
channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.webview_flutter_android.WebChromeClientFlutterApi.onConsoleMessage 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.webview_flutter_android.WebChromeClientFlutterApi.onConsoleMessage was null, expected non-null int.');
final ConsoleMessage? arg_message = (args[1] as ConsoleMessage?);
assert(arg_message != null,
'Argument for dev.flutter.pigeon.webview_flutter_android.WebChromeClientFlutterApi.onConsoleMessage was null, expected non-null ConsoleMessage.');
api.onConsoleMessage(arg_instanceId!, arg_message!);
return;
});
}
}
}
}

View File

@ -11,7 +11,8 @@ import 'android_webview.dart';
import 'android_webview.g.dart';
import 'instance_manager.dart';
export 'android_webview.g.dart' show FileChooserMode;
export 'android_webview.g.dart'
show ConsoleMessage, ConsoleMessageLevel, FileChooserMode;
/// Converts [WebResourceRequestData] to [WebResourceRequest]
WebResourceRequest _toWebResourceRequest(WebResourceRequestData data) {
@ -892,6 +893,17 @@ class WebChromeClientHostApiImpl extends WebChromeClientHostApi {
value,
);
}
/// Helper method to convert instances ids to objects.
Future<void> setSynchronousReturnValueForOnConsoleMessageFromInstance(
WebChromeClient instance,
bool value,
) {
return setSynchronousReturnValueForOnConsoleMessage(
instanceManager.getIdentifier(instance)!,
value,
);
}
}
/// Flutter api implementation for [DownloadListener].
@ -1017,6 +1029,13 @@ class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
);
}
}
@override
void onConsoleMessage(int instanceId, ConsoleMessage message) {
final WebChromeClient instance =
instanceManager.getInstanceWithWeakReference(instanceId)!;
instance.onConsoleMessage?.call(instance, message);
}
}
/// Host api implementation for [WebStorage].

View File

@ -13,6 +13,7 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_inte
import 'android_proxy.dart';
import 'android_webview.dart' as android_webview;
import 'android_webview_api_impls.dart';
import 'instance_manager.dart';
import 'platform_views_service_proxy.dart';
import 'weak_reference_utils.dart';
@ -188,6 +189,42 @@ class AndroidWebViewController extends PlatformWebViewController {
};
},
),
onConsoleMessage: withWeakReferenceTo(
this,
(WeakReference<AndroidWebViewController> weakReference) {
return (android_webview.WebChromeClient webChromeClient,
android_webview.ConsoleMessage consoleMessage) async {
final void Function(JavaScriptConsoleMessage)? callback =
weakReference.target?._onConsoleLogCallback;
if (callback != null) {
JavaScriptLogLevel logLevel;
switch (consoleMessage.level) {
// Android maps `console.debug` to `MessageLevel.TIP`, it seems
// `MessageLevel.DEBUG` if not being used.
case ConsoleMessageLevel.debug:
case ConsoleMessageLevel.tip:
logLevel = JavaScriptLogLevel.debug;
break;
case ConsoleMessageLevel.error:
logLevel = JavaScriptLogLevel.error;
break;
case ConsoleMessageLevel.warning:
logLevel = JavaScriptLogLevel.warning;
break;
case ConsoleMessageLevel.unknown:
case ConsoleMessageLevel.log:
logLevel = JavaScriptLogLevel.log;
break;
}
callback(JavaScriptConsoleMessage(
level: logLevel,
message: consoleMessage.message,
));
}
};
},
),
onPermissionRequest: withWeakReferenceTo(
this,
(WeakReference<AndroidWebViewController> weakReference) {
@ -255,6 +292,8 @@ class AndroidWebViewController extends PlatformWebViewController {
void Function(PlatformWebViewPermissionRequest)? _onPermissionRequestCallback;
void Function(JavaScriptConsoleMessage consoleMessage)? _onConsoleLogCallback;
/// Whether to enable the platform's webview content debugging tools.
///
/// Defaults to false.
@ -566,6 +605,18 @@ class AndroidWebViewController extends PlatformWebViewController {
_onShowCustomWidgetCallback = onShowCustomWidget;
_onHideCustomWidgetCallback = onHideCustomWidget;
}
/// Sets a callback that notifies the host application of any log messages
/// written to the JavaScript console.
@override
Future<void> setOnConsoleMessage(
void Function(JavaScriptConsoleMessage consoleMessage)
onConsoleMessage) async {
_onConsoleLogCallback = onConsoleMessage;
return _webChromeClient.setSynchronousReturnValueForOnConsoleMessage(
_onConsoleLogCallback != null);
}
}
/// Android implementation of [PlatformWebViewPermissionRequest].

View File

@ -57,6 +57,42 @@ enum FileChooserMode {
save,
}
/// Indicates the type of message logged to the console.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel.
enum ConsoleMessageLevel {
/// Indicates a message is logged for debugging.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#DEBUG.
debug,
/// Indicates a message is provided as an error.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#ERROR.
error,
/// Indicates a message is provided as a basic log message.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#LOG.
log,
/// Indicates a message is provided as a tip.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#TIP.
tip,
/// Indicates a message is provided as a warning.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage.MessageLevel#WARNING.
warning,
/// Indicates a message with an unknown level.
///
/// This does not represent an actual value provided by the platform and only
/// indicates a value was provided that isn't currently supported.
unknown,
}
class WebResourceRequestData {
WebResourceRequestData(
this.url,
@ -89,6 +125,16 @@ class WebViewPoint {
int y;
}
/// Represents a JavaScript console message from WebCore.
///
/// See https://developer.android.com/reference/android/webkit/ConsoleMessage
class ConsoleMessage {
late int lineNumber;
late String message;
late ConsoleMessageLevel level;
late String sourceId;
}
/// Handles methods calls to the native Java Object class.
///
/// Also handles calls to remove the reference to an instance with `dispose`.
@ -337,6 +383,11 @@ abstract class WebChromeClientHostApi {
int instanceId,
bool value,
);
void setSynchronousReturnValueForOnConsoleMessage(
int instanceId,
bool value,
);
}
@HostApi(dartHostTestHandler: 'TestAssetManagerHostApi')
@ -379,6 +430,9 @@ abstract class WebChromeClientFlutterApi {
/// Callback to Dart function `WebChromeClient.onGeolocationPermissionsHidePrompt`.
void onGeolocationPermissionsHidePrompt(int identifier);
/// Callback to Dart function `WebChromeClient.onConsoleMessage`.
void onConsoleMessage(int instanceId, ConsoleMessage message);
}
@HostApi(dartHostTestHandler: 'TestWebStorageHostApi')

View File

@ -2,7 +2,7 @@ name: webview_flutter_android
description: A Flutter plugin that provides a WebView widget on Android.
repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
version: 3.10.1
version: 3.11.0
environment:
sdk: ">=2.19.0 <4.0.0"
@ -20,7 +20,7 @@ flutter:
dependencies:
flutter:
sdk: flutter
webview_flutter_platform_interface: ^2.4.0
webview_flutter_platform_interface: ^2.6.0
dev_dependencies:
build_runner: ^2.1.4

View File

@ -520,6 +520,7 @@ class CapturingWebChromeClient extends android_webview.WebChromeClient {
super.onShowCustomView,
super.onHideCustomView,
super.onPermissionRequest,
super.onConsoleMessage,
super.binaryMessenger,
super.instanceManager,
}) : super.detached() {

View File

@ -73,6 +73,9 @@ void main() {
android_webview.CustomViewCallback callback)?
onShowCustomView,
void Function(android_webview.WebChromeClient instance)? onHideCustomView,
void Function(android_webview.WebChromeClient instance,
android_webview.ConsoleMessage message)?
onConsoleMessage,
})? createWebChromeClient,
android_webview.WebView? mockWebView,
android_webview.WebViewClient? mockWebViewClient,
@ -111,6 +114,9 @@ void main() {
onShowCustomView,
void Function(android_webview.WebChromeClient instance)?
onHideCustomView,
void Function(android_webview.WebChromeClient instance,
android_webview.ConsoleMessage message)?
onConsoleMessage,
}) =>
MockWebChromeClient(),
createAndroidWebView: () => nonNullMockWebView,
@ -606,6 +612,7 @@ void main() {
dynamic onPermissionRequest,
dynamic onShowCustomView,
dynamic onHideCustomView,
dynamic onConsoleMessage,
}) {
onShowFileChooserCallback = onShowFileChooser!;
return mockWebChromeClient;
@ -676,6 +683,7 @@ void main() {
dynamic onPermissionRequest,
dynamic onShowCustomView,
dynamic onHideCustomView,
dynamic onConsoleMessage,
}) {
onGeoPermissionHandle = onGeolocationPermissionsShowPrompt!;
onGeoPermissionHidePromptHandle = onGeolocationPermissionsHidePrompt!;
@ -750,6 +758,7 @@ void main() {
onShowCustomView,
void Function(android_webview.WebChromeClient instance)?
onHideCustomView,
dynamic onConsoleMessage,
}) {
onShowCustomViewHandle = onShowCustomView!;
onHideCustomViewHandle = onHideCustomView!;
@ -802,6 +811,7 @@ void main() {
)? onPermissionRequest,
dynamic onShowCustomView,
dynamic onHideCustomView,
dynamic onConsoleMessage,
}) {
onPermissionRequestCallback = onPermissionRequest!;
return mockWebChromeClient;
@ -856,6 +866,7 @@ void main() {
)? onPermissionRequest,
dynamic onShowCustomView,
dynamic onHideCustomView,
dynamic onConsoleMessage,
}) {
onPermissionRequestCallback = onPermissionRequest!;
return mockWebChromeClient;
@ -881,6 +892,104 @@ void main() {
expect(callbackCalled, isFalse);
});
test('setOnConsoleLogCallback', () async {
late final void Function(
android_webview.WebChromeClient instance,
android_webview.ConsoleMessage message,
) onConsoleMessageCallback;
final MockWebChromeClient mockWebChromeClient = MockWebChromeClient();
final AndroidWebViewController controller = createControllerWithMocks(
createWebChromeClient: ({
dynamic onProgressChanged,
dynamic onShowFileChooser,
dynamic onGeolocationPermissionsShowPrompt,
dynamic onGeolocationPermissionsHidePrompt,
dynamic onPermissionRequest,
dynamic onShowCustomView,
dynamic onHideCustomView,
void Function(
android_webview.WebChromeClient,
android_webview.ConsoleMessage,
)? onConsoleMessage,
}) {
onConsoleMessageCallback = onConsoleMessage!;
return mockWebChromeClient;
},
);
final Map<String, JavaScriptLogLevel> logs =
<String, JavaScriptLogLevel>{};
await controller.setOnConsoleMessage(
(JavaScriptConsoleMessage message) async {
logs[message.message] = message.level;
},
);
onConsoleMessageCallback(
mockWebChromeClient,
ConsoleMessage(
lineNumber: 42,
message: 'Debug message',
level: ConsoleMessageLevel.debug,
sourceId: 'source',
),
);
onConsoleMessageCallback(
mockWebChromeClient,
ConsoleMessage(
lineNumber: 42,
message: 'Error message',
level: ConsoleMessageLevel.error,
sourceId: 'source',
),
);
onConsoleMessageCallback(
mockWebChromeClient,
ConsoleMessage(
lineNumber: 42,
message: 'Log message',
level: ConsoleMessageLevel.log,
sourceId: 'source',
),
);
onConsoleMessageCallback(
mockWebChromeClient,
ConsoleMessage(
lineNumber: 42,
message: 'Tip message',
level: ConsoleMessageLevel.tip,
sourceId: 'source',
),
);
onConsoleMessageCallback(
mockWebChromeClient,
ConsoleMessage(
lineNumber: 42,
message: 'Warning message',
level: ConsoleMessageLevel.warning,
sourceId: 'source',
),
);
onConsoleMessageCallback(
mockWebChromeClient,
ConsoleMessage(
lineNumber: 42,
message: 'Unknown message',
level: ConsoleMessageLevel.unknown,
sourceId: 'source',
),
);
expect(logs.length, 6);
expect(logs['Debug message'], JavaScriptLogLevel.debug);
expect(logs['Error message'], JavaScriptLogLevel.error);
expect(logs['Log message'], JavaScriptLogLevel.log);
expect(logs['Tip message'], JavaScriptLogLevel.debug);
expect(logs['Warning message'], JavaScriptLogLevel.warning);
expect(logs['Unknown message'], JavaScriptLogLevel.log);
});
test('runJavaScript', () async {
final MockWebView mockWebView = MockWebView();
final AndroidWebViewController controller = createControllerWithMocks(
@ -1334,6 +1443,7 @@ void main() {
android_webview.CustomViewCallback callback)?
onShowCustomView,
dynamic onHideCustomView,
dynamic onConsoleMessage,
}) {
onShowCustomViewCallback = onShowCustomView;
return mockWebChromeClient;

View File

@ -739,8 +739,8 @@ class MockAndroidWebViewController extends _i1.Mock
) as _i9.Future<void>);
@override
_i9.Future<void> setCustomWidgetCallbacks({
_i8.OnShowCustomWidgetCallback? onShowCustomWidget,
_i8.OnHideCustomWidgetCallback? onHideCustomWidget,
required _i8.OnShowCustomWidgetCallback? onShowCustomWidget,
required _i8.OnHideCustomWidgetCallback? onHideCustomWidget,
}) =>
(super.noSuchMethod(
Invocation.method(
@ -754,6 +754,26 @@ class MockAndroidWebViewController extends _i1.Mock
returnValue: _i9.Future<void>.value(),
returnValueForMissingStub: _i9.Future<void>.value(),
) as _i9.Future<void>);
@override
_i9.Future<void> setOnConsoleMessage(
void Function(_i3.JavaScriptConsoleMessage)? onConsoleMessage) =>
(super.noSuchMethod(
Invocation.method(
#setOnConsoleMessage,
[onConsoleMessage],
),
returnValue: _i9.Future<void>.value(),
returnValueForMissingStub: _i9.Future<void>.value(),
) as _i9.Future<void>);
@override
_i9.Future<String?> getUserAgent() => (super.noSuchMethod(
Invocation.method(
#getUserAgent,
[],
),
returnValue: _i9.Future<String?>.value(),
returnValueForMissingStub: _i9.Future<String?>.value(),
) as _i9.Future<String?>);
}
/// A class which mocks [AndroidWebViewProxy].
@ -775,6 +795,10 @@ class MockAndroidWebViewProxy extends _i1.Mock
) as _i2.WebView Function());
@override
_i2.WebChromeClient Function({
void Function(
_i2.WebChromeClient,
_i2.ConsoleMessage,
)? onConsoleMessage,
void Function(_i2.WebChromeClient)? onGeolocationPermissionsHidePrompt,
_i9.Future<void> Function(
String,
@ -801,6 +825,10 @@ class MockAndroidWebViewProxy extends _i1.Mock
}) get createAndroidWebChromeClient => (super.noSuchMethod(
Invocation.getter(#createAndroidWebChromeClient),
returnValue: ({
void Function(
_i2.WebChromeClient,
_i2.ConsoleMessage,
)? onConsoleMessage,
void Function(_i2.WebChromeClient)?
onGeolocationPermissionsHidePrompt,
_i9.Future<void> Function(
@ -831,6 +859,10 @@ class MockAndroidWebViewProxy extends _i1.Mock
Invocation.getter(#createAndroidWebChromeClient),
),
returnValueForMissingStub: ({
void Function(
_i2.WebChromeClient,
_i2.ConsoleMessage,
)? onConsoleMessage,
void Function(_i2.WebChromeClient)?
onGeolocationPermissionsHidePrompt,
_i9.Future<void> Function(
@ -861,6 +893,10 @@ class MockAndroidWebViewProxy extends _i1.Mock
Invocation.getter(#createAndroidWebChromeClient),
),
) as _i2.WebChromeClient Function({
void Function(
_i2.WebChromeClient,
_i2.ConsoleMessage,
)? onConsoleMessage,
void Function(_i2.WebChromeClient)? onGeolocationPermissionsHidePrompt,
_i9.Future<void> Function(
String,
@ -1780,6 +1816,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
returnValueForMissingStub: _i9.Future<void>.value(),
) as _i9.Future<void>);
@override
_i9.Future<void> setSynchronousReturnValueForOnConsoleMessage(bool? value) =>
(super.noSuchMethod(
Invocation.method(
#setSynchronousReturnValueForOnConsoleMessage,
[value],
),
returnValue: _i9.Future<void>.value(),
returnValueForMissingStub: _i9.Future<void>.value(),
) as _i9.Future<void>);
@override
_i2.WebChromeClient copy() => (super.noSuchMethod(
Invocation.method(
#copy,

View File

@ -475,8 +475,8 @@ class MockAndroidWebViewController extends _i1.Mock
) as _i5.Future<void>);
@override
_i5.Future<void> setCustomWidgetCallbacks({
_i6.OnShowCustomWidgetCallback? onShowCustomWidget,
_i6.OnHideCustomWidgetCallback? onHideCustomWidget,
required _i6.OnShowCustomWidgetCallback? onShowCustomWidget,
required _i6.OnHideCustomWidgetCallback? onHideCustomWidget,
}) =>
(super.noSuchMethod(
Invocation.method(
@ -490,6 +490,25 @@ class MockAndroidWebViewController extends _i1.Mock
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
@override
_i5.Future<void> setOnConsoleMessage(
void Function(_i3.JavaScriptConsoleMessage)? onConsoleMessage) =>
(super.noSuchMethod(
Invocation.method(
#setOnConsoleMessage,
[onConsoleMessage],
),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
@override
_i5.Future<String?> getUserAgent() => (super.noSuchMethod(
Invocation.method(
#getUserAgent,
[],
),
returnValue: _i5.Future<String?>.value(),
) as _i5.Future<String?>);
}
/// A class which mocks [TestInstanceManagerHostApi].

View File

@ -1120,6 +1120,81 @@ void main() {
expect(callbackParameters, <Object?>[instance]);
});
test('onConsoleMessage', () async {
late final List<Object> result;
when(mockWebChromeClient.onConsoleMessage).thenReturn(
(WebChromeClient instance, ConsoleMessage message) {
result = <Object>[instance, message];
},
);
final ConsoleMessage message = ConsoleMessage(
lineNumber: 0,
message: 'message',
level: ConsoleMessageLevel.error,
sourceId: 'sourceId',
);
flutterApi.onConsoleMessage(
mockWebChromeClientInstanceId,
message,
);
expect(result[0], mockWebChromeClient);
expect(result[1], message);
});
test('setSynchronousReturnValueForOnConsoleMessage', () {
final MockTestWebChromeClientHostApi mockHostApi =
MockTestWebChromeClientHostApi();
TestWebChromeClientHostApi.setup(mockHostApi);
WebChromeClient.api =
WebChromeClientHostApiImpl(instanceManager: instanceManager);
final WebChromeClient webChromeClient = WebChromeClient.detached();
instanceManager.addHostCreatedInstance(webChromeClient, 2);
webChromeClient.setSynchronousReturnValueForOnConsoleMessage(false);
verify(
mockHostApi.setSynchronousReturnValueForOnConsoleMessage(2, false),
);
});
test(
'setSynchronousReturnValueForOnConsoleMessage throws StateError when onConsoleMessage is null',
() {
final MockTestWebChromeClientHostApi mockHostApi =
MockTestWebChromeClientHostApi();
TestWebChromeClientHostApi.setup(mockHostApi);
WebChromeClient.api =
WebChromeClientHostApiImpl(instanceManager: instanceManager);
final WebChromeClient clientWithNullCallback =
WebChromeClient.detached();
instanceManager.addHostCreatedInstance(clientWithNullCallback, 2);
expect(
() => clientWithNullCallback
.setSynchronousReturnValueForOnConsoleMessage(true),
throwsStateError,
);
final WebChromeClient clientWithNonnullCallback =
WebChromeClient.detached(
onConsoleMessage: (_, __) async {},
);
instanceManager.addHostCreatedInstance(clientWithNonnullCallback, 3);
clientWithNonnullCallback
.setSynchronousReturnValueForOnConsoleMessage(true);
verify(
mockHostApi.setSynchronousReturnValueForOnConsoleMessage(3, true),
);
});
test('copy', () {
expect(WebChromeClient.detached().copy(), isA<WebChromeClient>());
});

View File

@ -480,6 +480,21 @@ class MockTestWebChromeClientHostApi extends _i1.Mock
),
returnValueForMissingStub: null,
);
@override
void setSynchronousReturnValueForOnConsoleMessage(
int? instanceId,
bool? value,
) =>
super.noSuchMethod(
Invocation.method(
#setSynchronousReturnValueForOnConsoleMessage,
[
instanceId,
value,
],
),
returnValueForMissingStub: null,
);
}
/// A class which mocks [TestWebSettingsHostApi].
@ -1181,6 +1196,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
@override
_i5.Future<void> setSynchronousReturnValueForOnConsoleMessage(bool? value) =>
(super.noSuchMethod(
Invocation.method(
#setSynchronousReturnValueForOnConsoleMessage,
[value],
),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
@override
_i2.WebChromeClient copy() => (super.noSuchMethod(
Invocation.method(
#copy,

View File

@ -780,6 +780,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
@override
_i5.Future<void> setSynchronousReturnValueForOnConsoleMessage(bool? value) =>
(super.noSuchMethod(
Invocation.method(
#setSynchronousReturnValueForOnConsoleMessage,
[value],
),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);
@override
_i2.WebChromeClient copy() => (super.noSuchMethod(
Invocation.method(
#copy,

View File

@ -1516,6 +1516,8 @@ abstract class TestWebChromeClientHostApi {
void setSynchronousReturnValueForOnShowFileChooser(
int instanceId, bool value);
void setSynchronousReturnValueForOnConsoleMessage(int instanceId, bool value);
static void setup(TestWebChromeClientHostApi? api,
{BinaryMessenger? binaryMessenger}) {
{
@ -1568,6 +1570,33 @@ abstract class TestWebChromeClientHostApi {
});
}
}
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnConsoleMessage',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
_testBinaryMessengerBinding!.defaultBinaryMessenger
.setMockDecodedMessageHandler<Object?>(channel, null);
} else {
_testBinaryMessengerBinding!.defaultBinaryMessenger
.setMockDecodedMessageHandler<Object?>(channel,
(Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnConsoleMessage 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.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnConsoleMessage was null, expected non-null int.');
final bool? arg_value = (args[1] as bool?);
assert(arg_value != null,
'Argument for dev.flutter.pigeon.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnConsoleMessage was null, expected non-null bool.');
api.setSynchronousReturnValueForOnConsoleMessage(
arg_instanceId!, arg_value!);
return <Object?>[];
});
}
}
}
}