@@ -182,7 +183,7 @@ class _InlineExampleScreenState extends State {
},
shouldOverrideUrlLoading: (InAppWebViewController controller, String url) {
print("override $url");
- controller.loadUrl(url);
+ controller.loadUrl(url: url);
},
onLoadResource: (InAppWebViewController controller, LoadedResource response) {
print("Resource type: '"+response.initiatorType + "' started at: " +
@@ -212,14 +213,14 @@ class _InlineExampleScreenState extends State {
onLoadResourceCustomScheme: (InAppWebViewController controller, String scheme, String url) async {
if (scheme == "my-special-custom-scheme") {
var bytes = await rootBundle.load("assets/" + url.replaceFirst("my-special-custom-scheme://", "", 0));
- var response = new CustomSchemeResponse(bytes.buffer.asUint8List(), "image/svg+xml", contentEnconding: "utf-8");
+ var response = new CustomSchemeResponse(data: bytes.buffer.asUint8List(), contentType: "image/svg+xml", contentEnconding: "utf-8");
return response;
}
return null;
},
onTargetBlank: (InAppWebViewController controller, String url) {
print("target _blank: " + url);
- controller.loadUrl(url);
+ controller.loadUrl(url: url);
},
onGeolocationPermissionsShowPrompt: (InAppWebViewController controller, String origin) async {
GeolocationPermissionShowPromptResponse response;
@@ -234,14 +235,14 @@ class _InlineExampleScreenState extends State {
FlatButton(
child: Text("Close"),
onPressed: () {
- response = new GeolocationPermissionShowPromptResponse(origin, false, false);
+ response = new GeolocationPermissionShowPromptResponse(origin: origin, allow: false, retain: false);
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Accept"),
onPressed: () {
- response = new GeolocationPermissionShowPromptResponse(origin, true, true);
+ response = new GeolocationPermissionShowPromptResponse(origin: origin, allow: true, retain: true);
Navigator.of(context).pop();
},
),
@@ -288,21 +289,24 @@ class _InlineExampleScreenState extends State {
print("Current highlighted: $activeMatchOrdinal, Number of matches found: $numberOfMatches, find operation completed: $isDoneCounting");
},
shouldInterceptAjaxRequest: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
- //print("AJAX REQUEST: ${ajaxRequest.method} - ${ajaxRequest.url}, DATA: ${ajaxRequest.data}");
+ print("AJAX REQUEST: ${ajaxRequest.method} - ${ajaxRequest.url}, DATA: ${ajaxRequest.data}");
+ if (ajaxRequest.url == "http://192.168.1.20:8082/test-ajax-post") {
+ ajaxRequest.responseType = 'json';
+ }
// ajaxRequest.method = "GET";
// ajaxRequest.url = "http://192.168.1.20:8082/test-download-file";
// ajaxRequest.headers = {
// "Custom-Header": "Custom-Value"
// };
// return ajaxRequest;
- return null;
+ return ajaxRequest;
},
onAjaxReadyStateChange: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
- //print("AJAX READY STATE CHANGE: ${ajaxRequest.method} - ${ajaxRequest.url}, ${ajaxRequest.status}, ${ajaxRequest.readyState}, ${ajaxRequest.responseType}, ${ajaxRequest.responseText}, ${ajaxRequest.responseHeaders}");
+ print("AJAX READY STATE CHANGE: ${ajaxRequest.method} - ${ajaxRequest.url}, ${ajaxRequest.status}, ${ajaxRequest.readyState}, ${ajaxRequest.responseType}, ${ajaxRequest.responseText}, ${ajaxRequest.response}, ${ajaxRequest.responseHeaders}");
return AjaxRequestAction.PROCEED;
},
onAjaxProgress: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
- //print("AJAX EVENT: ${ajaxRequest.method} - ${ajaxRequest.url}, ${ajaxRequest.event.type}, LOADED: ${ajaxRequest.event.loaded}, ${ajaxRequest.responseHeaders}");
+ print("AJAX EVENT: ${ajaxRequest.method} - ${ajaxRequest.url}, ${ajaxRequest.event.type}, LOADED: ${ajaxRequest.event.loaded}, ${ajaxRequest.responseHeaders}");
return AjaxRequestAction.PROCEED;
},
shouldInterceptFetchRequest: (InAppWebViewController controller, FetchRequest fetchRequest) async {
@@ -312,7 +316,7 @@ class _InlineExampleScreenState extends State {
return fetchRequest;
},
onNavigationStateChange: (InAppWebViewController controller, String url) async {
- print("NAVIGATION STATE CHANGE: ${url}");
+ print("NAVIGATION STATE CHANGE: $url");
setState(() {
this.url = url;
});
diff --git a/example/lib/webview_example.screen.dart b/example/lib/webview_example.screen.dart
index 091b3594..d3f61fa9 100644
--- a/example/lib/webview_example.screen.dart
+++ b/example/lib/webview_example.screen.dart
@@ -3,7 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappbrowser/flutter_inappbrowser.dart';
-class MyInappBrowser extends InAppBrowser {
+class MyInAppBrowser extends InAppBrowser {
@override
Future onBrowserCreated() async {
@@ -43,7 +43,7 @@ class MyInappBrowser extends InAppBrowser {
@override
void shouldOverrideUrlLoading(String url) {
print("\n\n override $url\n\n");
- this.webViewController.loadUrl(url);
+ this.webViewController.loadUrl(url: url);
}
@override
@@ -75,11 +75,13 @@ class MyInappBrowser extends InAppBrowser {
@override
Future onLoadResourceCustomScheme(String scheme, String url) async {
print("custom scheme: " + scheme);
+ return null;
}
@override
Future onGeolocationPermissionsShowPrompt(String origin) async {
print("request Geolocation permission API");
+ return null;
}
@override
@@ -89,18 +91,18 @@ class MyInappBrowser extends InAppBrowser {
@override
Future onJsConfirm(String message) {
-
+ return null;
}
@override
Future onJsPrompt(String message, String defaultValue) {
-
+ return null;
}
}
class WebviewExampleScreen extends StatefulWidget {
- final MyInappBrowser browser = new MyInappBrowser();
- static BuildContext context = null;
+ final MyInAppBrowser browser = new MyInAppBrowser();
+ static BuildContext context;
@override
_WebviewExampleScreenState createState() => new _WebviewExampleScreenState();
@@ -119,7 +121,7 @@ class _WebviewExampleScreenState extends State {
child: new RaisedButton(
onPressed: () {
widget.browser.openFile(
- "assets/index.html",
+ assetFilePath: "assets/index.html",
//url: "https://www.google.com/",
options: InAppBrowserClassOptions(
inAppWebViewWidgetOptions: InAppWebViewWidgetOptions(
diff --git a/ios/Classes/InAppWebView.swift b/ios/Classes/InAppWebView.swift
index 7a3f9ca9..edb14ad6 100755
--- a/ios/Classes/InAppWebView.swift
+++ b/ios/Classes/InAppWebView.swift
@@ -82,8 +82,6 @@ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
}
"""
-let platformReadyJS = "window.dispatchEvent(new Event('flutterInAppBrowserPlatformReady'));";
-
let findTextHighlightJS = """
var wkwebview_SearchResultCount = 0;
var wkwebview_CurrentHighlight = 0;
@@ -266,6 +264,29 @@ let interceptAjaxRequestsJS = """
ajax.prototype._flutter_inappbrowser_password = null;
ajax.prototype._flutter_inappbrowser_already_onreadystatechange_wrapped = false;
ajax.prototype._flutter_inappbrowser_request_headers = {};
+ function convertRequestResponse(request, callback) {
+ if (request.response != null && request.responseType != null) {
+ switch (request.responseType) {
+ case 'arraybuffer':
+ callback(new Uint8Array(request.response));
+ return;
+ case 'blob':
+ const reader = new FileReader();
+ reader.addEventListener('loadend', function() {
+ callback(new Uint8Array(reader.result));
+ });
+ reader.readAsArrayBuffer(blob);
+ return;
+ case 'document':
+ callback(request.response.documentElement.outerHTML);
+ return;
+ case 'json':
+ callback(request.response);
+ return;
+ };
+ }
+ callback(null);
+ };
ajax.prototype.open = function(method, url, isAsync, user, password) {
isAsync = (isAsync != null) ? isAsync : true;
this._flutter_inappbrowser_url = url;
@@ -293,35 +314,40 @@ let interceptAjaxRequestsJS = """
responseHeaders[header] = value;
});
}
- var ajaxRequest = {
- method: this._flutter_inappbrowser_method,
- url: this._flutter_inappbrowser_url,
- isAsync: this._flutter_inappbrowser_isAsync,
- user: this._flutter_inappbrowser_user,
- password: this._flutter_inappbrowser_password,
- withCredentials: this.withCredentials,
- headers: this._flutter_inappbrowser_request_headers,
- readyState: this.readyState,
- status: this.status,
- responseURL: this.responseURL,
- responseType: this.responseType,
- responseText: this.responseText,
- statusText: this.statusText,
- responseHeaders, responseHeaders,
- event: {
- type: e.type,
- loaded: e.loaded,
- lengthComputable: e.lengthComputable
- }
- };
- window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxProgress', ajaxRequest).then(function(result) {
- if (result != null) {
- switch (result) {
- case 0:
- self.abort();
- return;
- };
- }
+ convertRequestResponse(this, function(response) {
+ var ajaxRequest = {
+ method: self._flutter_inappbrowser_method,
+ url: self._flutter_inappbrowser_url,
+ isAsync: self._flutter_inappbrowser_isAsync,
+ user: self._flutter_inappbrowser_user,
+ password: self._flutter_inappbrowser_password,
+ withCredentials: self.withCredentials,
+ headers: self._flutter_inappbrowser_request_headers,
+ readyState: self.readyState,
+ status: self.status,
+ responseURL: self.responseURL,
+ responseType: self.responseType,
+ response: response,
+ responseText: (self.responseType == 'text' || self.responseType == '') ? self.responseText : null,
+ responseXML: (self.responseType == 'document' && self.responseXML != null) ? self.responseXML.documentElement.outerHTML : null,
+ statusText: self.statusText,
+ responseHeaders, responseHeaders,
+ event: {
+ type: e.type,
+ loaded: e.loaded,
+ lengthComputable: e.lengthComputable,
+ total: e.total
+ }
+ };
+ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxProgress', ajaxRequest).then(function(result) {
+ if (result != null) {
+ switch (result) {
+ case 0:
+ self.abort();
+ return;
+ };
+ }
+ });
});
}
};
@@ -344,33 +370,37 @@ let interceptAjaxRequestsJS = """
responseHeaders[header] = value;
});
}
- var ajaxRequest = {
- method: this._flutter_inappbrowser_method,
- url: this._flutter_inappbrowser_url,
- isAsync: this._flutter_inappbrowser_isAsync,
- user: this._flutter_inappbrowser_user,
- password: this._flutter_inappbrowser_password,
- withCredentials: this.withCredentials,
- headers: this._flutter_inappbrowser_request_headers,
- readyState: this.readyState,
- status: this.status,
- responseURL: this.responseURL,
- responseType: this.responseType,
- responseText: this.responseText,
- statusText: this.statusText,
- responseHeaders: responseHeaders
- };
- window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxReadyStateChange', ajaxRequest).then(function(result) {
- if (result != null) {
- switch (result) {
- case 0:
- self.abort();
- return;
- };
- }
- if (onreadystatechange != null) {
- onreadystatechange();
- }
+ convertRequestResponse(this, function(response) {
+ var ajaxRequest = {
+ method: self._flutter_inappbrowser_method,
+ url: self._flutter_inappbrowser_url,
+ isAsync: self._flutter_inappbrowser_isAsync,
+ user: self._flutter_inappbrowser_user,
+ password: self._flutter_inappbrowser_password,
+ withCredentials: self.withCredentials,
+ headers: self._flutter_inappbrowser_request_headers,
+ readyState: self.readyState,
+ status: self.status,
+ responseURL: self.responseURL,
+ responseType: self.responseType,
+ response: response,
+ responseText: (self.responseType == 'text' || self.responseType == '') ? self.responseText : null,
+ responseXML: (self.responseType == 'document' && self.responseXML != null) ? self.responseXML.documentElement.outerHTML : null,
+ statusText: self.statusText,
+ responseHeaders: responseHeaders
+ };
+ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxReadyStateChange', ajaxRequest).then(function(result) {
+ if (result != null) {
+ switch (result) {
+ case 0:
+ self.abort();
+ return;
+ };
+ }
+ if (onreadystatechange != null) {
+ onreadystatechange();
+ }
+ });
});
} else if (onreadystatechange != null) {
onreadystatechange();
@@ -383,6 +413,7 @@ let interceptAjaxRequestsJS = """
this.addEventListener('progress', handleEvent);
this.addEventListener('error', handleEvent);
this.addEventListener('abort', handleEvent);
+ this.addEventListener('timeout', handleEvent);
var ajaxRequest = {
data: data,
method: this._flutter_inappbrowser_method,
@@ -391,7 +422,8 @@ let interceptAjaxRequestsJS = """
user: this._flutter_inappbrowser_user,
password: this._flutter_inappbrowser_password,
withCredentials: this.withCredentials,
- headers: this._flutter_inappbrowser_request_headers
+ headers: this._flutter_inappbrowser_request_headers,
+ responseType: this.responseType
};
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {
if (result != null) {
@@ -402,6 +434,9 @@ let interceptAjaxRequestsJS = """
};
data = result.data;
self.withCredentials = result.withCredentials;
+ if (result.responseType != null) {
+ self.responseType = result.responseType;
+ };
for (var header in result.headers) {
var value = result.headers[header];
self.setRequestHeader(header, value);
@@ -557,7 +592,7 @@ let interceptFetchRequestsJS = """
controller.abort();
break;
}
- var resultResource = (result.resource != null) ? result.resource : resource;
+ var resultResource = (result.url != null) ? result.url : resource;
var resultInit = init;
if (result.init != null) {
resultInit.method = result.method;
@@ -1308,7 +1343,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
currentURL = url
InAppWebView.credentialsProposed = []
onLoadStop(url: (currentURL?.absoluteString)!)
- evaluateJavaScript(platformReadyJS, completionHandler: nil)
if IABController != nil {
IABController!.updateUrlTextField(url: (currentURL?.absoluteString)!)
diff --git a/lib/src/chrome_safari_browser.dart b/lib/src/chrome_safari_browser.dart
index 8582ffef..9d031694 100644
--- a/lib/src/chrome_safari_browser.dart
+++ b/lib/src/chrome_safari_browser.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
+import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'types.dart';
@@ -12,16 +13,16 @@ import 'in_app_browser.dart';
///This class uses native [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android
///and [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
///
-///[browserFallback] represents the [InAppBrowser] instance fallback in case [Chrome Custom Tabs]/[SFSafariViewController] is not available.
+///[browserFallback] represents the [InAppBrowser] instance fallback in case `Chrome Custom Tabs`/`SFSafariViewController` is not available.
class ChromeSafariBrowser {
String uuid;
InAppBrowser browserFallback;
bool _isOpened = false;
///Initialize the [ChromeSafariBrowser] instance with an [InAppBrowser] fallback instance or `null`.
- ChromeSafariBrowser (bf) {
+ ChromeSafariBrowser ({bFallback}) {
uuid = uuidGenerator.v4();
- browserFallback = bf;
+ browserFallback = bFallback;
ChannelManager.addListener(uuid, handleMethod);
_isOpened = false;
}
@@ -45,32 +46,14 @@ class ChromeSafariBrowser {
///Opens an [url] in a new [ChromeSafariBrowser] instance.
///
- ///- [url]: The [url] to load. Call [encodeUriComponent()] on this if the [url] contains Unicode characters.
+ ///[url]: The [url] to load. Call [encodeUriComponent()] on this if the [url] contains Unicode characters.
///
- ///- [options]: Options for the [ChromeSafariBrowser].
+ ///[options]: Options for the [ChromeSafariBrowser].
///
- ///- [headersFallback]: The additional header of the [InAppBrowser] instance fallback to be used in the HTTP request for this URL, specified as a map from name to value.
+ ///[headersFallback]: The additional header of the [InAppBrowser] instance fallback to be used in the HTTP request for this URL, specified as a map from name to value.
///
- ///- [optionsFallback]: Options used by the [InAppBrowser] instance fallback.
- ///
- ///**Android** supports these options:
- ///
- ///- __addShareButton__: Set to `false` if you don't want the default share button. The default value is `true`.
- ///- __showTitle__: Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`.
- ///- __toolbarBackgroundColor__: Set the custom background color of the toolbar.
- ///- __enableUrlBarHiding__: Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`.
- ///- __instantAppsEnabled__: Set to `true` to enable Instant Apps. The default value is `false`.
- ///
- ///**iOS** supports these options:
- ///
- ///- __entersReaderIfAvailable__: Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`.
- ///- __barCollapsingEnabled__: Set to `true` to enable bar collapsing. The default value is `false`.
- ///- __dismissButtonStyle__: Set the custom style for the dismiss button. The default value is `0 //done`. See [SFSafariViewController.DismissButtonStyle](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller/dismissbuttonstyle) for all the available styles.
- ///- __preferredBarTintColor__: Set the custom background color of the navigation bar and the toolbar.
- ///- __preferredControlTintColor__: Set the custom color of the control buttons on the navigation bar and the toolbar.
- ///- __presentationStyle__: Set the custom modal presentation style when presenting the WebView. The default value is `0 //fullscreen`. See [UIModalPresentationStyle](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle) for all the available styles.
- ///- __transitionStyle__: Set to the custom transition style when presenting the WebView. The default value is `0 //crossDissolve`. See [UIModalTransitionStyle](https://developer.apple.com/documentation/uikit/uimodaltransitionStyle) for all the available styles.
- Future open(String url, {ChromeSafariBrowserClassOptions options, Map headersFallback = const {}, InAppBrowserClassOptions optionsFallback}) async {
+ ///[optionsFallback]: Options used by the [InAppBrowser] instance fallback.
+ Future open({@required String url, ChromeSafariBrowserClassOptions options, Map headersFallback = const {}, InAppBrowserClassOptions optionsFallback}) async {
assert(url != null && url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!');
diff --git a/lib/src/content_blocker.dart b/lib/src/content_blocker.dart
index 5f667a42..2e775888 100644
--- a/lib/src/content_blocker.dart
+++ b/lib/src/content_blocker.dart
@@ -1,8 +1,19 @@
+import 'package:flutter/foundation.dart';
+
+///ContentBlocker class represents a set of rules to use block content in the browser window.
+///
+///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.
+///
+///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`.
class ContentBlocker {
+ ///Trigger of the content blocker. The trigger tells to the WebView when to perform the corresponding action.
ContentBlockerTrigger trigger;
+ ///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched.
ContentBlockerAction action;
- ContentBlocker(this.trigger, this.action);
+ ContentBlocker({@required this.trigger,@required this.action});
Map> toMap() {
return {
@@ -13,21 +24,22 @@ class ContentBlocker {
static ContentBlocker fromMap(Map> map) {
return ContentBlocker(
- ContentBlockerTrigger.fromMap(
+ trigger: ContentBlockerTrigger.fromMap(
Map.from(map["trigger"])
),
- ContentBlockerAction.fromMap(
+ action: ContentBlockerAction.fromMap(
Map.from(map["action"])
)
);
}
}
+///ContentBlockerTriggerResourceType class represents the possible resource type defined for a [ContentBlockerTrigger].
class ContentBlockerTriggerResourceType {
final String _value;
const ContentBlockerTriggerResourceType._internal(this._value);
static ContentBlockerTriggerResourceType fromValue(String value) {
- return (["document", "image", "LINK", "style-sheet", "script", "font",
+ return (["document", "image", "style-sheet", "script", "font",
"media", "svg-document", "raw"].contains(value)) ? ContentBlockerTriggerResourceType._internal(value) : null;
}
toValue() => _value;
@@ -39,9 +51,11 @@ class ContentBlockerTriggerResourceType {
static const FONT = const ContentBlockerTriggerResourceType._internal('font');
static const MEDIA = const ContentBlockerTriggerResourceType._internal('media');
static const SVG_DOCUMENT = const ContentBlockerTriggerResourceType._internal('svg-document');
+ ///Any untyped load
static const RAW = const ContentBlockerTriggerResourceType._internal('raw');
}
+///ContentBlockerTriggerLoadType class represents the possible load type for a [ContentBlockerTrigger].
class ContentBlockerTriggerLoadType {
final String _value;
const ContentBlockerTriggerLoadType._internal(this._value);
@@ -50,24 +64,44 @@ class ContentBlockerTriggerLoadType {
}
toValue() => _value;
+ ///FIRST_PARTY is triggered only if the resource has the same scheme, domain, and port as the main page resource.
static const FIRST_PARTY = const ContentBlockerTriggerLoadType._internal('first-party');
+ ///THIRD_PARTY is triggered if the resource is not from the same domain as the main page resource.
static const THIRD_PARTY = const ContentBlockerTriggerLoadType._internal('third-party');
}
+///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.
class ContentBlockerTrigger {
+ ///A regular expression pattern to match the URL against.
String urlFilter;
+ ///Used only by iOS. A Boolean value. The default value is false.
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.
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].
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].
List unlessDomain;
+ ///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types.
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].
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].
List unlessTopUrl;
- ContentBlockerTrigger(String urlFilter, {bool urlFilterIsCaseSensitive = false, List resourceType = const [],
+ 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;
+ assert(this.urlFilter != null);
this.resourceType = resourceType;
this.urlFilterIsCaseSensitive = urlFilterIsCaseSensitive;
this.ifDomain = ifDomain;
@@ -124,7 +158,7 @@ class ContentBlockerTrigger {
});
return ContentBlockerTrigger(
- map["url-filter"],
+ urlFilter: map["url-filter"],
urlFilterIsCaseSensitive: map["url-filter-is-case-sensitive"],
ifDomain: List.from(map["if-domain"] ?? []),
unlessDomain: List.from(map["unless-domain"] ?? []),
@@ -136,6 +170,7 @@ class ContentBlockerTrigger {
}
}
+///ContentBlockerActionType class represents the kind of action that can be used with a [ContentBlockerTrigger].
class ContentBlockerActionType {
final String _value;
const ContentBlockerActionType._internal(this._value);
@@ -144,17 +179,31 @@ class ContentBlockerActionType {
}
toValue() => _value;
+ ///Stops loading of the resource. If the resource was cached, the cache is ignored.
static const BLOCK = const ContentBlockerActionType._internal('block');
+ ///Hides elements of the page based on a CSS selector. A selector field contains the selector list. Any matching element has its display property set to none, which hides it.
+ ///
+ ///**NOTE**: on Android, JavaScript must be enabled.
static const CSS_DISPLAY_NONE = const ContentBlockerActionType._internal('css-display-none');
+ ///Changes a URL from http to https. URLs with a specified (nondefault) port and links using other protocols are unaffected.
static const MAKE_HTTPS = const ContentBlockerActionType._internal('make-https');
}
+///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.
class ContentBlockerAction {
+ ///Type of the action.
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(ContentBlockerActionType type, {String selector}) {
+ ContentBlockerAction({@required ContentBlockerActionType type, String selector}) {
this.type = type;
+ assert(this.type != null);
if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) {
assert(selector != null);
}
@@ -177,7 +226,7 @@ class ContentBlockerAction {
static ContentBlockerAction fromMap(Map map) {
return ContentBlockerAction(
- ContentBlockerActionType.fromValue(map["type"]),
+ type: ContentBlockerActionType.fromValue(map["type"]),
selector: map["selector"]
);
}
diff --git a/lib/src/cookie_manager.dart b/lib/src/cookie_manager.dart
index 9cb8c484..346edd96 100644
--- a/lib/src/cookie_manager.dart
+++ b/lib/src/cookie_manager.dart
@@ -1,5 +1,6 @@
import 'dart:async';
+import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
///Manages the cookies used by WebView instances.
@@ -26,8 +27,8 @@ class CookieManager {
///
///The default value of [path] is `"/"`.
///If [domain] is `null`, its default value will be the domain name of [url].
- Future setCookie(String url, String name, String value,
- { String domain,
+ Future setCookie({@required String url, @required String name, @required String value,
+ String domain,
String path = "/",
int expiresDate,
int maxAge,
@@ -55,7 +56,7 @@ class CookieManager {
}
///Gets all the cookies for the given [url].
- Future>> getCookies(String url) async {
+ Future>> getCookies({@required String url}) async {
assert(url != null && url.isNotEmpty);
Map args = {};
@@ -70,7 +71,7 @@ class CookieManager {
}
///Gets a cookie by its [name] for the given [url].
- Future