Added InAppWebView.headlessWebView property to convert an HeadlessWebView to InAppWebView widget
This commit is contained in:
parent
840aeabdb6
commit
92eba92a6c
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,3 +1,7 @@
|
|||
## 6.0.0-beta.4
|
||||
|
||||
- Added `InAppWebView.headlessWebView` property to convert an `HeadlessWebView` to `InAppWebView` widget
|
||||
|
||||
## 6.0.0-beta.3
|
||||
|
||||
- Added MacOS support
|
||||
|
@ -46,6 +50,12 @@
|
|||
- Removed `URLProtectionSpace.iosIsProxy` property
|
||||
- `historyUrl` and `baseUrl` of `InAppWebViewInitialData` can be `null`
|
||||
|
||||
## 5.5.0+4
|
||||
|
||||
- Fixed "Many crashes on iOS: Completion handler was not called" [#1221](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1221)
|
||||
- Fixed "webView:didReceiveAuthenticationChallenge:completionHandler" [#1128](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1128)
|
||||
- Merged "Fix missing import for Flutter 2.8.1" [#1381](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1381) (thanks to [chandrabezzo](https://github.com/chandrabezzo))
|
||||
|
||||
## 5.5.0+3
|
||||
|
||||
- Fixed iOS `toolbarTopTintColor` InAppBrowser option
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.pichillilorenzo.flutter_inappwebview;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebView;
|
||||
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.FlutterWebView;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.PlatformWebView;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebViewImplementation;
|
||||
|
@ -24,15 +26,25 @@ public class FlutterWebViewFactory extends PlatformViewFactory {
|
|||
@Override
|
||||
public PlatformView create(Context context, int id, Object args) {
|
||||
HashMap<String, Object> params = (HashMap<String, Object>) args;
|
||||
PlatformWebView flutterWebView = null;
|
||||
|
||||
PlatformWebView flutterWebView;
|
||||
WebViewImplementation implementation = WebViewImplementation.fromValue((Integer) params.get("implementation"));
|
||||
switch (implementation) {
|
||||
case NATIVE:
|
||||
default:
|
||||
flutterWebView = new FlutterWebView(plugin, context, id, params);
|
||||
String headlessWebViewId = (String) params.get("headlessWebViewId");
|
||||
if (headlessWebViewId != null) {
|
||||
HeadlessInAppWebView headlessInAppWebView = HeadlessInAppWebViewManager.webViews.get(headlessWebViewId);
|
||||
if (headlessInAppWebView != null) {
|
||||
flutterWebView = headlessInAppWebView.disposeAndGetFlutterWebView();
|
||||
}
|
||||
}
|
||||
|
||||
if (flutterWebView == null) {
|
||||
WebViewImplementation implementation = WebViewImplementation.fromValue((Integer) params.get("implementation"));
|
||||
switch (implementation) {
|
||||
case NATIVE:
|
||||
default:
|
||||
flutterWebView = new FlutterWebView(plugin, context, id, params);
|
||||
}
|
||||
flutterWebView.makeInitialLoad(params);
|
||||
}
|
||||
flutterWebView.makeInitialLoad(params);
|
||||
|
||||
return flutterWebView;
|
||||
}
|
||||
|
|
|
@ -2,18 +2,25 @@ package com.pichillilorenzo.flutter_inappwebview;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.net.http.SslCertificate;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Size2D;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.SyncBaseCallbackResultImpl;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
@ -242,6 +249,31 @@ public class Util {
|
|||
return context.getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
public static Size2D getFullscreenSize(Context context) {
|
||||
Size2D fullscreenSize = new Size2D(-1, -1);
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
if (wm != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
final WindowMetrics metrics = wm.getCurrentWindowMetrics();
|
||||
// Gets all excluding insets
|
||||
final WindowInsets windowInsets = metrics.getWindowInsets();
|
||||
Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
|
||||
| WindowInsets.Type.displayCutout());
|
||||
int insetsWidth = insets.right + insets.left;
|
||||
int insetsHeight = insets.top + insets.bottom;
|
||||
final Rect bounds = metrics.getBounds();
|
||||
fullscreenSize.setWidth(bounds.width() - insetsWidth);
|
||||
fullscreenSize.setHeight(bounds.height() - insetsHeight);
|
||||
} else {
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
wm.getDefaultDisplay().getMetrics(displayMetrics);
|
||||
fullscreenSize.setWidth(displayMetrics.widthPixels);
|
||||
fullscreenSize.setHeight(displayMetrics.heightPixels);
|
||||
}
|
||||
}
|
||||
return fullscreenSize;
|
||||
}
|
||||
|
||||
public static boolean isClass(String className) {
|
||||
try {
|
||||
Class.forName(className);
|
||||
|
|
|
@ -9,13 +9,12 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.FlutterWebView;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Size2D;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.FlutterWebView;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
public class HeadlessInAppWebView implements Disposable {
|
||||
|
@ -54,15 +53,16 @@ public class HeadlessInAppWebView implements Disposable {
|
|||
ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0);
|
||||
if (mainView != null && flutterWebView != null) {
|
||||
View view = flutterWebView.getView();
|
||||
final Map<String, Object> initialSize = (Map<String, Object>) params.get("initialSize");
|
||||
Size2D size = Size2D.fromMap(initialSize);
|
||||
if (size != null) {
|
||||
if (view != null) {
|
||||
final Map<String, Object> initialSize = (Map<String, Object>) params.get("initialSize");
|
||||
Size2D size = Size2D.fromMap(initialSize);
|
||||
if (size == null) {
|
||||
size = new Size2D(-1, -1);
|
||||
}
|
||||
setSize(size);
|
||||
} else {
|
||||
view.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
mainView.addView(view, 0);
|
||||
view.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
mainView.addView(view, 0);
|
||||
view.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,8 +71,13 @@ public class HeadlessInAppWebView implements Disposable {
|
|||
public void setSize(@NonNull Size2D size) {
|
||||
if (flutterWebView != null && flutterWebView.webView != null) {
|
||||
View view = flutterWebView.getView();
|
||||
float scale = Util.getPixelDensity(view.getContext());
|
||||
view.setLayoutParams(new FrameLayout.LayoutParams((int) (size.getWidth() * scale), (int) (size.getHeight() * scale)));
|
||||
if (view != null) {
|
||||
float scale = Util.getPixelDensity(view.getContext());
|
||||
Size2D fullscreenSize = Util.getFullscreenSize(view.getContext());
|
||||
int width = (int) (size.getWidth() == -1 ? fullscreenSize.getWidth() : (size.getWidth() * scale));
|
||||
int height = (int) (size.getWidth() == -1 ? fullscreenSize.getHeight() : (size.getHeight() * scale));
|
||||
view.setLayoutParams(new FrameLayout.LayoutParams(width, height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,13 +85,41 @@ public class HeadlessInAppWebView implements Disposable {
|
|||
public Size2D getSize() {
|
||||
if (flutterWebView != null && flutterWebView.webView != null) {
|
||||
View view = flutterWebView.getView();
|
||||
float scale = Util.getPixelDensity(view.getContext());
|
||||
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
return new Size2D(layoutParams.width / scale, layoutParams.height / scale);
|
||||
if (view != null) {
|
||||
float scale = Util.getPixelDensity(view.getContext());
|
||||
Size2D fullscreenSize = Util.getFullscreenSize(view.getContext());
|
||||
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
return new Size2D(
|
||||
fullscreenSize.getWidth() == layoutParams.width ? layoutParams.width : (layoutParams.width / scale),
|
||||
fullscreenSize.getHeight() == layoutParams.height ? layoutParams.height : (layoutParams.height / scale)
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlutterWebView disposeAndGetFlutterWebView() {
|
||||
FlutterWebView newFlutterWebView = flutterWebView;
|
||||
if (flutterWebView != null) {
|
||||
View view = flutterWebView.getView();
|
||||
if (view != null) {
|
||||
// restore WebView layout params and visibility
|
||||
view.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
view.setVisibility(View.VISIBLE);
|
||||
// remove from parent
|
||||
ViewGroup parent = (ViewGroup) view.getParent();
|
||||
if (parent != null) {
|
||||
parent.removeView(view);
|
||||
}
|
||||
}
|
||||
// set to null to avoid to be disposed before calling "dispose()"
|
||||
flutterWebView = null;
|
||||
dispose();
|
||||
}
|
||||
return newFlutterWebView;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (channelDelegate != null) {
|
||||
channelDelegate.dispose();
|
||||
|
@ -100,7 +133,10 @@ public class HeadlessInAppWebView implements Disposable {
|
|||
if (contentView != null) {
|
||||
ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0);
|
||||
if (mainView != null && flutterWebView != null) {
|
||||
mainView.removeView(flutterWebView.getView());
|
||||
View view = flutterWebView.getView();
|
||||
if (view != null) {
|
||||
mainView.removeView(flutterWebView.getView());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../constants.dart';
|
||||
|
||||
void convertToInAppWebView() {
|
||||
final shouldSkip = kIsWeb
|
||||
? false
|
||||
: ![
|
||||
TargetPlatform.android,
|
||||
TargetPlatform.iOS,
|
||||
].contains(defaultTargetPlatform);
|
||||
|
||||
testWidgets('convert to InAppWebView', (WidgetTester tester) async {
|
||||
final Completer<InAppWebViewController> controllerCompleter =
|
||||
Completer<InAppWebViewController>();
|
||||
final Completer<void> pageLoaded = Completer<void>();
|
||||
|
||||
var headlessWebView = new HeadlessInAppWebView(
|
||||
initialUrlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_1),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
);
|
||||
headlessWebView.onLoadStop = (controller, url) async {
|
||||
pageLoaded.complete();
|
||||
};
|
||||
|
||||
await headlessWebView.run();
|
||||
expect(headlessWebView.isRunning(), true);
|
||||
|
||||
final InAppWebViewController controller = await controllerCompleter.future;
|
||||
await pageLoaded.future;
|
||||
|
||||
final String? url = (await controller.getUrl())?.toString();
|
||||
expect(url, TEST_CROSS_PLATFORM_URL_1.toString());
|
||||
|
||||
final Completer<InAppWebViewController> widgetControllerCompleter =
|
||||
Completer<InAppWebViewController>();
|
||||
final Completer<String> loadedUrl = Completer<String>();
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
headlessWebView: headlessWebView,
|
||||
onWebViewCreated: (controller) {
|
||||
widgetControllerCompleter.complete(controller);
|
||||
},
|
||||
onLoadStop: (controller, url) {
|
||||
if (url.toString() == TEST_CROSS_PLATFORM_URL_2.toString() &&
|
||||
!loadedUrl.isCompleted) {
|
||||
loadedUrl.complete(url.toString());
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
final InAppWebViewController widgetController = await widgetControllerCompleter.future;
|
||||
|
||||
expect(headlessWebView.isRunning(), false);
|
||||
|
||||
expect((await widgetController.getUrl())?.toString(), TEST_CROSS_PLATFORM_URL_1.toString());
|
||||
|
||||
await widgetController.loadUrl(
|
||||
urlRequest: URLRequest(url: TEST_CROSS_PLATFORM_URL_2));
|
||||
expect(await loadedUrl.future, TEST_CROSS_PLATFORM_URL_2.toString());
|
||||
}, skip: shouldSkip);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'convert_to_inappwebview.dart';
|
||||
import 'take_screenshot.dart';
|
||||
import 'custom_size.dart';
|
||||
import 'run_and_dispose.dart';
|
||||
|
@ -11,5 +12,6 @@ void main() {
|
|||
takeScreenshot();
|
||||
customSize();
|
||||
setGetSettings();
|
||||
convertToInAppWebView();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
@ -26,7 +27,9 @@ void takeScreenshot() {
|
|||
controllerCompleter.complete(controller);
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
pageLoaded.complete();
|
||||
if (!pageLoaded.isCompleted) {
|
||||
pageLoaded.complete();
|
||||
}
|
||||
});
|
||||
|
||||
await headlessWebView.run();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
export "FLUTTER_ROOT=/Users/lorenzopichilli/fvm/versions/2.10.4"
|
||||
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
|
||||
export "FLUTTER_TARGET=lib/main.dart"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||
export "FLUTTER_BUILD_NUMBER=1"
|
||||
export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/.dart_tool/package_config.json"
|
||||
export "PACKAGE_CONFIG=.dart_tool/package_config.json"
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// ignore_for_file: directives_ordering
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
// ignore_for_file: depend_on_referenced_packages
|
||||
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:url_launcher_web/url_launcher_web.dart';
|
||||
|
||||
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||
|
||||
// ignore: public_member_api_docs
|
||||
void registerPlugins(Registrar registrar) {
|
||||
FlutterInAppWebViewWebPlatform.registerWith(registrar);
|
||||
UrlLauncherPlugin.registerWith(registrar);
|
||||
registrar.registerMessageHandler();
|
||||
}
|
|
@ -114,8 +114,9 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
children: [
|
||||
InAppWebView(
|
||||
key: webViewKey,
|
||||
headlessWebView: headlessWebView,
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev')),
|
||||
URLRequest(url: Uri.parse('https://google.com')),
|
||||
// initialUrlRequest:
|
||||
// URLRequest(url: Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')),
|
||||
// initialFile: "assets/index.html",
|
||||
|
@ -125,6 +126,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
pullToRefreshController: pullToRefreshController,
|
||||
onWebViewCreated: (controller) async {
|
||||
webViewController = controller;
|
||||
print(await controller.getUrl());
|
||||
},
|
||||
onLoadStart: (controller, url) async {
|
||||
setState(() {
|
||||
|
|
|
@ -15,6 +15,13 @@ import 'package:pointer_interceptor/pointer_interceptor.dart';
|
|||
|
||||
InAppLocalhostServer localhostServer = new InAppLocalhostServer(documentRoot: 'assets');
|
||||
|
||||
var headlessWebView = new HeadlessInAppWebView(
|
||||
initialUrlRequest: URLRequest(url: Uri.parse('https://flutter.dev')),
|
||||
shouldOverrideUrlLoading: (controller, navigationAction) async {
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
},
|
||||
);
|
||||
|
||||
Future main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
// await Permission.camera.request();
|
||||
|
@ -29,6 +36,10 @@ Future main() async {
|
|||
await localhostServer.start();
|
||||
}
|
||||
|
||||
headlessWebView.run();
|
||||
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,19 @@ public class HeadlessInAppWebView : Disposable {
|
|||
return nil
|
||||
}
|
||||
|
||||
public func disposeAndGetFlutterWebView(withFrame frame: CGRect) -> FlutterWebViewController? {
|
||||
let newFlutterWebView = flutterWebView
|
||||
if let view = flutterWebView?.view() {
|
||||
// restore WebView frame and alpha
|
||||
view.frame = frame
|
||||
view.alpha = 1.0
|
||||
// remove from parent
|
||||
view.removeFromSuperview()
|
||||
dispose()
|
||||
}
|
||||
return newFlutterWebView
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
|
|
|
@ -23,6 +23,13 @@ public class FlutterWebViewFactory: NSObject, FlutterPlatformViewFactory {
|
|||
|
||||
public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
|
||||
let arguments = args as? NSDictionary
|
||||
|
||||
if let headlessWebViewId = arguments?["headlessWebViewId"] as? String,
|
||||
let headlessWebView = HeadlessInAppWebViewManager.webViews[headlessWebViewId],
|
||||
let platformView = headlessWebView?.disposeAndGetFlutterWebView(withFrame: frame) {
|
||||
return platformView
|
||||
}
|
||||
|
||||
let webviewController = FlutterWebViewController(registrar: registrar!,
|
||||
withFrame: frame,
|
||||
viewIdentifier: viewId,
|
||||
|
|
|
@ -1570,9 +1570,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
let origin = "\(origin.protocol)://\(origin.host)\(origin.port != 0 ? ":" + String(origin.port) : "")"
|
||||
let permissionRequest = PermissionRequest(origin: origin, resources: [type.rawValue], frame: frame)
|
||||
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.PermissionRequestCallback()
|
||||
callback.nonNullSuccess = { (response: PermissionResponse) in
|
||||
if let action = response.action {
|
||||
decisionHandlerCalled = true
|
||||
switch action {
|
||||
case 1:
|
||||
decisionHandler(.grant)
|
||||
|
@ -1588,7 +1590,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: PermissionResponse?) in
|
||||
decisionHandler(.deny)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(.deny)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1610,9 +1615,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
let origin = "\(origin.protocol)://\(origin.host)\(origin.port != 0 ? ":" + String(origin.port) : "")"
|
||||
let permissionRequest = PermissionRequest(origin: origin, resources: ["deviceOrientationAndMotion"], frame: frame)
|
||||
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.PermissionRequestCallback()
|
||||
callback.nonNullSuccess = { (response: PermissionResponse) in
|
||||
if let action = response.action {
|
||||
decisionHandlerCalled = true
|
||||
switch action {
|
||||
case 1:
|
||||
decisionHandler(.grant)
|
||||
|
@ -1628,7 +1635,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: PermissionResponse?) in
|
||||
decisionHandler(.deny)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(.deny)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1694,13 +1704,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return
|
||||
}
|
||||
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.ShouldOverrideUrlLoadingCallback()
|
||||
callback.nonNullSuccess = { (response: WKNavigationActionPolicy) in
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(response)
|
||||
return false
|
||||
}
|
||||
callback.defaultBehaviour = { (response: WKNavigationActionPolicy?) in
|
||||
decisionHandler(.allow)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1726,13 +1741,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
let useOnNavigationResponse = settings?.useOnNavigationResponse
|
||||
|
||||
if useOnNavigationResponse != nil, useOnNavigationResponse! {
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.NavigationResponseCallback()
|
||||
callback.nonNullSuccess = { (response: WKNavigationResponsePolicy) in
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(response)
|
||||
return false
|
||||
}
|
||||
callback.defaultBehaviour = { (response: WKNavigationResponsePolicy?) in
|
||||
decisionHandler(.allow)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1747,7 +1767,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
}
|
||||
|
||||
if let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
|
||||
if #available(iOS 14.5, *), !navigationResponse.canShowMIMEType {
|
||||
if #available(iOS 14.5, *), !navigationResponse.canShowMIMEType, useOnNavigationResponse == nil || !useOnNavigationResponse! {
|
||||
decisionHandler(.download)
|
||||
return
|
||||
} else {
|
||||
|
@ -1856,6 +1876,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return
|
||||
}
|
||||
|
||||
var completionHandlerCalled = false
|
||||
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic ||
|
||||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault ||
|
||||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest ||
|
||||
|
@ -1869,6 +1890,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
let callback = WebViewChannelDelegate.ReceivedHttpAuthRequestCallback()
|
||||
callback.nonNullSuccess = { (response: HttpAuthResponse) in
|
||||
if let action = response.action {
|
||||
completionHandlerCalled = true
|
||||
switch action {
|
||||
case 0:
|
||||
InAppWebView.credentialsProposed = []
|
||||
|
@ -1917,7 +1939,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: HttpAuthResponse?) in
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1944,6 +1969,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
let callback = WebViewChannelDelegate.ReceivedServerTrustAuthRequestCallback()
|
||||
callback.nonNullSuccess = { (response: ServerTrustAuthResponse) in
|
||||
if let action = response.action {
|
||||
completionHandlerCalled = true
|
||||
switch action {
|
||||
case 0:
|
||||
InAppWebView.credentialsProposed = []
|
||||
|
@ -1964,7 +1990,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: ServerTrustAuthResponse?) in
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1981,6 +2010,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
let callback = WebViewChannelDelegate.ReceivedClientCertRequestCallback()
|
||||
callback.nonNullSuccess = { (response: ClientCertResponse) in
|
||||
if let action = response.action {
|
||||
completionHandlerCalled = true
|
||||
switch action {
|
||||
case 0:
|
||||
completionHandler(.cancelAuthenticationChallenge, nil)
|
||||
|
@ -2017,7 +2047,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: ClientCertResponse?) in
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -2102,9 +2135,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return
|
||||
}
|
||||
|
||||
var completionHandlerCalled = false
|
||||
|
||||
let callback = WebViewChannelDelegate.JsAlertCallback()
|
||||
callback.nonNullSuccess = { (response: JsAlertResponse) in
|
||||
if response.handledByClient {
|
||||
completionHandlerCalled = true
|
||||
let action = response.action ?? 1
|
||||
switch action {
|
||||
case 0:
|
||||
|
@ -2118,14 +2154,20 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: JsAlertResponse?) in
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
self.createAlertDialog(message: message, responseMessage: responseMessage,
|
||||
confirmButtonTitle: confirmButtonTitle, completionHandler: completionHandler)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
self.createAlertDialog(message: message, responseMessage: responseMessage,
|
||||
confirmButtonTitle: confirmButtonTitle, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
callback.error = { (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler()
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler()
|
||||
}
|
||||
}
|
||||
|
||||
if let channelDelegate = channelDelegate {
|
||||
|
@ -2159,9 +2201,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
|
||||
public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo,
|
||||
completionHandler: @escaping (Bool) -> Void) {
|
||||
var completionHandlerCalled = false
|
||||
|
||||
let callback = WebViewChannelDelegate.JsConfirmCallback()
|
||||
callback.nonNullSuccess = { (response: JsConfirmResponse) in
|
||||
if response.handledByClient {
|
||||
completionHandlerCalled = true
|
||||
let action = response.action ?? 1
|
||||
switch action {
|
||||
case 0:
|
||||
|
@ -2178,14 +2223,20 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: JsConfirmResponse?) in
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
let cancelButtonTitle = response?.cancelButtonTitle
|
||||
self.createConfirmDialog(message: message, responseMessage: responseMessage, confirmButtonTitle: confirmButtonTitle, cancelButtonTitle: cancelButtonTitle, completionHandler: completionHandler)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
let cancelButtonTitle = response?.cancelButtonTitle
|
||||
self.createConfirmDialog(message: message, responseMessage: responseMessage, confirmButtonTitle: confirmButtonTitle, cancelButtonTitle: cancelButtonTitle, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
callback.error = { (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler(false)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler(false)
|
||||
}
|
||||
}
|
||||
|
||||
if let channelDelegate = channelDelegate {
|
||||
|
@ -2230,9 +2281,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
|
||||
public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt message: String, defaultText defaultValue: String?, initiatedByFrame frame: WKFrameInfo,
|
||||
completionHandler: @escaping (String?) -> Void) {
|
||||
|
||||
var completionHandlerCalled = false
|
||||
|
||||
let callback = WebViewChannelDelegate.JsPromptCallback()
|
||||
callback.nonNullSuccess = { (response: JsPromptResponse) in
|
||||
if response.handledByClient {
|
||||
completionHandlerCalled = true
|
||||
let action = response.action ?? 1
|
||||
switch action {
|
||||
case 0:
|
||||
|
@ -2249,16 +2304,22 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: JsPromptResponse?) in
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
let cancelButtonTitle = response?.cancelButtonTitle
|
||||
let value = response?.value
|
||||
self.createPromptDialog(message: message, defaultValue: defaultValue, responseMessage: responseMessage, confirmButtonTitle: confirmButtonTitle,
|
||||
cancelButtonTitle: cancelButtonTitle, value: value, completionHandler: completionHandler)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
let cancelButtonTitle = response?.cancelButtonTitle
|
||||
let value = response?.value
|
||||
self.createPromptDialog(message: message, defaultValue: defaultValue, responseMessage: responseMessage, confirmButtonTitle: confirmButtonTitle,
|
||||
cancelButtonTitle: cancelButtonTitle, value: value, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
callback.error = { (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler(nil)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if let channelDelegate = channelDelegate {
|
||||
|
@ -2375,13 +2436,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
return
|
||||
}
|
||||
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.ShouldAllowDeprecatedTLSCallback()
|
||||
callback.nonNullSuccess = { (action: Bool) in
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(action)
|
||||
return false
|
||||
}
|
||||
callback.defaultBehaviour = { (action: Bool?) in
|
||||
decisionHandler(false)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(false)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
|
|
@ -740,6 +740,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return JsAlertResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onJsAlert(url: URL?, message: String, isMainFrame: Bool, callback: JsAlertCallback) {
|
||||
|
@ -762,6 +766,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return JsConfirmResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onJsConfirm(url: URL?, message: String, isMainFrame: Bool, callback: JsConfirmCallback) {
|
||||
|
@ -784,6 +792,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return JsPromptResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onJsPrompt(url: URL?, message: String, defaultValue: String?, isMainFrame: Bool, callback: JsPromptCallback) {
|
||||
|
@ -851,6 +863,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return PermissionResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onPermissionRequest(request: PermissionRequest, callback: PermissionRequestCallback) {
|
||||
|
@ -871,6 +887,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return WKNavigationActionPolicy.cancel
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func shouldOverrideUrlLoading(navigationAction: WKNavigationAction, callback: ShouldOverrideUrlLoadingCallback) {
|
||||
|
@ -922,6 +942,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return HttpAuthResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onReceivedHttpAuthRequest(challenge: HttpAuthenticationChallenge, callback: ReceivedHttpAuthRequestCallback) {
|
||||
|
@ -939,6 +963,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return ServerTrustAuthResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onReceivedServerTrustAuthRequest(challenge: ServerTrustChallenge, callback: ReceivedServerTrustAuthRequestCallback) {
|
||||
|
@ -956,6 +984,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return ClientCertResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onReceivedClientCertRequest(challenge: ClientCertChallenge, callback: ReceivedClientCertRequestCallback) {
|
||||
|
@ -1030,6 +1062,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return WKNavigationResponsePolicy.cancel
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onNavigationResponse(navigationResponse: WKNavigationResponse, callback: NavigationResponseCallback) {
|
||||
|
@ -1050,6 +1086,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func shouldAllowDeprecatedTLS(challenge: URLAuthenticationChallenge, callback: ShouldAllowDeprecatedTLSCallback) {
|
||||
|
|
|
@ -5,6 +5,7 @@ window.flutter_inappwebview = {
|
|||
viewId: viewId,
|
||||
iframeId: iframeId,
|
||||
iframe: null,
|
||||
iframeContainer: null,
|
||||
windowAutoincrementId: 0,
|
||||
windows: {},
|
||||
isFullscreen: false,
|
||||
|
@ -19,6 +20,7 @@ window.flutter_inappwebview = {
|
|||
prepare: function(settings) {
|
||||
webView.settings = settings;
|
||||
var iframe = document.getElementById(iframeId);
|
||||
var iframeContainer = document.getElementById(iframeId + '-container');
|
||||
|
||||
document.addEventListener('fullscreenchange', function(event) {
|
||||
// document.fullscreenElement will point to the element that
|
||||
|
@ -37,6 +39,7 @@ window.flutter_inappwebview = {
|
|||
|
||||
if (iframe != null) {
|
||||
webView.iframe = iframe;
|
||||
webView.iframeContainer = iframeContainer;
|
||||
iframe.addEventListener('load', function (event) {
|
||||
webView.windowAutoincrementId = 0;
|
||||
webView.windows = {};
|
||||
|
@ -543,20 +546,20 @@ window.flutter_inappwebview = {
|
|||
return false;
|
||||
},
|
||||
getSize: function() {
|
||||
var iframe = webView.iframe;
|
||||
var iframeContainer = webView.iframeContainer;
|
||||
var width = 0.0;
|
||||
var height = 0.0;
|
||||
if (iframe.style.width != null && iframe.style.width != '' && iframe.style.width.indexOf('px') > 0) {
|
||||
width = parseFloat(iframe.style.width);
|
||||
if (iframeContainer.style.width != null && iframeContainer.style.width != '' && iframeContainer.style.width.indexOf('px') > 0) {
|
||||
width = parseFloat(iframeContainer.style.width);
|
||||
}
|
||||
if (width == null || width == 0.0) {
|
||||
width = iframe.getBoundingClientRect().width;
|
||||
width = iframeContainer.getBoundingClientRect().width;
|
||||
}
|
||||
if (iframe.style.height != null && iframe.style.height != '' && iframe.style.height.indexOf('px') > 0) {
|
||||
height = parseFloat(iframe.style.height);
|
||||
if (iframeContainer.style.height != null && iframeContainer.style.height != '' && iframeContainer.style.height.indexOf('px') > 0) {
|
||||
height = parseFloat(iframeContainer.style.height);
|
||||
}
|
||||
if (height == null || height == 0.0) {
|
||||
height = iframe.getBoundingClientRect().height;
|
||||
height = iframeContainer.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:core';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview/src/util.dart';
|
||||
|
@ -714,3 +716,10 @@ class HeadlessInAppWebView implements WebView, Disposable {
|
|||
MediaCaptureState? newState,
|
||||
)? onMicrophoneCaptureStateChanged;
|
||||
}
|
||||
|
||||
extension InternalHeadlessInAppWebView on HeadlessInAppWebView {
|
||||
Future<void> internalDispose() async {
|
||||
_started = false;
|
||||
_running = false;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -7,6 +8,8 @@ import 'package:flutter/rendering.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter_inappwebview/src/in_app_webview/headless_in_app_webview.dart';
|
||||
import 'package:flutter_inappwebview/src/util.dart';
|
||||
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import '../web/web_platform_manager.dart';
|
||||
|
@ -37,9 +40,21 @@ class InAppWebView extends StatefulWidget implements WebView {
|
|||
final Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers;
|
||||
|
||||
///The window id of a [CreateWindowAction.windowId].
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
@override
|
||||
final int? windowId;
|
||||
|
||||
///The [HeadlessInAppWebView] to use to initialize this widget
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
final HeadlessInAppWebView? headlessWebView;
|
||||
|
||||
const InAppWebView({
|
||||
Key? key,
|
||||
this.windowId,
|
||||
|
@ -148,6 +163,7 @@ class InAppWebView extends StatefulWidget implements WebView {
|
|||
this.onCameraCaptureStateChanged,
|
||||
this.onMicrophoneCaptureStateChanged,
|
||||
this.gestureRecognizers,
|
||||
this.headlessWebView,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -609,8 +625,11 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
webViewHtmlElement.initialUrlRequest = widget.initialUrlRequest;
|
||||
webViewHtmlElement.initialFile = widget.initialFile;
|
||||
webViewHtmlElement.initialData = widget.initialData;
|
||||
webViewHtmlElement.headlessWebViewId = widget.headlessWebView?.isRunning() ?? false ? widget.headlessWebView?.id : null;
|
||||
webViewHtmlElement.prepare();
|
||||
webViewHtmlElement.makeInitialLoad();
|
||||
if (webViewHtmlElement.headlessWebViewId == null) {
|
||||
webViewHtmlElement.makeInitialLoad();
|
||||
}
|
||||
_onPlatformViewCreated(viewId);
|
||||
},
|
||||
);
|
||||
|
@ -653,6 +672,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
'initialSettings': initialSettings,
|
||||
'contextMenu': widget.contextMenu?.toMap() ?? {},
|
||||
'windowId': widget.windowId,
|
||||
'headlessWebViewId': widget.headlessWebView?.isRunning() ?? false ? widget.headlessWebView?.id : null,
|
||||
'implementation': widget.implementation.toNativeValue(),
|
||||
'initialUserScripts':
|
||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ??
|
||||
|
@ -680,6 +700,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
'initialSettings': initialSettings,
|
||||
'contextMenu': widget.contextMenu?.toMap() ?? {},
|
||||
'windowId': widget.windowId,
|
||||
'headlessWebViewId': widget.headlessWebView?.isRunning() ?? false ? widget.headlessWebView?.id : null,
|
||||
'implementation': widget.implementation.toNativeValue(),
|
||||
'initialUserScripts':
|
||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||
|
@ -702,6 +723,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
'initialSettings': initialSettings,
|
||||
'contextMenu': widget.contextMenu?.toMap() ?? {},
|
||||
'windowId': widget.windowId,
|
||||
'headlessWebViewId': widget.headlessWebView?.isRunning() ?? false ? widget.headlessWebView?.id : null,
|
||||
'implementation': widget.implementation.toNativeValue(),
|
||||
'initialUserScripts':
|
||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||
|
@ -732,10 +754,19 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
}
|
||||
|
||||
void _onPlatformViewCreated(int id) {
|
||||
_controller = InAppWebViewController(id, widget);
|
||||
widget.pullToRefreshController?.initMethodChannel(id);
|
||||
widget.findInteractionController?.initMethodChannel(id);
|
||||
final viewId = (!kIsWeb && (widget.headlessWebView?.isRunning() ?? false)) ? widget.headlessWebView?.id : id;
|
||||
widget.headlessWebView?.internalDispose();
|
||||
_controller = InAppWebViewController(viewId, widget);
|
||||
widget.pullToRefreshController?.initMethodChannel(viewId);
|
||||
widget.findInteractionController?.initMethodChannel(viewId);
|
||||
if (widget.onWebViewCreated != null) {
|
||||
debugLog(
|
||||
className: "InAppWebView",
|
||||
name: "WebView",
|
||||
id: viewId?.toString(),
|
||||
debugLoggingSettings: WebView.debugLoggingSettings,
|
||||
method: "onWebViewCreated",
|
||||
args: []);
|
||||
widget.onWebViewCreated!(_controller!);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import 'dart:collection';
|
|||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
|
|
@ -7,7 +7,7 @@ export 'in_app_webview_settings.dart'
|
|||
InAppWebViewGroupOptions,
|
||||
WebViewOptions,
|
||||
InAppWebViewOptions;
|
||||
export 'headless_in_app_webview.dart';
|
||||
export 'headless_in_app_webview.dart' hide InternalHeadlessInAppWebView;
|
||||
export 'android/main.dart';
|
||||
export 'apple/main.dart';
|
||||
export '../find_interaction/find_interaction_controller.dart';
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import '../in_app_webview/webview.dart';
|
||||
import '../in_app_browser/in_app_browser.dart';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:math';
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'headless_inappwebview_manager.dart';
|
||||
import 'in_app_web_view_web_element.dart';
|
||||
import '../util.dart';
|
||||
import '../types/disposable.dart';
|
||||
|
@ -59,14 +61,21 @@ class HeadlessInAppWebViewWebElement implements Disposable {
|
|||
}
|
||||
|
||||
void setSize(Size size) {
|
||||
webView?.iframe.style.width = size.width.toString() + "px";
|
||||
webView?.iframe.style.height = size.height.toString() + "px";
|
||||
webView?.iframeContainer.style.width = size.width.toString() + "px";
|
||||
webView?.iframeContainer.style.height = size.height.toString() + "px";
|
||||
}
|
||||
|
||||
InAppWebViewWebElement? disposeAndGetFlutterWebView() {
|
||||
InAppWebViewWebElement? newFlutterWebView = webView;
|
||||
dispose();
|
||||
return newFlutterWebView;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_channel?.setMethodCallHandler(null);
|
||||
_channel = null;
|
||||
HeadlessInAppWebViewManager.webViews.putIfAbsent(id, () => null);
|
||||
webView?.dispose();
|
||||
webView = null;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import 'headless_in_app_web_view_web_element.dart';
|
|||
import '../types/main.dart';
|
||||
|
||||
class HeadlessInAppWebViewManager {
|
||||
static final Map<String, HeadlessInAppWebViewWebElement?> webViews = {};
|
||||
|
||||
static late MethodChannel _sharedChannel;
|
||||
|
||||
late BinaryMessenger _messenger;
|
||||
|
@ -50,17 +52,18 @@ class HeadlessInAppWebViewManager {
|
|||
var headlessWebView = HeadlessInAppWebViewWebElement(
|
||||
id: id, messenger: _messenger, webView: webView);
|
||||
WebPlatformManager.webViews.putIfAbsent(id, () => webView);
|
||||
HeadlessInAppWebViewManager.webViews.putIfAbsent(id, () => headlessWebView);
|
||||
prepare(webView, params);
|
||||
headlessWebView.onWebViewCreated();
|
||||
webView.makeInitialLoad();
|
||||
}
|
||||
|
||||
void prepare(InAppWebViewWebElement webView, Map<String, dynamic> params) {
|
||||
webView.iframe.style.display = 'none';
|
||||
webView.iframeContainer.style.display = 'none';
|
||||
Map<String, num>? initialSize = params["initialSize"]?.cast<String, num>();
|
||||
if (initialSize != null) {
|
||||
webView.iframe.style.width = initialSize["width"].toString() + 'px';
|
||||
webView.iframe.style.height = initialSize["height"].toString() + 'px';
|
||||
webView.iframeContainer.style.width = initialSize["width"].toString() + 'px';
|
||||
webView.iframeContainer.style.height = initialSize["height"].toString() + 'px';
|
||||
}
|
||||
Map<String, dynamic> initialSettings =
|
||||
params["initialSettings"].cast<String, dynamic>();
|
||||
|
@ -74,7 +77,7 @@ class HeadlessInAppWebViewManager {
|
|||
webView.initialFile = params["initialFile"];
|
||||
webView.initialData = InAppWebViewInitialData.fromMap(
|
||||
params["initialData"]?.cast<String, dynamic>());
|
||||
document.body?.append(webView.iframe);
|
||||
document.body?.append(webView.iframeContainer);
|
||||
webView.prepare();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:html';
|
||||
import 'dart:js' as js;
|
||||
|
||||
import 'headless_inappwebview_manager.dart';
|
||||
import 'web_platform_manager.dart';
|
||||
import '../in_app_webview/in_app_webview_settings.dart';
|
||||
import '../types/main.dart';
|
||||
|
@ -11,14 +14,16 @@ import '../types/disposable.dart';
|
|||
class InAppWebViewWebElement implements Disposable {
|
||||
late dynamic _viewId;
|
||||
late BinaryMessenger _messenger;
|
||||
late DivElement iframeContainer;
|
||||
late IFrameElement iframe;
|
||||
late MethodChannel? _channel;
|
||||
InAppWebViewSettings? initialSettings;
|
||||
URLRequest? initialUrlRequest;
|
||||
InAppWebViewInitialData? initialData;
|
||||
String? initialFile;
|
||||
String? headlessWebViewId;
|
||||
|
||||
late InAppWebViewSettings settings;
|
||||
InAppWebViewSettings? settings;
|
||||
late js.JsObject bridgeJsObject;
|
||||
bool isLoading = false;
|
||||
|
||||
|
@ -26,11 +31,17 @@ class InAppWebViewWebElement implements Disposable {
|
|||
{required dynamic viewId, required BinaryMessenger messenger}) {
|
||||
this._viewId = viewId;
|
||||
this._messenger = messenger;
|
||||
iframeContainer = DivElement()
|
||||
..id = 'flutter_inappwebview-$_viewId-container'
|
||||
..style.height = '100%'
|
||||
..style.width = '100%'
|
||||
..style.border = 'none';
|
||||
iframe = IFrameElement()
|
||||
..id = 'flutter_inappwebview-$_viewId'
|
||||
..style.height = '100%'
|
||||
..style.width = '100%'
|
||||
..style.border = 'none';
|
||||
iframeContainer.append(iframe);
|
||||
|
||||
_channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_$_viewId',
|
||||
|
@ -173,35 +184,59 @@ class InAppWebViewWebElement implements Disposable {
|
|||
}
|
||||
|
||||
void prepare() {
|
||||
settings = initialSettings ?? InAppWebViewSettings();
|
||||
if (headlessWebViewId != null) {
|
||||
final headlessWebView = HeadlessInAppWebViewManager.webViews[headlessWebViewId!];
|
||||
if (headlessWebView != null && headlessWebView.webView != null) {
|
||||
final webView = headlessWebView.disposeAndGetFlutterWebView();
|
||||
if (webView != null) {
|
||||
webView.iframe.id = iframe.id;
|
||||
iframe.remove();
|
||||
iframeContainer.append(webView.iframe);
|
||||
iframe = webView.iframe;
|
||||
|
||||
Set<Sandbox> sandbox = Set.from(Sandbox.values);
|
||||
initialSettings = webView.initialSettings;
|
||||
settings = webView.settings;
|
||||
initialUrlRequest = webView.initialUrlRequest;
|
||||
initialData = webView.initialData;
|
||||
initialFile = webView.initialFile;
|
||||
|
||||
if (settings.javaScriptEnabled != null && !settings.javaScriptEnabled!) {
|
||||
sandbox.remove(Sandbox.ALLOW_SCRIPTS);
|
||||
bridgeJsObject['webViews'][_viewId] = bridgeJsObject
|
||||
.callMethod("createFlutterInAppWebView", [_viewId, iframe.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iframe.allow = settings.iframeAllow ?? iframe.allow;
|
||||
iframe.allowFullscreen =
|
||||
settings.iframeAllowFullscreen ?? iframe.allowFullscreen;
|
||||
iframe.referrerPolicy =
|
||||
settings.iframeReferrerPolicy?.toNativeValue() ?? iframe.referrerPolicy;
|
||||
iframe.name = settings.iframeName ?? iframe.name;
|
||||
iframe.csp = settings.iframeCsp ?? iframe.csp;
|
||||
if (headlessWebViewId == null && settings == null) {
|
||||
settings = initialSettings ?? InAppWebViewSettings();
|
||||
|
||||
if (settings.iframeSandbox != null &&
|
||||
settings.iframeSandbox != Sandbox.ALLOW_ALL) {
|
||||
iframe.setAttribute("sandbox",
|
||||
settings.iframeSandbox!.map((e) => e.toNativeValue()).join(" "));
|
||||
} else if (settings.iframeSandbox == Sandbox.ALLOW_ALL) {
|
||||
iframe.removeAttribute("sandbox");
|
||||
} else if (sandbox != Sandbox.values) {
|
||||
iframe.setAttribute(
|
||||
"sandbox", sandbox.map((e) => e.toNativeValue()).join(" "));
|
||||
settings.iframeSandbox = sandbox;
|
||||
Set<Sandbox> sandbox = Set.from(Sandbox.values);
|
||||
|
||||
if (settings!.javaScriptEnabled != null && !settings!.javaScriptEnabled!) {
|
||||
sandbox.remove(Sandbox.ALLOW_SCRIPTS);
|
||||
}
|
||||
|
||||
iframe.allow = settings!.iframeAllow ?? iframe.allow;
|
||||
iframe.allowFullscreen =
|
||||
settings!.iframeAllowFullscreen ?? iframe.allowFullscreen;
|
||||
iframe.referrerPolicy =
|
||||
settings!.iframeReferrerPolicy?.toNativeValue() ?? iframe.referrerPolicy;
|
||||
iframe.name = settings!.iframeName ?? iframe.name;
|
||||
iframe.csp = settings!.iframeCsp ?? iframe.csp;
|
||||
|
||||
if (settings!.iframeSandbox != null &&
|
||||
settings!.iframeSandbox != Sandbox.ALLOW_ALL) {
|
||||
iframe.setAttribute("sandbox",
|
||||
settings!.iframeSandbox!.map((e) => e.toNativeValue()).join(" "));
|
||||
} else if (settings!.iframeSandbox == Sandbox.ALLOW_ALL) {
|
||||
iframe.removeAttribute("sandbox");
|
||||
} else if (sandbox != Sandbox.values) {
|
||||
iframe.setAttribute(
|
||||
"sandbox", sandbox.map((e) => e.toNativeValue()).join(" "));
|
||||
settings!.iframeSandbox = sandbox;
|
||||
}
|
||||
}
|
||||
|
||||
_callMethod("prepare", [js.JsObject.jsify(settings.toMap())]);
|
||||
_callMethod("prepare", [js.JsObject.jsify(settings!.toMap())]);
|
||||
}
|
||||
|
||||
dynamic _callMethod(Object method, [List? args]) {
|
||||
|
@ -405,7 +440,7 @@ class InAppWebViewWebElement implements Disposable {
|
|||
Set<Sandbox> sandbox = getSandbox();
|
||||
|
||||
if (newSettings.javaScriptEnabled != null &&
|
||||
settings.javaScriptEnabled != newSettings.javaScriptEnabled) {
|
||||
settings!.javaScriptEnabled != newSettings.javaScriptEnabled) {
|
||||
if (!newSettings.javaScriptEnabled!) {
|
||||
sandbox.remove(Sandbox.ALLOW_SCRIPTS);
|
||||
} else {
|
||||
|
@ -413,23 +448,23 @@ class InAppWebViewWebElement implements Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
if (settings.iframeAllow != newSettings.iframeAllow) {
|
||||
if (settings!.iframeAllow != newSettings.iframeAllow) {
|
||||
iframe.allow = newSettings.iframeAllow;
|
||||
}
|
||||
if (settings.iframeAllowFullscreen != newSettings.iframeAllowFullscreen) {
|
||||
if (settings!.iframeAllowFullscreen != newSettings.iframeAllowFullscreen) {
|
||||
iframe.allowFullscreen = newSettings.iframeAllowFullscreen;
|
||||
}
|
||||
if (settings.iframeReferrerPolicy != newSettings.iframeReferrerPolicy) {
|
||||
if (settings!.iframeReferrerPolicy != newSettings.iframeReferrerPolicy) {
|
||||
iframe.referrerPolicy = newSettings.iframeReferrerPolicy?.toNativeValue();
|
||||
}
|
||||
if (settings.iframeName != newSettings.iframeName) {
|
||||
if (settings!.iframeName != newSettings.iframeName) {
|
||||
iframe.name = newSettings.iframeName;
|
||||
}
|
||||
if (settings.iframeCsp != newSettings.iframeCsp) {
|
||||
if (settings!.iframeCsp != newSettings.iframeCsp) {
|
||||
iframe.csp = newSettings.iframeCsp;
|
||||
}
|
||||
|
||||
if (settings.iframeSandbox != newSettings.iframeSandbox) {
|
||||
if (settings!.iframeSandbox != newSettings.iframeSandbox) {
|
||||
var sandbox = newSettings.iframeSandbox;
|
||||
if (sandbox != null && sandbox != Sandbox.ALLOW_ALL) {
|
||||
iframe.setAttribute(
|
||||
|
@ -449,7 +484,7 @@ class InAppWebViewWebElement implements Disposable {
|
|||
}
|
||||
|
||||
Future<Map<String, dynamic>> getSettings() async {
|
||||
return settings.toMap();
|
||||
return settings!.toMap();
|
||||
}
|
||||
|
||||
void onLoadStart(String url) async {
|
||||
|
@ -569,7 +604,7 @@ class InAppWebViewWebElement implements Disposable {
|
|||
void dispose() {
|
||||
_channel?.setMethodCallHandler(null);
|
||||
_channel = null;
|
||||
iframe.remove();
|
||||
iframeContainer.remove();
|
||||
if (WebPlatformManager.webViews.containsKey(_viewId)) {
|
||||
WebPlatformManager.webViews.remove(_viewId);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class FlutterInAppWebViewWebPlatform {
|
|||
var webView =
|
||||
InAppWebViewWebElement(viewId: viewId, messenger: registrar);
|
||||
WebPlatformManager.webViews.putIfAbsent(viewId, () => webView);
|
||||
return webView.iframe;
|
||||
return webView.iframeContainer;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,20 @@ public class HeadlessInAppWebView : Disposable {
|
|||
return nil
|
||||
}
|
||||
|
||||
public func disposeAndGetFlutterWebView(withFrame frame: CGRect) -> FlutterWebViewController? {
|
||||
let newFlutterWebView = flutterWebView
|
||||
if let view = flutterWebView?.view() {
|
||||
// restore WebView frame and alpha
|
||||
view.frame = frame
|
||||
view.alphaValue = 1.0
|
||||
// remove from parent
|
||||
view.removeFromSuperview()
|
||||
dispose()
|
||||
}
|
||||
return newFlutterWebView
|
||||
}
|
||||
|
||||
|
||||
public func dispose() {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
|
|
|
@ -25,6 +25,13 @@ public class FlutterWebViewFactory: NSObject, FlutterPlatformViewFactory {
|
|||
|
||||
public func create(withViewIdentifier viewId: Int64, arguments args: Any?) -> NSView {
|
||||
let arguments = args as? NSDictionary
|
||||
|
||||
if let headlessWebViewId = arguments?["headlessWebViewId"] as? String,
|
||||
let headlessWebView = HeadlessInAppWebViewManager.webViews[headlessWebViewId],
|
||||
let platformView = headlessWebView?.disposeAndGetFlutterWebView(withFrame: .zero) {
|
||||
return platformView.view()
|
||||
}
|
||||
|
||||
let webviewController = FlutterWebViewController(registrar: registrar!,
|
||||
withFrame: .zero,
|
||||
viewIdentifier: viewId,
|
||||
|
|
|
@ -1073,9 +1073,11 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
let origin = "\(origin.protocol)://\(origin.host)\(origin.port != 0 ? ":" + String(origin.port) : "")"
|
||||
let permissionRequest = PermissionRequest(origin: origin, resources: [type.rawValue], frame: frame)
|
||||
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.PermissionRequestCallback()
|
||||
callback.nonNullSuccess = { (response: PermissionResponse) in
|
||||
if let action = response.action {
|
||||
decisionHandlerCalled = true
|
||||
switch action {
|
||||
case 1:
|
||||
decisionHandler(.grant)
|
||||
|
@ -1091,7 +1093,10 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: PermissionResponse?) in
|
||||
decisionHandler(.deny)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(.deny)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1157,13 +1162,18 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return
|
||||
}
|
||||
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.ShouldOverrideUrlLoadingCallback()
|
||||
callback.nonNullSuccess = { (response: WKNavigationActionPolicy) in
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(response)
|
||||
return false
|
||||
}
|
||||
callback.defaultBehaviour = { (response: WKNavigationActionPolicy?) in
|
||||
decisionHandler(.allow)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1189,13 +1199,18 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
let useOnNavigationResponse = settings?.useOnNavigationResponse
|
||||
|
||||
if useOnNavigationResponse != nil, useOnNavigationResponse! {
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.NavigationResponseCallback()
|
||||
callback.nonNullSuccess = { (response: WKNavigationResponsePolicy) in
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(response)
|
||||
return false
|
||||
}
|
||||
callback.defaultBehaviour = { (response: WKNavigationResponsePolicy?) in
|
||||
decisionHandler(.allow)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1210,7 +1225,7 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
}
|
||||
|
||||
if let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
|
||||
if #available(macOS 11.3, *), !navigationResponse.canShowMIMEType {
|
||||
if #available(macOS 11.3, *), !navigationResponse.canShowMIMEType, useOnNavigationResponse == nil || !useOnNavigationResponse! {
|
||||
decisionHandler(.download)
|
||||
return
|
||||
} else {
|
||||
|
@ -1310,6 +1325,7 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return
|
||||
}
|
||||
|
||||
var completionHandlerCalled = false
|
||||
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic ||
|
||||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault ||
|
||||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest ||
|
||||
|
@ -1323,6 +1339,7 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
let callback = WebViewChannelDelegate.ReceivedHttpAuthRequestCallback()
|
||||
callback.nonNullSuccess = { (response: HttpAuthResponse) in
|
||||
if let action = response.action {
|
||||
completionHandlerCalled = true
|
||||
switch action {
|
||||
case 0:
|
||||
InAppWebView.credentialsProposed = []
|
||||
|
@ -1371,7 +1388,10 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: HttpAuthResponse?) in
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1398,6 +1418,7 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
let callback = WebViewChannelDelegate.ReceivedServerTrustAuthRequestCallback()
|
||||
callback.nonNullSuccess = { (response: ServerTrustAuthResponse) in
|
||||
if let action = response.action {
|
||||
completionHandlerCalled = true
|
||||
switch action {
|
||||
case 0:
|
||||
InAppWebView.credentialsProposed = []
|
||||
|
@ -1418,7 +1439,10 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: ServerTrustAuthResponse?) in
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1435,6 +1459,7 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
let callback = WebViewChannelDelegate.ReceivedClientCertRequestCallback()
|
||||
callback.nonNullSuccess = { (response: ClientCertResponse) in
|
||||
if let action = response.action {
|
||||
completionHandlerCalled = true
|
||||
switch action {
|
||||
case 0:
|
||||
completionHandler(.cancelAuthenticationChallenge, nil)
|
||||
|
@ -1471,7 +1496,10 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: ClientCertResponse?) in
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
completionHandler(.performDefaultHandling, nil)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
@ -1573,9 +1601,12 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return
|
||||
}
|
||||
|
||||
var completionHandlerCalled = false
|
||||
|
||||
let callback = WebViewChannelDelegate.JsAlertCallback()
|
||||
callback.nonNullSuccess = { (response: JsAlertResponse) in
|
||||
if response.handledByClient {
|
||||
completionHandlerCalled = true
|
||||
let action = response.action ?? 1
|
||||
switch action {
|
||||
case 0:
|
||||
|
@ -1589,14 +1620,20 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: JsAlertResponse?) in
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
self.createAlertDialog(message: message, responseMessage: responseMessage,
|
||||
confirmButtonTitle: confirmButtonTitle, completionHandler: completionHandler)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
self.createAlertDialog(message: message, responseMessage: responseMessage,
|
||||
confirmButtonTitle: confirmButtonTitle, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
callback.error = { (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler()
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler()
|
||||
}
|
||||
}
|
||||
|
||||
if let channelDelegate = channelDelegate {
|
||||
|
@ -1622,9 +1659,12 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
|
||||
public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo,
|
||||
completionHandler: @escaping (Bool) -> Void) {
|
||||
var completionHandlerCalled = false
|
||||
|
||||
let callback = WebViewChannelDelegate.JsConfirmCallback()
|
||||
callback.nonNullSuccess = { (response: JsConfirmResponse) in
|
||||
if response.handledByClient {
|
||||
completionHandlerCalled = true
|
||||
let action = response.action ?? 1
|
||||
switch action {
|
||||
case 0:
|
||||
|
@ -1641,14 +1681,20 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: JsConfirmResponse?) in
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
let cancelButtonTitle = response?.cancelButtonTitle
|
||||
self.createConfirmDialog(message: message, responseMessage: responseMessage, confirmButtonTitle: confirmButtonTitle, cancelButtonTitle: cancelButtonTitle, completionHandler: completionHandler)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
let cancelButtonTitle = response?.cancelButtonTitle
|
||||
self.createConfirmDialog(message: message, responseMessage: responseMessage, confirmButtonTitle: confirmButtonTitle, cancelButtonTitle: cancelButtonTitle, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
callback.error = { (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler(false)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler(false)
|
||||
}
|
||||
}
|
||||
|
||||
if let channelDelegate = channelDelegate {
|
||||
|
@ -1678,9 +1724,13 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
|
||||
public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt message: String, defaultText defaultValue: String?, initiatedByFrame frame: WKFrameInfo,
|
||||
completionHandler: @escaping (String?) -> Void) {
|
||||
|
||||
var completionHandlerCalled = false
|
||||
|
||||
let callback = WebViewChannelDelegate.JsPromptCallback()
|
||||
callback.nonNullSuccess = { (response: JsPromptResponse) in
|
||||
if response.handledByClient {
|
||||
completionHandlerCalled = true
|
||||
let action = response.action ?? 1
|
||||
switch action {
|
||||
case 0:
|
||||
|
@ -1697,16 +1747,22 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return true
|
||||
}
|
||||
callback.defaultBehaviour = { (response: JsPromptResponse?) in
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
let cancelButtonTitle = response?.cancelButtonTitle
|
||||
let value = response?.value
|
||||
self.createPromptDialog(message: message, defaultValue: defaultValue, responseMessage: responseMessage, confirmButtonTitle: confirmButtonTitle,
|
||||
cancelButtonTitle: cancelButtonTitle, value: value, completionHandler: completionHandler)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
let responseMessage = response?.message
|
||||
let confirmButtonTitle = response?.confirmButtonTitle
|
||||
let cancelButtonTitle = response?.cancelButtonTitle
|
||||
let value = response?.value
|
||||
self.createPromptDialog(message: message, defaultValue: defaultValue, responseMessage: responseMessage, confirmButtonTitle: confirmButtonTitle,
|
||||
cancelButtonTitle: cancelButtonTitle, value: value, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
callback.error = { (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler(nil)
|
||||
if !completionHandlerCalled {
|
||||
completionHandlerCalled = true
|
||||
print(code + ", " + (message ?? ""))
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if let channelDelegate = channelDelegate {
|
||||
|
@ -1768,13 +1824,18 @@ public class InAppWebView: WKWebView, WKUIDelegate,
|
|||
return
|
||||
}
|
||||
|
||||
var decisionHandlerCalled = false
|
||||
let callback = WebViewChannelDelegate.ShouldAllowDeprecatedTLSCallback()
|
||||
callback.nonNullSuccess = { (action: Bool) in
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(action)
|
||||
return false
|
||||
}
|
||||
callback.defaultBehaviour = { (action: Bool?) in
|
||||
decisionHandler(false)
|
||||
if !decisionHandlerCalled {
|
||||
decisionHandlerCalled = true
|
||||
decisionHandler(false)
|
||||
}
|
||||
}
|
||||
callback.error = { [weak callback] (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
|
|
|
@ -834,6 +834,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return PermissionResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onPermissionRequest(request: PermissionRequest, callback: PermissionRequestCallback) {
|
||||
|
@ -854,6 +858,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return WKNavigationActionPolicy.cancel
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func shouldOverrideUrlLoading(navigationAction: WKNavigationAction, callback: ShouldOverrideUrlLoadingCallback) {
|
||||
|
@ -905,6 +913,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return HttpAuthResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onReceivedHttpAuthRequest(challenge: HttpAuthenticationChallenge, callback: ReceivedHttpAuthRequestCallback) {
|
||||
|
@ -922,6 +934,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return ServerTrustAuthResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onReceivedServerTrustAuthRequest(challenge: ServerTrustChallenge, callback: ReceivedServerTrustAuthRequestCallback) {
|
||||
|
@ -939,6 +955,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return ClientCertResponse.fromMap(map: obj as? [String:Any?])
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onReceivedClientCertRequest(challenge: ClientCertChallenge, callback: ReceivedClientCertRequestCallback) {
|
||||
|
@ -1013,6 +1033,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return WKNavigationResponsePolicy.cancel
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func onNavigationResponse(navigationResponse: WKNavigationResponse, callback: NavigationResponseCallback) {
|
||||
|
@ -1033,6 +1057,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func shouldAllowDeprecatedTLS(challenge: URLAuthenticationChallenge, callback: ShouldAllowDeprecatedTLSCallback) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.
|
||||
version: 6.0.0-beta.3
|
||||
version: 6.0.0-beta.4
|
||||
homepage: https://inappwebview.dev/
|
||||
repository: https://github.com/pichillilorenzo/flutter_inappwebview
|
||||
issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues
|
||||
|
|
Loading…
Reference in New Issue