285 lines
8.5 KiB
Dart
285 lines
8.5 KiB
Dart
///Class that is used by [WebView.shouldOverrideUrlLoading] event.
|
||
///It represents the policy to pass back to the decision handler.
|
||
class NavigationActionPolicy {
|
||
final int _value;
|
||
|
||
const NavigationActionPolicy._internal(this._value);
|
||
|
||
int toValue() => _value;
|
||
|
||
///Cancel the navigation.
|
||
static const CANCEL = NavigationActionPolicy._internal(0);
|
||
|
||
///Allow the navigation to continue.
|
||
static const ALLOW = NavigationActionPolicy._internal(1);
|
||
|
||
@override
|
||
bool operator ==(value) => value == _value;
|
||
|
||
@override
|
||
int get hashCode => _value.hashCode;
|
||
|
||
Map<String, dynamic> toMap() {
|
||
return {
|
||
'action': _value,
|
||
};
|
||
}
|
||
}
|
||
|
||
///Class that represents the WebView context menu. It used by [WebView.contextMenu].
|
||
///
|
||
///**NOTE**: To make it work properly on Android, JavaScript should be enabled!
|
||
class ContextMenu {
|
||
///Event fired when the context menu for this WebView is being built.
|
||
///
|
||
///[hitTestResult] represents the hit result for hitting an HTML elements.
|
||
final void Function(dynamic hitTestResult)? onCreateContextMenu;
|
||
|
||
///Event fired when the context menu for this WebView is being hidden.
|
||
final void Function()? onHideContextMenu;
|
||
|
||
///Event fired when a context menu item has been clicked.
|
||
///
|
||
///[contextMenuItemClicked] represents the [ContextMenuItem] clicked.
|
||
final void Function(ContextMenuItem contextMenuItemClicked)?
|
||
onContextMenuActionItemClicked;
|
||
|
||
///Context menu options.
|
||
final ContextMenuOptions? options;
|
||
|
||
///List of the custom [ContextMenuItem].
|
||
final List<ContextMenuItem> menuItems;
|
||
|
||
ContextMenu(
|
||
{this.menuItems = const [],
|
||
this.onCreateContextMenu,
|
||
this.onHideContextMenu,
|
||
this.options,
|
||
this.onContextMenuActionItemClicked});
|
||
|
||
Map<String, dynamic> toMap() {
|
||
return {
|
||
'menuItems': menuItems.map((menuItem) => menuItem.toMap()).toList(),
|
||
'options': options?.toMap()
|
||
};
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return toMap();
|
||
}
|
||
|
||
@override
|
||
String toString() {
|
||
return toMap().toString();
|
||
}
|
||
}
|
||
|
||
///Class that represent an item of the [ContextMenu].
|
||
class ContextMenuItem {
|
||
///Android menu item ID.
|
||
int? androidId;
|
||
|
||
///iOS menu item ID.
|
||
String? iosId;
|
||
|
||
///Menu item title.
|
||
String title;
|
||
|
||
///Menu item action that will be called when an user clicks on it.
|
||
Function()? action;
|
||
|
||
ContextMenuItem(
|
||
{this.androidId, this.iosId, required this.title, this.action});
|
||
|
||
Map<String, dynamic> toMap() {
|
||
return {'androidId': androidId, 'iosId': iosId, 'title': title};
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return toMap();
|
||
}
|
||
|
||
@override
|
||
String toString() {
|
||
return toMap().toString();
|
||
}
|
||
}
|
||
|
||
///Class that represents available options used by [ContextMenu].
|
||
class ContextMenuOptions {
|
||
///Whether all the default system context menu items should be hidden or not. The default value is `false`.
|
||
bool hideDefaultSystemContextMenuItems;
|
||
|
||
ContextMenuOptions({this.hideDefaultSystemContextMenuItems = false});
|
||
|
||
Map<String, dynamic> toMap() {
|
||
return {
|
||
'hideDefaultSystemContextMenuItems': hideDefaultSystemContextMenuItems
|
||
};
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return toMap();
|
||
}
|
||
|
||
@override
|
||
String toString() {
|
||
return toMap().toString();
|
||
}
|
||
}
|
||
|
||
///Class that represents contains the constants for the times at which to inject script content into a [WebView] used by an [UserScript].
|
||
class UserScriptInjectionTime {
|
||
final int _value;
|
||
|
||
const UserScriptInjectionTime._internal(this._value);
|
||
|
||
static final Set<UserScriptInjectionTime> values = {
|
||
UserScriptInjectionTime.AT_DOCUMENT_START,
|
||
UserScriptInjectionTime.AT_DOCUMENT_END,
|
||
};
|
||
|
||
static UserScriptInjectionTime? fromValue(int? value) {
|
||
if (value != null) {
|
||
try {
|
||
return UserScriptInjectionTime.values
|
||
.firstWhere((element) => element.toValue() == value);
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
int toValue() => _value;
|
||
|
||
@override
|
||
String toString() {
|
||
switch (_value) {
|
||
case 1:
|
||
return 'AT_DOCUMENT_END';
|
||
case 0:
|
||
default:
|
||
return 'AT_DOCUMENT_START';
|
||
}
|
||
}
|
||
|
||
///**NOTE for iOS**: A constant to inject the script after the creation of the webpage’s document element, but before loading any other content.
|
||
///
|
||
///**NOTE for Android**: A constant to try to inject the script as soon as the page starts loading.
|
||
static const AT_DOCUMENT_START = UserScriptInjectionTime._internal(0);
|
||
|
||
///**NOTE for iOS**: A constant to inject the script after the document finishes loading, but before loading any other subresources.
|
||
///
|
||
///**NOTE for Android**: A constant to inject the script as soon as the page finishes loading.
|
||
static const AT_DOCUMENT_END = UserScriptInjectionTime._internal(1);
|
||
|
||
@override
|
||
bool operator ==(value) => value == _value;
|
||
|
||
@override
|
||
int get hashCode => _value.hashCode;
|
||
}
|
||
|
||
///Class that represents a script that the [WebView] injects into the web page.
|
||
class UserScript {
|
||
///The script’s group name.
|
||
String? groupName;
|
||
|
||
///The script’s source code.
|
||
String source;
|
||
|
||
///The time at which to inject the script into the [WebView].
|
||
UserScriptInjectionTime injectionTime;
|
||
|
||
///A Boolean value that indicates whether to inject the script into the main frame.
|
||
///Specify true to inject the script only into the main frame, or false to inject it into all frames.
|
||
///The default value is `true`.
|
||
///
|
||
///**NOTE**: available only on iOS.
|
||
bool iosForMainFrameOnly;
|
||
|
||
///A scope of execution in which to evaluate the script to prevent conflicts between different scripts.
|
||
///For more information about content worlds, see [ContentWorld].
|
||
late ContentWorld contentWorld;
|
||
|
||
UserScript(
|
||
{this.groupName,
|
||
required this.source,
|
||
required this.injectionTime,
|
||
this.iosForMainFrameOnly = true,
|
||
ContentWorld? contentWorld}) {
|
||
this.contentWorld = contentWorld ?? ContentWorld.PAGE;
|
||
}
|
||
|
||
Map<String, dynamic> toMap() {
|
||
return {
|
||
'groupName': groupName,
|
||
'source': source,
|
||
'injectionTime': injectionTime.toValue(),
|
||
'iosForMainFrameOnly': iosForMainFrameOnly,
|
||
'contentWorld': contentWorld.toMap()
|
||
};
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return toMap();
|
||
}
|
||
|
||
@override
|
||
String toString() {
|
||
return toMap().toString();
|
||
}
|
||
}
|
||
|
||
final _contentWorldNameRegExp = RegExp(r'[\s]');
|
||
|
||
///Class that represents an object that defines a scope of execution for JavaScript code and which you use to prevent conflicts between different scripts.
|
||
///
|
||
///**NOTE for iOS**: available on iOS 14.0+. This class represents the native [WKContentWorld](https://developer.apple.com/documentation/webkit/wkcontentworld) class.
|
||
///
|
||
///**NOTE for Android**: it will create and append an `<iframe>` HTML element with `id` attribute equals to `flutter_inappwebview_[name]`
|
||
///to the webpage's content that contains only the inline `<script>` HTML elements in order to define a new scope of execution for JavaScript code.
|
||
///Unfortunately, there isn't any other way to do it.
|
||
///There are some limitations:
|
||
///- for any [ContentWorld], except [ContentWorld.PAGE] (that is the webpage itself), if you need to access to the `window` or `document` global Object,
|
||
///you need to use `window.top` and `window.top.document` because the code runs inside an `<iframe>`;
|
||
///- also, the execution of the inline `<script>` could be blocked by the `Content-Security-Policy` header.
|
||
class ContentWorld {
|
||
///The name of a custom content world.
|
||
///It cannot contain space characters.
|
||
final String name;
|
||
|
||
///Returns the custom content world with the specified name.
|
||
ContentWorld.world({required this.name}) {
|
||
// WINDOW-ID- is used internally by the plugin!
|
||
assert(!name.startsWith('WINDOW-ID-') &&
|
||
!name.contains(_contentWorldNameRegExp));
|
||
}
|
||
|
||
///The default world for clients.
|
||
// ignore: non_constant_identifier_names
|
||
static final ContentWorld DEFAULT_CLIENT =
|
||
ContentWorld.world(name: 'defaultClient');
|
||
|
||
///The content world for the current webpage’s content.
|
||
///This property contains the content world for scripts that the current webpage executes.
|
||
///Be careful when manipulating variables in this content world.
|
||
///If you modify a variable with the same name as one the webpage uses, you may unintentionally disrupt the normal operation of that page.
|
||
// ignore: non_constant_identifier_names
|
||
static final ContentWorld PAGE = ContentWorld.world(name: 'page');
|
||
|
||
Map<String, dynamic> toMap() {
|
||
return {'name': name};
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return toMap();
|
||
}
|
||
|
||
@override
|
||
String toString() {
|
||
return toMap().toString();
|
||
}
|
||
}
|