Updated window.flutter_inappwebview.callHandler implementation: if there is an error/exception on Flutter/Dart side, the callHandler will reject the JavaScript promise with the error/exception message, so you can catch it also on JavaScript side, Fixed Android Web Storage Manager deleteAllData and deleteOrigin methods implementation, fix #1462, fix #1475

This commit is contained in:
Lorenzo Pichilli 2022-12-15 17:43:29 +01:00
parent 0f1c7e3029
commit a42b0e4dfd
22 changed files with 120 additions and 39 deletions

View File

@ -1,3 +1,10 @@
## 6.0.0-beta.22
- Updated `window.flutter_inappwebview.callHandler` implementation: if there is an error/exception on Flutter/Dart side, the `callHandler` will reject the JavaScript promise with the error/exception message, so you can catch it also on JavaScript side
- Fixed Android Web Storage Manager `deleteAllData` and `deleteOrigin` methods implementation
- Fixed "Xiaomi store - Conflict of Privacy Permissions, android.permission.MY_READ_INSTALLED_PACKAGES" [#1462](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1462)
- Fixed "Flutter 3.0.5 compilation issue" [#1475](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1475)
## 6.0.0-beta.21 ## 6.0.0-beta.21
- Fixed "Android plugin version 6 - UserScripts not executing on new tabs." [#1455](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1455) - Fixed "Android plugin version 6 - UserScripts not executing on new tabs." [#1455](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1455)
@ -174,6 +181,10 @@
- Removed `URLProtectionSpace.iosIsProxy` property - Removed `URLProtectionSpace.iosIsProxy` property
- `historyUrl` and `baseUrl` of `InAppWebViewInitialData` can be `null` - `historyUrl` and `baseUrl` of `InAppWebViewInitialData` can be `null`
## 5.7.2+3
- Fixed "Xiaomi store - Conflict of Privacy Permissions, android.permission.MY_READ_INSTALLED_PACKAGES" [#1462](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1462)
## 5.7.2+2 ## 5.7.2+2
- Fixed "Unexpected addWebMessageListener behaviour" [#1422](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1422) - Fixed "Unexpected addWebMessageListener behaviour" [#1422](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1422)

View File

@ -30,14 +30,21 @@ public class MyCookieManager extends ChannelDelegateImpl {
@Nullable @Nullable
public InAppWebViewFlutterPlugin plugin; public InAppWebViewFlutterPlugin plugin;
public MyCookieManager(final InAppWebViewFlutterPlugin plugin) { public MyCookieManager(@NonNull final InAppWebViewFlutterPlugin plugin) {
super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME)); super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME));
this.plugin = plugin; this.plugin = plugin;
cookieManager = getCookieManager(); }
public static void init() {
if (cookieManager == null) {
cookieManager = getCookieManager();
}
} }
@Override @Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
init();
switch (call.method) { switch (call.method) {
case "setCookie": case "setCookie":
{ {

View File

@ -25,20 +25,27 @@ public class MyWebStorage extends ChannelDelegateImpl {
@Nullable @Nullable
public InAppWebViewFlutterPlugin plugin; public InAppWebViewFlutterPlugin plugin;
public MyWebStorage(final InAppWebViewFlutterPlugin plugin) { public MyWebStorage(@NonNull final InAppWebViewFlutterPlugin plugin) {
super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME)); super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME));
this.plugin = plugin; this.plugin = plugin;
webStorageManager = WebStorage.getInstance(); }
public static void init() {
if (webStorageManager == null) {
webStorageManager = WebStorage.getInstance();
}
} }
@Override @Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
init();
switch (call.method) { switch (call.method) {
case "getOrigins": case "getOrigins":
getOrigins(result); getOrigins(result);
break; break;
case "deleteAllData": case "deleteAllData":
if (webStorageManager == null) { if (webStorageManager != null) {
webStorageManager.deleteAllData(); webStorageManager.deleteAllData();
result.success(true); result.success(true);
} else { } else {
@ -47,7 +54,7 @@ public class MyWebStorage extends ChannelDelegateImpl {
break; break;
case "deleteOrigin": case "deleteOrigin":
{ {
if (webStorageManager == null) { if (webStorageManager != null) {
String origin = (String) call.argument("origin"); String origin = (String) call.argument("origin");
webStorageManager.deleteOrigin(origin); webStorageManager.deleteOrigin(origin);
result.success(true); result.success(true);

View File

@ -16,7 +16,7 @@ public class WebViewFeatureManager extends ChannelDelegateImpl {
@Nullable @Nullable
public InAppWebViewFlutterPlugin plugin; public InAppWebViewFlutterPlugin plugin;
public WebViewFeatureManager(final InAppWebViewFlutterPlugin plugin) { public WebViewFeatureManager(@NonNull final InAppWebViewFlutterPlugin plugin) {
super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME)); super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME));
this.plugin = plugin; this.plugin = plugin;
} }

View File

@ -30,14 +30,23 @@ public class CredentialDatabaseHandler extends ChannelDelegateImpl {
@Nullable @Nullable
public InAppWebViewFlutterPlugin plugin; public InAppWebViewFlutterPlugin plugin;
public CredentialDatabaseHandler(final InAppWebViewFlutterPlugin plugin) { public CredentialDatabaseHandler(@NonNull final InAppWebViewFlutterPlugin plugin) {
super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME)); super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME));
this.plugin = plugin; this.plugin = plugin;
credentialDatabase = CredentialDatabase.getInstance(plugin.applicationContext); }
public static void init(@NonNull InAppWebViewFlutterPlugin plugin) {
if (credentialDatabase == null) {
credentialDatabase = CredentialDatabase.getInstance(plugin.applicationContext);
}
} }
@Override @Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (plugin != null) {
init(plugin);
}
switch (call.method) { switch (call.method) {
case "getAllAuthCredentials": case "getAllAuthCredentials":
{ {

View File

@ -220,7 +220,7 @@ public class JavaScriptBridgeJS {
" var _callHandlerID = setTimeout(function(){});" + " var _callHandlerID = setTimeout(function(){});" +
" window." + JAVASCRIPT_BRIDGE_NAME + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" + " window." + JAVASCRIPT_BRIDGE_NAME + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" +
" return new Promise(function(resolve, reject) {" + " return new Promise(function(resolve, reject) {" +
" window." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = resolve;" + " window." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = {resolve: resolve, reject: reject};" +
" });" + " });" +
" };" + " };" +
" }"+ " }"+
@ -230,7 +230,7 @@ public class JavaScriptBridgeJS {
" var _callHandlerID = setTimeout(function(){});" + " var _callHandlerID = setTimeout(function(){});" +
" window.top." + JAVASCRIPT_BRIDGE_NAME + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" + " window.top." + JAVASCRIPT_BRIDGE_NAME + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" +
" return new Promise(function(resolve, reject) {" + " return new Promise(function(resolve, reject) {" +
" window.top." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = resolve;" + " window.top." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = {resolve: resolve, reject: reject};" +
" });" + " });" +
" };" + " };" +
"}" + "}" +

View File

@ -25,18 +25,22 @@ public class ProxyManager extends ChannelDelegateImpl {
@Nullable @Nullable
public InAppWebViewFlutterPlugin plugin; public InAppWebViewFlutterPlugin plugin;
public ProxyManager(final InAppWebViewFlutterPlugin plugin) { public ProxyManager(@NonNull final InAppWebViewFlutterPlugin plugin) {
super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME)); super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME));
this.plugin = plugin; this.plugin = plugin;
if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) { }
public static void init() {
if (proxyController == null &&
WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) {
proxyController = ProxyController.getInstance(); proxyController = ProxyController.getInstance();
} else {
proxyController = null;
} }
} }
@Override @Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
init();
switch (call.method) { switch (call.method) {
case "setProxyOverride": case "setProxyOverride":
if (proxyController != null) { if (proxyController != null) {

View File

@ -36,6 +36,7 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl {
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
@Override @Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
ServiceWorkerManager.init();
ServiceWorkerControllerCompat serviceWorkerController = ServiceWorkerManager.serviceWorkerController; ServiceWorkerControllerCompat serviceWorkerController = ServiceWorkerManager.serviceWorkerController;
ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ? ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ?
serviceWorkerController.getServiceWorkerWebSettings() : null; serviceWorkerController.getServiceWorkerWebSettings() : null;

View File

@ -33,14 +33,16 @@ public class ServiceWorkerManager implements Disposable {
@Nullable @Nullable
public InAppWebViewFlutterPlugin plugin; public InAppWebViewFlutterPlugin plugin;
public ServiceWorkerManager(final InAppWebViewFlutterPlugin plugin) { public ServiceWorkerManager(@NonNull final InAppWebViewFlutterPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
final MethodChannel channel = new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME); final MethodChannel channel = new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME);
this.channelDelegate = new ServiceWorkerChannelDelegate(this, channel); this.channelDelegate = new ServiceWorkerChannelDelegate(this, channel);
if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) { }
public static void init() {
if (serviceWorkerController == null &&
WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) {
serviceWorkerController = ServiceWorkerControllerCompat.getInstance(); serviceWorkerController = ServiceWorkerControllerCompat.getInstance();
} else {
serviceWorkerController = null;
} }
} }

View File

@ -27,6 +27,7 @@ public class TracingControllerChannelDelegate extends ChannelDelegateImpl {
@Override @Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
TracingControllerManager.init();
TracingController tracingController = TracingControllerManager.tracingController; TracingController tracingController = TracingControllerManager.tracingController;
switch (call.method) { switch (call.method) {

View File

@ -1,6 +1,7 @@
package com.pichillilorenzo.flutter_inappwebview.tracing; package com.pichillilorenzo.flutter_inappwebview.tracing;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.webkit.ProxyController;
import androidx.webkit.TracingConfig; import androidx.webkit.TracingConfig;
import androidx.webkit.TracingController; import androidx.webkit.TracingController;
import androidx.webkit.WebViewFeature; import androidx.webkit.WebViewFeature;
@ -25,10 +26,12 @@ public class TracingControllerManager implements Disposable {
this.plugin = plugin; this.plugin = plugin;
final MethodChannel channel = new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME); final MethodChannel channel = new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME);
this.channelDelegate = new TracingControllerChannelDelegate(this, channel); this.channelDelegate = new TracingControllerChannelDelegate(this, channel);
if (WebViewFeature.isFeatureSupported(WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE)) { }
public static void init() {
if (tracingController == null &&
WebViewFeature.isFeatureSupported(WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE)) {
tracingController = TracingController.getInstance(); tracingController = TracingController.getInstance();
} else {
tracingController = null;
} }
} }

View File

@ -130,7 +130,7 @@ public class JavaScriptBridgeInterface {
return; return;
} }
String sourceCode = "if (window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "] != null) { " + String sourceCode = "if (window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "] != null) { " +
"window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "](" + json + "); " + "window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "].resolve(" + json + "); " +
"delete window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "]; " + "delete window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "]; " +
"}"; "}";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@ -143,7 +143,24 @@ public class JavaScriptBridgeInterface {
@Override @Override
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) { public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : "")); String message = errorCode + ((errorMessage != null) ? ", " + errorMessage : "");
Log.e(LOG_TAG, message);
if (inAppWebView == null) {
// The webview has already been disposed, ignore.
return;
}
String sourceCode = "if (window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "] != null) { " +
"window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "].reject(new Error(" + JSONObject.quote(message) + ")); " +
"delete window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "[" + _callHandlerID + "]; " +
"}";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
inAppWebView.evaluateJavascript(sourceCode, (ValueCallback<String>) null);
}
else {
inAppWebView.loadUrl("javascript:" + sourceCode);
}
} }
}); });
} }

