Commit cf0c2029 authored by Lorenzo Pichilli's avatar Lorenzo Pichilli

Added addUserScript, addUserScripts, removeUserScript, removeUserScripts,...

Added addUserScript, addUserScripts, removeUserScript, removeUserScripts, removeAllUserScripts WebView methods, Added initialUserScripts WebView option, Added UserScript and UserScriptInjectionTime classes, updated README.md, fix some wrong iOS swift return value on method call handler, added InAppWebViewMethodHandler native class
parent a5b90a33
......@@ -5,6 +5,9 @@
- Added `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options also for iOS (also thanks to [liranhao](https://github.com/liranhao))
- Added limited cookies support on iOS below 11.0 using JavaScript
- Added `IOSCookieManager` class and `CookieManager.instance().ios.getAllCookies` iOS-specific method
- Added `UserScript` and `UserScriptInjectionTime` classes
- Added `initialUserScripts` WebView option
- Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeAllUserScripts` WebView methods
- 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))
......
This diff is collapsed.
......@@ -72,7 +72,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
Map<String, String> headers = (Map<String, String>) call.argument("headers");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId");
openUrl(activity, uuid, url, options, headers, contextMenu, windowId);
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
openUrl(activity, uuid, url, options, headers, contextMenu, windowId, initialUserScripts);
}
result.success(true);
break;
......@@ -90,7 +91,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
Map<String, String> headers = (Map<String, String>) call.argument("headers");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId");
openUrl(activity, uuid, url, options, headers, contextMenu, windowId);
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
openUrl(activity, uuid, url, options, headers, contextMenu, windowId, initialUserScripts);
}
result.success(true);
break;
......@@ -104,7 +106,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
String historyUrl = (String) call.argument("historyUrl");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId");
openData(activity, uuid, options, data, mimeType, encoding, baseUrl, historyUrl, contextMenu, windowId);
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
openData(activity, uuid, options, data, mimeType, encoding, baseUrl, historyUrl, contextMenu, windowId, initialUserScripts);
}
result.success(true);
break;
......@@ -196,7 +199,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
}
public void openUrl(Activity activity, String uuid, String url, HashMap<String, Object> options, Map<String, String> headers,
HashMap<String, Object> contextMenu, Integer windowId) {
HashMap<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
Bundle extras = new Bundle();
extras.putString("fromActivity", activity.getClass().getName());
extras.putString("url", url);
......@@ -206,11 +209,12 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
extras.putSerializable("headers", (Serializable) headers);
extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1);
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
startInAppBrowserActivity(activity, extras);
}
public void openData(Activity activity, String uuid, HashMap<String, Object> options, String data, String mimeType, String encoding,
String baseUrl, String historyUrl, HashMap<String, Object> contextMenu, Integer windowId) {
String baseUrl, String historyUrl, HashMap<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
Bundle extras = new Bundle();
extras.putBoolean("isData", true);
extras.putString("uuid", uuid);
......@@ -222,6 +226,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
extras.putString("historyUrl", historyUrl);
extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1);
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
startInAppBrowserActivity(activity, extras);
}
......
......@@ -104,6 +104,7 @@ final public class InAppWebView extends InputAwareWebView {
public Map<String, Object> contextMenu = null;
public Handler headlessHandler = new Handler(Looper.getMainLooper());
static Handler mHandler = new Handler();
public List<Map<String, Object>> userScripts = new ArrayList<>();
public Runnable checkScrollStoppedTask;
public int initialPositionScrollStoppedTask;
......@@ -632,7 +633,10 @@ final public class InAppWebView extends InputAwareWebView {
super(context, attrs, defaultStyle);
}
public InAppWebView(Context context, Object obj, Object id, Integer windowId, InAppWebViewOptions options, Map<String, Object> contextMenu, View containerView) {
public InAppWebView(Context context, Object obj, Object id,
Integer windowId, InAppWebViewOptions options,
Map<String, Object> contextMenu, View containerView,
List<Map<String, Object>> userScripts) {
super(context, containerView);
if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
......@@ -643,6 +647,7 @@ final public class InAppWebView extends InputAwareWebView {
this.windowId = windowId;
this.options = options;
this.contextMenu = contextMenu;
this.userScripts = userScripts;
Shared.activity.registerForContextMenu(this);
}
......@@ -1975,6 +1980,18 @@ final public class InAppWebView extends InputAwareWebView {
return null;
}
public boolean addUserScript(Map<String, Object> userScript) {
return userScripts.add(userScript);
}
public Map<String, Object> removeUserScript(int index) {
return userScripts.remove(index);
}
public void removeAllUserScripts() {
userScripts.clear();
}
@Override
public void dispose() {
if (windowId != null && InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
......
......@@ -162,44 +162,99 @@ public class InAppWebViewClient extends WebViewClient {
});
}
private void loadCustomJavaScript(WebView view) {
private void loadCustomJavaScriptOnPageStarted(WebView view) {
InAppWebView webView = (InAppWebView) view;
String js = InAppWebView.consoleLogJS.replaceAll("[\r\n]+", "");
js += JavaScriptBridgeInterface.flutterInAppBroserJSClass.replaceAll("[\r\n]+", "");
String js = preparePluginUserScripts(webView);
js += prepareUserScriptsAtDocumentStart(webView);
js = InAppWebView.scriptsWrapperJS
.replace("$PLACEHOLDER_VALUE", js);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
} else {
webView.loadUrl("javascript:" + js.replaceAll("[\r\n]+", ""));
}
}
private void loadCustomJavaScriptOnPageFinished(WebView view) {
InAppWebView webView = (InAppWebView) view;
// try to reload also custom scripts if they were not loaded during the onPageStarted event
String js = preparePluginUserScripts(webView);
js += prepareUserScriptsAtDocumentStart(webView);
js += prepareUserScriptsAtDocumentEnd(webView);
js = InAppWebView.scriptsWrapperJS
.replace("$PLACEHOLDER_VALUE", js);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
} else {
webView.loadUrl("javascript:" + js.replaceAll("[\r\n]+", ""));
}
}
private String preparePluginUserScripts(InAppWebView webView) {
String js = InAppWebView.consoleLogJS;
js += JavaScriptBridgeInterface.flutterInAppBroserJSClass;
if (webView.options.useShouldInterceptAjaxRequest) {
js += InAppWebView.interceptAjaxRequestsJS.replaceAll("[\r\n]+", "");
js += InAppWebView.interceptAjaxRequestsJS;
}
if (webView.options.useShouldInterceptFetchRequest) {
js += InAppWebView.interceptFetchRequestsJS.replaceAll("[\r\n]+", "");
js += InAppWebView.interceptFetchRequestsJS;
}
if (webView.options.useOnLoadResource) {
js += InAppWebView.resourceObserverJS.replaceAll("[\r\n]+", "");
js += InAppWebView.resourceObserverJS;
}
if (!webView.options.useHybridComposition) {
js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS.replaceAll("[\r\n]+", "");
js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS;
}
js += InAppWebView.onWindowFocusEventJS.replaceAll("[\r\n]+", "");
js += InAppWebView.onWindowBlurEventJS.replaceAll("[\r\n]+", "");
js += InAppWebView.printJS.replaceAll("[\r\n]+", "");
js += InAppWebView.onWindowFocusEventJS;
js += InAppWebView.onWindowBlurEventJS;
js += InAppWebView.printJS;
js = InAppWebView.scriptsWrapperJS
.replace("$PLACEHOLDER_VALUE", js)
.replaceAll("[\r\n]+", "");
return js;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
} else {
webView.loadUrl("javascript:" + js);
private String prepareUserScriptsAtDocumentStart(InAppWebView webView) {
StringBuilder js = new StringBuilder();
for (Map<String, Object> userScript : webView.userScripts) {
Integer injectionTime = (Integer) userScript.get("injectionTime");
if (injectionTime == null || injectionTime == 0) {
String source = (String) userScript.get("source");
if (source != null) {
js.append("(function(){").append(source).append("})();");
}
}
}
return js.toString();
}
private String prepareUserScriptsAtDocumentEnd(InAppWebView webView) {
StringBuilder js = new StringBuilder();
for (Map<String, Object> userScript : webView.userScripts) {
Integer injectionTime = (Integer) userScript.get("injectionTime");
if (injectionTime == 1) {
String source = (String) userScript.get("source");
if (source != null) {
js.append("(function(){").append(source).append("})();");
}
}
}
return js.toString();
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
final InAppWebView webView = (InAppWebView) view;
InAppWebView webView = (InAppWebView) view;
loadCustomJavaScript(webView);
loadCustomJavaScriptOnPageStarted(webView);
super.onPageStarted(view, url, favicon);
......@@ -219,8 +274,7 @@ public class InAppWebViewClient extends WebViewClient {
public void onPageFinished(WebView view, String url) {
final InAppWebView webView = (InAppWebView) view;
// try to reload custom javascript scripts if they were not loaded during the onPageStarted event
loadCustomJavaScript(webView);
loadCustomJavaScriptOnPageFinished(webView);
super.onPageFinished(view, url);
......@@ -228,19 +282,19 @@ public class InAppWebViewClient extends WebViewClient {
previousAuthRequestFailureCount = 0;
credentialsProposed = null;
// CB-10395 InAppBrowserManager's WebView not storing cookies reliable to local device storage
// WebView not storing cookies reliable to local device storage
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush();
} else {
CookieSyncManager.getInstance().sync();
}
String js = InAppWebView.platformReadyJS.replaceAll("[\r\n]+", "");
String js = InAppWebView.platformReadyJS;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
} else {
webView.loadUrl("javascript:" + js);
webView.loadUrl("javascript:" + js.replaceAll("[\r\n]+", ""));
}
Map<String, Object> obj = new HashMap<>();
......@@ -251,7 +305,7 @@ public class InAppWebViewClient extends WebViewClient {
}
@Override
public void doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
......
{"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-01-31 21:44:40.583578","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-01 14:15:06.451560","version":"1.26.0-18.0.pre.90"}
\ No newline at end of file
import 'dart:async';
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
......@@ -6,6 +7,9 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main.dart';
class MyInAppBrowser extends InAppBrowser {
MyInAppBrowser({int? windowId, UnmodifiableListView<UserScript>? initialUserScripts}) : super(windowId: windowId, initialUserScripts: initialUserScripts);
@override
Future onBrowserCreated() async {
print("\n\nBrowser Created!\n\n");
......@@ -18,6 +22,7 @@ class MyInAppBrowser extends InAppBrowser {
@override
Future onLoadStop(url) async {
print(await this.webViewController.getTitle());
print("\n\nStopped $url\n\n");
}
......
import 'dart:collection';
import 'dart:io';
import 'package:flutter/material.dart';
......
......@@ -41,9 +41,10 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
let optionsFallback = arguments!["optionsFallback"] as? [String: Any?]
let contextMenuFallback = arguments!["contextMenuFallback"] as? [String: Any]
let windowIdFallback = arguments!["windowIdFallback"] as? Int64
let initialUserScriptsFallback = arguments!["initialUserScriptsFallback"] as? [[String: Any]]
open(uuid: uuid, url: url, options: options, menuItemList: menuItemList, uuidFallback: uuidFallback,
headersFallback: headersFallback, optionsFallback: optionsFallback, contextMenuFallback: contextMenuFallback,
windowIdFallback: windowIdFallback, result: result)
windowIdFallback: windowIdFallback, initialUserScriptsFallback: initialUserScriptsFallback, result: result)
break
case "isAvailable":
if #available(iOS 9.0, *) {
......@@ -60,7 +61,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
public func open(uuid: String, url: String, options: [String: Any?], menuItemList: [[String: Any]], uuidFallback: String?,
headersFallback: [String: String]?, optionsFallback: [String: Any?]?, contextMenuFallback: [String: Any]?,
windowIdFallback: Int64?, result: @escaping FlutterResult) {
windowIdFallback: Int64?, initialUserScriptsFallback: [[String: Any]]?, result: @escaping FlutterResult) {
let absoluteUrl = URL(string: url)!.absoluteURL
if #available(iOS 9.0, *) {
......@@ -104,7 +105,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
return
}
SwiftFlutterPlugin.instance!.inAppBrowserManager!.openUrl(uuid: uuidFallback!, url: url, options: optionsFallback ?? [:], headers: headersFallback ?? [:], contextMenu: contextMenuFallback ?? [:], windowId: windowIdFallback)
SwiftFlutterPlugin.instance!.inAppBrowserManager!.openUrl(uuid: uuidFallback!, url: url, options: optionsFallback ?? [:], headers: headersFallback ?? [:], contextMenu: contextMenuFallback ?? [:], windowId: windowIdFallback, initialUserScripts: initialUserScriptsFallback)
}
}
}
This diff is collapsed.
......@@ -43,7 +43,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let headers = arguments!["headers"] as! [String: String]
let contextMenu = arguments!["contextMenu"] as! [String: Any]
let windowId = arguments!["windowId"] as? Int64
openUrl(uuid: uuid, url: url, options: options, headers: headers, contextMenu: contextMenu, windowId: windowId)
let initialUserScripts = arguments!["initialUserScripts"] as? [[String: Any]]
openUrl(uuid: uuid, url: url, options: options, headers: headers, contextMenu: contextMenu, windowId: windowId, initialUserScripts: initialUserScripts)
result(true)
break
case "openFile":
......@@ -61,7 +62,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let headers = arguments!["headers"] as! [String: String]
let contextMenu = arguments!["contextMenu"] as! [String: Any]
let windowId = arguments!["windowId"] as? Int64
openUrl(uuid: uuid, url: url, options: options, headers: headers, contextMenu: contextMenu, windowId: windowId)
let initialUserScripts = arguments!["initialUserScripts"] as? [[String: Any]]
openUrl(uuid: uuid, url: url, options: options, headers: headers, contextMenu: contextMenu, windowId: windowId, initialUserScripts: initialUserScripts)
result(true)
break
case "openData":
......@@ -73,7 +75,9 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let baseUrl = arguments!["baseUrl"] as! String
let contextMenu = arguments!["contextMenu"] as! [String: Any]
let windowId = arguments!["windowId"] as? Int64
openData(uuid: uuid, options: options, data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl, contextMenu: contextMenu, windowId: windowId)
let initialUserScripts = arguments!["initialUserScripts"] as? [[String: Any]]
openData(uuid: uuid, options: options, data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl,
contextMenu: contextMenu, windowId: windowId, initialUserScripts: initialUserScripts)
result(true)
break
case "openWithSystemBrowser":
......@@ -118,16 +122,16 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
}
public func openUrl(uuid: String, url: String, options: [String: Any?], headers: [String: String],
contextMenu: [String: Any], windowId: Int64?) {
contextMenu: [String: Any], windowId: Int64?, initialUserScripts: [[String: Any]]?) {
let absoluteUrl = URL(string: url)!.absoluteURL
let webViewController = prepareInAppBrowserWebViewController(options: options)
webViewController.uuid = uuid
webViewController.prepareMethodChannel()
webViewController.initURL = absoluteUrl
webViewController.initHeaders = headers
webViewController.contextMenu = contextMenu
webViewController.windowId = windowId
webViewController.initUserScripts = initialUserScripts ?? []
if webViewController.isHidden {
webViewController.view.isHidden = true
......@@ -147,17 +151,17 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
}
public func openData(uuid: String, options: [String: Any?], data: String, mimeType: String, encoding: String,
baseUrl: String, contextMenu: [String: Any], windowId: Int64?) {
baseUrl: String, contextMenu: [String: Any], windowId: Int64?, initialUserScripts: [[String: Any]]?) {
let webViewController = prepareInAppBrowserWebViewController(options: options)
webViewController.uuid = uuid
webViewController.prepareMethodChannel()
webViewController.initData = data
webViewController.initMimeType = mimeType
webViewController.initEncoding = encoding
webViewController.initBaseUrl = baseUrl
webViewController.contextMenu = contextMenu
webViewController.windowId = windowId
webViewController.initUserScripts = initialUserScripts ?? []
if webViewController.isHidden {
webViewController.view.isHidden = true
......
This diff is collapsed.
This diff is collapsed.
......@@ -90,6 +90,7 @@ class ChromeSafariBrowser {
args.putIfAbsent('optionsFallback', () => optionsFallback?.toMap() ?? {});
args.putIfAbsent('contextMenuFallback',
() => browserFallback?.contextMenu?.toMap() ?? {});
args.putIfAbsent('initialUserScriptsFallback', () => browserFallback?.initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
await _sharedChannel.invokeMethod('open', args);
this._isOpened = true;
}
......
This diff is collapsed.
import 'dart:collection';
import 'dart:typed_data';
import 'package:flutter/services.dart';
......@@ -81,7 +82,8 @@ class HeadlessInAppWebView implements WebView {
this.initialData,
this.initialHeaders,
this.initialOptions,
this.contextMenu}) {
this.contextMenu,
this.initialUserScripts}) {
uuid = uuidGenerator.v4();
webViewController = new InAppWebViewController(uuid, this);
}
......@@ -115,7 +117,8 @@ class HeadlessInAppWebView implements WebView {
'initialHeaders': this.initialHeaders,
'initialOptions': this.initialOptions?.toMap() ?? {},
'contextMenu': this.contextMenu?.toMap() ?? {},
'windowId': this.windowId
'windowId': this.windowId,
'initialUserScripts': this.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
});
await _sharedChannel.invokeMethod('createHeadlessWebView', args);
}
......@@ -168,6 +171,9 @@ class HeadlessInAppWebView implements WebView {
@override
final String? initialUrl;
@override
final UnmodifiableListView<UserScript>? initialUserScripts;
@override
final void Function(InAppWebViewController controller, String? url)?
onPageCommitVisible;
......
......@@ -19,8 +19,9 @@ class InAppBrowser {
///Context menu used by the browser. It should be set before opening the browser.
ContextMenu? contextMenu;
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
HashMap<String, JavaScriptHandlerCallback>();
///Initial list of user scripts to be loaded at start or end of a page loading.
UnmodifiableListView<UserScript>? initialUserScripts;
bool _isOpened = false;
late MethodChannel _channel;
static const MethodChannel _sharedChannel =
......@@ -33,14 +34,14 @@ class InAppBrowser {
final int? windowId;
///
InAppBrowser({this.windowId}) {
InAppBrowser({this.windowId, this.initialUserScripts}) {
uuid = uuidGenerator.v4();
this._channel =
MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$uuid');
this._channel.setMethodCallHandler(handleMethod);
_isOpened = false;
webViewController =
new InAppWebViewController.fromInAppBrowser(uuid, this._channel, this);
new InAppWebViewController.fromInAppBrowser(uuid, this._channel, this, this.initialUserScripts);
}
Future<dynamic> handleMethod(MethodCall call) async {
......@@ -79,6 +80,7 @@ class InAppBrowser {
args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId);
args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
await _sharedChannel.invokeMethod('openUrl', args);
}
......@@ -129,6 +131,7 @@ class InAppBrowser {
args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId);
args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
await _sharedChannel.invokeMethod('openFile', args);
}
......@@ -158,6 +161,7 @@ class InAppBrowser {
args.putIfAbsent('historyUrl', () => androidHistoryUrl);
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId);
args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
await _sharedChannel.invokeMethod('openData', args);
}
......
import 'dart:async';
import 'dart:collection';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
......@@ -35,6 +36,7 @@ class InAppWebView extends StatefulWidget implements WebView {
this.initialData,
this.initialHeaders = const {},
this.initialOptions,
this.initialUserScripts,
this.contextMenu,
this.onWebViewCreated,
this.onLoadStart,
......@@ -127,6 +129,9 @@ class InAppWebView extends StatefulWidget implements WebView {
@override
final String? initialUrl;
@override
final UnmodifiableListView<UserScript>? initialUserScripts;
@override
final ContextMenu? contextMenu;
......@@ -365,7 +370,8 @@ class _InAppWebViewState extends State<InAppWebView> {
'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions?.toMap() ?? {},
'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId
'windowId': widget.windowId,
'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
},
creationParamsCodec: const StandardMessageCodec(),
)
......@@ -387,7 +393,8 @@ class _InAppWebViewState extends State<InAppWebView> {
'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions?.toMap() ?? {},
'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId
'windowId': widget.windowId,
'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
},
creationParamsCodec: const StandardMessageCodec(),
);
......@@ -404,7 +411,8 @@ class _InAppWebViewState extends State<InAppWebView> {
'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions?.toMap() ?? {},
'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId
'windowId': widget.windowId,
'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
},
creationParamsCodec: const StandardMessageCodec(),
);
......
......@@ -45,6 +45,7 @@ class InAppWebViewController {
MethodChannel('com.pichillilorenzo/flutter_inappwebview_static');
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
HashMap<String, JavaScriptHandlerCallback>();
List<UserScript> _userScripts = [];
// ignore: unused_field
bool _isOpened = false;
......@@ -72,14 +73,16 @@ class InAppWebViewController {
MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id');
this._channel.setMethodCallHandler(handleMethod);
this._webview = webview;
this._userScripts = List<UserScript>.from(webview.initialUserScripts ?? []);
this._init();
}
InAppWebViewController.fromInAppBrowser(
String uuid, MethodChannel channel, InAppBrowser inAppBrowser) {
String uuid, MethodChannel channel, InAppBrowser inAppBrowser, UnmodifiableListView<UserScript>? initialUserScripts) {
this._inAppBrowserUuid = uuid;
this._channel = channel;
this._inAppBrowser = inAppBrowser;
this._userScripts = List<UserScript>.from(initialUserScripts ?? []);
this._init();
}
......@@ -1972,6 +1975,59 @@ class InAppWebViewController {
return null;
}
///Injects the specified [userScript] into the webpage’s content.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1537448-adduserscript
Future<void> addUserScript(UserScript userScript) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('userScript', () => userScript.toMap());
if (!_userScripts.contains(userScript)) {
_userScripts.add(userScript);
await _channel.invokeMethod('addUserScript', args);
}
}
///Injects the [userScripts] into the webpage’s content.
Future<void> addUserScripts(List<UserScript> userScripts) async {
for (var i = 0; i < userScripts.length; i++) {
await addUserScript(userScripts[i]);
}
}
///Removes the specified [userScript] from the webpage’s content.
///User scripts already loaded into the webpage's content cannot be removed. This will have effect only on the next page load.
///Returns `true` if [userScript] was in the list, `false` otherwise.
Future<bool> removeUserScript(UserScript userScript) async {
var index = _userScripts.indexOf(userScript);
if (index == -1) {
return false;
}
_userScripts.remove(userScript);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('index', () => index);
await _channel.invokeMethod('removeUserScript', args);
return true;
}
///Removes the [userScripts] from the webpage’s content.
///User scripts already loaded into the webpage's content cannot be removed. This will have effect only on the next page load.
Future<void> removeUserScripts(List<UserScript> userScripts) async {
for (var i = 0; i < userScripts.length; i++) {
await removeUserScript(userScripts[i]);
}
}
///Removes all the user scripts from the webpage’s content.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1536540-removealluserscripts
Future<void> removeAllUserScripts() async {
_userScripts.clear();
Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('removeAllUserScripts', args);
}
///Gets the default user agent.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context)
......
......@@ -4503,3 +4503,86 @@ class LoginRequest {
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,
].toSet();
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 =
const 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 =
const UserScriptInjectionTime._internal(1);
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 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`. Available only on iOS.
bool iosForMainFrameOnly;
UserScript({required this.source, required this.injectionTime, this.iosForMainFrameOnly = true});
Map<String, dynamic> toMap() {
return {"source": source, "injectionTime": injectionTime.toValue(), "iosForMainFrameOnly": iosForMainFrameOnly};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
\ No newline at end of file
import 'dart:collection';
import 'dart:typed_data';
import 'context_menu.dart';
......@@ -621,6 +622,11 @@ abstract class WebView {
///Context menu which contains custom menu items to be shown when [ContextMenu] is presented.
final ContextMenu? contextMenu;
///Initial list of user scripts to be loaded at start or end of a page loading.
///To add or remove user scripts, you have to use the [InAppWebViewController]'s methods such as [InAppWebViewController.addUserScript],
///[InAppWebViewController.removeUserScript], [InAppWebViewController.removeAllUserScripts], etc.
final UnmodifiableListView<UserScript>? initialUserScripts;
WebView(
{this.windowId,
this.onWebViewCreated,
......@@ -679,5 +685,6 @@ abstract class WebView {
this.initialData,
this.initialHeaders,
this.initialOptions,
this.contextMenu});
this.contextMenu,
this.initialUserScripts});
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment