diff --git a/CHANGELOG.md b/CHANGELOG.md index d6381ff0..ec4aa09b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - Added `handlesURLScheme`, `createPdf`, `createWebArchiveData` iOS-specific WebView methods - Added `iosAnimated` optional argument to `zoomBy` WebView method - Added `screenshotConfiguration` optional argument to `takeScreenshot` WebView method +- Added `scriptHtmlTagAttributes` optional argument to `injectJavascriptFileFromUrl` WebView method +- Added `cssLinkHtmlTagAttributes` optional argument to `injectCSSFileFromUrl` WebView method - Updated integration tests - Merge "Upgraded appcompat to 1.2.0-rc-02" [#465](https://github.com/pichillilorenzo/flutter_inappwebview/pull/465) (thanks to [andreidiaconu](https://github.com/andreidiaconu)) - Merge "Added missing field 'headers' which returned by WebResourceResponse.toMap()" [#490](https://github.com/pichillilorenzo/flutter_inappwebview/pull/490) (thanks to [Doflatango](https://github.com/Doflatango)) @@ -38,7 +40,7 @@ ### BREAKING CHANGES -- Minimum Flutter version required is `1.22.0` and Dart SDK `>=2.12.0-0 <3.0.0` +- Minimum Flutter version required is `1.22.2` and Dart SDK `>=2.12.0-0 <3.0.0` - iOS Xcode version `>= 12` - Removed `debuggingEnabled` WebView option; on Android you should use now the `AndroidInAppWebViewController.setWebContentsDebuggingEnabled(bool debuggingEnabled)` static method; on iOS, debugging is always enabled - `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options moved from Android-specific options to cross-platform options diff --git a/README.md b/README.md index 0d1f76a5..998878df 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # Flutter InAppWebView Plugin [![Share on Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Flutter%20InAppBrowser%20plugin!&url=https://github.com/pichillilorenzo/flutter_inappwebview&hashtags=flutter,flutterio,dart,dartlang,webview) [![Share on Facebook](https://img.shields.io/badge/share-facebook-blue.svg?longCache=true&style=flat&colorB=%234267b2)](https://www.facebook.com/sharer/sharer.php?u=https%3A//github.com/pichillilorenzo/flutter_inappwebview) [![Pub](https://img.shields.io/pub/v/flutter_inappwebview.svg)](https://pub.dartlang.org/packages/flutter_inappwebview) -[![Awesome Flutter](https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true&style=flat-square)](https://stackoverflow.com/questions/tagged/flutter+webview?sort=votes) +[![pub points](https://badges.bar/flutter_inappwebview/pub%20points)](https://pub.dev/packages/flutter_inappwebview/score) +[![popularity](https://badges.bar/flutter_inappwebview/popularity)](https://pub.dev/packages/flutter_inappwebview/score) +[![likes](https://badges.bar/flutter_inappwebview/likes)](https://pub.dev/packages/flutter_inappwebview/score) +[![Awesome Flutter](https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true&style=flat-square)](https://stackoverflow.com/questions/tagged/flutter-inappwebview) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](/LICENSE) [![Donate to this project using Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/LorenzoPichilli) @@ -22,6 +25,8 @@ Note that the API shown in this `README.md` file shows only a **part** of the do So, here you could have methods, options, and events that **aren't published/released yet**! If you need a specific version, please change the **GitHub branch** of this repository to your version or use the **online [API Reference](https://pub.dartlang.org/documentation/flutter_inappwebview/latest/)** (recommended). +Also, check the [example/integration_test/webview_flutter_test.dart](https://github.com/pichillilorenzo/flutter_inappwebview/blob/master/example/integration_test/webview_flutter_test.dart) file for code examples. + ## Articles/Resources - [InAppWebView: The Real Power of WebViews in Flutter](https://medium.com/flutter-community/inappwebview-the-real-power-of-webviews-in-flutter-c6d52374209d?source=friends_link&sk=cb74487219bcd85e610a670ee0b447d0) @@ -426,9 +431,9 @@ Screenshots: * `goTo({required WebHistoryItem historyItem})`: Navigates to a `WebHistoryItem` from the back-forward `WebHistory.list` and sets it as the current item. * `injectCSSCode({required String source})`: Injects CSS into the WebView. * `injectCSSFileFromAsset({required String assetFilePath})`: Injects a CSS file into the WebView from the flutter assets directory. -* `injectCSSFileFromUrl({required String urlFile})`: Injects an external CSS file into the WebView from a defined url. +* `injectCSSFileFromUrl({required String urlFile, CSSLinkHtmlTagAttributes? cssLinkHtmlTagAttributes})`: Injects an external CSS file into the WebView from a defined url. * `injectJavascriptFileFromAsset({required String assetFilePath})`: Injects a JavaScript file into the WebView from the flutter assets directory. -* `injectJavascriptFileFromUrl({required String urlFile})`: Injects an external JavaScript file into the WebView from a defined url. +* `injectJavascriptFileFromUrl({required String urlFile, ScriptHtmlTagAttributes? scriptHtmlTagAttributes})`: Injects an external JavaScript file into the WebView from a defined url. * `isLoading`: Check if the WebView instance is in a loading state. * `loadData({required String data, String mimeType = "text/html", String encoding = "utf8", String baseUrl = "about:blank", String androidHistoryUrl = "about:blank"})`: Loads the given data into this WebView. * `loadFile({required String assetFilePath, Map headers = const {}})`: Loads the given `assetFilePath` with optional headers specified as a map from name to value. diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java index 3e156fd7..f9a70935 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java @@ -1523,7 +1523,7 @@ final public class InAppWebView extends InputAwareWebView { return (options != null) ? options.getRealOptions(this) : null; } - public void injectDeferredObject(String source, @Nullable final String contentWorldName, String jsWrapper, final MethodChannel.Result result) { + public void injectDeferredObject(String source, @Nullable final String contentWorldName, String jsWrapper, @Nullable final MethodChannel.Result result) { String scriptToInject = source; if (jsWrapper != null) { org.json.JSONArray jsonEsc = new org.json.JSONArray(); @@ -1577,18 +1577,95 @@ final public class InAppWebView extends InputAwareWebView { injectDeferredObject(source, contentWorldName, null, result); } - public void injectJavascriptFileFromUrl(String urlFile) { - String jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document);"; + public void injectJavascriptFileFromUrl(String urlFile, @Nullable Map scriptHtmlTagAttributes) { + String scriptAttributes = ""; + if (scriptHtmlTagAttributes != null) { + String typeAttr = (String) scriptHtmlTagAttributes.get("type"); + if (typeAttr != null) { + scriptAttributes += " script.type = '" + typeAttr.replaceAll("'", "\\\\'") + "'; "; + } + String idAttr = (String) scriptHtmlTagAttributes.get("id"); + if (idAttr != null) { + scriptAttributes += " script.id = '" + idAttr.replaceAll("'", "\\\\'") + "'; "; + } + Boolean asyncAttr = (Boolean) scriptHtmlTagAttributes.get("async"); + if (asyncAttr != null && asyncAttr) { + scriptAttributes += " script.async = true; "; + } + Boolean deferAttr = (Boolean) scriptHtmlTagAttributes.get("defer"); + if (deferAttr != null && deferAttr) { + scriptAttributes += " script.defer = true; "; + } + String crossOriginAttr = (String) scriptHtmlTagAttributes.get("crossOrigin"); + if (crossOriginAttr != null) { + scriptAttributes += " script.crossOrigin = '" + crossOriginAttr.replaceAll("'", "\\\\'") + "'; "; + } + String integrityAttr = (String) scriptHtmlTagAttributes.get("integrity"); + if (integrityAttr != null) { + scriptAttributes += " script.integrity = '" + integrityAttr.replaceAll("'", "\\\\'") + "'; "; + } + Boolean noModuleAttr = (Boolean) scriptHtmlTagAttributes.get("noModule"); + if (noModuleAttr != null && noModuleAttr) { + scriptAttributes += " script.noModule = true; "; + } + String nonceAttr = (String) scriptHtmlTagAttributes.get("nonce"); + if (nonceAttr != null) { + scriptAttributes += " script.nonce = '" + nonceAttr.replaceAll("'", "\\\\'") + "'; "; + } + String referrerPolicyAttr = (String) scriptHtmlTagAttributes.get("referrerPolicy"); + if (referrerPolicyAttr != null) { + scriptAttributes += " script.referrerPolicy = '" + referrerPolicyAttr.replaceAll("'", "\\\\'") + "'; "; + } + } + String jsWrapper = "(function(d) { var script = d.createElement('script'); " + scriptAttributes + + " script.src = %s; d.body.appendChild(script); })(document);"; injectDeferredObject(urlFile, null, jsWrapper, null); } public void injectCSSCode(String source) { - String jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document);"; + String jsWrapper = "(function(d) { var style = d.createElement('style'); style.innerHTML = %s; d.body.appendChild(style); })(document);"; injectDeferredObject(source, null, jsWrapper, null); } - public void injectCSSFileFromUrl(String urlFile) { - String jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document);"; + public void injectCSSFileFromUrl(String urlFile, @Nullable Map cssLinkHtmlTagAttributes) { + String cssLinkAttributes = ""; + String alternateStylesheet = ""; + if (cssLinkHtmlTagAttributes != null) { + String idAttr = (String) cssLinkHtmlTagAttributes.get("id"); + if (idAttr != null) { + cssLinkAttributes += " link.id = '" + idAttr.replaceAll("'", "\\\\'") + "'; "; + } + String mediaAttr = (String) cssLinkHtmlTagAttributes.get("media"); + if (mediaAttr != null) { + cssLinkAttributes += " link.media = '" + mediaAttr.replaceAll("'", "\\\\'") + "'; "; + } + String crossOriginAttr = (String) cssLinkHtmlTagAttributes.get("crossOrigin"); + if (crossOriginAttr != null) { + cssLinkAttributes += " link.crossOrigin = '" + crossOriginAttr.replaceAll("'", "\\\\'") + "'; "; + } + String integrityAttr = (String) cssLinkHtmlTagAttributes.get("integrity"); + if (integrityAttr != null) { + cssLinkAttributes += " link.integrity = '" + integrityAttr.replaceAll("'", "\\\\'") + "'; "; + } + String referrerPolicyAttr = (String) cssLinkHtmlTagAttributes.get("referrerPolicy"); + if (referrerPolicyAttr != null) { + cssLinkAttributes += " link.referrerPolicy = '" + referrerPolicyAttr.replaceAll("'", "\\\\'") + "'; "; + } + Boolean disabledAttr = (Boolean) cssLinkHtmlTagAttributes.get("disabled"); + if (disabledAttr != null && disabledAttr) { + cssLinkAttributes += " link.disabled = true; "; + } + Boolean alternateAttr = (Boolean) cssLinkHtmlTagAttributes.get("alternate"); + if (alternateAttr != null && alternateAttr) { + alternateStylesheet = "alternate "; + } + String titleAttr = (String) cssLinkHtmlTagAttributes.get("title"); + if (titleAttr != null) { + cssLinkAttributes += " link.title = '" + titleAttr.replaceAll("'", "\\\\'") + "'; "; + } + } + String jsWrapper = "(function(d) { var link = d.createElement('link'); link.rel='" + alternateStylesheet + "stylesheet'; link.type='text/css'; " + + cssLinkAttributes + " link.href = %s; d.head.appendChild(link); })(document);"; injectDeferredObject(urlFile, null, jsWrapper, null); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewMethodHandler.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewMethodHandler.java index 79fb2e16..a7e40cfd 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewMethodHandler.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewMethodHandler.java @@ -84,7 +84,8 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle case "injectJavascriptFileFromUrl": if (webView != null) { String urlFile = (String) call.argument("urlFile"); - webView.injectJavascriptFileFromUrl(urlFile); + Map scriptHtmlTagAttributes = (Map) call.argument("scriptHtmlTagAttributes"); + webView.injectJavascriptFileFromUrl(urlFile, scriptHtmlTagAttributes); } result.success(true); break; @@ -98,7 +99,8 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle case "injectCSSFileFromUrl": if (webView != null) { String urlFile = (String) call.argument("urlFile"); - webView.injectCSSFileFromUrl(urlFile); + Map cssLinkHtmlTagAttributes = (Map) call.argument("cssLinkHtmlTagAttributes"); + webView.injectCSSFileFromUrl(urlFile, cssLinkHtmlTagAttributes); } result.success(true); break; diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index 8e77e80c..d2b236ff 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-02-09 01:23:18.308862","version":"1.26.0-18.0.pre.90"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-02-09 21:00:13.907504","version":"1.26.0-18.0.pre.90"} \ No newline at end of file diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 079cd6b2..8033a280 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -115,6 +115,9 @@ class _InAppWebViewExampleScreenState extends State { this.url = url ?? ''; }); }, + androidOnPermissionRequest: (InAppWebViewController controller, String origin, List resources) async { + return PermissionRequestResponse(resources: resources, action: PermissionRequestResponseAction.GRANT); + }, shouldOverrideUrlLoading: (controller, shouldOverrideUrlLoadingRequest) async { var url = shouldOverrideUrlLoadingRequest.url; var uri = Uri.parse(url); diff --git a/flutter_inappwebview.iml b/flutter_inappwebview.iml index 3acc9934..0adae5aa 100755 --- a/flutter_inappwebview.iml +++ b/flutter_inappwebview.iml @@ -14,6 +14,12 @@ + + + + + + diff --git a/ios/Classes/InAppWebView.swift b/ios/Classes/InAppWebView.swift index 1f182c79..a69b973f 100755 --- a/ios/Classes/InAppWebView.swift +++ b/ios/Classes/InAppWebView.swift @@ -2303,18 +2303,76 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi } } - public func injectJavascriptFileFromUrl(urlFile: String) { - let jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document);" + public func injectJavascriptFileFromUrl(urlFile: String, scriptHtmlTagAttributes: [String:Any?]?) { + var scriptAttributes = "" + if let scriptHtmlTagAttributes = scriptHtmlTagAttributes { + if let typeAttr = scriptHtmlTagAttributes["type"] as? String { + scriptAttributes += " script.type = '\(typeAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let idAttr = scriptHtmlTagAttributes["id"] as? String { + scriptAttributes += " script.id = '\(idAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let asyncAttr = scriptHtmlTagAttributes["async"] as? Bool, asyncAttr { + scriptAttributes += " script.async = true; " + } + if let deferAttr = scriptHtmlTagAttributes["defer"] as? Bool, deferAttr { + scriptAttributes += " script.defer = true; " + } + if let crossOriginAttr = scriptHtmlTagAttributes["crossOrigin"] as? String { + scriptAttributes += " script.crossOrigin = '\(crossOriginAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let integrityAttr = scriptHtmlTagAttributes["integrity"] as? String { + scriptAttributes += " script.integrity = '\(integrityAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let noModuleAttr = scriptHtmlTagAttributes["noModule"] as? Bool, noModuleAttr { + scriptAttributes += " script.noModule = true; " + } + if let nonceAttr = scriptHtmlTagAttributes["nonce"] as? String { + scriptAttributes += " script.nonce = '\(nonceAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let referrerPolicyAttr = scriptHtmlTagAttributes["referrerPolicy"] as? String { + scriptAttributes += " script.referrerPolicy = '\(referrerPolicyAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + } + let jsWrapper = "(function(d) { var script = d.createElement('script'); \(scriptAttributes) script.src = %@; d.body.appendChild(script); })(document);" injectDeferredObject(source: urlFile, contentWorldName: nil, withWrapper: jsWrapper, result: nil) } public func injectCSSCode(source: String) { - let jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document);" + let jsWrapper = "(function(d) { var style = d.createElement('style'); style.innerHTML = %@; d.body.appendChild(style); })(document);" injectDeferredObject(source: source, contentWorldName: nil, withWrapper: jsWrapper, result: nil) } - public func injectCSSFileFromUrl(urlFile: String) { - let jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document);" + public func injectCSSFileFromUrl(urlFile: String, cssLinkHtmlTagAttributes: [String:Any?]?) { + var cssLinkAttributes = "" + var alternateStylesheet = "" + if let cssLinkHtmlTagAttributes = cssLinkHtmlTagAttributes { + if let idAttr = cssLinkHtmlTagAttributes["id"] as? String { + cssLinkAttributes += " link.id = '\(idAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let mediaAttr = cssLinkHtmlTagAttributes["media"] as? String { + cssLinkAttributes += " link.media = '\(mediaAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let crossOriginAttr = cssLinkHtmlTagAttributes["crossOrigin"] as? String { + cssLinkAttributes += " link.crossOrigin = '\(crossOriginAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let integrityAttr = cssLinkHtmlTagAttributes["integrity"] as? String { + cssLinkAttributes += " link.integrity = '\(integrityAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let referrerPolicyAttr = cssLinkHtmlTagAttributes["referrerPolicy"] as? String { + cssLinkAttributes += " link.referrerPolicy = '\(referrerPolicyAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + if let disabledAttr = cssLinkHtmlTagAttributes["disabled"] as? Bool, disabledAttr { + cssLinkAttributes += " link.disabled = true; " + } + if let alternateAttr = cssLinkHtmlTagAttributes["alternate"] as? Bool, alternateAttr { + alternateStylesheet = "alternate " + } + if let titleAttr = cssLinkHtmlTagAttributes["title"] as? String { + cssLinkAttributes += " link.title = '\(titleAttr.replacingOccurrences(of: "\'", with: "\\'"))'; " + } + } + let jsWrapper = "(function(d) { var link = d.createElement('link'); link.rel='\(alternateStylesheet)stylesheet', link.type='text/css'; \(cssLinkAttributes) link.href = %@; d.body.appendChild(link); })(document);" injectDeferredObject(source: urlFile, contentWorldName: nil, withWrapper: jsWrapper, result: nil) } diff --git a/ios/Classes/InAppWebViewMethodHandler.swift b/ios/Classes/InAppWebViewMethodHandler.swift index 2b154154..5d072299 100644 --- a/ios/Classes/InAppWebViewMethodHandler.swift +++ b/ios/Classes/InAppWebViewMethodHandler.swift @@ -80,7 +80,8 @@ class InAppWebViewMethodHandler: FlutterMethodCallDelegate { break case "injectJavascriptFileFromUrl": let urlFile = arguments!["urlFile"] as! String - webView?.injectJavascriptFileFromUrl(urlFile: urlFile) + let scriptHtmlTagAttributes = arguments!["scriptHtmlTagAttributes"] as? [String:Any?] + webView?.injectJavascriptFileFromUrl(urlFile: urlFile, scriptHtmlTagAttributes: scriptHtmlTagAttributes) result(true) break case "injectCSSCode": @@ -90,7 +91,8 @@ class InAppWebViewMethodHandler: FlutterMethodCallDelegate { break case "injectCSSFileFromUrl": let urlFile = arguments!["urlFile"] as! String - webView?.injectCSSFileFromUrl(urlFile: urlFile) + let cssLinkHtmlTagAttributes = arguments!["cssLinkHtmlTagAttributes"] as? [String:Any?] + webView?.injectCSSFileFromUrl(urlFile: urlFile, cssLinkHtmlTagAttributes: cssLinkHtmlTagAttributes) result(true) break case "reload": diff --git a/lib/src/in_app_webview_controller.dart b/lib/src/in_app_webview_controller.dart index 079a4234..002b27f7 100644 --- a/lib/src/in_app_webview_controller.dart +++ b/lib/src/in_app_webview_controller.dart @@ -1382,17 +1382,20 @@ class InAppWebViewController { ///Injects an external JavaScript file into the WebView from a defined url. /// + ///[scriptHtmlTagAttributes] represents the possible the `