diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f4b7a56..4a5fb627 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,12 @@ - Added `PrintJobController` to manage print jobs - Added `WebAuthenticationSession` for iOS - Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState`, `isInFullscreen`, `getCameraCaptureState`, `setCameraCaptureState`, `getMicrophoneCaptureState`, `setMicrophoneCaptureState` WebView controller methods -- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy`, `willSuppressErrorPage`, `algorithmicDarkeningAllowed` WebView settings +- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy`, `willSuppressErrorPage`, `algorithmicDarkeningAllowed`, `requestedWithHeaderMode` WebView settings - Added `onCameraCaptureStateChanged`, `onMicrophoneCaptureStateChanged` WebView events - Added support for `onPermissionRequest` event on iOS 15.0+ - Added `debugLoggingSettings` static property for WebView and ChromeSafariBrowser - Added `WebViewFeature.DOCUMENT_START_SCRIPT` Android feature support +- Added `getRequestedWithHeaderMode`, `setRequestedWithHeaderMode` ServiceWorkerController methods - Updated `getMetaThemeColor` on iOS 15.0+ - Deprecated `onLoadError` for `onReceivedError`. `onReceivedError` will be called also for subframes - Deprecated `onLoadHttpError` for `onReceivedError`. `onReceivedHttpError` will be called also for subframes diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/service_worker/ServiceWorkerChannelDelegate.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/service_worker/ServiceWorkerChannelDelegate.java index ec52036e..6961f2bc 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/service_worker/ServiceWorkerChannelDelegate.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/service_worker/ServiceWorkerChannelDelegate.java @@ -1,5 +1,6 @@ package com.pichillilorenzo.flutter_inappwebview.service_worker; +import android.annotation.SuppressLint; import android.os.Build; import android.webkit.ServiceWorkerController; @@ -35,6 +36,7 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl { this.serviceWorkerManager = serviceWorkerManager; } + @SuppressLint("RestrictedApi") @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { ServiceWorkerControllerCompat serviceWorkerController = ServiceWorkerManager.serviceWorkerController; @@ -72,6 +74,13 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl { result.success(false); } break; + case "getRequestedWithHeaderMode": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL)) { + result.success(serviceWorkerWebSettings.getRequestedWithHeaderMode()); + } else { + result.success(null); + } + break; case "getCacheMode": if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CACHE_MODE)) { result.success(serviceWorkerWebSettings.getCacheMode()); @@ -107,6 +116,13 @@ public class ServiceWorkerChannelDelegate extends ChannelDelegateImpl { } result.success(true); break; + case "setRequestedWithHeaderMode": + if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL)) { + Integer mode = (Integer) call.argument("mode"); + serviceWorkerWebSettings.setRequestedWithHeaderMode(mode); + } + result.success(true); + break; default: result.notImplemented(); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java index 672504ac..6dc3ee4c 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java @@ -384,10 +384,13 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie if (WebViewFeature.isFeatureSupported(WebViewFeature.SUPPRESS_ERROR_PAGE)) { WebSettingsCompat.setWillSuppressErrorPage(settings, customSettings.willSuppressErrorPage); } - if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { WebSettingsCompat.setAlgorithmicDarkeningAllowed(settings, customSettings.algorithmicDarkeningAllowed); } + if (WebViewFeature.isFeatureSupported(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL) && + customSettings.requestedWithHeaderMode != null) { + WebSettingsCompat.setRequestedWithHeaderMode(settings, customSettings.requestedWithHeaderMode); + } contentBlockerHandler.getRuleList().clear(); for (Map> contentBlocker : customSettings.contentBlockers) { @@ -1029,12 +1032,17 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie WebViewFeature.isFeatureSupported(WebViewFeature.SUPPRESS_ERROR_PAGE)) { WebSettingsCompat.setWillSuppressErrorPage(settings, newCustomSettings.willSuppressErrorPage); } - if (newSettingsMap.get("algorithmicDarkeningAllowed") != null && !Util.objEquals(customSettings.algorithmicDarkeningAllowed, newCustomSettings.algorithmicDarkeningAllowed) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { WebSettingsCompat.setAlgorithmicDarkeningAllowed(settings, newCustomSettings.algorithmicDarkeningAllowed); } + if (newSettingsMap.get("requestedWithHeaderMode") != null && + !Util.objEquals(customSettings.requestedWithHeaderMode, newCustomSettings.requestedWithHeaderMode) && + WebViewFeature.isFeatureSupported(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL) && + newCustomSettings.requestedWithHeaderMode != null) { + WebSettingsCompat.setRequestedWithHeaderMode(settings, newCustomSettings.requestedWithHeaderMode); + } customSettings = newCustomSettings; } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewSettings.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewSettings.java index 4a92ef17..3edbc110 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewSettings.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewSettings.java @@ -115,6 +115,8 @@ public class InAppWebViewSettings implements ISettings { public String horizontalScrollbarTrackColor; public Boolean willSuppressErrorPage = false; public Boolean algorithmicDarkeningAllowed = false; + @Nullable + public Integer requestedWithHeaderMode; @NonNull @Override @@ -379,6 +381,9 @@ public class InAppWebViewSettings implements ISettings { case "algorithmicDarkeningAllowed": algorithmicDarkeningAllowed = (Boolean) value; break; + case "requestedWithHeaderMode": + requestedWithHeaderMode = (Integer) value; + break; } } @@ -473,6 +478,7 @@ public class InAppWebViewSettings implements ISettings { settings.put("horizontalScrollbarTrackColor", horizontalScrollbarTrackColor); settings.put("willSuppressErrorPage", willSuppressErrorPage); settings.put("algorithmicDarkeningAllowed", algorithmicDarkeningAllowed); + settings.put("requestedWithHeaderMode", requestedWithHeaderMode); return settings; } @@ -564,6 +570,9 @@ public class InAppWebViewSettings implements ISettings { if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { realSettings.put("algorithmicDarkeningAllowed", WebSettingsCompat.isAlgorithmicDarkeningAllowed(settings)); } + if (WebViewFeature.isFeatureSupported(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL)) { + realSettings.put("requestedWithHeaderMode", WebSettingsCompat.getRequestedWithHeaderMode(settings)); + } } return realSettings; } diff --git a/lib/src/android/service_worker_controller.dart b/lib/src/android/service_worker_controller.dart index cbec5bb5..4bf042dc 100644 --- a/lib/src/android/service_worker_controller.dart +++ b/lib/src/android/service_worker_controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'webview_feature.dart'; import '../types/main.dart'; +import '../in_app_webview/in_app_webview_settings.dart'; ///Class that manages Service Workers used by [WebView]. /// @@ -118,6 +119,16 @@ class ServiceWorkerController { await _channel.invokeMethod('getCacheMode', args)); } + ///Gets how Service Workers will set the `X-Requested-With` header on HTTP requests. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.REQUESTED_WITH_HEADER_CONTROL]. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getRequestedWithHeaderMode() + static Future getRequestedWithHeaderMode() async { + Map args = {}; + return RequestedWithHeaderMode.fromNativeValue( + await _channel.invokeMethod('getRequestedWithHeaderMode', args)); + } + ///Enables or disables content URL access from Service Workers. ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS]. /// @@ -165,6 +176,19 @@ class ServiceWorkerController { args.putIfAbsent("mode", () => mode.toNativeValue()); await _channel.invokeMethod('setCacheMode', args); } + + ///Sets how Service Workers will set the `X-Requested-With` header on requests. + ///If you are calling this method, you may also want to call [InAppWebViewSettings.requestedWithHeaderMode] + ///with the same parameter value to configure non-ServiceWorker requests. + ///The default behavior may vary depending on the WebView implementation. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.REQUESTED_WITH_HEADER_CONTROL]. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setRequestedWithHeaderMode(int) + static Future setRequestedWithHeaderMode(RequestedWithHeaderMode mode) async { + Map args = {}; + args.putIfAbsent("mode", () => mode.toNativeValue()); + await _channel.invokeMethod('setRequestedWithHeaderMode', args); + } } ///Class used by clients to capture Service Worker related callbacks. diff --git a/lib/src/android/webview_feature.dart b/lib/src/android/webview_feature.dart index 7826a93a..299d4239 100644 --- a/lib/src/android/webview_feature.dart +++ b/lib/src/android/webview_feature.dart @@ -200,6 +200,10 @@ class WebViewFeature_ { static const ALGORITHMIC_DARKENING = const WebViewFeature_._internal("ALGORITHMIC_DARKENING"); + ///This feature covers [InAppWebViewSettings.requestedWithHeaderMode]. + static const REQUESTED_WITH_HEADER_CONTROL = + const WebViewFeature_._internal("REQUESTED_WITH_HEADER_CONTROL"); + ///Return whether a feature is supported at run-time. On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher, ///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device, ///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`. @@ -411,6 +415,10 @@ class AndroidWebViewFeature_ { static const ALGORITHMIC_DARKENING = const AndroidWebViewFeature_._internal("ALGORITHMIC_DARKENING"); + ///This feature covers [InAppWebViewSettings.requestedWithHeaderMode]. + static const REQUESTED_WITH_HEADER_CONTROL = + const AndroidWebViewFeature_._internal("REQUESTED_WITH_HEADER_CONTROL"); + ///Return whether a feature is supported at run-time. On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher, ///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device, ///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`. diff --git a/lib/src/android/webview_feature.g.dart b/lib/src/android/webview_feature.g.dart index 81c65b10..1f33c22b 100644 --- a/lib/src/android/webview_feature.g.dart +++ b/lib/src/android/webview_feature.g.dart @@ -201,6 +201,10 @@ class WebViewFeature { static const ALGORITHMIC_DARKENING = WebViewFeature._internal( 'ALGORITHMIC_DARKENING', 'ALGORITHMIC_DARKENING'); + ///This feature covers [InAppWebViewSettings.requestedWithHeaderMode]. + static const REQUESTED_WITH_HEADER_CONTROL = WebViewFeature._internal( + 'REQUESTED_WITH_HEADER_CONTROL', 'REQUESTED_WITH_HEADER_CONTROL'); + ///Set of all values of [WebViewFeature]. static final Set values = [ WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, @@ -247,6 +251,7 @@ class WebViewFeature { WebViewFeature.DOCUMENT_START_SCRIPT, WebViewFeature.SUPPRESS_ERROR_PAGE, WebViewFeature.ALGORITHMIC_DARKENING, + WebViewFeature.REQUESTED_WITH_HEADER_CONTROL, ].toSet(); ///Gets a possible [WebViewFeature] instance from [String] value. @@ -506,6 +511,10 @@ class AndroidWebViewFeature { static const ALGORITHMIC_DARKENING = AndroidWebViewFeature._internal( 'ALGORITHMIC_DARKENING', 'ALGORITHMIC_DARKENING'); + ///This feature covers [InAppWebViewSettings.requestedWithHeaderMode]. + static const REQUESTED_WITH_HEADER_CONTROL = AndroidWebViewFeature._internal( + 'REQUESTED_WITH_HEADER_CONTROL', 'REQUESTED_WITH_HEADER_CONTROL'); + ///Set of all values of [AndroidWebViewFeature]. static final Set values = [ AndroidWebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, @@ -552,6 +561,7 @@ class AndroidWebViewFeature { AndroidWebViewFeature.DOCUMENT_START_SCRIPT, AndroidWebViewFeature.SUPPRESS_ERROR_PAGE, AndroidWebViewFeature.ALGORITHMIC_DARKENING, + AndroidWebViewFeature.REQUESTED_WITH_HEADER_CONTROL, ].toSet(); ///Gets a possible [AndroidWebViewFeature] instance from [String] value. diff --git a/lib/src/in_app_webview/in_app_webview_settings.dart b/lib/src/in_app_webview/in_app_webview_settings.dart index 821a91f8..a0c15603 100755 --- a/lib/src/in_app_webview/in_app_webview_settings.dart +++ b/lib/src/in_app_webview/in_app_webview_settings.dart @@ -2,6 +2,7 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; +import '../types/requested_with_header_mode.dart'; import 'android/in_app_webview_options.dart'; import 'apple/in_app_webview_options.dart'; import '../content_blocker.dart'; @@ -672,9 +673,17 @@ class InAppWebViewSettings { /// ///If Android is applying Force Dark to WebView then WebView will ignore the value of this setting and behave as if it were set to true. /// - ///**NOTE**: available on Android 29+. + ///**NOTE**: available on Android 29+ and only if [WebViewFeature.ALGORITHMIC_DARKENING] feature is supported. bool algorithmicDarkeningAllowed; + ///Sets how the WebView will set the `X-Requested-With` header on requests. + ///If you are calling this method, you may also want to call [ServiceWorkerWebSettingsCompat.setRequestedWithHeaderMode] + ///with the same parameter value to configure ServiceWorker requests. + ///The default behavior may vary depending on the WebView implementation. + /// + ///**NOTE**: available on Android only if [WebViewFeature.REQUESTED_WITH_HEADER_CONTROL] feature is supported. + RequestedWithHeaderMode? requestedWithHeaderMode; + ///Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`. /// ///**Supported Platforms/Implementations**: @@ -1151,6 +1160,7 @@ class InAppWebViewSettings { this.horizontalScrollbarTrackColor, this.willSuppressErrorPage = false, this.algorithmicDarkeningAllowed = false, + this.requestedWithHeaderMode, this.disallowOverScroll = false, this.enableViewportScale = false, this.suppressesIncrementalRendering = false, @@ -1299,6 +1309,7 @@ class InAppWebViewSettings { "horizontalScrollbarTrackColor": horizontalScrollbarTrackColor?.toHex(), "willSuppressErrorPage": willSuppressErrorPage, "algorithmicDarkeningAllowed": algorithmicDarkeningAllowed, + "requestedWithHeaderMode": requestedWithHeaderMode?.toNativeValue(), "disallowOverScroll": disallowOverScroll, "enableViewportScale": enableViewportScale, "suppressesIncrementalRendering": suppressesIncrementalRendering, @@ -1484,6 +1495,7 @@ class InAppWebViewSettings { UtilColor.fromHex(map["horizontalScrollbarTrackColor"]); settings.willSuppressErrorPage = map["willSuppressErrorPage"]; settings.algorithmicDarkeningAllowed = map["algorithmicDarkeningAllowed"]; + settings.requestedWithHeaderMode = RequestedWithHeaderMode.fromNativeValue(map["requestedWithHeaderMode"]); } else if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS) { diff --git a/lib/src/types/main.dart b/lib/src/types/main.dart index 1abe6550..be106fc0 100644 --- a/lib/src/types/main.dart +++ b/lib/src/types/main.dart @@ -159,4 +159,5 @@ export 'website_data_type.dart' show WebsiteDataType, IOSWKWebsiteDataType; export 'webview_implementation.dart' show WebViewImplementation; export 'webview_package_info.dart' show WebViewPackageInfo, AndroidWebViewPackageInfo; export 'webview_render_process_action.dart' show WebViewRenderProcessAction; -export 'window_features.dart' show WindowFeatures, IOSWKWindowFeatures; \ No newline at end of file +export 'window_features.dart' show WindowFeatures, IOSWKWindowFeatures; +export 'requested_with_header_mode.dart' show RequestedWithHeaderMode; diff --git a/lib/src/types/requested_with_header_mode.dart b/lib/src/types/requested_with_header_mode.dart new file mode 100644 index 00000000..9b868ca9 --- /dev/null +++ b/lib/src/types/requested_with_header_mode.dart @@ -0,0 +1,19 @@ +import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; + +part 'requested_with_header_mode.g.dart'; + +///Class used to set how the WebView will set the `X-Requested-With` header on requests. +@ExchangeableEnum() +class RequestedWithHeaderMode_ { + // ignore: unused_field + final int _value; + const RequestedWithHeaderMode_._internal(this._value); + + ///In this mode the WebView will not add an `X-Requested-With` header on HTTP requests automatically. + static const NO_HEADER = const RequestedWithHeaderMode_._internal(0); + + ///In this mode the WebView automatically add an `X-Requested-With` header to outgoing requests, + ///if the application or the loaded webpage has not already set a header value. + ///The value of this automatically added header will be the package name of the app. This is the default mode. + static const APP_PACKAGE_NAME = const RequestedWithHeaderMode_._internal(1); +} \ No newline at end of file diff --git a/lib/src/types/requested_with_header_mode.g.dart b/lib/src/types/requested_with_header_mode.g.dart new file mode 100644 index 00000000..b9003419 --- /dev/null +++ b/lib/src/types/requested_with_header_mode.g.dart @@ -0,0 +1,81 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'requested_with_header_mode.dart'; + +// ************************************************************************** +// ExchangeableEnumGenerator +// ************************************************************************** + +///Class used to set how the WebView will set the `X-Requested-With` header on requests. +class RequestedWithHeaderMode { + final int _value; + final int _nativeValue; + const RequestedWithHeaderMode._internal(this._value, this._nativeValue); +// ignore: unused_element + factory RequestedWithHeaderMode._internalMultiPlatform( + int value, Function nativeValue) => + RequestedWithHeaderMode._internal(value, nativeValue()); + + ///In this mode the WebView will not add an `X-Requested-With` header on HTTP requests automatically. + static const NO_HEADER = RequestedWithHeaderMode._internal(0, 0); + + ///In this mode the WebView automatically add an `X-Requested-With` header to outgoing requests, + ///if the application or the loaded webpage has not already set a header value. + ///The value of this automatically added header will be the package name of the app. This is the default mode. + static const APP_PACKAGE_NAME = RequestedWithHeaderMode._internal(1, 1); + + ///Set of all values of [RequestedWithHeaderMode]. + static final Set values = [ + RequestedWithHeaderMode.NO_HEADER, + RequestedWithHeaderMode.APP_PACKAGE_NAME, + ].toSet(); + + ///Gets a possible [RequestedWithHeaderMode] instance from [int] value. + static RequestedWithHeaderMode? fromValue(int? value) { + if (value != null) { + try { + return RequestedWithHeaderMode.values + .firstWhere((element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets a possible [RequestedWithHeaderMode] instance from a native value. + static RequestedWithHeaderMode? fromNativeValue(int? value) { + if (value != null) { + try { + return RequestedWithHeaderMode.values + .firstWhere((element) => element.toNativeValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets [int] value. + int toValue() => _value; + + ///Gets [int] native value. + int toNativeValue() => _nativeValue; + + @override + int get hashCode => _value.hashCode; + + @override + bool operator ==(value) => value == _value; + + @override + String toString() { + switch (_value) { + case 0: + return 'NO_HEADER'; + case 1: + return 'APP_PACKAGE_NAME'; + } + return _value.toString(); + } +}