View File

@ -45,7 +45,7 @@ void openAndClose() {
activityButton: ActivityButton( activityButton: ActivityButton(
templateImage: UIImage(systemName: "sun.max"), templateImage: UIImage(systemName: "sun.max"),
extensionIdentifier: extensionIdentifier:
"com.pichillilorenzo.flutter-inappwebview6-example.test"))); "com.pichillilorenzo.flutter-inappwebview-example6.test")));
await chromeSafariBrowser.opened.future; await chromeSafariBrowser.opened.future;
expect(chromeSafariBrowser.isOpened(), true); expect(chromeSafariBrowser.isOpened(), true);
expect(() async { expect(() async {

View File

@ -451,7 +451,7 @@
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview6-example.test"; PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-example6.test";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
@ -484,7 +484,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview6-example.test"; PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-example6.test";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@ -627,7 +627,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview6-example"; PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-example6";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -659,7 +659,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview6-example"; PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-example6";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View File

@ -2738,13 +2738,21 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
self.evaluateJavaScript(""" self.evaluateJavaScript("""
if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) { if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)](\(json)); window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)].resolve(\(json));
delete window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)]; delete window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)];
} }
""", completionHandler: nil) """, completionHandler: nil)
} }
callback.error = { (code: String, message: String?, details: Any?) in callback.error = { (code: String, message: String?, details: Any?) in
print(code + ", " + (message ?? "")) let errorMessage = code + (message != nil ? ", " + (message ?? "") : "")
print(errorMessage)
self.evaluateJavaScript("""
if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)].reject(new Error('\(errorMessage.replacingOccurrences(of: "\'", with: "\\'"))'));
delete window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)];
}
""", completionHandler: nil)
} }
if let channelDelegate = webView.channelDelegate { if let channelDelegate = webView.channelDelegate {

View File

@ -26,7 +26,7 @@ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
var _callHandlerID = setTimeout(function(){}); var _callHandlerID = setTimeout(function(){});
window.webkit.messageHandlers['callHandler'].postMessage( {'handlerName': arguments[0], '_callHandlerID': _callHandlerID, 'args': JSON.stringify(Array.prototype.slice.call(arguments, 1)), '_windowId': _windowId} ); window.webkit.messageHandlers['callHandler'].postMessage( {'handlerName': arguments[0], '_callHandlerID': _callHandlerID, 'args': JSON.stringify(Array.prototype.slice.call(arguments, 1)), '_windowId': _windowId} );
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
window.\(JAVASCRIPT_BRIDGE_NAME)[_callHandlerID] = resolve; window.\(JAVASCRIPT_BRIDGE_NAME)[_callHandlerID] = {resolve: resolve, reject: reject};
}); });
}; };
\(WEB_MESSAGE_LISTENER_JS_SOURCE) \(WEB_MESSAGE_LISTENER_JS_SOURCE)

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';

