mirror of
https://github.com/flutter/packages.git
synced 2025-07-01 23:51:55 +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
|
## 3.10.1
|
||||||
|
|
||||||
* Bumps androidx.annotation:annotation from 1.5.0 to 1.7.0.
|
* 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. */
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
public static final class WebResourceRequestData {
|
public static final class WebResourceRequestData {
|
||||||
private @NonNull String url;
|
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> {
|
public interface Result<T> {
|
||||||
@SuppressWarnings("UnknownNullness")
|
@SuppressWarnings("UnknownNullness")
|
||||||
void success(T result);
|
void success(T result);
|
||||||
@ -2401,6 +2587,9 @@ public class GeneratedAndroidWebView {
|
|||||||
void setSynchronousReturnValueForOnShowFileChooser(
|
void setSynchronousReturnValueForOnShowFileChooser(
|
||||||
@NonNull Long instanceId, @NonNull Boolean value);
|
@NonNull Long instanceId, @NonNull Boolean value);
|
||||||
|
|
||||||
|
void setSynchronousReturnValueForOnConsoleMessage(
|
||||||
|
@NonNull Long instanceId, @NonNull Boolean value);
|
||||||
|
|
||||||
/** The codec used by WebChromeClientHostApi. */
|
/** The codec used by WebChromeClientHostApi. */
|
||||||
static @NonNull MessageCodec<Object> getCodec() {
|
static @NonNull MessageCodec<Object> getCodec() {
|
||||||
return new StandardMessageCodec();
|
return new StandardMessageCodec();
|
||||||
@ -2463,6 +2652,33 @@ public class GeneratedAndroidWebView {
|
|||||||
channel.setMessageHandler(null);
|
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. */
|
/** 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. */
|
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
|
||||||
public static class WebChromeClientFlutterApi {
|
public static class WebChromeClientFlutterApi {
|
||||||
private final @NonNull BinaryMessenger binaryMessenger;
|
private final @NonNull BinaryMessenger binaryMessenger;
|
||||||
@ -2551,7 +2795,7 @@ public class GeneratedAndroidWebView {
|
|||||||
}
|
}
|
||||||
/** The codec used by WebChromeClientFlutterApi. */
|
/** The codec used by WebChromeClientFlutterApi. */
|
||||||
static @NonNull MessageCodec<Object> getCodec() {
|
static @NonNull MessageCodec<Object> getCodec() {
|
||||||
return new StandardMessageCodec();
|
return WebChromeClientFlutterApiCodec.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onProgressChanged(
|
public void onProgressChanged(
|
||||||
@ -2656,6 +2900,20 @@ public class GeneratedAndroidWebView {
|
|||||||
new ArrayList<Object>(Collections.singletonList(identifierArg)),
|
new ArrayList<Object>(Collections.singletonList(identifierArg)),
|
||||||
channelReply -> callback.reply(null));
|
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. */
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
public interface WebStorageHostApi {
|
public interface WebStorageHostApi {
|
||||||
|
@ -6,6 +6,7 @@ package io.flutter.plugins.webviewflutter;
|
|||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.webkit.ConsoleMessage;
|
||||||
import android.webkit.GeolocationPermissions;
|
import android.webkit.GeolocationPermissions;
|
||||||
import android.webkit.PermissionRequest;
|
import android.webkit.PermissionRequest;
|
||||||
import android.webkit.WebChromeClient;
|
import android.webkit.WebChromeClient;
|
||||||
@ -27,6 +28,24 @@ public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
|
|||||||
private final InstanceManager instanceManager;
|
private final InstanceManager instanceManager;
|
||||||
private final WebViewFlutterApiImpl webViewFlutterApi;
|
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.
|
* Creates a Flutter api that sends messages to Dart.
|
||||||
*
|
*
|
||||||
@ -149,6 +168,25 @@ public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
|
|||||||
callback);
|
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) {
|
private long getIdentifierForClient(WebChromeClient webChromeClient) {
|
||||||
final Long identifier = instanceManager.getIdentifierForStrongReference(webChromeClient);
|
final Long identifier = instanceManager.getIdentifierForStrongReference(webChromeClient);
|
||||||
if (identifier == null) {
|
if (identifier == null) {
|
||||||
|
@ -9,6 +9,7 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.webkit.ConsoleMessage;
|
||||||
import android.webkit.GeolocationPermissions;
|
import android.webkit.GeolocationPermissions;
|
||||||
import android.webkit.PermissionRequest;
|
import android.webkit.PermissionRequest;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
@ -40,6 +41,7 @@ public class WebChromeClientHostApiImpl implements WebChromeClientHostApi {
|
|||||||
public static class WebChromeClientImpl extends SecureWebChromeClient {
|
public static class WebChromeClientImpl extends SecureWebChromeClient {
|
||||||
private final WebChromeClientFlutterApiImpl flutterApi;
|
private final WebChromeClientFlutterApiImpl flutterApi;
|
||||||
private boolean returnValueForOnShowFileChooser = false;
|
private boolean returnValueForOnShowFileChooser = false;
|
||||||
|
private boolean returnValueForOnConsoleMessage = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link WebChromeClient} that passes arguments of callbacks methods to Dart.
|
* 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 -> {});
|
flutterApi.onPermissionRequest(this, request, reply -> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
|
||||||
|
flutterApi.onConsoleMessage(this, consoleMessage, reply -> {});
|
||||||
|
return returnValueForOnConsoleMessage;
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets return value for {@link #onShowFileChooser}. */
|
/** Sets return value for {@link #onShowFileChooser}. */
|
||||||
public void setReturnValueForOnShowFileChooser(boolean value) {
|
public void setReturnValueForOnShowFileChooser(boolean value) {
|
||||||
returnValueForOnShowFileChooser = 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));
|
Objects.requireNonNull(instanceManager.getInstance(instanceId));
|
||||||
webChromeClient.setReturnValueForOnShowFileChooser(value);
|
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.net.Uri;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.webkit.ConsoleMessage;
|
||||||
import android.webkit.GeolocationPermissions;
|
import android.webkit.GeolocationPermissions;
|
||||||
import android.webkit.PermissionRequest;
|
import android.webkit.PermissionRequest;
|
||||||
import android.webkit.WebChromeClient;
|
import android.webkit.WebChromeClient;
|
||||||
@ -162,4 +163,17 @@ public class WebChromeClientTest {
|
|||||||
webChromeClient.onGeolocationPermissionsHidePrompt();
|
webChromeClient.onGeolocationPermissionsHidePrompt();
|
||||||
verify(mockFlutterApi).onGeolocationPermissionsHidePrompt(eq(webChromeClient), any());
|
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.
|
/// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests.
|
||||||
|
@ -73,6 +73,40 @@ const String kTransparentBackgroundPage = '''
|
|||||||
</html>
|
</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 {
|
class WebViewExample extends StatefulWidget {
|
||||||
const WebViewExample({super.key, this.cookieManager});
|
const WebViewExample({super.key, this.cookieManager});
|
||||||
|
|
||||||
@ -202,6 +236,7 @@ enum MenuOptions {
|
|||||||
transparentBackground,
|
transparentBackground,
|
||||||
setCookie,
|
setCookie,
|
||||||
videoExample,
|
videoExample,
|
||||||
|
logExample,
|
||||||
}
|
}
|
||||||
|
|
||||||
class SampleMenu extends StatelessWidget {
|
class SampleMenu extends StatelessWidget {
|
||||||
@ -265,6 +300,9 @@ class SampleMenu extends StatelessWidget {
|
|||||||
case MenuOptions.videoExample:
|
case MenuOptions.videoExample:
|
||||||
_onVideoExample(context);
|
_onVideoExample(context);
|
||||||
break;
|
break;
|
||||||
|
case MenuOptions.logExample:
|
||||||
|
_onLogExample();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
|
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
|
||||||
@ -321,6 +359,10 @@ class SampleMenu extends StatelessWidget {
|
|||||||
value: MenuOptions.transparentBackground,
|
value: MenuOptions.transparentBackground,
|
||||||
child: Text('Transparent background example'),
|
child: Text('Transparent background example'),
|
||||||
),
|
),
|
||||||
|
const PopupMenuItem<MenuOptions>(
|
||||||
|
value: MenuOptions.logExample,
|
||||||
|
child: Text('Log example'),
|
||||||
|
),
|
||||||
const PopupMenuItem<MenuOptions>(
|
const PopupMenuItem<MenuOptions>(
|
||||||
value: MenuOptions.videoExample,
|
value: MenuOptions.videoExample,
|
||||||
child: Text('Video example'),
|
child: Text('Video example'),
|
||||||
@ -497,6 +539,16 @@ class SampleMenu extends StatelessWidget {
|
|||||||
|
|
||||||
return indexFile.path;
|
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 {
|
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 example app is bundled with the plugin so we use a path dependency on
|
||||||
# the parent directory to use the current plugin's version.
|
# the parent directory to use the current plugin's version.
|
||||||
path: ../
|
path: ../
|
||||||
webview_flutter_platform_interface: ^2.4.0
|
webview_flutter_platform_interface: ^2.6.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
espresso: ^0.2.0
|
espresso: ^0.2.0
|
||||||
|
@ -44,6 +44,9 @@ class AndroidWebViewProxy {
|
|||||||
onGeolocationPermissionsShowPrompt,
|
onGeolocationPermissionsShowPrompt,
|
||||||
void Function(android_webview.WebChromeClient instance)?
|
void Function(android_webview.WebChromeClient instance)?
|
||||||
onGeolocationPermissionsHidePrompt,
|
onGeolocationPermissionsHidePrompt,
|
||||||
|
void Function(android_webview.WebChromeClient instance,
|
||||||
|
android_webview.ConsoleMessage message)?
|
||||||
|
onConsoleMessage,
|
||||||
void Function(
|
void Function(
|
||||||
android_webview.WebChromeClient instance,
|
android_webview.WebChromeClient instance,
|
||||||
android_webview.View view,
|
android_webview.View view,
|
||||||
|
@ -12,7 +12,8 @@ import 'android_webview.g.dart';
|
|||||||
import 'android_webview_api_impls.dart';
|
import 'android_webview_api_impls.dart';
|
||||||
import 'instance_manager.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.
|
/// Root of the Java class hierarchy.
|
||||||
///
|
///
|
||||||
@ -1047,6 +1048,7 @@ class WebChromeClient extends JavaObject {
|
|||||||
this.onGeolocationPermissionsHidePrompt,
|
this.onGeolocationPermissionsHidePrompt,
|
||||||
this.onShowCustomView,
|
this.onShowCustomView,
|
||||||
this.onHideCustomView,
|
this.onHideCustomView,
|
||||||
|
this.onConsoleMessage,
|
||||||
@visibleForTesting super.binaryMessenger,
|
@visibleForTesting super.binaryMessenger,
|
||||||
@visibleForTesting super.instanceManager,
|
@visibleForTesting super.instanceManager,
|
||||||
}) : super.detached() {
|
}) : super.detached() {
|
||||||
@ -1068,6 +1070,7 @@ class WebChromeClient extends JavaObject {
|
|||||||
this.onGeolocationPermissionsHidePrompt,
|
this.onGeolocationPermissionsHidePrompt,
|
||||||
this.onShowCustomView,
|
this.onShowCustomView,
|
||||||
this.onHideCustomView,
|
this.onHideCustomView,
|
||||||
|
this.onConsoleMessage,
|
||||||
super.binaryMessenger,
|
super.binaryMessenger,
|
||||||
super.instanceManager,
|
super.instanceManager,
|
||||||
}) : super.detached();
|
}) : super.detached();
|
||||||
@ -1121,6 +1124,10 @@ class WebChromeClient extends JavaObject {
|
|||||||
/// mode.
|
/// mode.
|
||||||
final HideCustomViewCallback? onHideCustomView;
|
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,
|
/// Sets the required synchronous return value for the Java method,
|
||||||
/// `WebChromeClient.onShowFileChooser(...)`.
|
/// `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
|
@override
|
||||||
WebChromeClient copy() {
|
WebChromeClient copy() {
|
||||||
return WebChromeClient.detached(
|
return WebChromeClient.detached(
|
||||||
@ -1160,6 +1194,7 @@ class WebChromeClient extends JavaObject {
|
|||||||
onGeolocationPermissionsHidePrompt: onGeolocationPermissionsHidePrompt,
|
onGeolocationPermissionsHidePrompt: onGeolocationPermissionsHidePrompt,
|
||||||
onShowCustomView: onShowCustomView,
|
onShowCustomView: onShowCustomView,
|
||||||
onHideCustomView: onHideCustomView,
|
onHideCustomView: onHideCustomView,
|
||||||
|
onConsoleMessage: onConsoleMessage,
|
||||||
binaryMessenger: _api.binaryMessenger,
|
binaryMessenger: _api.binaryMessenger,
|
||||||
instanceManager: _api.instanceManager,
|
instanceManager: _api.instanceManager,
|
||||||
);
|
);
|
||||||
|
@ -32,6 +32,42 @@ enum FileChooserMode {
|
|||||||
save,
|
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 {
|
class WebResourceRequestData {
|
||||||
WebResourceRequestData({
|
WebResourceRequestData({
|
||||||
required this.url,
|
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`.
|
/// Host API for managing the native `InstanceManager`.
|
||||||
class InstanceManagerHostApi {
|
class InstanceManagerHostApi {
|
||||||
/// Constructor for [InstanceManagerHostApi]. The [binaryMessenger] named argument is
|
/// Constructor for [InstanceManagerHostApi]. The [binaryMessenger] named argument is
|
||||||
@ -1943,6 +2018,30 @@ class WebChromeClientHostApi {
|
|||||||
return;
|
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 {
|
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 {
|
abstract class WebChromeClientFlutterApi {
|
||||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
static const MessageCodec<Object?> codec = _WebChromeClientFlutterApiCodec();
|
||||||
|
|
||||||
void onProgressChanged(int instanceId, int webViewInstanceId, int progress);
|
void onProgressChanged(int instanceId, int webViewInstanceId, int progress);
|
||||||
|
|
||||||
@ -2037,6 +2159,9 @@ abstract class WebChromeClientFlutterApi {
|
|||||||
/// Callback to Dart function `WebChromeClient.onGeolocationPermissionsHidePrompt`.
|
/// Callback to Dart function `WebChromeClient.onGeolocationPermissionsHidePrompt`.
|
||||||
void onGeolocationPermissionsHidePrompt(int identifier);
|
void onGeolocationPermissionsHidePrompt(int identifier);
|
||||||
|
|
||||||
|
/// Callback to Dart function `WebChromeClient.onConsoleMessage`.
|
||||||
|
void onConsoleMessage(int instanceId, ConsoleMessage message);
|
||||||
|
|
||||||
static void setup(WebChromeClientFlutterApi? api,
|
static void setup(WebChromeClientFlutterApi? api,
|
||||||
{BinaryMessenger? binaryMessenger}) {
|
{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 'android_webview.g.dart';
|
||||||
import 'instance_manager.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]
|
/// Converts [WebResourceRequestData] to [WebResourceRequest]
|
||||||
WebResourceRequest _toWebResourceRequest(WebResourceRequestData data) {
|
WebResourceRequest _toWebResourceRequest(WebResourceRequestData data) {
|
||||||
@ -892,6 +893,17 @@ class WebChromeClientHostApiImpl extends WebChromeClientHostApi {
|
|||||||
value,
|
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].
|
/// 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].
|
/// 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_proxy.dart';
|
||||||
import 'android_webview.dart' as android_webview;
|
import 'android_webview.dart' as android_webview;
|
||||||
|
import 'android_webview_api_impls.dart';
|
||||||
import 'instance_manager.dart';
|
import 'instance_manager.dart';
|
||||||
import 'platform_views_service_proxy.dart';
|
import 'platform_views_service_proxy.dart';
|
||||||
import 'weak_reference_utils.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(
|
onPermissionRequest: withWeakReferenceTo(
|
||||||
this,
|
this,
|
||||||
(WeakReference<AndroidWebViewController> weakReference) {
|
(WeakReference<AndroidWebViewController> weakReference) {
|
||||||
@ -255,6 +292,8 @@ class AndroidWebViewController extends PlatformWebViewController {
|
|||||||
|
|
||||||
void Function(PlatformWebViewPermissionRequest)? _onPermissionRequestCallback;
|
void Function(PlatformWebViewPermissionRequest)? _onPermissionRequestCallback;
|
||||||
|
|
||||||
|
void Function(JavaScriptConsoleMessage consoleMessage)? _onConsoleLogCallback;
|
||||||
|
|
||||||
/// Whether to enable the platform's webview content debugging tools.
|
/// Whether to enable the platform's webview content debugging tools.
|
||||||
///
|
///
|
||||||
/// Defaults to false.
|
/// Defaults to false.
|
||||||
@ -566,6 +605,18 @@ class AndroidWebViewController extends PlatformWebViewController {
|
|||||||
_onShowCustomWidgetCallback = onShowCustomWidget;
|
_onShowCustomWidgetCallback = onShowCustomWidget;
|
||||||
_onHideCustomWidgetCallback = onHideCustomWidget;
|
_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].
|
/// Android implementation of [PlatformWebViewPermissionRequest].
|
||||||
|
@ -57,6 +57,42 @@ enum FileChooserMode {
|
|||||||
save,
|
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 {
|
class WebResourceRequestData {
|
||||||
WebResourceRequestData(
|
WebResourceRequestData(
|
||||||
this.url,
|
this.url,
|
||||||
@ -89,6 +125,16 @@ class WebViewPoint {
|
|||||||
int y;
|
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.
|
/// Handles methods calls to the native Java Object class.
|
||||||
///
|
///
|
||||||
/// Also handles calls to remove the reference to an instance with `dispose`.
|
/// Also handles calls to remove the reference to an instance with `dispose`.
|
||||||
@ -337,6 +383,11 @@ abstract class WebChromeClientHostApi {
|
|||||||
int instanceId,
|
int instanceId,
|
||||||
bool value,
|
bool value,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void setSynchronousReturnValueForOnConsoleMessage(
|
||||||
|
int instanceId,
|
||||||
|
bool value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostApi(dartHostTestHandler: 'TestAssetManagerHostApi')
|
@HostApi(dartHostTestHandler: 'TestAssetManagerHostApi')
|
||||||
@ -379,6 +430,9 @@ abstract class WebChromeClientFlutterApi {
|
|||||||
|
|
||||||
/// Callback to Dart function `WebChromeClient.onGeolocationPermissionsHidePrompt`.
|
/// Callback to Dart function `WebChromeClient.onGeolocationPermissionsHidePrompt`.
|
||||||
void onGeolocationPermissionsHidePrompt(int identifier);
|
void onGeolocationPermissionsHidePrompt(int identifier);
|
||||||
|
|
||||||
|
/// Callback to Dart function `WebChromeClient.onConsoleMessage`.
|
||||||
|
void onConsoleMessage(int instanceId, ConsoleMessage message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostApi(dartHostTestHandler: 'TestWebStorageHostApi')
|
@HostApi(dartHostTestHandler: 'TestWebStorageHostApi')
|
||||||
|
@ -2,7 +2,7 @@ name: webview_flutter_android
|
|||||||
description: A Flutter plugin that provides a WebView widget on 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
|
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
|
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:
|
environment:
|
||||||
sdk: ">=2.19.0 <4.0.0"
|
sdk: ">=2.19.0 <4.0.0"
|
||||||
@ -20,7 +20,7 @@ flutter:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
webview_flutter_platform_interface: ^2.4.0
|
webview_flutter_platform_interface: ^2.6.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.1.4
|
build_runner: ^2.1.4
|
||||||
|
@ -520,6 +520,7 @@ class CapturingWebChromeClient extends android_webview.WebChromeClient {
|
|||||||
super.onShowCustomView,
|
super.onShowCustomView,
|
||||||
super.onHideCustomView,
|
super.onHideCustomView,
|
||||||
super.onPermissionRequest,
|
super.onPermissionRequest,
|
||||||
|
super.onConsoleMessage,
|
||||||
super.binaryMessenger,
|
super.binaryMessenger,
|
||||||
super.instanceManager,
|
super.instanceManager,
|
||||||
}) : super.detached() {
|
}) : super.detached() {
|
||||||
|
@ -73,6 +73,9 @@ void main() {
|
|||||||
android_webview.CustomViewCallback callback)?
|
android_webview.CustomViewCallback callback)?
|
||||||
onShowCustomView,
|
onShowCustomView,
|
||||||
void Function(android_webview.WebChromeClient instance)? onHideCustomView,
|
void Function(android_webview.WebChromeClient instance)? onHideCustomView,
|
||||||
|
void Function(android_webview.WebChromeClient instance,
|
||||||
|
android_webview.ConsoleMessage message)?
|
||||||
|
onConsoleMessage,
|
||||||
})? createWebChromeClient,
|
})? createWebChromeClient,
|
||||||
android_webview.WebView? mockWebView,
|
android_webview.WebView? mockWebView,
|
||||||
android_webview.WebViewClient? mockWebViewClient,
|
android_webview.WebViewClient? mockWebViewClient,
|
||||||
@ -111,6 +114,9 @@ void main() {
|
|||||||
onShowCustomView,
|
onShowCustomView,
|
||||||
void Function(android_webview.WebChromeClient instance)?
|
void Function(android_webview.WebChromeClient instance)?
|
||||||
onHideCustomView,
|
onHideCustomView,
|
||||||
|
void Function(android_webview.WebChromeClient instance,
|
||||||
|
android_webview.ConsoleMessage message)?
|
||||||
|
onConsoleMessage,
|
||||||
}) =>
|
}) =>
|
||||||
MockWebChromeClient(),
|
MockWebChromeClient(),
|
||||||
createAndroidWebView: () => nonNullMockWebView,
|
createAndroidWebView: () => nonNullMockWebView,
|
||||||
@ -606,6 +612,7 @@ void main() {
|
|||||||
dynamic onPermissionRequest,
|
dynamic onPermissionRequest,
|
||||||
dynamic onShowCustomView,
|
dynamic onShowCustomView,
|
||||||
dynamic onHideCustomView,
|
dynamic onHideCustomView,
|
||||||
|
dynamic onConsoleMessage,
|
||||||
}) {
|
}) {
|
||||||
onShowFileChooserCallback = onShowFileChooser!;
|
onShowFileChooserCallback = onShowFileChooser!;
|
||||||
return mockWebChromeClient;
|
return mockWebChromeClient;
|
||||||
@ -676,6 +683,7 @@ void main() {
|
|||||||
dynamic onPermissionRequest,
|
dynamic onPermissionRequest,
|
||||||
dynamic onShowCustomView,
|
dynamic onShowCustomView,
|
||||||
dynamic onHideCustomView,
|
dynamic onHideCustomView,
|
||||||
|
dynamic onConsoleMessage,
|
||||||
}) {
|
}) {
|
||||||
onGeoPermissionHandle = onGeolocationPermissionsShowPrompt!;
|
onGeoPermissionHandle = onGeolocationPermissionsShowPrompt!;
|
||||||
onGeoPermissionHidePromptHandle = onGeolocationPermissionsHidePrompt!;
|
onGeoPermissionHidePromptHandle = onGeolocationPermissionsHidePrompt!;
|
||||||
@ -750,6 +758,7 @@ void main() {
|
|||||||
onShowCustomView,
|
onShowCustomView,
|
||||||
void Function(android_webview.WebChromeClient instance)?
|
void Function(android_webview.WebChromeClient instance)?
|
||||||
onHideCustomView,
|
onHideCustomView,
|
||||||
|
dynamic onConsoleMessage,
|
||||||
}) {
|
}) {
|
||||||
onShowCustomViewHandle = onShowCustomView!;
|
onShowCustomViewHandle = onShowCustomView!;
|
||||||
onHideCustomViewHandle = onHideCustomView!;
|
onHideCustomViewHandle = onHideCustomView!;
|
||||||
@ -802,6 +811,7 @@ void main() {
|
|||||||
)? onPermissionRequest,
|
)? onPermissionRequest,
|
||||||
dynamic onShowCustomView,
|
dynamic onShowCustomView,
|
||||||
dynamic onHideCustomView,
|
dynamic onHideCustomView,
|
||||||
|
dynamic onConsoleMessage,
|
||||||
}) {
|
}) {
|
||||||
onPermissionRequestCallback = onPermissionRequest!;
|
onPermissionRequestCallback = onPermissionRequest!;
|
||||||
return mockWebChromeClient;
|
return mockWebChromeClient;
|
||||||
@ -856,6 +866,7 @@ void main() {
|
|||||||
)? onPermissionRequest,
|
)? onPermissionRequest,
|
||||||
dynamic onShowCustomView,
|
dynamic onShowCustomView,
|
||||||
dynamic onHideCustomView,
|
dynamic onHideCustomView,
|
||||||
|
dynamic onConsoleMessage,
|
||||||
}) {
|
}) {
|
||||||
onPermissionRequestCallback = onPermissionRequest!;
|
onPermissionRequestCallback = onPermissionRequest!;
|
||||||
return mockWebChromeClient;
|
return mockWebChromeClient;
|
||||||
@ -881,6 +892,104 @@ void main() {
|
|||||||
expect(callbackCalled, isFalse);
|
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 {
|
test('runJavaScript', () async {
|
||||||
final MockWebView mockWebView = MockWebView();
|
final MockWebView mockWebView = MockWebView();
|
||||||
final AndroidWebViewController controller = createControllerWithMocks(
|
final AndroidWebViewController controller = createControllerWithMocks(
|
||||||
@ -1334,6 +1443,7 @@ void main() {
|
|||||||
android_webview.CustomViewCallback callback)?
|
android_webview.CustomViewCallback callback)?
|
||||||
onShowCustomView,
|
onShowCustomView,
|
||||||
dynamic onHideCustomView,
|
dynamic onHideCustomView,
|
||||||
|
dynamic onConsoleMessage,
|
||||||
}) {
|
}) {
|
||||||
onShowCustomViewCallback = onShowCustomView;
|
onShowCustomViewCallback = onShowCustomView;
|
||||||
return mockWebChromeClient;
|
return mockWebChromeClient;
|
||||||
|
@ -739,8 +739,8 @@ class MockAndroidWebViewController extends _i1.Mock
|
|||||||
) as _i9.Future<void>);
|
) as _i9.Future<void>);
|
||||||
@override
|
@override
|
||||||
_i9.Future<void> setCustomWidgetCallbacks({
|
_i9.Future<void> setCustomWidgetCallbacks({
|
||||||
_i8.OnShowCustomWidgetCallback? onShowCustomWidget,
|
required _i8.OnShowCustomWidgetCallback? onShowCustomWidget,
|
||||||
_i8.OnHideCustomWidgetCallback? onHideCustomWidget,
|
required _i8.OnHideCustomWidgetCallback? onHideCustomWidget,
|
||||||
}) =>
|
}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
@ -754,6 +754,26 @@ class MockAndroidWebViewController extends _i1.Mock
|
|||||||
returnValue: _i9.Future<void>.value(),
|
returnValue: _i9.Future<void>.value(),
|
||||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||||
) as _i9.Future<void>);
|
) 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].
|
/// A class which mocks [AndroidWebViewProxy].
|
||||||
@ -775,6 +795,10 @@ class MockAndroidWebViewProxy extends _i1.Mock
|
|||||||
) as _i2.WebView Function());
|
) as _i2.WebView Function());
|
||||||
@override
|
@override
|
||||||
_i2.WebChromeClient Function({
|
_i2.WebChromeClient Function({
|
||||||
|
void Function(
|
||||||
|
_i2.WebChromeClient,
|
||||||
|
_i2.ConsoleMessage,
|
||||||
|
)? onConsoleMessage,
|
||||||
void Function(_i2.WebChromeClient)? onGeolocationPermissionsHidePrompt,
|
void Function(_i2.WebChromeClient)? onGeolocationPermissionsHidePrompt,
|
||||||
_i9.Future<void> Function(
|
_i9.Future<void> Function(
|
||||||
String,
|
String,
|
||||||
@ -801,6 +825,10 @@ class MockAndroidWebViewProxy extends _i1.Mock
|
|||||||
}) get createAndroidWebChromeClient => (super.noSuchMethod(
|
}) get createAndroidWebChromeClient => (super.noSuchMethod(
|
||||||
Invocation.getter(#createAndroidWebChromeClient),
|
Invocation.getter(#createAndroidWebChromeClient),
|
||||||
returnValue: ({
|
returnValue: ({
|
||||||
|
void Function(
|
||||||
|
_i2.WebChromeClient,
|
||||||
|
_i2.ConsoleMessage,
|
||||||
|
)? onConsoleMessage,
|
||||||
void Function(_i2.WebChromeClient)?
|
void Function(_i2.WebChromeClient)?
|
||||||
onGeolocationPermissionsHidePrompt,
|
onGeolocationPermissionsHidePrompt,
|
||||||
_i9.Future<void> Function(
|
_i9.Future<void> Function(
|
||||||
@ -831,6 +859,10 @@ class MockAndroidWebViewProxy extends _i1.Mock
|
|||||||
Invocation.getter(#createAndroidWebChromeClient),
|
Invocation.getter(#createAndroidWebChromeClient),
|
||||||
),
|
),
|
||||||
returnValueForMissingStub: ({
|
returnValueForMissingStub: ({
|
||||||
|
void Function(
|
||||||
|
_i2.WebChromeClient,
|
||||||
|
_i2.ConsoleMessage,
|
||||||
|
)? onConsoleMessage,
|
||||||
void Function(_i2.WebChromeClient)?
|
void Function(_i2.WebChromeClient)?
|
||||||
onGeolocationPermissionsHidePrompt,
|
onGeolocationPermissionsHidePrompt,
|
||||||
_i9.Future<void> Function(
|
_i9.Future<void> Function(
|
||||||
@ -861,6 +893,10 @@ class MockAndroidWebViewProxy extends _i1.Mock
|
|||||||
Invocation.getter(#createAndroidWebChromeClient),
|
Invocation.getter(#createAndroidWebChromeClient),
|
||||||
),
|
),
|
||||||
) as _i2.WebChromeClient Function({
|
) as _i2.WebChromeClient Function({
|
||||||
|
void Function(
|
||||||
|
_i2.WebChromeClient,
|
||||||
|
_i2.ConsoleMessage,
|
||||||
|
)? onConsoleMessage,
|
||||||
void Function(_i2.WebChromeClient)? onGeolocationPermissionsHidePrompt,
|
void Function(_i2.WebChromeClient)? onGeolocationPermissionsHidePrompt,
|
||||||
_i9.Future<void> Function(
|
_i9.Future<void> Function(
|
||||||
String,
|
String,
|
||||||
@ -1780,6 +1816,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
|
|||||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||||
) as _i9.Future<void>);
|
) as _i9.Future<void>);
|
||||||
@override
|
@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(
|
_i2.WebChromeClient copy() => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#copy,
|
#copy,
|
||||||
|
@ -475,8 +475,8 @@ class MockAndroidWebViewController extends _i1.Mock
|
|||||||
) as _i5.Future<void>);
|
) as _i5.Future<void>);
|
||||||
@override
|
@override
|
||||||
_i5.Future<void> setCustomWidgetCallbacks({
|
_i5.Future<void> setCustomWidgetCallbacks({
|
||||||
_i6.OnShowCustomWidgetCallback? onShowCustomWidget,
|
required _i6.OnShowCustomWidgetCallback? onShowCustomWidget,
|
||||||
_i6.OnHideCustomWidgetCallback? onHideCustomWidget,
|
required _i6.OnHideCustomWidgetCallback? onHideCustomWidget,
|
||||||
}) =>
|
}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
@ -490,6 +490,25 @@ class MockAndroidWebViewController extends _i1.Mock
|
|||||||
returnValue: _i5.Future<void>.value(),
|
returnValue: _i5.Future<void>.value(),
|
||||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||||
) as _i5.Future<void>);
|
) 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].
|
/// A class which mocks [TestInstanceManagerHostApi].
|
||||||
|
@ -1120,6 +1120,81 @@ void main() {
|
|||||||
expect(callbackParameters, <Object?>[instance]);
|
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', () {
|
test('copy', () {
|
||||||
expect(WebChromeClient.detached().copy(), isA<WebChromeClient>());
|
expect(WebChromeClient.detached().copy(), isA<WebChromeClient>());
|
||||||
});
|
});
|
||||||
|
@ -480,6 +480,21 @@ class MockTestWebChromeClientHostApi extends _i1.Mock
|
|||||||
),
|
),
|
||||||
returnValueForMissingStub: null,
|
returnValueForMissingStub: null,
|
||||||
);
|
);
|
||||||
|
@override
|
||||||
|
void setSynchronousReturnValueForOnConsoleMessage(
|
||||||
|
int? instanceId,
|
||||||
|
bool? value,
|
||||||
|
) =>
|
||||||
|
super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#setSynchronousReturnValueForOnConsoleMessage,
|
||||||
|
[
|
||||||
|
instanceId,
|
||||||
|
value,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A class which mocks [TestWebSettingsHostApi].
|
/// A class which mocks [TestWebSettingsHostApi].
|
||||||
@ -1181,6 +1196,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
|
|||||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||||
) as _i5.Future<void>);
|
) as _i5.Future<void>);
|
||||||
@override
|
@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(
|
_i2.WebChromeClient copy() => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#copy,
|
#copy,
|
||||||
|
@ -780,6 +780,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
|
|||||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||||
) as _i5.Future<void>);
|
) as _i5.Future<void>);
|
||||||
@override
|
@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(
|
_i2.WebChromeClient copy() => (super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#copy,
|
#copy,
|
||||||
|
@ -1516,6 +1516,8 @@ abstract class TestWebChromeClientHostApi {
|
|||||||
void setSynchronousReturnValueForOnShowFileChooser(
|
void setSynchronousReturnValueForOnShowFileChooser(
|
||||||
int instanceId, bool value);
|
int instanceId, bool value);
|
||||||
|
|
||||||
|
void setSynchronousReturnValueForOnConsoleMessage(int instanceId, bool value);
|
||||||
|
|
||||||
static void setup(TestWebChromeClientHostApi? api,
|
static void setup(TestWebChromeClientHostApi? api,
|
||||||
{BinaryMessenger? binaryMessenger}) {
|
{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