2022-04-26 23:03:42 +00:00
import ' types/main.dart ' ;
2019-11-10 13:11:30 +00:00
2020-05-11 00:48:41 +00:00
///Class that represents a set of rules to use block content in the browser window.
2019-11-10 13:11:30 +00:00
///
///On iOS, it uses [WKContentRuleListStore](https://developer.apple.com/documentation/webkit/wkcontentruleliststore).
///On Android, it uses a custom implementation because such functionality doesn't exist.
2019-11-10 10:50:01 +00:00
///
2019-11-10 13:11:30 +00:00
///In general, this [article](https://developer.apple.com/documentation/safariservices/creating_a_content_blocker) can be used to get an overview about this functionality
///but on Android there are two types of [action] that are unavailable: `block-cookies` and `ignore-previous-rules`.
2019-10-26 20:11:23 +00:00
class ContentBlocker {
2019-11-10 13:11:30 +00:00
///Trigger of the content blocker. The trigger tells to the WebView when to perform the corresponding action.
2019-10-26 20:11:23 +00:00
ContentBlockerTrigger trigger ;
2019-12-01 11:55:06 +00:00
2019-11-10 13:11:30 +00:00
///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched.
2019-10-26 20:11:23 +00:00
ContentBlockerAction action ;
2021-01-28 16:10:15 +00:00
ContentBlocker ( { required this . trigger , required this . action } ) ;
2019-10-26 20:11:23 +00:00
Map < String , Map < String , dynamic > > toMap ( ) {
2019-12-01 11:55:06 +00:00
return { " trigger " : trigger . toMap ( ) , " action " : action . toMap ( ) } ;
2019-10-26 20:11:23 +00:00
}
2019-11-04 00:39:23 +00:00
static ContentBlocker fromMap ( Map < dynamic , Map < dynamic , dynamic > > map ) {
return ContentBlocker (
2019-11-10 13:11:30 +00:00
trigger: ContentBlockerTrigger . fromMap (
2021-01-28 16:10:15 +00:00
Map < String , dynamic > . from ( map [ " trigger " ] ! ) ) ,
2019-11-10 13:11:30 +00:00
action: ContentBlockerAction . fromMap (
2021-01-28 16:10:15 +00:00
Map < String , dynamic > . from ( map [ " action " ] ! ) ) ) ;
2019-11-04 00:39:23 +00:00
}
2019-10-26 20:11:23 +00:00
}
2019-11-10 13:11:30 +00:00
///Trigger of the content blocker. The trigger tells to the WebView when to perform the corresponding action.
///A trigger dictionary must include an [ContentBlockerTrigger.urlFilter], which specifies a pattern to match the URL against.
///The remaining properties are optional and modify the behavior of the trigger.
///For example, you can limit the trigger to specific domains or have it not apply when a match is found on a specific domain.
2019-10-26 20:11:23 +00:00
class ContentBlockerTrigger {
2019-11-10 13:11:30 +00:00
///A regular expression pattern to match the URL against.
2022-10-08 15:48:16 +00:00
String urlFilter ;
2019-12-01 11:55:06 +00:00
2022-10-08 15:48:16 +00:00
///A list of regular expressions to match iframes URL against.
///
///*NOTE*: available only on iOS.
List < String > ifFrameUrl ;
///A Boolean value. The default value is `false`.
///
///*NOTE*: available only on iOS.
bool urlFilterIsCaseSensitive ;
2019-12-01 11:55:06 +00:00
2019-11-10 13:11:30 +00:00
///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.
2022-10-08 15:48:16 +00:00
List < ContentBlockerTriggerResourceType > resourceType ;
2019-12-01 11:55:06 +00:00
2019-11-10 13:11:30 +00:00
///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].
2022-10-08 15:48:16 +00:00
List < String > ifDomain ;
2019-12-01 11:55:06 +00:00
2019-11-10 13:11:30 +00:00
///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].
2022-10-08 15:48:16 +00:00
List < String > unlessDomain ;
2019-12-01 11:55:06 +00:00
2019-11-10 13:11:30 +00:00
///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types.
2022-10-08 15:48:16 +00:00
List < ContentBlockerTriggerLoadType > loadType ;
2019-12-01 11:55:06 +00:00
2019-11-10 13:11:30 +00:00
///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].
2022-10-08 15:48:16 +00:00
List < String > ifTopUrl ;
2019-12-01 11:55:06 +00:00
2019-11-10 13:11:30 +00:00
///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].
2022-10-08 15:48:16 +00:00
List < String > unlessTopUrl ;
///An array of strings that specify loading contexts.
///
///*NOTE*: available only on iOS.
List < ContentBlockerTriggerLoadContext > loadContext ;
2019-10-26 20:11:23 +00:00
2019-12-01 11:55:06 +00:00
ContentBlockerTrigger (
2022-10-08 15:48:16 +00:00
{ required this . urlFilter ,
this . ifFrameUrl = const < String > [ ] ,
this . urlFilterIsCaseSensitive = false ,
this . resourceType = const < ContentBlockerTriggerResourceType > [ ] ,
this . ifDomain = const < String > [ ] ,
this . unlessDomain = const < String > [ ] ,
this . loadType = const < ContentBlockerTriggerLoadType > [ ] ,
this . ifTopUrl = const < String > [ ] ,
this . unlessTopUrl = const < String > [ ] ,
this . loadContext = const < ContentBlockerTriggerLoadContext > [ ] } ) {
2019-10-27 03:35:05 +00:00
assert ( ! ( this . ifDomain . isEmpty | | this . unlessDomain . isEmpty ) = = false ) ;
assert ( this . loadType . length < = 2 ) ;
assert ( ! ( this . ifTopUrl . isEmpty | | this . unlessTopUrl . isEmpty ) = = false ) ;
}
2019-10-26 20:11:23 +00:00
Map < String , dynamic > toMap ( ) {
List < String > resourceTypeStringList = [ ] ;
resourceType . forEach ( ( type ) {
2022-10-05 11:52:07 +00:00
resourceTypeStringList . add ( type . toNativeValue ( ) ) ;
2019-10-26 20:11:23 +00:00
} ) ;
2019-10-27 03:35:05 +00:00
List < String > loadTypeStringList = [ ] ;
loadType . forEach ( ( type ) {
2022-10-05 11:52:07 +00:00
loadTypeStringList . add ( type . toNativeValue ( ) ) ;
2019-10-27 03:35:05 +00:00
} ) ;
2022-10-08 15:48:16 +00:00
List < String > loadContextStringList = [ ] ;
loadContext . forEach ( ( type ) {
loadContextStringList . add ( type . toNativeValue ( ) ) ;
} ) ;
2019-10-26 20:11:23 +00:00
2019-10-27 03:35:05 +00:00
Map < String , dynamic > map = {
2019-10-26 20:11:23 +00:00
" url-filter " : urlFilter ,
2022-10-08 15:48:16 +00:00
" if-frame-url " : ifFrameUrl ,
2019-10-27 03:35:05 +00:00
" url-filter-is-case-sensitive " : urlFilterIsCaseSensitive ,
" if-domain " : ifDomain ,
" unless-domain " : unlessDomain ,
" resource-type " : resourceTypeStringList ,
" load-type " : loadTypeStringList ,
" if-top-url " : ifTopUrl ,
2022-10-08 15:48:16 +00:00
" unless-top-url " : unlessTopUrl ,
" load-context " : loadContextStringList
2019-10-26 20:11:23 +00:00
} ;
2019-10-27 03:35:05 +00:00
map . keys
2019-12-01 11:55:06 +00:00
. where ( ( key ) = >
map [ key ] = = null | |
( map [ key ] is List & & ( map [ key ] as List ) . length = = 0 ) ) // filter keys
2019-10-27 03:35:05 +00:00
. toList ( ) // create a copy to avoid concurrent modifications
. forEach ( map . remove ) ;
return map ;
2019-10-26 20:11:23 +00:00
}
2019-11-04 00:39:23 +00:00
static ContentBlockerTrigger fromMap ( Map < String , dynamic > map ) {
List < ContentBlockerTriggerResourceType > resourceType = [ ] ;
List < ContentBlockerTriggerLoadType > loadType = [ ] ;
2022-10-08 15:48:16 +00:00
List < ContentBlockerTriggerLoadContext > loadContext = [ ] ;
2019-11-04 00:39:23 +00:00
2019-12-01 11:55:06 +00:00
List < String > resourceTypeStringList =
List < String > . from ( map [ " resource-type " ] ? ? [ ] ) ;
2021-01-28 16:10:15 +00:00
resourceTypeStringList . forEach ( ( typeValue ) {
2022-10-05 11:52:07 +00:00
var type = ContentBlockerTriggerResourceType . fromNativeValue ( typeValue ) ;
2021-01-28 16:10:15 +00:00
if ( type ! = null ) {
resourceType . add ( type ) ;
}
2019-11-04 00:39:23 +00:00
} ) ;
List < String > loadTypeStringList = List < String > . from ( map [ " load-type " ] ? ? [ ] ) ;
2021-01-28 16:10:15 +00:00
loadTypeStringList . forEach ( ( typeValue ) {
2022-10-05 11:52:07 +00:00
var type = ContentBlockerTriggerLoadType . fromNativeValue ( typeValue ) ;
2021-01-28 16:10:15 +00:00
if ( type ! = null ) {
loadType . add ( type ) ;
}
2019-11-04 00:39:23 +00:00
} ) ;
2022-10-11 14:19:36 +00:00
List < String > loadContextStringList =
List < String > . from ( map [ " load-context " ] ? ? [ ] ) ;
2022-10-08 15:48:16 +00:00
loadContextStringList . forEach ( ( typeValue ) {
var context = ContentBlockerTriggerLoadContext . fromNativeValue ( typeValue ) ;
if ( context ! = null ) {
loadContext . add ( context ) ;
}
} ) ;
2019-11-04 00:39:23 +00:00
return ContentBlockerTrigger (
2019-11-10 13:11:30 +00:00
urlFilter: map [ " url-filter " ] ,
2022-10-08 15:48:16 +00:00
ifFrameUrl: map [ " if-frame-url " ] ,
2019-11-04 00:39:23 +00:00
urlFilterIsCaseSensitive: map [ " url-filter-is-case-sensitive " ] ,
ifDomain: List < String > . from ( map [ " if-domain " ] ? ? [ ] ) ,
unlessDomain: List < String > . from ( map [ " unless-domain " ] ? ? [ ] ) ,
resourceType: resourceType ,
loadType: loadType ,
ifTopUrl: List < String > . from ( map [ " if-top-url " ] ? ? [ ] ) ,
2022-10-08 15:48:16 +00:00
unlessTopUrl: List < String > . from ( map [ " unless-top-url " ] ? ? [ ] ) ,
loadContext: loadContext ) ;
2019-11-04 00:39:23 +00:00
}
2019-10-26 20:11:23 +00:00
}
2019-11-10 13:11:30 +00:00
///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched.
///When a trigger matches a resource, the browser queues the associated action for execution.
///The WebView evaluates all the triggers, it executes the actions in order.
///When a domain matches a trigger, all rules after the triggered rule that specify the same action are skipped.
///Group the rules with similar actions together to improve performance.
2019-10-26 20:11:23 +00:00
class ContentBlockerAction {
2019-11-10 13:11:30 +00:00
///Type of the action.
2022-10-08 15:48:16 +00:00
ContentBlockerActionType type ;
2019-12-01 11:55:06 +00:00
2019-11-10 13:11:30 +00:00
///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.
2021-01-28 16:10:15 +00:00
String ? selector ;
2019-10-26 20:11:23 +00:00
2022-10-11 14:19:36 +00:00
ContentBlockerAction ( { required this . type , this . selector } ) {
2019-10-26 20:11:23 +00:00
if ( this . type = = ContentBlockerActionType . CSS_DISPLAY_NONE ) {
2022-10-08 15:48:16 +00:00
assert ( this . selector ! = null ) ;
2019-10-26 20:11:23 +00:00
}
}
Map < String , dynamic > toMap ( ) {
2022-10-11 14:19:36 +00:00
Map < String , dynamic > map = {
" type " : type . toNativeValue ( ) ,
" selector " : selector
} ;
2019-10-27 03:35:05 +00:00
map . keys
2019-12-01 11:55:06 +00:00
. where ( ( key ) = >
map [ key ] = = null | |
( map [ key ] is List & & ( map [ key ] as List ) . length = = 0 ) ) // filter keys
2019-10-27 03:35:05 +00:00
. toList ( ) // create a copy to avoid concurrent modifications
. forEach ( map . remove ) ;
return map ;
2019-10-26 20:11:23 +00:00
}
2019-11-04 00:39:23 +00:00
static ContentBlockerAction fromMap ( Map < String , dynamic > map ) {
return ContentBlockerAction (
2022-10-05 11:52:07 +00:00
type: ContentBlockerActionType . fromNativeValue ( map [ " type " ] ) ! ,
2019-12-01 11:55:06 +00:00
selector: map [ " selector " ] ) ;
2019-11-04 00:39:23 +00:00
}
2019-12-01 11:55:06 +00:00
}