View File

@ -1378,9 +1378,10 @@ class InAppWebViewController {
// convert result to json // convert result to json
try { try {
return jsonEncode(await javaScriptHandlersMap[handlerName]!(args)); return jsonEncode(await javaScriptHandlersMap[handlerName]!(args));
} catch (error) { } catch (error, stacktrace) {
developer.log(error.toString(), name: this.runtimeType.toString()); developer.log(error.toString() + '\n' + stacktrace.toString(),
return null; name: 'JavaScript Handler "$handlerName"');
throw Exception(error.toString().replaceFirst('Exception: ', ''));
} }
} }
break; break;

View File

@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_inappwebview/src/types/user_preferred_content_mode.dart'; import 'package:flutter_inappwebview/src/types/user_preferred_content_mode.dart';
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
import 'dart:typed_data';
import '../android/webview_asset_loader.dart'; import '../android/webview_asset_loader.dart';
import '../types/action_mode_menu_item.dart'; import '../types/action_mode_menu_item.dart';

View File

@ -2102,13 +2102,21 @@ public class InAppWebView: WKWebView, WKUIDelegate,
self.evaluateJavaScript(""" self.evaluateJavaScript("""
if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) { if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)](\(json)); window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)].resolve(\(json));
delete window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)]; delete window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)];
} }
""", completionHandler: nil) """, completionHandler: nil)
} }
callback.error = { (code: String, message: String?, details: Any?) in callback.error = { (code: String, message: String?, details: Any?) in
print(code + ", " + (message ?? "")) let errorMessage = code + (message != nil ? ", " + (message ?? "") : "")
print(errorMessage)
self.evaluateJavaScript("""
if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)].reject(new Error('\(errorMessage.replacingOccurrences(of: "\'", with: "\\'"))'));
delete window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)];
}
""", completionHandler: nil)
} }
if let channelDelegate = webView.channelDelegate { if let channelDelegate = webView.channelDelegate {

View File

@ -26,7 +26,7 @@ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
var _callHandlerID = setTimeout(function(){}); var _callHandlerID = setTimeout(function(){});
window.webkit.messageHandlers['callHandler'].postMessage( {'handlerName': arguments[0], '_callHandlerID': _callHandlerID, 'args': JSON.stringify(Array.prototype.slice.call(arguments, 1)), '_windowId': _windowId} ); window.webkit.messageHandlers['callHandler'].postMessage( {'handlerName': arguments[0], '_callHandlerID': _callHandlerID, 'args': JSON.stringify(Array.prototype.slice.call(arguments, 1)), '_windowId': _windowId} );
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
window.\(JAVASCRIPT_BRIDGE_NAME)[_callHandlerID] = resolve; window.\(JAVASCRIPT_BRIDGE_NAME)[_callHandlerID] = {resolve: resolve, reject: reject};
}); });
}; };
\(WEB_MESSAGE_LISTENER_JS_SOURCE) \(WEB_MESSAGE_LISTENER_JS_SOURCE)

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 6.0.0-beta.21 version: 6.0.0-beta.22
homepage: https://inappwebview.dev/ homepage: https://inappwebview.dev/
repository: https://github.com/pichillilorenzo/flutter_inappwebview repository: https://github.com/pichillilorenzo/flutter_inappwebview
issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues