diff --git a/CHANGELOG.md b/CHANGELOG.md index ef69434e..45cafba1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Added `debugLoggingSettings` static property for WebView and ChromeSafariBrowser - Added `WebViewFeature.DOCUMENT_START_SCRIPT` Android feature support - Added `getRequestedWithHeaderMode`, `setRequestedWithHeaderMode` ServiceWorkerController methods +- Added `ContentBlockerTrigger.ifFrameUrl` and `ContentBlockerTrigger.loadContext` properties - 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/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 3847f26e..2d97b1e2 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -116,7 +116,7 @@ class _InAppWebViewExampleScreenState extends State { InAppWebView( key: webViewKey, initialUrlRequest: - URLRequest(url: Uri.parse('https://developer.apple.com/videos/play/wwdc2022/10049/?time=264')), + URLRequest(url: Uri.parse('https://github.com/flutter')), // initialUrlRequest: // URLRequest(url: Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')), // initialFile: "assets/index.html", diff --git a/lib/src/content_blocker.dart b/lib/src/content_blocker.dart index cdc5c5ba..b9935d82 100755 --- a/lib/src/content_blocker.dart +++ b/lib/src/content_blocker.dart @@ -35,53 +35,59 @@ class ContentBlocker { ///For example, you can limit the trigger to specific domains or have it not apply when a match is found on a specific domain. class ContentBlockerTrigger { ///A regular expression pattern to match the URL against. - late String urlFilter; + String urlFilter; - ///Used only by iOS. A Boolean value. The default value is false. - late bool urlFilterIsCaseSensitive; + ///A list of regular expressions to match iframes URL against. + /// + ///*NOTE*: available only on iOS. + List ifFrameUrl; + + ///A Boolean value. The default value is `false`. + /// + ///*NOTE*: available only on iOS. + bool urlFilterIsCaseSensitive; ///A list of [ContentBlockerTriggerResourceType] representing the resource types (how the browser intends to use the resource) that the rule should match. ///If not specified, the rule matches all resource types. - late List resourceType; + List resourceType; ///A list of strings matched to a URL's domain; limits action to a list of specific domains. ///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.unlessDomain]. - late List ifDomain; + List ifDomain; ///A list of strings matched to a URL's domain; acts on any site except domains in a provided list. ///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.ifDomain]. - late List unlessDomain; + List unlessDomain; ///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types. - late List loadType; + List loadType; ///A list of strings matched to the entire main document URL; limits the action to a specific list of URL patterns. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.unlessTopUrl]. - late List ifTopUrl; + List ifTopUrl; ///An array of strings matched to the entire main document URL; acts on any site except URL patterns in provided list. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.ifTopUrl]. - late List unlessTopUrl; + List unlessTopUrl; + + ///An array of strings that specify loading contexts. + /// + ///*NOTE*: available only on iOS. + List loadContext; ContentBlockerTrigger( - {required String urlFilter, - bool urlFilterIsCaseSensitive = false, - List resourceType = const [], - List ifDomain = const [], - List unlessDomain = const [], - List loadType = const [], - List ifTopUrl = const [], - List unlessTopUrl = const []}) { - this.urlFilter = urlFilter; - this.resourceType = resourceType; - this.urlFilterIsCaseSensitive = urlFilterIsCaseSensitive; - this.ifDomain = ifDomain; - this.unlessDomain = unlessDomain; + {required this.urlFilter, + this.ifFrameUrl = const [], + this.urlFilterIsCaseSensitive = false, + this.resourceType = const [], + this.ifDomain = const [], + this.unlessDomain = const [], + this.loadType = const [], + this.ifTopUrl = const [], + this.unlessTopUrl = const [], + this.loadContext = const []}) { assert(!(this.ifDomain.isEmpty || this.unlessDomain.isEmpty) == false); - this.loadType = loadType; assert(this.loadType.length <= 2); - this.ifTopUrl = ifTopUrl; - this.unlessTopUrl = unlessTopUrl; assert(!(this.ifTopUrl.isEmpty || this.unlessTopUrl.isEmpty) == false); } @@ -94,16 +100,22 @@ class ContentBlockerTrigger { loadType.forEach((type) { loadTypeStringList.add(type.toNativeValue()); }); + List loadContextStringList = []; + loadContext.forEach((type) { + loadContextStringList.add(type.toNativeValue()); + }); Map map = { "url-filter": urlFilter, + "if-frame-url": ifFrameUrl, "url-filter-is-case-sensitive": urlFilterIsCaseSensitive, "if-domain": ifDomain, "unless-domain": unlessDomain, "resource-type": resourceTypeStringList, "load-type": loadTypeStringList, "if-top-url": ifTopUrl, - "unless-top-url": unlessTopUrl + "unless-top-url": unlessTopUrl, + "load-context": loadContextStringList }; map.keys @@ -119,6 +131,7 @@ class ContentBlockerTrigger { static ContentBlockerTrigger fromMap(Map map) { List resourceType = []; List loadType = []; + List loadContext = []; List resourceTypeStringList = List.from(map["resource-type"] ?? []); @@ -137,15 +150,25 @@ class ContentBlockerTrigger { } }); + List loadContextStringList = List.from(map["load-context"] ?? []); + loadContextStringList.forEach((typeValue) { + var context = ContentBlockerTriggerLoadContext.fromNativeValue(typeValue); + if (context != null) { + loadContext.add(context); + } + }); + return ContentBlockerTrigger( urlFilter: map["url-filter"], + ifFrameUrl: map["if-frame-url"], urlFilterIsCaseSensitive: map["url-filter-is-case-sensitive"], ifDomain: List.from(map["if-domain"] ?? []), unlessDomain: List.from(map["unless-domain"] ?? []), resourceType: resourceType, loadType: loadType, ifTopUrl: List.from(map["if-top-url"] ?? []), - unlessTopUrl: List.from(map["unless-top-url"] ?? [])); + unlessTopUrl: List.from(map["unless-top-url"] ?? []), + loadContext: loadContext); } } @@ -156,19 +179,17 @@ class ContentBlockerTrigger { ///Group the rules with similar actions together to improve performance. class ContentBlockerAction { ///Type of the action. - late ContentBlockerActionType type; + ContentBlockerActionType type; ///If the action type is [ContentBlockerActionType.CSS_DISPLAY_NONE], then also the [selector] property is required, otherwise it is ignored. ///It specify a string that defines a selector list. Use CSS identifiers as the individual selector values, separated by commas. String? selector; ContentBlockerAction( - {required ContentBlockerActionType type, String? selector}) { - this.type = type; + {required this.type, this.selector}) { if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) { - assert(selector != null); + assert(this.selector != null); } - this.selector = selector; } Map toMap() { diff --git a/lib/src/types/content_blocker_trigger_load_context.dart b/lib/src/types/content_blocker_trigger_load_context.dart new file mode 100644 index 00000000..4cbd6e7d --- /dev/null +++ b/lib/src/types/content_blocker_trigger_load_context.dart @@ -0,0 +1,22 @@ +import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; + +import '../content_blocker.dart'; + +part 'content_blocker_trigger_load_context.g.dart'; + +///Class that represents the kind of load context that can be used with a [ContentBlockerTrigger]. +@ExchangeableEnum() +class ContentBlockerTriggerLoadContext_ { + // ignore: unused_field + final String _value; + + const ContentBlockerTriggerLoadContext_._internal(this._value); + + ///Top frame load context + static const TOP_FRAME = + const ContentBlockerTriggerLoadContext_._internal('top-frame'); + + ///Child frame load context + static const CHILD_FRAME = + const ContentBlockerTriggerLoadContext_._internal('child-frame'); +} diff --git a/lib/src/types/content_blocker_trigger_load_context.g.dart b/lib/src/types/content_blocker_trigger_load_context.g.dart new file mode 100644 index 00000000..f71ccaaf --- /dev/null +++ b/lib/src/types/content_blocker_trigger_load_context.g.dart @@ -0,0 +1,76 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'content_blocker_trigger_load_context.dart'; + +// ************************************************************************** +// ExchangeableEnumGenerator +// ************************************************************************** + +///Class that represents the kind of load context that can be used with a [ContentBlockerTrigger]. +class ContentBlockerTriggerLoadContext { + final String _value; + final String _nativeValue; + const ContentBlockerTriggerLoadContext._internal( + this._value, this._nativeValue); +// ignore: unused_element + factory ContentBlockerTriggerLoadContext._internalMultiPlatform( + String value, Function nativeValue) => + ContentBlockerTriggerLoadContext._internal(value, nativeValue()); + + ///Top frame load context + static const TOP_FRAME = + ContentBlockerTriggerLoadContext._internal('top-frame', 'top-frame'); + + ///Child frame load context + static const CHILD_FRAME = + ContentBlockerTriggerLoadContext._internal('child-frame', 'child-frame'); + + ///Set of all values of [ContentBlockerTriggerLoadContext]. + static final Set values = [ + ContentBlockerTriggerLoadContext.TOP_FRAME, + ContentBlockerTriggerLoadContext.CHILD_FRAME, + ].toSet(); + + ///Gets a possible [ContentBlockerTriggerLoadContext] instance from [String] value. + static ContentBlockerTriggerLoadContext? fromValue(String? value) { + if (value != null) { + try { + return ContentBlockerTriggerLoadContext.values + .firstWhere((element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets a possible [ContentBlockerTriggerLoadContext] instance from a native value. + static ContentBlockerTriggerLoadContext? fromNativeValue(String? value) { + if (value != null) { + try { + return ContentBlockerTriggerLoadContext.values + .firstWhere((element) => element.toNativeValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets [String] value. + String toValue() => _value; + + ///Gets [String] native value. + String toNativeValue() => _nativeValue; + + @override + int get hashCode => _value.hashCode; + + @override + bool operator ==(value) => value == _value; + + @override + String toString() { + return _value; + } +} diff --git a/lib/src/types/main.dart b/lib/src/types/main.dart index 20f8cffd..87dcdef6 100644 --- a/lib/src/types/main.dart +++ b/lib/src/types/main.dart @@ -162,4 +162,5 @@ export 'webview_render_process_action.dart' show WebViewRenderProcessAction; export 'window_features.dart' show WindowFeatures, IOSWKWindowFeatures; export 'requested_with_header_mode.dart' show RequestedWithHeaderMode; export 'find_session.dart' show FindSession; -export 'search_result_display_style.dart' show SearchResultDisplayStyle; \ No newline at end of file +export 'search_result_display_style.dart' show SearchResultDisplayStyle; +export 'content_blocker_trigger_load_context.dart' show ContentBlockerTriggerLoadContext; \ No newline at end of file