Skip to content

Commit

Permalink
Added onLoad and onError callbacks in ScriptHtmlTagAttributes class u…
Browse files Browse the repository at this point in the history
…sed by InAppWebViewController.injectJavascriptFileFromUrl, InAppWebViewController.injectJavascriptFileFromAsset returns a Future<dynamic> type now
  • Loading branch information
pichillilorenzo committed Apr 7, 2021
1 parent 9b61042 commit f06bcdf
Show file tree
Hide file tree
Showing 12 changed files with 530 additions and 18 deletions.
420 changes: 420 additions & 0 deletions .idea/libraries/Dart_Packages.xml

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions .idea/libraries/Flutter_Plugins.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 5.3.2

- Added `onLoad` and `onError` callbacks in `ScriptHtmlTagAttributes` class used by `InAppWebViewController.injectJavascriptFileFromUrl`
- `InAppWebViewController.injectJavascriptFileFromAsset` returns a `Future<dynamic>` type now

## 5.3.1+1

- Removed duplicate lib exports
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1019,7 +1019,18 @@ public void injectJavascriptFileFromUrl(String urlFile, @Nullable Map<String, Ob
}
String idAttr = (String) scriptHtmlTagAttributes.get("id");
if (idAttr != null) {
scriptAttributes += " script.id = '" + idAttr.replaceAll("'", "\\\\'") + "'; ";
String scriptIdEscaped = idAttr.replaceAll("'", "\\\\'");
scriptAttributes += " script.id = '" + scriptIdEscaped + "'; ";
scriptAttributes += " script.onload = function() {" +
" if (window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + " != null) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onInjectedScriptLoaded', '" + scriptIdEscaped + "');" +
" }" +
"};";
scriptAttributes += " script.onerror = function() {" +
" if (window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + " != null) {" +
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('onInjectedScriptError', '" + scriptIdEscaped + "');" +
" }" +
"};";
}
Boolean asyncAttr = (Boolean) scriptHtmlTagAttributes.get("async");
if (asyncAttr != null && asyncAttr) {
Expand Down
2 changes: 1 addition & 1 deletion example/.flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"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":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"android":[{"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":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/","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.2.0-nullsafety/","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.1.0-nullsafety.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":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"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-04-06 11:48:11.518272","version":"2.1.0-10.0.pre"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/fvm/versions/2.1.0-10.0.pre/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/fvm/versions/2.1.0-10.0.pre/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"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-04-07 18:05:10.935076","version":"2.1.0-10.0.pre"}
22 changes: 20 additions & 2 deletions example/integration_test/webview_flutter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4142,6 +4142,8 @@ setTimeout(function() {
testWidgets('injectJavascriptFileFromUrl', (WidgetTester tester) async {
final Completer controllerCompleter = Completer<InAppWebViewController>();
final Completer<void> pageLoaded = Completer<void>();
final Completer<void> jQueryLoaded = Completer<void>();
final Completer<void> jQueryLoadError = Completer<void>();

await tester.pumpWidget(
Directionality(
Expand All @@ -4163,10 +4165,26 @@ setTimeout(function() {
await controllerCompleter.future;
await pageLoaded.future;

await controller.injectJavascriptFileFromUrl(
urlFile: Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'),
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery-error', onError: () {
jQueryLoadError.complete();
},));
await jQueryLoadError.future;
expect(
await controller.evaluateJavascript(
source: "document.body.querySelector('#jquery-error') == null;"),
false);
expect(
await controller.evaluateJavascript(source: "window.jQuery == null;"),
true);

await controller.injectJavascriptFileFromUrl(
urlFile: Uri.parse('https://code.jquery.com/jquery-3.3.1.min.js'),
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery'));
await Future.delayed(Duration(seconds: 4));
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery', onLoad: () {
jQueryLoaded.complete();
},));
await jQueryLoaded.future;
expect(
await controller.evaluateJavascript(
source: "document.body.querySelector('#jquery') == null;"),
Expand Down
6 changes: 3 additions & 3 deletions example/ios/Flutter/flutter_export_environment.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
export "FLUTTER_ROOT=/Users/lorenzopichilli/fvm/versions/2.1.0-10.0.pre"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
export "FLUTTER_TARGET=integration_test/webview_flutter_test.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
Expand Down
1 change: 1 addition & 0 deletions flutter_inappwebview.iml
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,6 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
17 changes: 16 additions & 1 deletion ios/Classes/InAppWebView/InAppWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1336,7 +1336,22 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
scriptAttributes += " script.type = '\(typeAttr.replacingOccurrences(of: "\'", with: "\\'"))'; "
}
if let idAttr = scriptHtmlTagAttributes["id"] as? String {
scriptAttributes += " script.id = '\(idAttr.replacingOccurrences(of: "\'", with: "\\'"))'; "
let scriptIdEscaped = idAttr.replacingOccurrences(of: "\'", with: "\\'")
scriptAttributes += " script.id = '\(scriptIdEscaped)'; "
scriptAttributes += """
script.onload = function() {
if (window.\(JAVASCRIPT_BRIDGE_NAME) != null) {
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onInjectedScriptLoaded', '\(scriptIdEscaped)');
}
};
"""
scriptAttributes += """
script.onerror = function() {
if (window.\(JAVASCRIPT_BRIDGE_NAME) != null) {
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onInjectedScriptError', '\(scriptIdEscaped)');
}
};
"""
}
if let asyncAttr = scriptHtmlTagAttributes["async"] as? Bool, asyncAttr {
scriptAttributes += " script.async = true; "
Expand Down
37 changes: 32 additions & 5 deletions lib/src/in_app_webview/in_app_webview_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class InAppWebViewController {
HashMap<String, JavaScriptHandlerCallback>();
List<UserScript> _userScripts = [];
Set<String> _webMessageListenerObjNames = Set();
Map<String, ScriptHtmlTagAttributes> _injectedScriptsFromURL = {};

// ignore: unused_field
dynamic _id;
Expand Down Expand Up @@ -102,6 +103,7 @@ class InAppWebViewController {
Future<dynamic> handleMethod(MethodCall call) async {
switch (call.method) {
case "onLoadStart":
_injectedScriptsFromURL.clear();
if ((_webview != null && _webview!.onLoadStart != null) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
Expand Down Expand Up @@ -865,6 +867,22 @@ class InAppWebViewController {
_webview!.onWindowBlur!(this);
else if (_inAppBrowser != null) _inAppBrowser!.onWindowBlur();
return null;
case "onInjectedScriptLoaded":
String id = args[0];
var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad;
if ((_webview != null || _inAppBrowser != null) &&
onLoadCallback != null) {
onLoadCallback();
}
return null;
case "onInjectedScriptError":
String id = args[0];
var onErrorCallback = _injectedScriptsFromURL[id]?.onError;
if ((_webview != null || _inAppBrowser != null) &&
onErrorCallback != null) {
onErrorCallback();
}
return null;
}

if (javaScriptHandlersMap.containsKey(handlerName)) {
Expand Down Expand Up @@ -1372,6 +1390,10 @@ class InAppWebViewController {
{required Uri urlFile,
ScriptHtmlTagAttributes? scriptHtmlTagAttributes}) async {
assert(urlFile.toString().isNotEmpty);
var id = scriptHtmlTagAttributes?.id;
if (scriptHtmlTagAttributes != null && id != null) {
_injectedScriptsFromURL[id] = scriptHtmlTagAttributes;
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('urlFile', () => urlFile.toString());
args.putIfAbsent(
Expand All @@ -1385,10 +1407,10 @@ class InAppWebViewController {
///because, in these events, the [WebView] is not ready to handle it yet.
///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".
Future<void> injectJavascriptFileFromAsset(
Future<dynamic> injectJavascriptFileFromAsset(
{required String assetFilePath}) async {
String source = await rootBundle.loadString(assetFilePath);
await evaluateJavascript(source: source);
return await evaluateJavascript(source: source);
}

///Injects CSS into the WebView.
Expand Down Expand Up @@ -2044,13 +2066,13 @@ class InAppWebViewController {
///[functionBody] is the JavaScript string to use as the function body.
///This method treats the string as an anonymous JavaScript function body and calls it with the named arguments in the arguments parameter.
///
///[arguments] is a dictionary of the arguments to pass to the function call.
///Each key in the dictionary corresponds to the name of an argument in the [functionBody] string,
///[arguments] is a `Map` of the arguments to pass to the function call.
///Each key in the `Map` corresponds to the name of an argument in the [functionBody] string,
///and the value of that key is the value to use during the evaluation of the code.
///Supported value types can be found in the official Flutter docs:
///[Platform channel data types support and codecs](https://flutter.dev/docs/development/platform-integration/platform-channels#codec),
///except for [Uint8List], [Int32List], [Int64List], and [Float64List] that should be converted into a [List].
///All items in an array or dictionary must also be one of the supported types.
///All items in a `List` or `Map` must also be one of the supported types.
///
///[contentWorld], on iOS, it represents the namespace in which to evaluate the JavaScript [source] code.
///Instead, on Android, it will run the [source] code into an iframe.
Expand All @@ -2059,6 +2081,11 @@ class InAppWebViewController {
///For more information about content worlds, see [ContentWorld].
///Available on iOS 14.0+.
///
///**NOTE**: This method shouldn't be called in the [WebView.onWebViewCreated] or [WebView.onLoadStart] events,
///because, in these events, the [WebView] is not ready to handle it yet.
///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".
///
///**NOTE for iOS**: available only on iOS 10.3+.
///
///**NOTE for Android**: available only on Android 21+.
Expand Down
19 changes: 18 additions & 1 deletion lib/src/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6037,6 +6037,16 @@ class ScriptHtmlTagAttributes {
///Indicates which referrer to send when fetching the script, or resources fetched by the script.
ReferrerPolicy? referrerPolicy;

///Represents a callback function that will be called as soon as the script has been loaded successfully.
///
///**NOTE**: This callback requires the [id] property to be set.
Function()? onLoad;

///Represents a callback function that will be called if an error occurred while trying to load the script.
///
///**NOTE**: This callback requires the [id] property to be set.
Function()? onError;

ScriptHtmlTagAttributes(
{this.type = "text/javascript",
this.id,
Expand All @@ -6046,7 +6056,14 @@ class ScriptHtmlTagAttributes {
this.integrity,
this.noModule,
this.nonce,
this.referrerPolicy});
this.referrerPolicy,
this.onLoad,
this.onError}) {
if (this.onLoad != null || this.onError != null) {
assert(this.id != null,
'onLoad and onError callbacks require the id property to be set.');
}
}

Map<String, dynamic> toMap() {
return {
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 5.3.1+1
version: 5.3.2
homepage: https://github.com/pichillilorenzo/flutter_inappwebview

environment:
Expand Down

0 comments on commit f06bcdf

Please sign in to comment.