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))
......
......@@ -14,6 +14,14 @@
A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
## API Reference
See the online [API Reference](https://pub.dartlang.org/documentation/flutter_inappwebview/latest/) to get the **full documentation**.
Note that the API shown in this `README.md` file shows only a **part** of the documentation and, also, that conforms to the **GitHub master branch only**!
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).
## 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)
......@@ -213,14 +221,6 @@ Classes:
- [HttpAuthCredentialDatabase](#httpauthcredentialdatabase-class): This class implements a singleton object (shared instance) which manages the shared HTTP auth credentials cache.
- [WebStorageManager](#webstoragemanager-class): This class implements a singleton object (shared instance) which manages the web storage used by WebView instances.
## API Reference
See the online [API Reference](https://pub.dartlang.org/documentation/flutter_inappwebview/latest/) to get the full documentation.
The API showed in this `README.md` file shows only a part of the documentation that conforms to the master branch only.
So, here you could have methods, options, and events that aren't published yet.
If you need a specific version, change the **GitHub branch** to your version or use the **online API Reference** (recommended).
### Load a file inside `assets` folder
To be able to load your local files (assets, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found!
......@@ -389,60 +389,67 @@ Screenshots:
##### `InAppWebViewController` Cross-platform methods
* `getUrl`: Gets the URL for the current page.
* `getTitle`: Gets the title for the current page.
* `getProgress`: Gets the progress for the current page. The progress value is between 0 and 100.
* `getHtml`: Gets the content html of the page.
* `getFavicons`: Gets the list of all favicons for the current page.
* `loadUrl({required String url, Map<String, String> headers = const {}})`: Loads the given url with optional headers specified as a map from name to value.
* `postUrl({required String url, required Uint8List postData})`: Loads the given url with postData using `POST` method into this WebView.
* `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<String, String> headers = const {}})`: Loads the given `assetFilePath` with optional headers specified as a map from name to value.
* `reload`: Reloads the WebView.
* `goBack`: Goes back in the history of the WebView.
* `addJavaScriptHandler({required String handlerName, required JavaScriptHandlerCallback callback})`: Adds a JavaScript message handler callback that listen to post messages sent from JavaScript by the handler with name `handlerName`.
* `addUserScript(UserScript userScript)`: Injects the specified `userScript` into the webpage’s content.
* `addUserScripts(List<UserScript> userScripts)`: Injects the `userScripts` into the webpage’s content.
* `canGoBackOrForward({required int steps})`: Returns a boolean value indicating whether the WebView can go back or forward the given number of steps. Steps is negative if backward and positive if forward.
* `canGoBack`: Returns a boolean value indicating whether the WebView can move backward.
* `goForward`: Goes forward in the history of the WebView.
* `canGoForward`: Returns a boolean value indicating whether the WebView can move forward.
* `goBackOrForward({required int steps})`: 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.
* `canGoBackOrForward({required int steps})`: Returns a boolean value indicating whether the WebView can go back or forward the given number of steps. Steps is negative if backward and positive if forward.
* `goTo({required WebHistoryItem historyItem})`: Navigates to a `WebHistoryItem` from the back-forward `WebHistory.list` and sets it as the current item.
* `isLoading`: Check if the WebView instance is in a loading state.
* `stopLoading`: Stops the WebView from loading.
* `evaluateJavascript({required String source})`: Evaluates JavaScript code into the WebView and returns the result of the evaluation.
* `injectJavascriptFileFromUrl({required String urlFile})`: Injects an external JavaScript file into the WebView from a defined url.
* `injectJavascriptFileFromAsset({required String assetFilePath})`: Injects a JavaScript file into the WebView from the flutter assets directory.
* `injectCSSCode({required String source})`: Injects CSS into the WebView.
* `injectCSSFileFromUrl({required String urlFile})`: Injects an external CSS file into the WebView from a defined url.
* `injectCSSFileFromAsset({required String assetFilePath})`: Injects a CSS file into the WebView from the flutter assets directory.
* `addJavaScriptHandler({required String handlerName, required JavaScriptHandlerCallback callback})`: Adds a JavaScript message handler callback that listen to post messages sent from JavaScript by the handler with name `handlerName`.
* `removeJavaScriptHandler({required String handlerName})`: Removes a JavaScript message handler previously added with the `addJavaScriptHandler()` associated to `handlerName` key.
* `takeScreenshot`: Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it.
* `setOptions({required InAppWebViewGroupOptions options})`: Sets the WebView options with the new options and evaluates them.
* `getOptions`: Gets the current WebView options. Returns the options with `null` value if they are not set yet.
* `getCopyBackForwardList`: Gets the `WebHistory` for this WebView. This contains the back/forward list for use in querying each item in the history stack.
* `clearCache`: Clears all the webview's cache.
* `clearFocus`: Clears the current focus. It will clear also, for example, the current text selection.
* `clearMatches`: Clears the highlighting surrounding text matches created by `findAllAsync()`.
* `evaluateJavascript({required String source})`: Evaluates JavaScript code into the WebView and returns the result of the evaluation.
* `findAllAsync({required String find})`: Finds all instances of find on the page and highlights them. Notifies `onFindResultReceived` listener.
* `findNext({required bool forward})`: Highlights and scrolls to the next match found by `findAllAsync()`. Notifies `onFindResultReceived` listener.
* `clearMatches`: Clears the highlighting surrounding text matches created by `findAllAsync()`.
* `getTRexRunnerHtml`: Gets the html (with javascript) of the Chromium's t-rex runner game. Used in combination with `getTRexRunnerCss()`.
* `getTRexRunnerCss`: Gets the css of the Chromium's t-rex runner game. Used in combination with `getTRexRunnerHtml()`.
* `scrollTo({required int x, required int y, bool animated = false})`: Scrolls the WebView to the position.
* `scrollBy({required int x, required int y, bool animated = false})`: Moves the scrolled position of the WebView.
* `pauseTimers`: On Android, it pauses all layout, parsing, and JavaScript timers for all WebViews. This is a global requests, not restricted to just this WebView. This can be useful if the application has been paused. On iOS, it is restricted to just this WebView.
* `resumeTimers`: On Android, it resumes all layout, parsing, and JavaScript timers for all WebViews. This will resume dispatching all timers. On iOS, it resumes all layout, parsing, and JavaScript timers to just this WebView.
* `printCurrentPage`: Prints the current page.
* `getScale`: Gets the current scale of this WebView.
* `getSelectedText`: Gets the selected text.
* `getCertificate`: Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).
* `getContentHeight`: Gets the height of the HTML content.
* `getCopyBackForwardList`: Gets the `WebHistory` for this WebView. This contains the back/forward list for use in querying each item in the history stack.
* `getFavicons`: Gets the list of all favicons for the current page.
* `getHitTestResult`: Gets the hit result for hitting an HTML elements.
* `clearFocus`: Clears the current focus. It will clear also, for example, the current text selection.
* `setContextMenu(ContextMenu contextMenu)`: Sets or updates the WebView context menu to be used next time it will appear.
* `requestFocusNodeHref`: Requests the anchor or image element URL at the last tapped point.
* `requestImageRef`: Requests the URL of the image last touched by the user.
* `getHtml`: Gets the content html of the page.
* `getMetaTags`: Returns the list of `<meta>` tags of the current WebView.
* `getMetaThemeColor`: Returns an instance of `Color` representing the `content` value of the `<meta name="theme-color" content="">` tag of the current WebView, if available, otherwise `null`.
* `getOptions`: Gets the current WebView options. Returns the options with `null` value if they are not set yet.
* `getProgress`: Gets the progress for the current page. The progress value is between 0 and 100.
* `getScale`: Gets the current scale of this WebView.
* `getScrollX`: Returns the scrolled left position of the current WebView.
* `getScrollY`: Returns the scrolled top position of the current WebView.
* `getCertificate`: Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).
* `getSelectedText`: Gets the selected text.
* `getTRexRunnerCss`: Gets the css of the Chromium's t-rex runner game. Used in combination with `getTRexRunnerHtml()`.
* `getTRexRunnerHtml`: Gets the html (with javascript) of the Chromium's t-rex runner game. Used in combination with `getTRexRunnerCss()`.
* `getTitle`: Gets the title for the current page.
* `getUrl`: Gets the URL for the current page.
* `goBackOrForward({required int steps})`: 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.
* `goBack`: Goes back in the history of the WebView.
* `goForward`: Goes forward in the history of the WebView.
* `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.
* `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.
* `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<String, String> headers = const {}})`: Loads the given `assetFilePath` with optional headers specified as a map from name to value.
* `loadUrl({required String url, Map<String, String> headers = const {}})`: Loads the given url with optional headers specified as a map from name to value.
* `pauseTimers`: On Android, it pauses all layout, parsing, and JavaScript timers for all WebViews. This is a global requests, not restricted to just this WebView. This can be useful if the application has been paused. On iOS, it is restricted to just this WebView.
* `postUrl({required String url, required Uint8List postData})`: Loads the given url with postData using `POST` method into this WebView.
* `printCurrentPage`: Prints the current page.
* `reload`: Reloads the WebView.
* `removeAllUserScripts()`: Removes all the user scripts from the webpage’s content.
* `removeJavaScriptHandler({required String handlerName})`: Removes a JavaScript message handler previously added with the `addJavaScriptHandler()` associated to `handlerName` key.
* `removeUserScript(UserScript userScript)`: Removes the specified `userScript` from the webpage’s content.
* `removeUserScripts(List<UserScript> userScripts)`: Removes the `userScripts` from the webpage’s content.
* `requestFocusNodeHref`: Requests the anchor or image element URL at the last tapped point.
* `requestImageRef`: Requests the URL of the image last touched by the user.
* `resumeTimers`: On Android, it resumes all layout, parsing, and JavaScript timers for all WebViews. This will resume dispatching all timers. On iOS, it resumes all layout, parsing, and JavaScript timers to just this WebView.
* `scrollBy({required int x, required int y, bool animated = false})`: Moves the scrolled position of the WebView.
* `scrollTo({required int x, required int y, bool animated = false})`: Scrolls the WebView to the position.
* `setContextMenu(ContextMenu contextMenu)`: Sets or updates the WebView context menu to be used next time it will appear.
* `setOptions({required InAppWebViewGroupOptions options})`: Sets the WebView options with the new options and evaluates them.
* `stopLoading`: Stops the WebView from loading.
* `takeScreenshot`: Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it.
* `zoomBy`: Performs a zoom operation in this WebView.
* `static getDefaultUserAgent`: Gets the default user agent.
##### `InAppWebViewController.webStorage`
......
......@@ -3,7 +3,6 @@ package com.pichillilorenzo.flutter_inappwebview.InAppBrowser;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
......@@ -13,22 +12,19 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.SearchView;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewChromeClient;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;
......@@ -37,10 +33,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class InAppBrowserActivity extends AppCompatActivity implements MethodChannel.MethodCallHandler {
public class InAppBrowserActivity extends AppCompatActivity {
static final String LOG_TAG = "InAppBrowserActivity";
public MethodChannel channel;
......@@ -56,6 +51,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
public boolean isHidden = false;
public String fromActivity;
public List<ActivityResultListener> activityResultListeners = new ArrayList<>();
public InAppWebViewMethodHandler methodCallDelegate;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -70,7 +66,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
windowId = b.getInt("windowId");
channel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappbrowser_" + uuid);
channel.setMethodCallHandler(this);
setContentView(R.layout.activity_web_view);
......@@ -79,10 +74,14 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
webView.inAppBrowserActivity = this;
webView.channel = channel;
methodCallDelegate = new InAppWebViewMethodHandler(webView);
channel.setMethodCallHandler(methodCallDelegate);
fromActivity = b.getString("fromActivity");
HashMap<String, Object> optionsMap = (HashMap<String, Object>) b.getSerializable("options");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) b.getSerializable("contextMenu");
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) b.getSerializable("initialUserScripts");
options = new InAppBrowserOptions();
options.parse(optionsMap);
......@@ -91,6 +90,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
webViewOptions.parse(optionsMap);
webView.options = webViewOptions;
webView.contextMenu = contextMenu;
webView.userScripts = initialUserScripts;
actionBar = getSupportActionBar();
......@@ -124,298 +124,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
channel.invokeMethod("onBrowserCreated", obj);
}
@Override
public void onMethodCall(final MethodCall call, final MethodChannel.Result result) {
switch (call.method) {
case "getUrl":
result.success(getUrl());
break;
case "getTitle":
result.success(getWebViewTitle());
break;
case "getProgress":
result.success(getProgress());
break;
case "loadUrl":
{
String url = (String) call.argument("url");
Map<String, String> headers = (Map<String, String>) call.argument("headers");
if (headers != null)
loadUrl(url, headers, result);
else
loadUrl(url, result);
}
break;
case "postUrl":
postUrl((String) call.argument("url"), (byte[]) call.argument("postData"), result);
break;
case "loadData":
{
String data = (String) call.argument("data");
String mimeType = (String) call.argument("mimeType");
String encoding = (String) call.argument("encoding");
String baseUrl = (String) call.argument("baseUrl");
String historyUrl = (String) call.argument("historyUrl");
loadData(data, mimeType, encoding, baseUrl, historyUrl, result);
}
break;
case "loadFile":
{
String url = (String) call.argument("url");
Map<String, String> headers = (Map<String, String>) call.argument("headers");
if (headers != null)
loadFile(url, headers, result);
else
loadFile(url, result);
}
break;
case "close":
close(result);
break;
case "evaluateJavascript":
{
String source = (String) call.argument("source");
evaluateJavascript(source, result);
}
break;
case "injectJavascriptFileFromUrl":
{
String urlFile = (String) call.argument("urlFile");
injectJavascriptFileFromUrl(urlFile);
}
result.success(true);
break;
case "injectCSSCode":
{
String source = (String) call.argument("source");
injectCSSCode(source);
}
result.success(true);
break;
case "injectCSSFileFromUrl":
{
String urlFile = (String) call.argument("urlFile");
injectCSSFileFromUrl(urlFile);
}
result.success(true);
break;
case "show":
show();
result.success(true);
break;
case "hide":
hide();
result.success(true);
break;
case "reload":
reload();
result.success(true);
break;
case "goBack":
goBack();
result.success(true);
break;
case "canGoBack":
result.success(canGoBack());
break;
case "goForward":
goForward();
result.success(true);
break;
case "canGoForward":
result.success(canGoForward());
break;
case "goBackOrForward":
goBackOrForward((Integer) call.argument("steps"));
result.success(true);
break;
case "canGoBackOrForward":
result.success(canGoBackOrForward((Integer) call.argument("steps")));
break;
case "stopLoading":
stopLoading();
result.success(true);
break;
case "isLoading":
result.success(isLoading());
break;
case "isHidden":
result.success(isHidden);
break;
case "takeScreenshot":
takeScreenshot(result);
break;
case "setOptions":
{
String optionsType = (String) call.argument("optionsType");
switch (optionsType){
case "InAppBrowserOptions":
InAppBrowserOptions inAppBrowserOptions = new InAppBrowserOptions();
HashMap<String, Object> inAppBrowserOptionsMap = (HashMap<String, Object>) call.argument("options");
inAppBrowserOptions.parse(inAppBrowserOptionsMap);
setOptions(inAppBrowserOptions, inAppBrowserOptionsMap);
break;
default:
result.error(LOG_TAG, "Options " + optionsType + " not available.", null);
}
}
result.success(true);
break;
case "getOptions":
result.success(getOptions());
break;
case "getCopyBackForwardList":
result.success(getCopyBackForwardList());
break;
case "startSafeBrowsing":
startSafeBrowsing(result);
break;
case "clearCache":
clearCache();
result.success(true);
break;
case "clearSslPreferences":
clearSslPreferences();
result.success(true);
break;
case "findAllAsync":
String find = (String) call.argument("find");
findAllAsync(find);
result.success(true);
break;
case "findNext":
Boolean forward = (Boolean) call.argument("forward");
findNext(forward, result);
break;
case "clearMatches":
clearMatches(result);
break;
case "scrollTo":
{
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
Boolean animated = (Boolean) call.argument("animated");
scrollTo(x, y, animated);
}
result.success(true);
break;
case "scrollBy":
{
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
Boolean animated = (Boolean) call.argument("animated");
scrollBy(x, y, animated);
}
result.success(true);
break;
case "pause":
onPauseWebView();
result.success(true);
break;
case "resume":
onResumeWebView();
result.success(true);
break;
case "pauseTimers":
pauseTimers();
result.success(true);
break;
case "resumeTimers":
resumeTimers();
result.success(true);
break;
case "printCurrentPage":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
printCurrentPage();
}
result.success(true);
break;
case "getContentHeight":
result.success(getContentHeight());
break;
case "zoomBy":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Float zoomFactor = (Float) call.argument("zoomFactor");
zoomBy(zoomFactor);
}
result.success(true);
break;
case "getOriginalUrl":
result.success(getOriginalUrl());
break;
case "getScale":
result.success(getScale());
break;
case "getSelectedText":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getSelectedText(result);
} else {
result.success(null);
}
break;
case "getHitTestResult":
result.success(getHitTestResult());
break;
case "pageDown":
{
boolean bottom = (boolean) call.argument("bottom");
result.success(pageDown(bottom));
}
break;
case "pageUp":
{
boolean top = (boolean) call.argument("top");
result.success(pageUp(top));
}
break;
case "saveWebArchive":
{
String basename = (String) call.argument("basename");
boolean autoname = (boolean) call.argument("autoname");
saveWebArchive(basename, autoname, result);
}
break;
case "zoomIn":
result.success(zoomIn());
break;
case "zoomOut":
result.success(zoomOut());
break;
case "clearFocus":
clearFocus();
result.success(true);
break;
case "setContextMenu":
{
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
setContextMenu(contextMenu);
}
result.success(true);
break;
case "requestFocusNodeHref":
result.success(requestFocusNodeHref());
break;
case "requestImageRef":
result.success(requestImageRef());
break;
case "getScrollX":
result.success(getScrollX());
break;
case "getScrollY":
result.success(getScrollY());
break;
case "getCertificate":
result.success(getCertificate());
break;
case "clearHistory":
clearHistory();
result.success(true);
break;
default:
result.notImplemented();
}
}
private void prepareView() {
webView.prepare();
......@@ -505,72 +213,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
return true;
}
public String getUrl() {
if (webView != null)
return webView.getUrl();
return null;
}
public String getWebViewTitle() {
if (webView != null)
return webView.getTitle();
return null;
}
public Integer getProgress() {
if (webView != null)
return webView.getProgress();
return null;
}
public void loadUrl(String url, MethodChannel.Result result) {
if (webView != null) {
webView.loadUrl(url, result);
} else {
result.error(LOG_TAG, "webView is null", null);
}
}
public void loadUrl(String url, Map<String, String> headers, MethodChannel.Result result) {
if (webView != null) {
webView.loadUrl(url, headers, result);
} else {
result.error(LOG_TAG, "webView is null", null);
}
}
public void postUrl(String url, byte[] postData, MethodChannel.Result result) {
if (webView != null) {
webView.postUrl(url, postData, result);
} else {
result.error(LOG_TAG, "webView is null", null);
}
}
public void loadData(String data, String mimeType, String encoding, String baseUrl, String historyUrl, MethodChannel.Result result) {
if (webView != null) {
webView.loadData(data, mimeType, encoding, baseUrl, historyUrl, result);
} else {
result.error(LOG_TAG, "webView is null", null);
}
}
public void loadFile(String url, MethodChannel.Result result) {
if (webView != null) {
webView.loadFile(url, result);
} else {
result.error(LOG_TAG, "webView is null", null);
}
}
public void loadFile(String url, Map<String, String> headers, MethodChannel.Result result) {
if (webView != null) {
webView.loadFile(url, headers, result);
} else {
result.error(LOG_TAG, "webView is null", null);
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
if (canGoBack())
......@@ -620,17 +262,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
return false;
}
public void goBackOrForward(int steps) {
if (webView != null && canGoBackOrForward(steps))
webView.goBackOrForward(steps);
}
public boolean canGoBackOrForward(int steps) {
if (webView != null)
return webView.canGoBackOrForward(steps);
return false;
}
public void hide() {
try {
isHidden = true;
......@@ -650,17 +281,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
startActivityIfNeeded(openActivity, 0);
}
public void stopLoading() {
if (webView != null)
webView.stopLoading();
}
public boolean isLoading() {
if (webView != null)
return webView.isLoading;
return false;
}
public void goBackButtonClicked(MenuItem item) {
goBack();
}
......@@ -684,13 +304,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
close(null);
}
public void takeScreenshot(MethodChannel.Result result) {
if (webView != null)
webView.takeScreenshot(result);
else
result.success(null);
}
public void setOptions(InAppBrowserOptions newOptions, HashMap<String, Object> newOptionsMap) {
InAppWebViewOptions newInAppWebViewOptions = new InAppWebViewOptions();
......@@ -747,246 +360,13 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
return optionsMap;
}
public void evaluateJavascript(String source, MethodChannel.Result result) {
if (webView != null)
webView.evaluateJavascript(source, result);
else
result.success("");
}
public void injectJavascriptFileFromUrl(String urlFile) {
if (webView != null)
webView.injectJavascriptFileFromUrl(urlFile);
}
public void injectCSSCode(String source) {
if (webView != null)
webView.injectCSSCode(source);
}
public void injectCSSFileFromUrl(String urlFile) {
if (webView != null)
webView.injectCSSFileFromUrl(urlFile);
}
public HashMap<String, Object> getCopyBackForwardList() {
if (webView != null)
return webView.getCopyBackForwardList();
return null;
}
public void startSafeBrowsing(final MethodChannel.Result result) {
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 &&
WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
WebViewCompat.startSafeBrowsing(webView.getContext(), new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean success) {
result.success(success);
}
});
}
else {
result.success(false);
}
}
public void clearCache() {
if (webView != null)
webView.clearAllCache();
}
public void clearSslPreferences() {
if (webView != null)
webView.clearSslPreferences();
}
public void findAllAsync(String find) {
if (webView != null)
webView.findAllAsync(find);
}
public void findNext(Boolean forward, MethodChannel.Result result) {
if (webView != null) {
webView.findNext(forward);
result.success(true);
}
else
result.success(false);
}
public void clearMatches(MethodChannel.Result result) {
if (webView != null) {
webView.clearMatches();
result.success(true);
}
else
result.success(false);
}
public void scrollTo(Integer x, Integer y, Boolean animated) {
if (webView != null)
webView.scrollTo(x, y, animated);
}
public void scrollBy(Integer x, Integer y, Boolean animated) {
if (webView != null)
webView.scrollBy(x, y, animated);
}
public void onPauseWebView() {
if (webView != null)
webView.onPause();
}
public void onResumeWebView() {
if (webView != null)
webView.onResume();
}
public void pauseTimers() {
if (webView != null)
webView.pauseTimers();
}
public void resumeTimers() {
if (webView != null)
webView.resumeTimers();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void printCurrentPage() {
if (webView != null)
webView.printCurrentPage();
}
public Integer getContentHeight() {
if (webView != null)
return webView.getContentHeight();
return null;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void zoomBy(Float zoomFactor) {
if (webView != null)
webView.zoomBy(zoomFactor);
}
public String getOriginalUrl() {
if (webView != null)
return webView.getOriginalUrl();
return null;
}
public Float getScale() {
if (webView != null)
return webView.getUpdatedScale();
return null;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void getSelectedText(MethodChannel.Result result) {
if (webView != null)
webView.getSelectedText(result);
else
result.success(null);
}
public Map<String, Object> getHitTestResult() {
if (webView != null) {
WebView.HitTestResult hitTestResult = webView.getHitTestResult();
Map<String, Object> obj = new HashMap<>();
obj.put("type", hitTestResult.getType());
obj.put("extra", hitTestResult.getExtra());
return obj;
}
return null;
}
public boolean pageDown(boolean bottom) {
if (webView != null)
return webView.pageDown(bottom);
return false;
}
public boolean pageUp(boolean top) {
if (webView != null)
return webView.pageUp(top);
return false;
}
public void saveWebArchive(String basename, boolean autoname, final MethodChannel.Result result) {
if (webView != null) {
webView.saveWebArchive(basename, autoname, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
result.success(value);
}
});
} else {
result.success(null);
}
}
public boolean zoomIn() {
if (webView != null)
return webView.zoomIn();
return false;
}
public boolean zoomOut() {
if (webView != null)
return webView.zoomOut();
return false;
}
public void clearFocus() {
if (webView != null)
webView.clearFocus();
}
public void setContextMenu(Map<String, Object> contextMenu) {
if (webView != null)
webView.contextMenu = contextMenu;
}
public Map<String, Object> requestFocusNodeHref() {
if (webView != null)
return webView.requestFocusNodeHref();
return null;
}
public Map<String, Object> requestImageRef() {
if (webView != null)
return webView.requestImageRef();
return null;
}
public Integer getScrollX() {
if (webView != null)
return webView.getScrollX();
return null;
}
public Integer getScrollY() {
if (webView != null)
return webView.getScrollY();
return null;
}
public Map<String, Object> getCertificate() {
if (webView != null)
return webView.getCertificateMap();
return null;
}
public void clearHistory() {
if (webView != null)
webView.clearHistory();
}
public void dispose() {
channel.setMethodCallHandler(null);
activityResultListeners.clear();
if (methodCallDelegate != null) {
methodCallDelegate.dispose();
methodCallDelegate = null;
}
if (webView != null) {
if (Shared.activityPluginBinding != null) {
Shared.activityPluginBinding.removeActivityResultListener(webView.inAppWebViewChromeClient);
......
......@@ -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);
}
......
......@@ -2,48 +2,37 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.embedding.android.FlutterView;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.platform.PlatformView;
import static io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import static io.flutter.plugin.common.MethodChannel.Result;
public class FlutterWebView implements PlatformView, MethodCallHandler {
public class FlutterWebView implements PlatformView {
static final String LOG_TAG = "IAWFlutterWebView";
public InAppWebView webView;
public final MethodChannel channel;
public InAppWebViewMethodHandler methodCallDelegate;
public FlutterWebView(BinaryMessenger messenger, final Context context, Object id, HashMap<String, Object> params, View containerView) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_" + id);
channel.setMethodCallHandler(this);
DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
......@@ -56,6 +45,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
Map<String, Object> initialOptions = (Map<String, Object>) params.get("initialOptions");
Map<String, Object> contextMenu = (Map<String, Object>) params.get("contextMenu");
Integer windowId = (Integer) params.get("windowId");
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) params.get("initialUserScripts");
InAppWebViewOptions options = new InAppWebViewOptions();
options.parse(initialOptions);
......@@ -67,9 +57,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
"- See the official wiki here: https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects\n\n\n");
}
webView = new InAppWebView(context, this, id, windowId, options, contextMenu, containerView);
webView = new InAppWebView(context, this, id, windowId, options, contextMenu, containerView, initialUserScripts);
displayListenerProxy.onPostWebViewInitialization(displayManager);
methodCallDelegate = new InAppWebViewMethodHandler(webView);
channel.setMethodCallHandler(methodCallDelegate);
webView.prepare();
if (windowId != null) {
......@@ -114,392 +107,13 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
return webView;
}
@Override
public void onMethodCall(MethodCall call, final Result result) {
switch (call.method) {
case "getUrl":
result.success((webView != null) ? webView.getUrl() : null);
break;
case "getTitle":
result.success((webView != null) ? webView.getTitle() : null);
break;
case "getProgress":
result.success((webView != null) ? webView.getProgress() : null);
break;
case "loadUrl":
if (webView != null)
webView.loadUrl((String) call.argument("url"), (Map<String, String>) call.argument("headers"), result);
else
result.success(false);
break;
case "postUrl":
if (webView != null)
webView.postUrl((String) call.argument("url"), (byte[]) call.argument("postData"), result);
else
result.success(false);
break;
case "loadData":
{
String data = (String) call.argument("data");
String mimeType = (String) call.argument("mimeType");
String encoding = (String) call.argument("encoding");
String baseUrl = (String) call.argument("baseUrl");
String historyUrl = (String) call.argument("historyUrl");
if (webView != null)
webView.loadData(data, mimeType, encoding, baseUrl, historyUrl, result);
else
result.success(false);
}
break;
case "loadFile":
if (webView != null)
webView.loadFile((String) call.argument("url"), (Map<String, String>) call.argument("headers"), result);
else
result.success(false);
break;
case "evaluateJavascript":
if (webView != null) {
String source = (String) call.argument("source");
webView.evaluateJavascript(source, result);
}
else {
result.success("");
}
break;
case "injectJavascriptFileFromUrl":
if (webView != null) {
String urlFile = (String) call.argument("urlFile");
webView.injectJavascriptFileFromUrl(urlFile);
}
result.success(true);
break;
case "injectCSSCode":
if (webView != null) {
String source = (String) call.argument("source");
webView.injectCSSCode(source);
}
result.success(true);
break;
case "injectCSSFileFromUrl":
if (webView != null) {
String urlFile = (String) call.argument("urlFile");
webView.injectCSSFileFromUrl(urlFile);
}
result.success(true);
break;
case "reload":
if (webView != null)
webView.reload();
result.success(true);
break;
case "goBack":
if (webView != null)
webView.goBack();
result.success(true);
break;
case "canGoBack":
result.success((webView != null) && webView.canGoBack());
break;
case "goForward":
if (webView != null)
webView.goForward();
result.success(true);
break;
case "canGoForward":
result.success((webView != null) && webView.canGoForward());
break;
case "goBackOrForward":
if (webView != null)
webView.goBackOrForward((Integer) call.argument("steps"));
result.success(true);
break;
case "canGoBackOrForward":
result.success((webView != null) && webView.canGoBackOrForward((Integer) call.argument("steps")));
break;
case "stopLoading":
if (webView != null)
webView.stopLoading();
result.success(true);
break;
case "isLoading":
result.success((webView != null) && webView.isLoading());
break;
case "takeScreenshot":
if (webView != null)
webView.takeScreenshot(result);
else
result.success(null);
break;
case "setOptions":
if (webView != null) {
InAppWebViewOptions inAppWebViewOptions = new InAppWebViewOptions();
HashMap<String, Object> inAppWebViewOptionsMap = (HashMap<String, Object>) call.argument("options");
inAppWebViewOptions.parse(inAppWebViewOptionsMap);
webView.setOptions(inAppWebViewOptions, inAppWebViewOptionsMap);
}
result.success(true);
break;
case "getOptions":
result.success((webView != null) ? webView.getOptions() : null);
break;
case "getCopyBackForwardList":
result.success((webView != null) ? webView.getCopyBackForwardList() : null);
break;
case "startSafeBrowsing":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 &&
WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
WebViewCompat.startSafeBrowsing(webView.getContext(), new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean success) {
result.success(success);
}
});
}
else {
result.success(false);
}
break;
case "clearCache":
if (webView != null)
webView.clearAllCache();
result.success(true);
break;
case "clearSslPreferences":
if (webView != null)
webView.clearSslPreferences();
result.success(true);
break;
case "findAllAsync":
if (webView != null) {
String find = (String) call.argument("find");
webView.findAllAsync(find);
}
result.success(true);
break;
case "findNext":
if (webView != null) {
Boolean forward = (Boolean) call.argument("forward");
webView.findNext(forward);
result.success(true);
} else {
result.success(false);
}
break;
case "clearMatches":
if (webView != null) {
webView.clearMatches();
result.success(true);
} else {
result.success(false);
}
break;
case "scrollTo":
if (webView != null) {
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
Boolean animated = (Boolean) call.argument("animated");
webView.scrollTo(x, y, animated);
}
result.success(true);
break;
case "scrollBy":
if (webView != null) {
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
Boolean animated = (Boolean) call.argument("animated");
webView.scrollBy(x, y, animated);
}
result.success(true);
break;
case "pause":
if (webView != null) {
webView.onPause();
result.success(true);
} else {
result.success(false);
}
break;
case "resume":
if (webView != null) {
webView.onResume();
result.success(true);
} else {
result.success(false);
}
break;
case "pauseTimers":
if (webView != null) {
webView.pauseTimers();
result.success(true);
} else {
result.success(false);
}
break;
case "resumeTimers":
if (webView != null) {
webView.resumeTimers();
result.success(true);
} else {
result.success(false);
}
break;
case "printCurrentPage":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.printCurrentPage();
result.success(true);
} else {
result.success(false);
}
break;
case "getContentHeight":
result.success((webView != null) ? webView.getContentHeight() : null);
break;
case "zoomBy":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
double zoomFactor = (double) call.argument("zoomFactor");
webView.zoomBy((float) zoomFactor);
result.success(true);
} else {
result.success(false);
}
break;
case "getOriginalUrl":
result.success((webView != null) ? webView.getOriginalUrl() : null);
break;
case "getScale":
result.success((webView != null) ? webView.getUpdatedScale() : null);
break;
case "getSelectedText":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.getSelectedText(result);
} else {
result.success(null);
}
break;
case "getHitTestResult":
if (webView != null) {
WebView.HitTestResult hitTestResult = webView.getHitTestResult();
Map<String, Object> obj = new HashMap<>();
obj.put("type", hitTestResult.getType());
obj.put("extra", hitTestResult.getExtra());
result.success(obj);
} else {
result.success(null);
}
break;
case "pageDown":
if (webView != null) {
boolean bottom = (boolean) call.argument("bottom");
result.success(webView.pageDown(bottom));
} else {
result.success(false);
}
break;
case "pageUp":
if (webView != null) {
boolean top = (boolean) call.argument("top");
result.success(webView.pageUp(top));
} else {
result.success(false);
}
break;
case "saveWebArchive":
if (webView != null) {
String basename = (String) call.argument("basename");
boolean autoname = (boolean) call.argument("autoname");
webView.saveWebArchive(basename, autoname, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
result.success(value);
}
});
} else {
result.success(null);
}
break;
case "zoomIn":
if (webView != null) {
result.success(webView.zoomIn());
} else {
result.success(false);
}
break;
case "zoomOut":
if (webView != null) {
result.success(webView.zoomOut());
} else {
result.success(false);
}
break;
case "clearFocus":
if (webView != null) {
webView.clearFocus();
result.success(true);
} else {
result.success(false);
}
break;
case "setContextMenu":
if (webView != null) {
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
webView.contextMenu = contextMenu;
result.success(true);
} else {
result.success(false);
}
break;
case "requestFocusNodeHref":
if (webView != null) {
result.success(webView.requestFocusNodeHref());
} else {
result.success(null);
}
break;
case "requestImageRef":
if (webView != null) {
result.success(webView.requestImageRef());
} else {
result.success(null);
}
break;
case "getScrollX":
if (webView != null) {
result.success(webView.getScrollX());
} else {
result.success(null);
}
break;
case "getScrollY":
if (webView != null) {
result.success(webView.getScrollY());
} else {
result.success(null);
}
break;
case "getCertificate":
if (webView != null) {
result.success(webView.getCertificateMap());
} else {
result.success(null);
}
break;
case "clearHistory":
if (webView != null) {
webView.clearHistory();
result.success(true);
} else {
result.success(false);
}
break;
default:
result.notImplemented();
}
}
@Override
public void dispose() {
channel.setMethodCallHandler(null);
if (methodCallDelegate != null) {
methodCallDelegate.dispose();
methodCallDelegate = null;
}
if (webView != null) {
webView.inAppWebViewChromeClient.dispose();
webView.inAppWebViewClient.dispose();
......
......@@ -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);
......
package com.pichillilorenzo.flutter_inappwebview;
import android.os.Build;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserOptions;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandler {
static final String LOG_TAG = "IAWMethodHandler";
public InAppWebView webView;
public InAppWebViewMethodHandler(InAppWebView webView) {
this.webView = webView;
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull final MethodChannel.Result result) {
switch (call.method) {
case "getUrl":
result.success((webView != null) ? webView.getUrl() : null);
break;
case "getTitle":
result.success((webView != null) ? webView.getTitle() : null);
break;
case "getProgress":
result.success((webView != null) ? webView.getProgress() : null);
break;
case "loadUrl":
if (webView != null)
webView.loadUrl((String) call.argument("url"), (Map<String, String>) call.argument("headers"), result);
else
result.success(false);
break;
case "postUrl":
if (webView != null)
webView.postUrl((String) call.argument("url"), (byte[]) call.argument("postData"), result);
else
result.success(false);
break;
case "loadData":
{
String data = (String) call.argument("data");
String mimeType = (String) call.argument("mimeType");
String encoding = (String) call.argument("encoding");
String baseUrl = (String) call.argument("baseUrl");
String historyUrl = (String) call.argument("historyUrl");
if (webView != null)
webView.loadData(data, mimeType, encoding, baseUrl, historyUrl, result);
else
result.success(false);
}
break;
case "loadFile":
if (webView != null)
webView.loadFile((String) call.argument("url"), (Map<String, String>) call.argument("headers"), result);
else
result.success(false);
break;
case "evaluateJavascript":
if (webView != null) {
String source = (String) call.argument("source");
webView.evaluateJavascript(source, result);
}
else {
result.success(null);
}
break;
case "injectJavascriptFileFromUrl":
if (webView != null) {
String urlFile = (String) call.argument("urlFile");
webView.injectJavascriptFileFromUrl(urlFile);
}
result.success(true);
break;
case "injectCSSCode":
if (webView != null) {
String source = (String) call.argument("source");
webView.injectCSSCode(source);
}
result.success(true);
break;
case "injectCSSFileFromUrl":
if (webView != null) {
String urlFile = (String) call.argument("urlFile");
webView.injectCSSFileFromUrl(urlFile);
}
result.success(true);
break;
case "reload":
if (webView != null)
webView.reload();
result.success(true);
break;
case "goBack":
if (webView != null)
webView.goBack();
result.success(true);
break;
case "canGoBack":
result.success((webView != null) && webView.canGoBack());
break;
case "goForward":
if (webView != null)
webView.goForward();
result.success(true);
break;
case "canGoForward":
result.success((webView != null) && webView.canGoForward());
break;
case "goBackOrForward":
if (webView != null)
webView.goBackOrForward((Integer) call.argument("steps"));
result.success(true);
break;
case "canGoBackOrForward":
result.success((webView != null) && webView.canGoBackOrForward((Integer) call.argument("steps")));
break;
case "stopLoading":
if (webView != null)
webView.stopLoading();
result.success(true);
break;
case "isLoading":
result.success((webView != null) && webView.isLoading());
break;
case "takeScreenshot":
if (webView != null)
webView.takeScreenshot(result);
else
result.success(null);
break;
case "setOptions":
if (webView != null && webView.inAppBrowserActivity != null) {
InAppBrowserOptions inAppBrowserOptions = new InAppBrowserOptions();
HashMap<String, Object> inAppBrowserOptionsMap = (HashMap<String, Object>) call.argument("options");
inAppBrowserOptions.parse(inAppBrowserOptionsMap);
webView.inAppBrowserActivity.setOptions(inAppBrowserOptions, inAppBrowserOptionsMap);
} else if (webView != null) {
InAppWebViewOptions inAppWebViewOptions = new InAppWebViewOptions();
HashMap<String, Object> inAppWebViewOptionsMap = (HashMap<String, Object>) call.argument("options");
inAppWebViewOptions.parse(inAppWebViewOptionsMap);
webView.setOptions(inAppWebViewOptions, inAppWebViewOptionsMap);
}
result.success(true);
break;
case "getOptions":
if (webView != null && webView.inAppBrowserActivity != null) {
result.success(webView.inAppBrowserActivity.getOptions());
} else {
result.success((webView != null) ? webView.getOptions() : null);
}
break;
case "close":
if (webView != null && webView.inAppBrowserActivity != null) {
webView.inAppBrowserActivity.close(result);
} else {
result.notImplemented();
}
break;
case "show":
if (webView != null && webView.inAppBrowserActivity != null) {
webView.inAppBrowserActivity.show();
result.success(true);
} else {
result.notImplemented();
}
break;
case "hide":
if (webView != null && webView.inAppBrowserActivity != null) {
webView.inAppBrowserActivity.hide();
result.success(true);
} else {
result.notImplemented();
}
break;
case "getCopyBackForwardList":
result.success((webView != null) ? webView.getCopyBackForwardList() : null);
break;
case "startSafeBrowsing":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 &&
WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
WebViewCompat.startSafeBrowsing(webView.getContext(), new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean success) {
result.success(success);
}
});
}
else {
result.success(false);
}
break;
case "clearCache":
if (webView != null)
webView.clearAllCache();
result.success(true);
break;
case "clearSslPreferences":
if (webView != null)
webView.clearSslPreferences();
result.success(true);
break;
case "findAllAsync":
if (webView != null) {
String find = (String) call.argument("find");
webView.findAllAsync(find);
}
result.success(true);
break;
case "findNext":
if (webView != null) {
Boolean forward = (Boolean) call.argument("forward");
webView.findNext(forward);
}
result.success(true);
break;
case "clearMatches":
if (webView != null) {
webView.clearMatches();
}
result.success(true);
break;
case "scrollTo":
if (webView != null) {
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
Boolean animated = (Boolean) call.argument("animated");
webView.scrollTo(x, y, animated);
}
result.success(true);
break;
case "scrollBy":
if (webView != null) {
Integer x = (Integer) call.argument("x");
Integer y = (Integer) call.argument("y");
Boolean animated = (Boolean) call.argument("animated");
webView.scrollBy(x, y, animated);
}
result.success(true);
break;
case "pause":
if (webView != null) {
webView.onPause();
}
result.success(true);
break;
case "resume":
if (webView != null) {
webView.onResume();
}
result.success(true);
break;
case "pauseTimers":
if (webView != null) {
webView.pauseTimers();
}
result.success(true);
break;
case "resumeTimers":
if (webView != null) {
webView.resumeTimers();
}
result.success(true);
break;
case "printCurrentPage":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.printCurrentPage();
}
result.success(true);
break;
case "getContentHeight":
result.success((webView != null) ? webView.getContentHeight() : null);
break;
case "zoomBy":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
double zoomFactor = (double) call.argument("zoomFactor");
webView.zoomBy((float) zoomFactor);
}
result.success(true);
break;
case "getOriginalUrl":
result.success((webView != null) ? webView.getOriginalUrl() : null);
break;
case "getScale":
result.success((webView != null) ? webView.getUpdatedScale() : null);
break;
case "getSelectedText":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.getSelectedText(result);
} else {
result.success(null);
}
break;
case "getHitTestResult":
if (webView != null) {
WebView.HitTestResult hitTestResult = webView.getHitTestResult();
Map<String, Object> obj = new HashMap<>();
obj.put("type", hitTestResult.getType());
obj.put("extra", hitTestResult.getExtra());
result.success(obj);
} else {
result.success(null);
}
break;
case "pageDown":
if (webView != null) {
boolean bottom = (boolean) call.argument("bottom");
result.success(webView.pageDown(bottom));
} else {
result.success(false);
}
break;
case "pageUp":
if (webView != null) {
boolean top = (boolean) call.argument("top");
result.success(webView.pageUp(top));
} else {
result.success(false);
}
break;
case "saveWebArchive":
if (webView != null) {
String basename = (String) call.argument("basename");
boolean autoname = (boolean) call.argument("autoname");
webView.saveWebArchive(basename, autoname, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
result.success(value);
}
});
} else {
result.success(null);
}
break;
case "zoomIn":
if (webView != null) {
result.success(webView.zoomIn());
} else {
result.success(false);
}
break;
case "zoomOut":
if (webView != null) {
result.success(webView.zoomOut());
} else {
result.success(false);
}
break;
case "clearFocus":
if (webView != null) {
webView.clearFocus();
}
result.success(true);
break;
case "setContextMenu":
if (webView != null) {
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
webView.contextMenu = contextMenu;
}
result.success(true);
break;
case "requestFocusNodeHref":
if (webView != null) {
result.success(webView.requestFocusNodeHref());
} else {
result.success(null);
}
break;
case "requestImageRef":
if (webView != null) {
result.success(webView.requestImageRef());
} else {
result.success(null);
}
break;
case "getScrollX":
if (webView != null) {
result.success(webView.getScrollX());
} else {
result.success(null);
}
break;
case "getScrollY":
if (webView != null) {
result.success(webView.getScrollY());
} else {
result.success(null);
}
break;
case "getCertificate":
if (webView != null) {
result.success(webView.getCertificateMap());
} else {
result.success(null);
}
break;
case "clearHistory":
if (webView != null) {
webView.clearHistory();
}
result.success(true);
break;
case "addUserScript":
if (webView != null) {
Map<String, Object> userScript = (Map<String, Object>) call.argument("userScript");
result.success(webView.addUserScript(userScript));
} else {
result.success(false);
}
break;
case "removeUserScript":
if (webView != null) {
Integer index = (Integer) call.argument("index");
result.success(webView.removeUserScript(index));
} else {
result.success(false);
}
break;
case "removeAllUserScripts":
if (webView != null) {
webView.removeAllUserScripts();
}
result.success(true);
break;
default:
result.notImplemented();
}
}
public void dispose() {
webView = null;
}
}
{"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)
}
}
}
......@@ -8,13 +8,14 @@
import Foundation
import WebKit
public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatformView {
public class FlutterWebViewController: NSObject, FlutterPlatformView {
private weak var registrar: FlutterPluginRegistrar?
var webView: InAppWebView?
var viewId: Any = 0
var channel: FlutterMethodChannel?
var myView: UIView?
var methodCallDelegate: InAppWebViewMethodHandler?
init(registrar: FlutterPluginRegistrar, withFrame frame: CGRect, viewIdentifier viewId: Any, arguments args: NSDictionary) {
super.init()
......@@ -29,7 +30,6 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
channelName = "com.pichillilorenzo/flutter_inappwebview_" + id
}
channel = FlutterMethodChannel(name: channelName, binaryMessenger: registrar.messenger())
channel!.setMethodCallHandler(LeakAvoider(delegate: self).handle)
myView = UIView(frame: frame)
......@@ -40,6 +40,7 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
let initialOptions = args["initialOptions"] as! [String: Any?]
let contextMenu = args["contextMenu"] as? [String: Any]
let windowId = args["windowId"] as? Int64
let initialUserScripts = args["initialUserScripts"] as? [[String: Any]]
let options = InAppWebViewOptions()
let _ = options.parse(options: initialOptions)
......@@ -54,6 +55,9 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
} else {
webView = InAppWebView(frame: myView!.bounds, configuration: preWebviewConfiguration, IABController: nil, contextMenu: contextMenu, channel: channel!)
}
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
webView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
myView!.autoresizesSubviews = true
......@@ -61,6 +65,9 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
myView!.addSubview(webView!)
webView!.options = options
if let userScripts = initialUserScripts {
webView!.appendUserScripts(userScripts: userScripts)
}
webView!.prepare()
if windowId == nil {
......@@ -114,6 +121,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
deinit {
print("FlutterWebViewController - dealloc")
channel?.setMethodCallHandler(nil)
methodCallDelegate?.webView = nil
methodCallDelegate = nil
webView?.dispose()
webView = nil
myView = nil
......@@ -145,377 +154,4 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
webView!.loadUrl(url: url, headers: initialHeaders)
}
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "getUrl":
result( (webView != nil) ? webView!.url?.absoluteString : nil )
break
case "getTitle":
result( (webView != nil) ? webView!.title : nil )
break
case "getProgress":
result( (webView != nil) ? Int(webView!.estimatedProgress * 100) : nil )
break
case "loadUrl":
if webView != nil {
let url = (arguments!["url"] as? String)!
let headers = (arguments!["headers"] as? [String: String])!
webView!.loadUrl(url: URL(string: url)!, headers: headers)
result(true)
}
else {
result(false)
}
break
case "postUrl":
if webView != nil {
let url = (arguments!["url"] as? String)!
let postData = (arguments!["postData"] as? FlutterStandardTypedData)!
webView!.postUrl(url: URL(string: url)!, postData: postData.data, completionHandler: { () -> Void in
result(true)
})
}
else {
result(false)
}
break
case "loadData":
if webView != nil {
let data = (arguments!["data"] as? String)!
let mimeType = (arguments!["mimeType"] as? String)!
let encoding = (arguments!["encoding"] as? String)!
let baseUrl = (arguments!["baseUrl"] as? String)!
webView!.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl)
result(true)
}
else {
result(false)
}
break
case "loadFile":
if webView != nil {
let url = (arguments!["url"] as? String)!
let headers = (arguments!["headers"] as? [String: String])!
do {
try webView!.loadFile(url: url, headers: headers)
result(true)
}
catch let error as NSError {
result(FlutterError(code: "InAppBrowserFlutterPlugin", message: error.domain, details: nil))
return
}
}
else {
result(false)
}
break
case "evaluateJavascript":
if webView != nil {
let source = (arguments!["source"] as? String)!
webView!.evaluateJavascript(source: source, result: result)
}
else {
result(nil)
}
break
case "injectJavascriptFileFromUrl":
if webView != nil {
let urlFile = (arguments!["urlFile"] as? String)!
webView!.injectJavascriptFileFromUrl(urlFile: urlFile)
}
result(true)
break
case "injectCSSCode":
if webView != nil {
let source = (arguments!["source"] as? String)!
webView!.injectCSSCode(source: source)
}
result(true)
break
case "injectCSSFileFromUrl":
if webView != nil {
let urlFile = (arguments!["urlFile"] as? String)!
webView!.injectCSSFileFromUrl(urlFile: urlFile)
}
result(true)
break
case "reload":
if webView != nil {
webView!.reload()
}
result(true)
break
case "goBack":
if webView != nil {
webView!.goBack()
}
result(true)
break
case "canGoBack":
result((webView != nil) && webView!.canGoBack)
break
case "goForward":
if webView != nil {
webView!.goForward()
}
result(true)
break
case "canGoForward":
result((webView != nil) && webView!.canGoForward)
break
case "goBackOrForward":
if webView != nil {
let steps = (arguments!["steps"] as? Int)!
webView!.goBackOrForward(steps: steps)
}
result(true)
break
case "canGoBackOrForward":
let steps = (arguments!["steps"] as? Int)!
result((webView != nil) && webView!.canGoBackOrForward(steps: steps))
break
case "stopLoading":
if webView != nil {
webView!.stopLoading()
}
result(true)
break
case "isLoading":
result((webView != nil) && webView!.isLoading)
break
case "takeScreenshot":
if webView != nil {
webView!.takeScreenshot(completionHandler: { (screenshot) -> Void in
result(screenshot)
})
}
else {
result(nil)
}
break
case "setOptions":
if webView != nil {
let inAppWebViewOptions = InAppWebViewOptions()
let inAppWebViewOptionsMap = arguments!["options"] as! [String: Any]
let _ = inAppWebViewOptions.parse(options: inAppWebViewOptionsMap)
webView!.setOptions(newOptions: inAppWebViewOptions, newOptionsMap: inAppWebViewOptionsMap)
}
result(true)
break
case "getOptions":
result((webView != nil) ? webView!.getOptions() : nil)
break
case "getCopyBackForwardList":
result((webView != nil) ? webView!.getCopyBackForwardList() : nil)
break
case "findAllAsync":
if webView != nil {
let find = arguments!["find"] as! String
webView!.findAllAsync(find: find, completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "FlutterWebViewController", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "findNext":
if webView != nil {
let forward = arguments!["forward"] as! Bool
webView!.findNext(forward: forward, completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "FlutterWebViewController", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "clearMatches":
if webView != nil {
webView!.clearMatches(completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "FlutterWebViewController", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "clearCache":
if webView != nil {
webView!.clearCache()
}
result(true)
break
case "scrollTo":
if webView != nil {
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
let animated = arguments!["animated"] as! Bool
webView!.scrollTo(x: x, y: y, animated: animated)
}
result(true)
break
case "scrollBy":
if webView != nil {
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
let animated = arguments!["animated"] as! Bool
webView!.scrollBy(x: x, y: y, animated: animated)
}
result(true)
break
case "pauseTimers":
if webView != nil {
webView!.pauseTimers()
}
result(true)
break
case "resumeTimers":
if webView != nil {
webView!.resumeTimers()
}
result(true)
break
case "printCurrentPage":
if webView != nil {
webView!.printCurrentPage(printCompletionHandler: {(completed, error) in
if !completed, let err = error {
print(err.localizedDescription)
result(false)
return
}
result(true)
})
} else {
result(false)
}
break
case "getContentHeight":
result( (webView != nil) ? webView!.getContentHeight() : nil )
break
case "reloadFromOrigin":
if webView != nil {
webView!.reloadFromOrigin()
}
result(true)
break
case "getScale":
result( (webView != nil) ? webView!.getScale() : nil )
break
case "hasOnlySecureContent":
result( (webView != nil) ? webView!.hasOnlySecureContent : nil )
break
case "getSelectedText":
if webView != nil {
webView!.getSelectedText { (value, error) in
if let err = error {
print(err.localizedDescription)
result("")
return
}
result(value)
}
}
else {
result(nil)
}
break
case "getHitTestResult":
if webView != nil {
webView!.getHitTestResult { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
}
else {
result(nil)
}
break
case "clearFocus":
if webView != nil {
webView!.clearFocus()
result(true)
} else {
result(false)
}
break
case "setContextMenu":
if webView != nil {
let contextMenu = arguments!["contextMenu"] as? [String: Any]
webView!.contextMenu = contextMenu
result(true)
} else {
result(false)
}
break
case "requestFocusNodeHref":
if webView != nil {
webView!.requestFocusNodeHref { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
} else {
result(false)
}
break
case "requestImageRef":
if webView != nil {
webView!.requestImageRef { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
} else {
result(false)
}
break
case "getScrollX":
if webView != nil {
result(Int(webView!.scrollView.contentOffset.x))
} else {
result(false)
}
break
case "getScrollY":
if webView != nil {
result(Int(webView!.scrollView.contentOffset.y))
} else {
result(false)
}
break
case "getCertificate":
if webView != nil {
result(webView!.getCertificateMap())
} else {
result(false)
}
break
default:
result(FlutterMethodNotImplemented)
break
}
}
}
......@@ -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
......
......@@ -21,7 +21,7 @@ public class InAppWebView_IBWrapper: UIView {
}
}
public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIScrollViewDelegate, WKUIDelegate, UITextFieldDelegate {
public class InAppBrowserWebViewController: UIViewController, UIScrollViewDelegate, WKUIDelegate, UITextFieldDelegate {
@IBOutlet var containerWebView: InAppWebView_IBWrapper!
@IBOutlet var closeButton: UIButton!
......@@ -58,333 +58,17 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
var isHidden = false
var viewPrepared = false
var previousStatusBarStyle = -1
var initUserScripts: [[String: Any]] = []
var methodCallDelegate: InAppWebViewMethodHandler?
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
public static func register(with registrar: FlutterPluginRegistrar) {
}
public func prepareMethodChannel() {
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser_" + uuid, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
SwiftFlutterPlugin.instance!.registrar!.addMethodCallDelegate(self, channel: channel!)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "getUrl":
result(webView.url?.absoluteString)
break
case "getTitle":
result(webView.title)
break
case "getProgress":
let progress = Int(webView.estimatedProgress * 100)
result(progress)
break
case "loadUrl":
let url = arguments!["url"] as! String
let headers = arguments!["headers"] as? [String: String]
let absoluteUrl = URL(string: url)!.absoluteURL
webView.loadUrl(url: absoluteUrl, headers: headers)
result(true)
break
case "loadData":
let data = arguments!["data"] as! String
let mimeType = arguments!["mimeType"] as! String
let encoding = arguments!["encoding"] as! String
let baseUrl = arguments!["baseUrl"] as! String
webView.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl)
result(true)
break
case "postUrl":
let url = arguments!["url"] as! String
let postData = arguments!["postData"] as! FlutterStandardTypedData
let absoluteUrl = URL(string: url)!.absoluteURL
webView.postUrl(url: absoluteUrl, postData: postData.data, completionHandler: { () -> Void in
result(true)
})
break
case "loadFile":
let url = arguments!["url"] as! String
let headers = arguments!["headers"] as? [String: String]
do {
try webView.loadFile(url: url, headers: headers)
result(true)
}
catch let error as NSError {
dump(error)
result(FlutterError(code: "InAppBrowserWebViewController", message: error.localizedDescription, details: nil))
}
break
case "close":
close()
result(true)
break
case "show":
show()
result(true)
break
case "hide":
hide()
result(true)
break
case "reload":
webView.reload()
result(true)
break
case "goBack":
webView.goBack()
result(true)
break
case "canGoBack":
result(webView.canGoBack)
break
case "goForward":
webView.goForward()
result(true)
break
case "canGoForward":
result(webView.canGoForward)
break
case "goBackOrForward":
let steps = arguments!["steps"] as! Int
webView.goBackOrForward(steps: steps)
result(true)
break
case "canGoBackOrForward":
let steps = arguments!["steps"] as! Int
result(webView.canGoBackOrForward(steps: steps))
break
case "isLoading":
result(webView.isLoading == true)
break
case "stopLoading":
webView.stopLoading()
result(true)
break
case "isHidden":
result(isHidden == true)
break
case "evaluateJavascript":
let source = arguments!["source"] as! String
webView.evaluateJavascript(source: source, result: result)
break
case "injectJavascriptFileFromUrl":
let urlFile = arguments!["urlFile"] as! String
webView.injectJavascriptFileFromUrl(urlFile: urlFile)
result(true)
break
case "injectCSSCode":
let source = arguments!["source"] as! String
webView.injectCSSCode(source: source)
result(true)
break
case "injectCSSFileFromUrl":
let urlFile = arguments!["urlFile"] as! String
webView.injectCSSFileFromUrl(urlFile: urlFile)
result(true)
break
case "takeScreenshot":
webView.takeScreenshot(completionHandler: { (screenshot) -> Void in
result(screenshot)
})
break
case "setOptions":
let inAppBrowserOptions = InAppBrowserOptions()
let inAppBrowserOptionsMap = arguments!["options"] as! [String: Any]
let _ = inAppBrowserOptions.parse(options: inAppBrowserOptionsMap)
self.setOptions(newOptions: inAppBrowserOptions, newOptionsMap: inAppBrowserOptionsMap)
result(true)
break
case "getOptions":
result(getOptions())
break
case "getCopyBackForwardList":
result(webView.getCopyBackForwardList())
break
case "findAllAsync":
let find = arguments!["find"] as! String
webView.findAllAsync(find: find, completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppBrowserWebViewController", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
break
case "findNext":
let forward = arguments!["forward"] as! Bool
webView.findNext(forward: forward, completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppBrowserWebViewController", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
break
case "clearMatches":
webView.clearMatches(completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppBrowserWebViewController", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
break
case "clearCache":
webView.clearCache()
result(true)
break
case "scrollTo":
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
let animated = arguments!["animated"] as! Bool
webView.scrollTo(x: x, y: y, animated: animated)
result(true)
break
case "scrollBy":
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
let animated = arguments!["animated"] as! Bool
webView.scrollTo(x: x, y: y, animated: animated)
result(true)
break
case "pauseTimers":
webView.pauseTimers()
result(true)
break
case "resumeTimers":
webView.resumeTimers()
result(true)
break
case "printCurrentPage":
webView.printCurrentPage(printCompletionHandler: {(completed, error) in
if !completed, let _ = error {
result(false)
return
}
result(true)
})
break
case "getContentHeight":
result(webView.getContentHeight())
break
case "reloadFromOrigin":
webView.reloadFromOrigin()
result(true)
break
case "getScale":
result(webView.getScale())
break
case "hasOnlySecureContent":
result(webView.hasOnlySecureContent)
break
case "getSelectedText":
if webView != nil {
webView!.getSelectedText { (value, error) in
if let err = error {
print(err.localizedDescription)
}
result(value)
}
}
else {
result(nil)
}
break
case "getHitTestResult":
if webView != nil {
webView!.getHitTestResult { (value, error) in
if let err = error {
print(err.localizedDescription)
}
result(value)
}
}
else {
result(nil)
}
break
case "clearFocus":
if webView != nil {
webView!.clearFocus()
result(true)
} else {
result(false)
}
break
case "setContextMenu":
if webView != nil {
let contextMenu = arguments!["contextMenu"] as? [String: Any]
webView!.contextMenu = contextMenu
result(true)
} else {
result(false)
}
break
case "requestFocusNodeHref":
if webView != nil {
webView!.requestFocusNodeHref { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
} else {
result(false)
}
break
case "requestImageRef":
if webView != nil {
webView!.requestImageRef { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
} else {
result(false)
}
break
case "getScrollX":
if webView != nil {
result(Int(webView!.scrollView.contentOffset.x))
} else {
result(false)
}
break
case "getScrollY":
if webView != nil {
result(Int(webView!.scrollView.contentOffset.y))
} else {
result(false)
}
break
case "getCertificate":
if webView != nil {
result(webView!.getCertificateMap())
} else {
result(false)
}
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public override func viewWillAppear(_ animated: Bool) {
if !viewPrepared {
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser_" + uuid, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(options: webViewOptions)
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
self.webView = webViewTransport.webView
......@@ -398,6 +82,11 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
contextMenu: contextMenu,
channel: channel!)
}
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
self.webView.appendUserScripts(userScripts: initUserScripts)
self.containerWebView.addSubview(self.webView)
prepareConstraints()
prepareWebView()
......@@ -456,6 +145,11 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
urlField.delegate = self
urlField.text = self.initURL?.absoluteString
urlField.backgroundColor = .white
urlField.textColor = .black
urlField.layer.borderWidth = 1.0
urlField.layer.borderColor = UIColor.lightGray.cgColor
urlField.layer.cornerRadius = 4
closeButton.addTarget(self, action: #selector(self.close), for: .touchUpInside)
......@@ -799,6 +493,8 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
onExit()
channel?.setMethodCallHandler(nil)
channel = nil
methodCallDelegate?.webView = nil
methodCallDelegate = nil
}
public func onBrowserCreated() {
......
......@@ -858,6 +858,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
// in order to have the same behavior as Android
var activateShouldOverrideUrlLoading = false
var contextMenu: [String: Any]?
var userScripts: [WKUserScript] = []
// https://github.com/mozilla-mobile/firefox-ios/blob/50531a7e9e4d459fb11d4fcb7d4322e08103501f/Client/Frontend/Browser/ContextMenuHelper.swift
fileprivate var nativeHighlightLongPressRecognizer: UILongPressGestureRecognizer?
......@@ -1161,17 +1162,52 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
}
let userScript = WKUserScript(source: "window._flutter_inappwebview_windowId = \(windowId == nil ? "null" : String(windowId!));" , injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(userScript)
prepareAndAddUserScripts()
if windowId != nil {
// the new created window webview has the same WKWebViewConfiguration variable reference
return
}
configuration.userContentController = WKUserContentController()
configuration.preferences = WKPreferences()
if let options = options {
if #available(iOS 9.0, *) {
configuration.allowsAirPlayForMediaPlayback = options.allowsAirPlayForMediaPlayback
configuration.allowsPictureInPictureMediaPlayback = options.allowsPictureInPictureMediaPlayback
if !options.applicationNameForUserAgent.isEmpty {
configuration.applicationNameForUserAgent = options.applicationNameForUserAgent
}
}
configuration.preferences.javaScriptCanOpenWindowsAutomatically = options.javaScriptCanOpenWindowsAutomatically
configuration.preferences.javaScriptEnabled = options.javaScriptEnabled
configuration.preferences.minimumFontSize = CGFloat(options.minimumFontSize)
if #available(iOS 13.0, *) {
configuration.preferences.isFraudulentWebsiteWarningEnabled = options.isFraudulentWebsiteWarningEnabled
configuration.defaultWebpagePreferences.preferredContentMode = WKWebpagePreferences.ContentMode(rawValue: options.preferredContentMode)!
}
}
}
public func prepareAndAddUserScripts() -> Void {
addWindowIdUserScript()
if windowId != nil {
// the new created window webview has the same WKWebViewConfiguration variable reference
return
}
configuration.userContentController = WKUserContentController()
addPluginUserScripts()
addAllUserScripts()
}
func addWindowIdUserScript() -> Void {
let userScriptWindowId = WKUserScript(source: "window._flutter_inappwebview_windowId = \(windowId == nil ? "null" : String(windowId!));" , injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(userScriptWindowId)
}
func addPluginUserScripts() -> Void {
if let options = options {
let originalViewPortMetaTagContentJSScript = WKUserScript(source: originalViewPortMetaTagContentJS, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
......@@ -1192,14 +1228,20 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let javaScriptBridgeJSScript = WKUserScript(source: javaScriptBridgeJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(javaScriptBridgeJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
configuration.userContentController.add(self, name: "callHandler")
let consoleLogJSScript = WKUserScript(source: consoleLogJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(consoleLogJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog")
configuration.userContentController.add(self, name: "consoleLog")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleDebug")
configuration.userContentController.add(self, name: "consoleDebug")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleError")
configuration.userContentController.add(self, name: "consoleError")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleInfo")
configuration.userContentController.add(self, name: "consoleInfo")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
configuration.userContentController.add(self, name: "consoleWarn")
let findElementsAtPointJSScript = WKUserScript(source: findElementsAtPointJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
......@@ -1218,6 +1260,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let findTextHighlightJSScript = WKUserScript(source: findTextHighlightJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(findTextHighlightJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
configuration.userContentController.add(self, name: "onFindResultReceived")
let onWindowFocusEventJSScript = WKUserScript(source: onWindowFocusEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
......@@ -1235,26 +1278,57 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let interceptFetchRequestsJSScript = WKUserScript(source: interceptFetchRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
}
if #available(iOS 9.0, *) {
configuration.allowsAirPlayForMediaPlayback = options.allowsAirPlayForMediaPlayback
configuration.allowsPictureInPictureMediaPlayback = options.allowsPictureInPictureMediaPlayback
if !options.applicationNameForUserAgent.isEmpty {
configuration.applicationNameForUserAgent = options.applicationNameForUserAgent
}
}
configuration.preferences.javaScriptCanOpenWindowsAutomatically = options.javaScriptCanOpenWindowsAutomatically
configuration.preferences.javaScriptEnabled = options.javaScriptEnabled
configuration.preferences.minimumFontSize = CGFloat(options.minimumFontSize)
if #available(iOS 13.0, *) {
configuration.preferences.isFraudulentWebsiteWarningEnabled = options.isFraudulentWebsiteWarningEnabled
configuration.defaultWebpagePreferences.preferredContentMode = WKWebpagePreferences.ContentMode(rawValue: options.preferredContentMode)!
}
}
}
func addAllUserScripts() -> Void {
for userScript in userScripts {
configuration.userContentController.addUserScript(userScript)
}
}
public func addUserScript(wkUserScript: WKUserScript) -> Void {
userScripts.append(wkUserScript)
configuration.userContentController.addUserScript(wkUserScript)
}
public func appendUserScript(userScript: [String: Any]) -> Void {
let wkUserScript = WKUserScript(source: userScript["source"] as! String,
injectionTime: WKUserScriptInjectionTime.init(rawValue: userScript["injectionTime"] as! Int) ?? .atDocumentStart,
forMainFrameOnly: userScript["iosForMainFrameOnly"] as! Bool)
userScripts.append(wkUserScript)
}
public func appendUserScripts(wkUserScripts: [WKUserScript]) -> Void {
for wkUserScript in wkUserScripts {
userScripts.append(wkUserScript)
}
}
public func appendUserScripts(userScripts: [[String: Any]]) -> Void {
for userScript in userScripts {
appendUserScript(userScript: userScript)
}
}
public func removeUserScript(at index: Int) -> Void {
userScripts.remove(at: index)
// there isn't a way to remove a specific user script using WKUserContentController,
// so we remove all the user scripts and, then, we add them again without the one that has been removed
configuration.userContentController.removeAllUserScripts()
addWindowIdUserScript()
addPluginUserScripts()
addAllUserScripts()
}
public func removeAllUserScripts() -> Void {
userScripts.removeAll()
configuration.userContentController.removeAllUserScripts()
// add all the necessary base WKUserScripts of this plugin again
addWindowIdUserScript()
addPluginUserScripts()
}
@available(iOS 10.0, *)
static public func getDataDetectorType(type: String) -> WKDataDetectorTypes {
switch type {
......@@ -2000,7 +2074,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
InAppWebView.credentialsProposed = []
evaluateJavaScript(platformReadyJS, completionHandler: nil)
onLoadStop(url: url?.absoluteString)
if IABController != nil {
IABController!.updateUrlTextField(url: currentURL?.absoluteString ?? "")
IABController!.backButton.isEnabled = canGoBack
......@@ -2257,8 +2331,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
_ in completionHandler()}
);
let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window!.rootViewController!)
presentingViewController.present(alertController, animated: true, completion: {})
if let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window?.rootViewController!) {
presentingViewController.present(alertController, animated: true, completion: {})
} else {
completionHandler()
}
}
public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String,
......@@ -2320,8 +2397,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
completionHandler(false)
}))
let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window!.rootViewController!)
presentingViewController.present(alertController, animated: true, completion: nil)
if let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window?.rootViewController!) {
presentingViewController.present(alertController, animated: true, completion: nil)
} else {
completionHandler(false)
}
}
public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo,
......@@ -2393,8 +2473,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
completionHandler(nil)
}))
let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window!.rootViewController!)
presentingViewController.present(alertController, animated: true, completion: nil)
if let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window?.rootViewController!) {
presentingViewController.present(alertController, animated: true, completion: nil)
} else {
completionHandler(nil)
}
}
public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt message: String, defaultText defaultValue: String?, initiatedByFrame frame: WKFrameInfo,
......@@ -3158,6 +3241,9 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
if #available(iOS 14.0, *) {
configuration.userContentController.removeAllScriptMessageHandlers()
}
configuration.userContentController.removeAllUserScripts()
if #available(iOS 11.0, *) {
configuration.userContentController.removeAllContentRuleLists()
......
//
// WebViewMethodHandler.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 01/02/21.
//
import Foundation
import WebKit
class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
var webView: InAppWebView?
init(webView: InAppWebView) {
super.init()
self.webView = webView
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "getUrl":
result(webView?.url?.absoluteString)
break
case "getTitle":
result(webView?.title)
break
case "getProgress":
result( (webView != nil) ? Int(webView!.estimatedProgress * 100) : nil )
break
case "loadUrl":
let url = (arguments!["url"] as? String)!
let headers = (arguments!["headers"] as? [String: String])!
webView?.loadUrl(url: URL(string: url)!, headers: headers)
result(true)
break
case "postUrl":
if webView != nil {
let url = (arguments!["url"] as? String)!
let postData = (arguments!["postData"] as? FlutterStandardTypedData)!
webView!.postUrl(url: URL(string: url)!, postData: postData.data, completionHandler: { () -> Void in
result(true)
})
}
else {
result(false)
}
break
case "loadData":
let data = (arguments!["data"] as? String)!
let mimeType = (arguments!["mimeType"] as? String)!
let encoding = (arguments!["encoding"] as? String)!
let baseUrl = (arguments!["baseUrl"] as? String)!
webView?.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl)
result(true)
break
case "loadFile":
let url = (arguments!["url"] as? String)!
let headers = (arguments!["headers"] as? [String: String])!
do {
try webView?.loadFile(url: url, headers: headers)
}
catch let error as NSError {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error.domain, details: nil))
return
}
result(true)
break
case "evaluateJavascript":
if webView != nil {
let source = (arguments!["source"] as? String)!
webView!.evaluateJavascript(source: source, result: result)
}
else {
result(nil)
}
break
case "injectJavascriptFileFromUrl":
let urlFile = (arguments!["urlFile"] as? String)!
webView?.injectJavascriptFileFromUrl(urlFile: urlFile)
result(true)
break
case "injectCSSCode":
let source = (arguments!["source"] as? String)!
webView?.injectCSSCode(source: source)
result(true)
break
case "injectCSSFileFromUrl":
let urlFile = (arguments!["urlFile"] as? String)!
webView?.injectCSSFileFromUrl(urlFile: urlFile)
result(true)
break
case "reload":
webView?.reload()
result(true)
break
case "goBack":
webView?.goBack()
result(true)
break
case "canGoBack":
result(webView?.canGoBack ?? false)
break
case "goForward":
webView?.goForward()
result(true)
break
case "canGoForward":
result(webView?.canGoForward ?? false)
break
case "goBackOrForward":
let steps = (arguments!["steps"] as? Int)!
webView?.goBackOrForward(steps: steps)
result(true)
break
case "canGoBackOrForward":
let steps = (arguments!["steps"] as? Int)!
result(webView?.canGoBackOrForward(steps: steps) ?? false)
break
case "stopLoading":
webView?.stopLoading()
result(true)
break
case "isLoading":
result(webView?.isLoading ?? false)
break
case "takeScreenshot":
if webView != nil {
webView!.takeScreenshot(completionHandler: { (screenshot) -> Void in
result(screenshot)
})
}
else {
result(nil)
}
break
case "setOptions":
if let iabController = webView?.IABController {
let inAppBrowserOptions = InAppBrowserOptions()
let inAppBrowserOptionsMap = arguments!["options"] as! [String: Any]
let _ = inAppBrowserOptions.parse(options: inAppBrowserOptionsMap)
iabController.setOptions(newOptions: inAppBrowserOptions, newOptionsMap: inAppBrowserOptionsMap)
} else {
let inAppWebViewOptions = InAppWebViewOptions()
let inAppWebViewOptionsMap = arguments!["options"] as! [String: Any]
let _ = inAppWebViewOptions.parse(options: inAppWebViewOptionsMap)
webView?.setOptions(newOptions: inAppWebViewOptions, newOptionsMap: inAppWebViewOptionsMap)
}
result(true)
break
case "getOptions":
if let iabController = webView?.IABController {
result(iabController.getOptions())
} else {
result(webView?.getOptions())
}
break
case "close":
if let iabController = webView?.IABController {
iabController.close()
result(true)
} else {
result(FlutterMethodNotImplemented)
}
break
case "show":
if let iabController = webView?.IABController {
iabController.show()
result(true)
} else {
result(FlutterMethodNotImplemented)
}
break
case "hide":
if let iabController = webView?.IABController {
iabController.hide()
result(true)
} else {
result(FlutterMethodNotImplemented)
}
break
case "getCopyBackForwardList":
result(webView?.getCopyBackForwardList())
break
case "findAllAsync":
if webView != nil {
let find = arguments!["find"] as! String
webView!.findAllAsync(find: find, completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "findNext":
if webView != nil {
let forward = arguments!["forward"] as! Bool
webView!.findNext(forward: forward, completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "clearMatches":
if webView != nil {
webView!.clearMatches(completionHandler: {(value, error) in
if error != nil {
result(FlutterError(code: "InAppWebViewMethodHandler", message: error?.localizedDescription, details: nil))
return
}
result(true)
})
} else {
result(false)
}
break
case "clearCache":
webView?.clearCache()
result(true)
break
case "scrollTo":
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
let animated = arguments!["animated"] as! Bool
webView?.scrollTo(x: x, y: y, animated: animated)
result(true)
break
case "scrollBy":
let x = arguments!["x"] as! Int
let y = arguments!["y"] as! Int
let animated = arguments!["animated"] as! Bool
webView?.scrollBy(x: x, y: y, animated: animated)
result(true)
break
case "pauseTimers":
webView?.pauseTimers()
result(true)
break
case "resumeTimers":
webView?.resumeTimers()
result(true)
break
case "printCurrentPage":
if webView != nil {
webView!.printCurrentPage(printCompletionHandler: {(completed, error) in
if !completed, let err = error {
print(err.localizedDescription)
result(false)
return
}
result(true)
})
} else {
result(false)
}
break
case "getContentHeight":
result(webView?.getContentHeight())
break
case "reloadFromOrigin":
webView?.reloadFromOrigin()
result(true)
break
case "getScale":
result(webView?.getScale())
break
case "hasOnlySecureContent":
result(webView?.hasOnlySecureContent)
break
case "getSelectedText":
if webView != nil {
webView!.getSelectedText { (value, error) in
if let err = error {
print(err.localizedDescription)
result("")
return
}
result(value)
}
}
else {
result(nil)
}
break
case "getHitTestResult":
if webView != nil {
webView!.getHitTestResult { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
}
else {
result(nil)
}
break
case "clearFocus":
webView?.clearFocus()
result(true)
break
case "setContextMenu":
if webView != nil {
let contextMenu = arguments!["contextMenu"] as? [String: Any]
webView!.contextMenu = contextMenu
result(true)
} else {
result(false)
}
break
case "requestFocusNodeHref":
if webView != nil {
webView!.requestFocusNodeHref { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
} else {
result(nil)
}
break
case "requestImageRef":
if webView != nil {
webView!.requestImageRef { (value, error) in
if let err = error {
print(err.localizedDescription)
result(nil)
return
}
result(value)
}
} else {
result(nil)
}
break
case "getScrollX":
if webView != nil {
result(Int(webView!.scrollView.contentOffset.x))
} else {
result(nil)
}
break
case "getScrollY":
if webView != nil {
result(Int(webView!.scrollView.contentOffset.y))
} else {
result(nil)
}
break
case "getCertificate":
result(webView?.getCertificateMap())
break
case "addUserScript":
let userScript = arguments!["userScript"] as! [String: Any]
let wkUserScript = WKUserScript(source: userScript["source"] as! String,
injectionTime: WKUserScriptInjectionTime.init(rawValue: userScript["injectionTime"] as! Int) ?? .atDocumentStart,
forMainFrameOnly: userScript["iosForMainFrameOnly"] as! Bool)
webView?.addUserScript(wkUserScript: wkUserScript)
result(true)
break
case "removeUserScript":
let index = arguments!["index"] as! Int
webView?.removeUserScript(at: index)
result(true)
break
case "removeAllUserScripts":
webView?.removeAllUserScripts()
result(true)
break
default:
result(FlutterMethodNotImplemented)
break
}
}
deinit {
print("InAppWebViewMethodHandler - dealloc")
webView = nil
}
}
......@@ -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;
}
......
......@@ -72,49 +72,11 @@ class CookieManager {
if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
var cookieValue = name + "=" + value + "; Domain=" + domain + "; Path=" + path;
if (expiresDate != null)
cookieValue += "; Expires=" + _getCookieExpirationDate(expiresDate);
if (maxAge != null)
cookieValue += "; Max-Age=" + maxAge.toString();
if (isSecure != null && isSecure)
cookieValue += "; Secure";
if (sameSite != null)
cookieValue += "; SameSite=" + sameSite.toValue();
cookieValue += ";";
if (iosBelow11WebViewController != null) {
InAppWebViewGroupOptions? options = await iosBelow11WebViewController.getOptions();
if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled == true) {
await iosBelow11WebViewController.evaluateJavascript(
source: 'document.cookie="$cookieValue"');
return;
}
}
var setCookieCompleter = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrl: url,
onLoadStop: (controller, url) async {
await controller.evaluateJavascript(
source: 'document.cookie="$cookieValue"');
setCookieCompleter.complete();
},
);
await headlessWebView.run();
await setCookieCompleter.future;
await headlessWebView.dispose();
await _setCookieWithJavaScript(url: url, name: name, value: value, domain: domain,
path: path, expiresDate: expiresDate, maxAge: maxAge, isSecure: isSecure,
sameSite: sameSite, webViewController: iosBelow11WebViewController);
return;
}
}
......@@ -134,6 +96,57 @@ class CookieManager {
await _channel.invokeMethod('setCookie', args);
}
Future<void> _setCookieWithJavaScript(
{required String url,
required String name,
required String value,
required String domain,
String path = "/",
int? expiresDate,
int? maxAge,
bool? isSecure,
HTTPCookieSameSitePolicy? sameSite,
InAppWebViewController? webViewController}) async {
var cookieValue = name + "=" + value + "; Domain=" + domain + "; Path=" + path;
if (expiresDate != null)
cookieValue += "; Expires=" + _getCookieExpirationDate(expiresDate);
if (maxAge != null)
cookieValue += "; Max-Age=" + maxAge.toString();
if (isSecure != null && isSecure)
cookieValue += "; Secure";
if (sameSite != null)
cookieValue += "; SameSite=" + sameSite.toValue();
cookieValue += ";";
if (webViewController != null) {
InAppWebViewGroupOptions? options = await webViewController.getOptions();
if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled) {
await webViewController.evaluateJavascript(
source: 'document.cookie="$cookieValue"');
return;
}
}
var setCookieCompleter = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrl: url,
onLoadStop: (controller, url) async {
await controller.evaluateJavascript(
source: 'document.cookie="$cookieValue"');
setCookieCompleter.complete();
},
);
await headlessWebView.run();
await setCookieCompleter.future;
await headlessWebView.dispose();
}
///Gets all the cookies for the given [url].
///
///[iosBelow11WebViewController] is used for getting the cookies (also session-only cookies) using JavaScript (cookies with `isHttpOnly` enabled cannot be found, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
......@@ -146,59 +159,17 @@ class CookieManager {
Future<List<Cookie>> getCookies({required String url, InAppWebViewController? iosBelow11WebViewController}) async {
assert(url.isNotEmpty);
List<Cookie> cookies = [];
if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
if (iosBelow11WebViewController != null) {
InAppWebViewGroupOptions? options = await iosBelow11WebViewController.getOptions();
if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled == true) {
List<String> documentCookies = (await iosBelow11WebViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('=');
cookies.add(Cookie(
name: cookie[0],
value: cookie[1],
)
);
});
return cookies;
}
}
var pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrl: url,
onLoadStop: (controller, url) async {
pageLoaded.complete();
},
);
await headlessWebView.run();
await pageLoaded.future;
List<String> documentCookies = (await headlessWebView.webViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('=');
cookies.add(Cookie(
name: cookie[0],
value: cookie[1],
)
);
});
await headlessWebView.dispose();
return cookies;
return await _getCookiesWithJavaScript(url: url, webViewController: iosBelow11WebViewController);
}
}
List<Cookie> cookies = [];
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
List<dynamic> cookieListMap =
......@@ -221,6 +192,53 @@ class CookieManager {
return cookies;
}
Future<List<Cookie>> _getCookiesWithJavaScript({required String url, InAppWebViewController? webViewController}) async {
assert(url.isNotEmpty);
List<Cookie> cookies = [];
if (webViewController != null) {
InAppWebViewGroupOptions? options = await webViewController.getOptions();
if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled) {
List<String> documentCookies = (await webViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('=');
cookies.add(Cookie(
name: cookie[0],
value: cookie[1],
)
);
});
return cookies;
}
}
var pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrl: url,
onLoadStop: (controller, url) async {
pageLoaded.complete();
},
);
await headlessWebView.run();
await pageLoaded.future;
List<String> documentCookies = (await headlessWebView.webViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('=');
cookies.add(Cookie(
name: cookie[0],
value: cookie[1],
)
);
});
await headlessWebView.dispose();
return cookies;
}
///Gets a cookie by its [name] for the given [url].
///
///[iosBelow11WebViewController] is used for getting the cookie (also session-only cookie) using JavaScript (cookie with `isHttpOnly` enabled cannot be found, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
......@@ -239,50 +257,10 @@ class CookieManager {
if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
if (iosBelow11WebViewController != null) {
InAppWebViewGroupOptions? options = await iosBelow11WebViewController.getOptions();
if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled == true) {
List<String> documentCookies = (await iosBelow11WebViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
for (var i = 0; i < documentCookies.length; i++) {
List<String> cookie = documentCookies[i].split('=');
if (cookie[0] == name)
return Cookie(
name: cookie[0],
value: cookie[1]);
}
return null;
}
}
var pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrl: url,
onLoadStop: (controller, url) async {
pageLoaded.complete();
},
);
await headlessWebView.run();
await pageLoaded.future;
List<String> documentCookies = (await headlessWebView.webViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
await headlessWebView.dispose();
for (var i = 0; i < documentCookies.length; i++) {
List<String> cookie = documentCookies[i].split('=');
if (cookie[0] == name)
return Cookie(
name: cookie[0],
value: cookie[1]);
}
return null;
List<Cookie> cookies = await _getCookiesWithJavaScript(url: url, webViewController: iosBelow11WebViewController);
return cookies.cast<Cookie?>().firstWhere((cookie) => cookie!.name == name, orElse: () => null);
}
}
......@@ -335,7 +313,7 @@ class CookieManager {
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
await setCookie(url: url, name: name, value: "", path: path, domain: domain, maxAge: -1, iosBelow11WebViewController: iosBelow11WebViewController);
await _setCookieWithJavaScript(url: url, name: name, value: "", path: path, domain: domain, maxAge: -1, webViewController: iosBelow11WebViewController);
return;
}
}
......@@ -371,9 +349,9 @@ class CookieManager {
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
List<Cookie> cookies = await getCookies(url: url, iosBelow11WebViewController: iosBelow11WebViewController);
List<Cookie> cookies = await _getCookiesWithJavaScript(url: url, webViewController: iosBelow11WebViewController);
for (var i = 0; i < cookies.length; i++) {
await setCookie(url: url, name: cookies[i].name, value: "", path: path, domain: domain, maxAge: -1, iosBelow11WebViewController: iosBelow11WebViewController);
await _setCookieWithJavaScript(url: url, name: cookies[i].name, value: "", path: path, domain: domain, maxAge: -1, webViewController: iosBelow11WebViewController);
}
return;
}
......
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