updated web support

This commit is contained in:
Lorenzo Pichilli 2022-04-24 04:50:44 +02:00
parent 61a439893b
commit d0657c3b9a
7 changed files with 262 additions and 83 deletions

View File

@ -23,7 +23,8 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
useHybridComposition: true, useHybridComposition: true,
allowsInlineMediaPlayback: true, allowsInlineMediaPlayback: true,
iframeAllow: "camera; microphone", iframeAllow: "camera; microphone",
iframeAllowFullscreen: true iframeAllowFullscreen: true,
javaScriptCanOpenWindowsAutomatically: false
); );
PullToRefreshController? pullToRefreshController; PullToRefreshController? pullToRefreshController;

View File

@ -28,6 +28,7 @@
window.addEventListener('load', function (event) { window.addEventListener('load', function (event) {
setTimeout(function () { setTimeout(function () {
console.log('test'); console.log('test');
window.open('https://google.com');
}); });
}); });
</script> </script>

View File

@ -6,7 +6,8 @@ window.flutter_inappwebview = {
windows: {}, windows: {},
isFullscreen: false, isFullscreen: false,
documentTitle: null, documentTitle: null,
prepare: function () { functionMap: {},
prepare: function (settings) {
var iframe = document.getElementById(window.flutter_inappwebview.iframeId); var iframe = document.getElementById(window.flutter_inappwebview.iframeId);
document.addEventListener('fullscreenchange', function(event) { document.addEventListener('fullscreenchange', function(event) {
@ -30,6 +31,14 @@ window.flutter_inappwebview = {
window.flutter_inappwebview.windowAutoincrementId = 0; window.flutter_inappwebview.windowAutoincrementId = 0;
window.flutter_inappwebview.windows = {}; window.flutter_inappwebview.windows = {};
var url = iframe.src;
try {
url = iframe.contentWindow.location.href;
} catch (e) {
console.log(e);
}
window.flutter_inappwebview.nativeCommunication('onLoadStart', window.flutter_inappwebview.viewId, [url]);
try { try {
var oldLogs = { var oldLogs = {
'log': iframe.contentWindow.console.log, 'log': iframe.contentWindow.console.log,
@ -58,24 +67,6 @@ window.flutter_inappwebview = {
console.log(e); console.log(e);
} }
var url = iframe.src;
try {
url = iframe.contentWindow.location.href;
} catch (e) {
console.log(e);
}
window.flutter_inappwebview.nativeCommunication('onLoadStart', window.flutter_inappwebview.viewId, [url]);
window.flutter_inappwebview.nativeCommunication('onLoadStop', window.flutter_inappwebview.viewId, [url]);
iframe.contentWindow.addEventListener('popstate', function (event) {
var iframeUrl = iframe.src;
try {
iframeUrl = iframe.contentWindow.location.href;
} catch (e) {
console.log(e);
}
window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]);
});
try { try {
var originalPushState = iframe.contentWindow.history.pushState; var originalPushState = iframe.contentWindow.history.pushState;
iframe.contentWindow.history.pushState = function (state, unused, url) { iframe.contentWindow.history.pushState = function (state, unused, url) {
@ -88,6 +79,7 @@ window.flutter_inappwebview = {
} }
window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]); window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]);
}; };
var originalReplaceState = iframe.contentWindow.history.replaceState; var originalReplaceState = iframe.contentWindow.history.replaceState;
iframe.contentWindow.history.replaceState = function (state, unused, url) { iframe.contentWindow.history.replaceState = function (state, unused, url) {
originalReplaceState.call(iframe.contentWindow.history, state, unused, url); originalReplaceState.call(iframe.contentWindow.history, state, unused, url);
@ -99,11 +91,7 @@ window.flutter_inappwebview = {
} }
window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]); window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]);
}; };
} catch (e) {
console.log(e);
}
try {
var originalOpen = iframe.contentWindow.open; var originalOpen = iframe.contentWindow.open;
iframe.contentWindow.open = function (url, target, windowFeatures) { iframe.contentWindow.open = function (url, target, windowFeatures) {
var newWindow = originalOpen.call(iframe.contentWindow, ...arguments); var newWindow = originalOpen.call(iframe.contentWindow, ...arguments);
@ -111,18 +99,13 @@ window.flutter_inappwebview = {
window.flutter_inappwebview.windowAutoincrementId++; window.flutter_inappwebview.windowAutoincrementId++;
window.flutter_inappwebview.windows[windowId] = newWindow; window.flutter_inappwebview.windows[windowId] = newWindow;
window.flutter_inappwebview.nativeCommunication('onCreateWindow', window.flutter_inappwebview.viewId, [windowId, url, target, windowFeatures]).then(function(){}, function(handledByClient) { window.flutter_inappwebview.nativeCommunication('onCreateWindow', window.flutter_inappwebview.viewId, [windowId, url, target, windowFeatures]).then(function(){}, function(handledByClient) {
console.log(handledByClient);
if (handledByClient) { if (handledByClient) {
newWindow.close(); newWindow.close();
} }
}); });
return newWindow; return newWindow;
}; };
} catch (e) {
console.log(e);
}
try {
var originalPrint = iframe.contentWindow.print; var originalPrint = iframe.contentWindow.print;
iframe.contentWindow.print = function () { iframe.contentWindow.print = function () {
var iframeUrl = iframe.src; var iframeUrl = iframe.src;
@ -134,9 +117,46 @@ window.flutter_inappwebview = {
window.flutter_inappwebview.nativeCommunication('onPrint', window.flutter_inappwebview.viewId, [iframeUrl]); window.flutter_inappwebview.nativeCommunication('onPrint', window.flutter_inappwebview.viewId, [iframeUrl]);
originalPrint.call(iframe.contentWindow); originalPrint.call(iframe.contentWindow);
}; };
window.flutter_inappwebview.functionMap = {
"window.open": iframe.contentWindow.open,
"window.print": iframe.contentWindow.print,
"window.history.pushState": iframe.contentWindow.history.pushState,
"window.history.replaceState": iframe.contentWindow.history.replaceState,
}
var initialTitle = iframe.contentDocument.title;
window.flutter_inappwebview.documentTitle = initialTitle;
window.flutter_inappwebview.nativeCommunication('onTitleChanged', window.flutter_inappwebview.viewId, [initialTitle]);
new MutationObserver(function(mutations) {
var title = mutations[0].target.nodeValue;
if (title != window.flutter_inappwebview.documentTitle) {
window.flutter_inappwebview.documentTitle = title;
window.flutter_inappwebview.nativeCommunication('onTitleChanged', window.flutter_inappwebview.viewId, [title]);
}
}).observe(
iframe.contentDocument.querySelector('title'),
{ subtree: true, characterData: true, childList: true }
);
var oldPixelRatio = iframe.contentWindow.devicePixelRatio;
iframe.contentWindow.addEventListener('resize', function (e) {
var newPixelRatio = iframe.contentWindow.devicePixelRatio;
if(newPixelRatio !== oldPixelRatio){
window.flutter_inappwebview.nativeCommunication('onZoomScaleChanged', window.flutter_inappwebview.viewId, [oldPixelRatio, newPixelRatio]);
oldPixelRatio = newPixelRatio;
}
});
iframe.contentWindow.addEventListener('popstate', function (event) {
var iframeUrl = iframe.src;
try {
iframeUrl = iframe.contentWindow.location.href;
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]);
});
iframe.contentWindow.addEventListener('scroll', function (event) { iframe.contentWindow.addEventListener('scroll', function (event) {
var x = 0; var x = 0;
@ -157,40 +177,40 @@ window.flutter_inappwebview = {
iframe.contentWindow.addEventListener('blur', function (event) { iframe.contentWindow.addEventListener('blur', function (event) {
window.flutter_inappwebview.nativeCommunication('onWindowBlur', window.flutter_inappwebview.viewId); window.flutter_inappwebview.nativeCommunication('onWindowBlur', window.flutter_inappwebview.viewId);
}); });
try {
var initialTitle = iframe.contentDocument.title;
window.flutter_inappwebview.documentTitle = initialTitle;
window.flutter_inappwebview.nativeCommunication('onTitleChanged', window.flutter_inappwebview.viewId, [initialTitle]);
new MutationObserver(function(mutations) {
var title = mutations[0].target.nodeValue;
if (title != window.flutter_inappwebview.documentTitle) {
window.flutter_inappwebview.documentTitle = title;
window.flutter_inappwebview.nativeCommunication('onTitleChanged', window.flutter_inappwebview.viewId, [title]);
}
}).observe(
iframe.contentDocument.querySelector('title'),
{ subtree: true, characterData: true, childList: true }
);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
try { try {
var oldPixelRatio = iframe.contentWindow.devicePixelRatio; if (!settings.javaScriptCanOpenWindowsAutomatically) {
iframe.contentWindow.addEventListener('resize', function (e) { iframe.contentWindow.open = function () {
var newPixelRatio = iframe.contentWindow.devicePixelRatio; throw new Error('JavaScript cannot open windows automatically');
if(newPixelRatio !== oldPixelRatio){ };
window.flutter_inappwebview.nativeCommunication('onZoomScaleChanged', window.flutter_inappwebview.viewId, [oldPixelRatio, newPixelRatio]);
oldPixelRatio = newPixelRatio;
} }
});
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
window.flutter_inappwebview.nativeCommunication('onLoadStop', window.flutter_inappwebview.viewId, [url]);
}); });
} }
}, },
setSettings: function (settings, newSettings) {
var iframe = window.flutter_inappwebview.iframe;
try {
if (settings.javaScriptCanOpenWindowsAutomatically != newSettings.javaScriptCanOpenWindowsAutomatically) {
if (!newSettings.javaScriptCanOpenWindowsAutomatically) {
iframe.contentWindow.open = function () {
throw new Error('JavaScript cannot open windows automatically');
};
} else {
iframe.contentWindow.open = window.flutter_inappwebview.functionMap["window.open"];
}
}
} catch (e) {
console.log(e);
}
},
reload: function () { reload: function () {
var iframe = window.flutter_inappwebview.iframe; var iframe = window.flutter_inappwebview.iframe;
if (iframe != null && iframe.contentWindow != null) { if (iframe != null && iframe.contentWindow != null) {
@ -237,10 +257,8 @@ window.flutter_inappwebview = {
var result = null; var result = null;
if (iframe != null) { if (iframe != null) {
try { try {
result = iframe.contentWindow.eval(source); result = JSON.stringify(iframe.contentWindow.eval(source));
} catch (e) { } catch (e) {}
console.log(e);
}
} }
return result; return result;
}, },

View File

@ -1552,7 +1552,7 @@ class InAppWebViewController
///Reloads the WebView. ///Reloads the WebView.
/// ///
///**NOTE**: on Web, if `window.location.reload()` is not accessible inside the iframe, it will reload using the iframe `src` attribute. ///**NOTE for Web**: if `window.location.reload()` is not accessible inside the iframe, it will reload using the iframe `src` attribute.
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebView.reload](https://developer.android.com/reference/android/webkit/WebView#reload())) ///- Android native WebView ([Official API - WebView.reload](https://developer.android.com/reference/android/webkit/WebView#reload()))
@ -1565,6 +1565,8 @@ class InAppWebViewController
///Goes back in the history of the WebView. ///Goes back in the history of the WebView.
/// ///
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebView.goBack](https://developer.android.com/reference/android/webkit/WebView#goBack())) ///- Android native WebView ([Official API - WebView.goBack](https://developer.android.com/reference/android/webkit/WebView#goBack()))
///- iOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback)) ///- iOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback))
@ -1586,6 +1588,8 @@ class InAppWebViewController
///Goes forward in the history of the WebView. ///Goes forward in the history of the WebView.
/// ///
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebView.goForward](https://developer.android.com/reference/android/webkit/WebView#goForward())) ///- Android native WebView ([Official API - WebView.goForward](https://developer.android.com/reference/android/webkit/WebView#goForward()))
///- iOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward)) ///- iOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward))
@ -1607,6 +1611,8 @@ class InAppWebViewController
///Goes to the history item that is the number of steps away from the current item. Steps is negative if backward and positive if forward. ///Goes to the history item that is the number of steps away from the current item. Steps is negative if backward and positive if forward.
/// ///
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebView.goBackOrForward](https://developer.android.com/reference/android/webkit/WebView#goBackOrForward(int))) ///- Android native WebView ([Official API - WebView.goBackOrForward](https://developer.android.com/reference/android/webkit/WebView#goBackOrForward(int)))
///- iOS ([Official API - WKWebView.go](https://developer.apple.com/documentation/webkit/wkwebview/1414991-go)) ///- iOS ([Official API - WKWebView.go](https://developer.apple.com/documentation/webkit/wkwebview/1414991-go))
@ -1630,6 +1636,8 @@ class InAppWebViewController
///Navigates to a [WebHistoryItem] from the back-forward [WebHistory.list] and sets it as the current item. ///Navigates to a [WebHistoryItem] from the back-forward [WebHistory.list] and sets it as the current item.
/// ///
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
@ -1654,9 +1662,12 @@ class InAppWebViewController
///Stops the WebView from loading. ///Stops the WebView from loading.
/// ///
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebView.stopLoading](https://developer.android.com/reference/android/webkit/WebView#stopLoading())) ///- Android native WebView ([Official API - WebView.stopLoading](https://developer.android.com/reference/android/webkit/WebView#stopLoading()))
///- iOS ([Official API - WKWebView.stopLoading](https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading)) ///- iOS ([Official API - WKWebView.stopLoading](https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading))
///- Web ([Official API - Window.stop](https://developer.mozilla.org/en-US/docs/Web/API/Window/stop))
Future<void> stopLoading() async { Future<void> stopLoading() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('stopLoading', args); await _channel.invokeMethod('stopLoading', args);
@ -1677,6 +1688,8 @@ class InAppWebViewController
///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events ///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events
///where you know the page is ready "enough". ///where you know the page is ready "enough".
/// ///
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebView.evaluateJavascript](https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript(java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.String%3E))) ///- Android native WebView ([Official API - WebView.evaluateJavascript](https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript(java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.String%3E)))
///- iOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript)) ///- iOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript))
@ -1687,7 +1700,7 @@ class InAppWebViewController
args.putIfAbsent('source', () => source); args.putIfAbsent('source', () => source);
args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); args.putIfAbsent('contentWorld', () => contentWorld?.toMap());
var data = await _channel.invokeMethod('evaluateJavascript', args); var data = await _channel.invokeMethod('evaluateJavascript', args);
if (data != null && defaultTargetPlatform == TargetPlatform.android) { if (data != null && (defaultTargetPlatform == TargetPlatform.android || kIsWeb)) {
try { try {
// try to json decode the data coming from JavaScript // try to json decode the data coming from JavaScript
// otherwise return it as it is. // otherwise return it as it is.

View File

@ -87,13 +87,17 @@ class InAppWebViewSettings
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
///- Web
bool javaScriptEnabled; bool javaScriptEnabled;
///Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`. ///Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`.
/// ///
///**NOTE for Web**: this setting will have effect only if the iframe has the same origin.
///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
///- Web
bool javaScriptCanOpenWindowsAutomatically; bool javaScriptCanOpenWindowsAutomatically;
///Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`. ///Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`.
@ -995,31 +999,31 @@ class InAppWebViewSettings
///- iOS ///- iOS
bool upgradeKnownHostsToHTTPS; bool upgradeKnownHostsToHTTPS;
///Specifies a feature policy for the iframe. A list of origins the frame is allowed to display content from. ///Specifies a feature policy for the `<iframe>`.
///This attribute also accepts the values `self` and `src` which represent the origin in the iframe's src attribute. ///The policy defines what features are available to the `<iframe>` based on the origin of the request
///The default value is `src`. ///(e.g. access to the microphone, camera, battery, web-share API, etc.).
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Web ///- Web
String? iframeAllow; String? iframeAllow;
///A boolean value indicating whether the inline frame is willing to be placed into full screen mode. ///Set to true if the `<iframe>` can activate fullscreen mode by calling the `requestFullscreen()` method.
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Web ///- Web
bool? iframeAllowFullscreen; bool? iframeAllowFullscreen;
///A DOMTokenList that reflects the sandbox HTML attribute, indicating extra restrictions on the behavior of the nested content. ///Applies extra restrictions to the content in the frame.
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Web ///- Web
String? iframeSandox; List<Sandbox>? iframeSandbox;
///A string that reflects the `referrerpolicy` HTML attribute indicating which referrer to use when fetching the linked resource. ///A string that reflects the `referrerpolicy` HTML attribute indicating which referrer to use when fetching the linked resource.
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Web ///- Web
String? iframeReferrerPolicy; ReferrerPolicy? iframeReferrerPolicy;
///A string that reflects the `name` HTML attribute, containing a name by which to refer to the frame. ///A string that reflects the `name` HTML attribute, containing a name by which to refer to the frame.
/// ///
@ -1027,7 +1031,7 @@ class InAppWebViewSettings
///- Web ///- Web
String? iframeName; String? iframeName;
///Specifies the Content Security Policy that an embedded document must agree to enforce upon itself. ///A Content Security Policy enforced for the embedded resource.
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Web ///- Web
@ -1155,7 +1159,7 @@ class InAppWebViewSettings
this.upgradeKnownHostsToHTTPS = true, this.upgradeKnownHostsToHTTPS = true,
this.iframeAllow, this.iframeAllow,
this.iframeAllowFullscreen, this.iframeAllowFullscreen,
this.iframeSandox, this.iframeSandbox,
this.iframeReferrerPolicy, this.iframeReferrerPolicy,
this.iframeName, this.iframeName,
this.iframeCsp,}) { this.iframeCsp,}) {
@ -1303,7 +1307,7 @@ class InAppWebViewSettings
"upgradeKnownHostsToHTTPS": upgradeKnownHostsToHTTPS, "upgradeKnownHostsToHTTPS": upgradeKnownHostsToHTTPS,
"iframeAllow": iframeAllow, "iframeAllow": iframeAllow,
"iframeAllowFullscreen": iframeAllowFullscreen, "iframeAllowFullscreen": iframeAllowFullscreen,
"iframeSandox": iframeSandox, "iframeSandbox": iframeSandbox?.map((e) => e.toValue()),
"iframeReferrerPolicy": iframeReferrerPolicy, "iframeReferrerPolicy": iframeReferrerPolicy,
"iframeName": iframeName, "iframeName": iframeName,
"iframeCsp": iframeCsp, "iframeCsp": iframeCsp,
@ -1366,8 +1370,9 @@ class InAppWebViewSettings
if (kIsWeb) { if (kIsWeb) {
settings.iframeAllow = map["iframeAllow"]; settings.iframeAllow = map["iframeAllow"];
settings.iframeAllowFullscreen = map["iframeAllowFullscreen"]; settings.iframeAllowFullscreen = map["iframeAllowFullscreen"];
settings.iframeSandox = map["iframeSandox"]; settings.iframeSandbox = (map["iframeSandbox"] as List<String?>?)
settings.iframeReferrerPolicy = map["iframeReferrerPolicy"]; ?.map((e) => Sandbox.fromValue(e)) as List<Sandbox>?;
settings.iframeReferrerPolicy = ReferrerPolicy.fromValue(map["iframeReferrerPolicy"]);
settings.iframeName = map["iframeName"]; settings.iframeName = map["iframeName"];
settings.iframeCsp = map["iframeCsp"]; settings.iframeCsp = map["iframeCsp"];
} }

View File

@ -10724,3 +10724,97 @@ class MediaPlaybackState {
@override @override
int get hashCode => _value.hashCode; int get hashCode => _value.hashCode;
} }
///Class that describes what to allow in the iframe.
class Sandbox {
final String? _value;
const Sandbox._internal(this._value);
static final Set<Sandbox> values = [
Sandbox.ALLOW_DOWNLOADS,
Sandbox.ALLOW_FORMS,
Sandbox.ALLOW_MODALS,
Sandbox.ALLOW_ORIENTATION_LOCK,
Sandbox.ALLOW_POINTER_LOCK,
Sandbox.ALLOW_POPUPS,
Sandbox.ALLOW_POPUPS_TO_ESCAPE_SANDBOX,
Sandbox.ALLOW_PRESENTATION,
Sandbox.ALLOW_SAME_ORIGIN,
Sandbox.ALLOW_SCRIPTS,
Sandbox.ALLOW_TOP_NAVIGATION,
Sandbox.ALLOW_TOP_NAVIGATION_BY_USER_ACTIVATION,
].toSet();
static Sandbox? fromValue(String? value) {
if (value == null) {
return _ALL;
} else if (value == "") {
return _NONE;
}
try {
return Sandbox.values
.firstWhere((element) => element.toValue() == value);
} catch (e) {
return null;
}
}
String? toValue() => _value;
@override
String toString() => _value == null ? "allow-all" : (_value == "" ? "allow-none" : "");
static const _ALL = const Sandbox._internal(null);
static const _NONE = const Sandbox._internal("");
///Allow all.
static const ALLOW_ALL = const [_ALL];
///Allow none.
static const ALLOW_NONE = const [_NONE];
///Allows for downloads to occur with a gesture from the user.
static const ALLOW_DOWNLOADS = const Sandbox._internal("allow-downloads");
///Allows the resource to submit forms. If this keyword is not used, form submission is blocked.
static const ALLOW_FORMS = const Sandbox._internal("allow-forms");
///Lets the resource open modal windows.
static const ALLOW_MODALS = const Sandbox._internal("allow-modals");
///Lets the resource lock the screen orientation.
static const ALLOW_ORIENTATION_LOCK = const Sandbox._internal("allow-orientation-lock");
///Lets the resource use the Pointer Lock API.
static const ALLOW_POINTER_LOCK = const Sandbox._internal("allow-pointer-lock");
///Allows popups (such as `window.open()`, `target="_blank"`, or `showModalDialog()`).
///If this keyword is not used, the popup will silently fail to open.
static const ALLOW_POPUPS = const Sandbox._internal("allow-popups");
///Lets the sandboxed document open new windows without those windows inheriting the sandboxing.
///For example, this can safely sandbox an advertisement without forcing the same restrictions upon the page the ad links to.
static const ALLOW_POPUPS_TO_ESCAPE_SANDBOX = const Sandbox._internal("allow-popups-to-escape-sandbox");
///Lets the resource start a presentation session.
static const ALLOW_PRESENTATION = const Sandbox._internal("allow-presentation");
///If this token is not used, the resource is treated as being from a special origin that always fails the
///same-origin policy (potentially preventing access to data storage/cookies and some JavaScript APIs).
static const ALLOW_SAME_ORIGIN = const Sandbox._internal("allow-same-origin");
///Lets the resource run scripts (but not create popup windows).
static const ALLOW_SCRIPTS = const Sandbox._internal("allow-scripts");
///Lets the resource navigate the top-level browsing context (the one named `_top`).
static const ALLOW_TOP_NAVIGATION = const Sandbox._internal("allow-top-navigation");
///Lets the resource navigate the top-level browsing context, but only if initiated by a user gesture.
static const ALLOW_TOP_NAVIGATION_BY_USER_ACTIVATION = const Sandbox._internal("allow-top-navigation-by-user-activation");
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}

View File

@ -97,16 +97,28 @@ class InAppWebViewWebElement {
void prepare() { void prepare() {
settings = initialSettings ?? InAppWebViewSettings(); settings = initialSettings ?? InAppWebViewSettings();
Set<Sandbox> sandbox = Set.from(Sandbox.values);
if (!settings.javaScriptEnabled) {
sandbox.remove(Sandbox.ALLOW_SCRIPTS);
}
iframe.allow = settings.iframeAllow ?? iframe.allow; iframe.allow = settings.iframeAllow ?? iframe.allow;
iframe.allowFullscreen = settings.iframeAllowFullscreen ?? iframe.allowFullscreen; iframe.allowFullscreen = settings.iframeAllowFullscreen ?? iframe.allowFullscreen;
if (settings.iframeSandox != null) { iframe.referrerPolicy = settings.iframeReferrerPolicy?.toValue() ?? iframe.referrerPolicy;
iframe.setAttribute("sandbox", settings.iframeSandox ?? "");
}
iframe.referrerPolicy = settings.iframeReferrerPolicy ?? iframe.referrerPolicy;
iframe.name = settings.iframeName ?? iframe.name; iframe.name = settings.iframeName ?? iframe.name;
iframe.csp = settings.iframeCsp ?? iframe.csp; iframe.csp = settings.iframeCsp ?? iframe.csp;
bridgeJsObject.callMethod("prepare"); if (settings.iframeSandbox != null && settings.iframeSandbox != Sandbox.ALLOW_ALL) {
iframe.setAttribute("sandbox", settings.iframeSandbox!.map((e) => e.toValue()).join(" "));
} else if (settings.iframeSandbox == Sandbox.ALLOW_ALL) {
iframe.removeAttribute("sandbox");
} else if (sandbox != Sandbox.values) {
iframe.setAttribute("sandbox", sandbox.map((e) => e.toValue()).join(" "));
}
bridgeJsObject.callMethod("prepare", [js.JsObject.jsify(settings.toMap())]);
} }
void makeInitialLoad() async { void makeInitialLoad() async {
@ -179,18 +191,39 @@ class InAppWebViewWebElement {
bridgeJsObject.callMethod("stopLoading"); bridgeJsObject.callMethod("stopLoading");
} }
Set<Sandbox> getSandbox() {
var sandbox = iframe.sandbox;
Set<Sandbox> values = Set();
if (sandbox != null) {
for (int i = 0; i < sandbox.length; i++) {
var token = Sandbox.fromValue(sandbox.item(i));
if (token != null) {
values.add(token);
}
}
}
return values.isEmpty ? Set.from(Sandbox.values) : values;
}
Future<void> setSettings(InAppWebViewSettings newSettings) async { Future<void> setSettings(InAppWebViewSettings newSettings) async {
Set<Sandbox> sandbox = getSandbox();
if (settings.javaScriptEnabled != newSettings.javaScriptEnabled) {
if (!newSettings.javaScriptEnabled) {
sandbox.remove(Sandbox.ALLOW_SCRIPTS);
} else {
sandbox.add(Sandbox.ALLOW_SCRIPTS);
}
}
if (settings.iframeAllow != newSettings.iframeAllow) { if (settings.iframeAllow != newSettings.iframeAllow) {
iframe.allow = newSettings.iframeAllow; iframe.allow = newSettings.iframeAllow;
} }
if (settings.iframeAllowFullscreen != newSettings.iframeAllowFullscreen) { if (settings.iframeAllowFullscreen != newSettings.iframeAllowFullscreen) {
iframe.allowFullscreen = newSettings.iframeAllowFullscreen; iframe.allowFullscreen = newSettings.iframeAllowFullscreen;
} }
if (settings.iframeSandox != newSettings.iframeSandox) {
iframe.setAttribute("sandbox", newSettings.iframeSandox ?? "");
}
if (settings.iframeReferrerPolicy != newSettings.iframeReferrerPolicy) { if (settings.iframeReferrerPolicy != newSettings.iframeReferrerPolicy) {
iframe.referrerPolicy = newSettings.iframeReferrerPolicy; iframe.referrerPolicy = newSettings.iframeReferrerPolicy?.toValue();
} }
if (settings.iframeName != newSettings.iframeName) { if (settings.iframeName != newSettings.iframeName) {
iframe.name = newSettings.iframeName; iframe.name = newSettings.iframeName;
@ -198,6 +231,20 @@ class InAppWebViewWebElement {
if (settings.iframeCsp != newSettings.iframeCsp) { if (settings.iframeCsp != newSettings.iframeCsp) {
iframe.csp = newSettings.iframeCsp; iframe.csp = newSettings.iframeCsp;
} }
if (settings.iframeSandbox != newSettings.iframeSandbox) {
var sandbox = newSettings.iframeSandbox;
if (sandbox != null && sandbox != Sandbox.ALLOW_ALL) {
iframe.setAttribute("sandbox", sandbox.map((e) => e.toValue()).join(" "));
} else if (sandbox == Sandbox.ALLOW_ALL) {
iframe.removeAttribute("sandbox");
}
} else if (sandbox != Sandbox.values) {
iframe.setAttribute("sandbox", sandbox.map((e) => e.toValue()).join(" "));
}
bridgeJsObject.callMethod("setSettings", [js.JsObject.jsify(settings.toMap()), js.JsObject.jsify(newSettings.toMap())]);
settings = newSettings; settings = newSettings;
} }