mirror of
https://github.com/flutter/packages.git
synced 2025-06-30 14:47:22 +08:00
[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:

committed by
GitHub

parent
95b9959463
commit
eac45de20d
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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].
|
||||
|
@ -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].
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -520,6 +520,7 @@ class CapturingWebChromeClient extends android_webview.WebChromeClient {
|
||||
super.onShowCustomView,
|
||||
super.onHideCustomView,
|
||||
super.onPermissionRequest,
|
||||
super.onConsoleMessage,
|
||||
super.binaryMessenger,
|
||||
super.instanceManager,
|
||||
}) : super.detached() {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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].
|
||||
|
@ -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>());
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user