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 @@ ...@@ -5,6 +5,9 @@
- Added `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options also for iOS (also thanks to [liranhao](https://github.com/liranhao)) - 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 limited cookies support on iOS below 11.0 using JavaScript
- Added `IOSCookieManager` class and `CookieManager.instance().ios.getAllCookies` iOS-specific method - 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 - 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 "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)) - 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 @@ ...@@ -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. 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 ## 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) - [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: ...@@ -213,14 +221,6 @@ Classes:
- [HttpAuthCredentialDatabase](#httpauthcredentialdatabase-class): This class implements a singleton object (shared instance) which manages the shared HTTP auth credentials cache. - [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. - [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 ### 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! 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: ...@@ -389,60 +389,67 @@ Screenshots:
##### `InAppWebViewController` Cross-platform methods ##### `InAppWebViewController` Cross-platform methods
* `getUrl`: Gets the URL for the current page. * `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`.
* `getTitle`: Gets the title for the current page. * `addUserScript(UserScript userScript)`: Injects the specified `userScript` into the webpage’s content.
* `getProgress`: Gets the progress for the current page. The progress value is between 0 and 100. * `addUserScripts(List<UserScript> userScripts)`: Injects the `userScripts` into the webpage’s content.
* `getHtml`: Gets the content html of the page. * `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.
* `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.
* `canGoBack`: Returns a boolean value indicating whether the WebView can move backward. * `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. * `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. * `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. * `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. * `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()`. * `getCertificate`: Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).
* `getTRexRunnerHtml`: Gets the html (with javascript) of the Chromium's t-rex runner game. Used in combination with `getTRexRunnerCss()`. * `getContentHeight`: Gets the height of the HTML content.
* `getTRexRunnerCss`: Gets the css of the Chromium's t-rex runner game. Used in combination with `getTRexRunnerHtml()`. * `getCopyBackForwardList`: Gets the `WebHistory` for this WebView. This contains the back/forward list for use in querying each item in the history stack.
* `scrollTo({required int x, required int y, bool animated = false})`: Scrolls the WebView to the position. * `getFavicons`: Gets the list of all favicons for the current page.
* `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.
* `getHitTestResult`: Gets the hit result for hitting an HTML elements. * `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. * `getHtml`: Gets the content html of the page.
* `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.
* `getMetaTags`: Returns the list of `<meta>` tags of the current WebView. * `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`. * `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. * `getScrollX`: Returns the scrolled left position of the current WebView.
* `getScrollY`: Returns the scrolled top 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. * `static getDefaultUserAgent`: Gets the default user agent.
##### `InAppWebViewController.webStorage` ##### `InAppWebViewController.webStorage`
......
...@@ -3,7 +3,6 @@ package com.pichillilorenzo.flutter_inappwebview.InAppBrowser; ...@@ -3,7 +3,6 @@ package com.pichillilorenzo.flutter_inappwebview.InAppBrowser;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message; import android.os.Message;
import android.util.Log; import android.util.Log;
...@@ -13,22 +12,19 @@ import android.view.MenuInflater; ...@@ -13,22 +12,19 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient; import android.webkit.WebChromeClient;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.SearchView; import android.widget.SearchView;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; 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.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewChromeClient; import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewChromeClient;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions; import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
import com.pichillilorenzo.flutter_inappwebview.R; import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared; import com.pichillilorenzo.flutter_inappwebview.Shared;
...@@ -37,10 +33,9 @@ import java.util.HashMap; ...@@ -37,10 +33,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel; 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"; static final String LOG_TAG = "InAppBrowserActivity";
public MethodChannel channel; public MethodChannel channel;
...@@ -56,6 +51,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -56,6 +51,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
public boolean isHidden = false; public boolean isHidden = false;
public String fromActivity; public String fromActivity;
public List<ActivityResultListener> activityResultListeners = new ArrayList<>(); public List<ActivityResultListener> activityResultListeners = new ArrayList<>();
public InAppWebViewMethodHandler methodCallDelegate;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
...@@ -70,7 +66,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -70,7 +66,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
windowId = b.getInt("windowId"); windowId = b.getInt("windowId");
channel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappbrowser_" + uuid); channel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappbrowser_" + uuid);
channel.setMethodCallHandler(this);
setContentView(R.layout.activity_web_view); setContentView(R.layout.activity_web_view);
...@@ -79,10 +74,14 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -79,10 +74,14 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
webView.inAppBrowserActivity = this; webView.inAppBrowserActivity = this;
webView.channel = channel; webView.channel = channel;
methodCallDelegate = new InAppWebViewMethodHandler(webView);
channel.setMethodCallHandler(methodCallDelegate);
fromActivity = b.getString("fromActivity"); fromActivity = b.getString("fromActivity");
HashMap<String, Object> optionsMap = (HashMap<String, Object>) b.getSerializable("options"); HashMap<String, Object> optionsMap = (HashMap<String, Object>) b.getSerializable("options");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) b.getSerializable("contextMenu"); 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 = new InAppBrowserOptions();
options.parse(optionsMap); options.parse(optionsMap);
...@@ -91,6 +90,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -91,6 +90,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
webViewOptions.parse(optionsMap); webViewOptions.parse(optionsMap);
webView.options = webViewOptions; webView.options = webViewOptions;
webView.contextMenu = contextMenu; webView.contextMenu = contextMenu;
webView.userScripts = initialUserScripts;
actionBar = getSupportActionBar(); actionBar = getSupportActionBar();
...@@ -124,298 +124,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -124,298 +124,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
channel.invokeMethod("onBrowserCreated", obj); 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() { private void prepareView() {
webView.prepare(); webView.prepare();
...@@ -505,72 +213,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -505,72 +213,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
return true; 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) { public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) { if ((keyCode == KeyEvent.KEYCODE_BACK)) {
if (canGoBack()) if (canGoBack())
...@@ -620,17 +262,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -620,17 +262,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
return false; 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() { public void hide() {
try { try {
isHidden = true; isHidden = true;
...@@ -650,17 +281,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -650,17 +281,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
startActivityIfNeeded(openActivity, 0); 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) { public void goBackButtonClicked(MenuItem item) {
goBack(); goBack();
} }
...@@ -684,13 +304,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -684,13 +304,6 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
close(null); 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) { public void setOptions(InAppBrowserOptions newOptions, HashMap<String, Object> newOptionsMap) {
InAppWebViewOptions newInAppWebViewOptions = new InAppWebViewOptions(); InAppWebViewOptions newInAppWebViewOptions = new InAppWebViewOptions();
...@@ -747,246 +360,13 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha ...@@ -747,246 +360,13 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
return optionsMap; 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() { public void dispose() {
channel.setMethodCallHandler(null); channel.setMethodCallHandler(null);
activityResultListeners.clear(); activityResultListeners.clear();
if (methodCallDelegate != null) {
methodCallDelegate.dispose();
methodCallDelegate = null;
}
if (webView != null) { if (webView != null) {
if (Shared.activityPluginBinding != null) { if (Shared.activityPluginBinding != null) {
Shared.activityPluginBinding.removeActivityResultListener(webView.inAppWebViewChromeClient); Shared.activityPluginBinding.removeActivityResultListener(webView.inAppWebViewChromeClient);
......
...@@ -72,7 +72,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler { ...@@ -72,7 +72,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
Map<String, String> headers = (Map<String, String>) call.argument("headers"); Map<String, String> headers = (Map<String, String>) call.argument("headers");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu"); HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId"); 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); result.success(true);
break; break;
...@@ -90,7 +91,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler { ...@@ -90,7 +91,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
Map<String, String> headers = (Map<String, String>) call.argument("headers"); Map<String, String> headers = (Map<String, String>) call.argument("headers");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu"); HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId"); 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); result.success(true);
break; break;
...@@ -104,7 +106,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler { ...@@ -104,7 +106,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
String historyUrl = (String) call.argument("historyUrl"); String historyUrl = (String) call.argument("historyUrl");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu"); HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
Integer windowId = (Integer) call.argument("windowId"); 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); result.success(true);
break; break;
...@@ -196,7 +199,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler { ...@@ -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, 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(); Bundle extras = new Bundle();
extras.putString("fromActivity", activity.getClass().getName()); extras.putString("fromActivity", activity.getClass().getName());
extras.putString("url", url); extras.putString("url", url);
...@@ -206,11 +209,12 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler { ...@@ -206,11 +209,12 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
extras.putSerializable("headers", (Serializable) headers); extras.putSerializable("headers", (Serializable) headers);
extras.putSerializable("contextMenu", (Serializable) contextMenu); extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1); extras.putInt("windowId", windowId != null ? windowId : -1);
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
startInAppBrowserActivity(activity, extras); startInAppBrowserActivity(activity, extras);
} }
public void openData(Activity activity, String uuid, HashMap<String, Object> options, String data, String mimeType, String encoding, 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(); Bundle extras = new Bundle();
extras.putBoolean("isData", true); extras.putBoolean("isData", true);
extras.putString("uuid", uuid); extras.putString("uuid", uuid);
...@@ -222,6 +226,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler { ...@@ -222,6 +226,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
extras.putString("historyUrl", historyUrl); extras.putString("historyUrl", historyUrl);
extras.putSerializable("contextMenu", (Serializable) contextMenu); extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1); extras.putInt("windowId", windowId != null ? windowId : -1);
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
startInAppBrowserActivity(activity, extras); startInAppBrowserActivity(activity, extras);
} }
......
...@@ -2,48 +2,37 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView; ...@@ -2,48 +2,37 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.content.Context; import android.content.Context;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient; import android.webkit.WebChromeClient;
import android.webkit.WebSettings; import android.webkit.WebSettings;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import androidx.webkit.WebViewCompat; import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.Shared; import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util; import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import io.flutter.embedding.android.FlutterView;
import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.platform.PlatformView; import io.flutter.plugin.platform.PlatformView;
import static io.flutter.plugin.common.MethodChannel.MethodCallHandler; public class FlutterWebView implements PlatformView {
import static io.flutter.plugin.common.MethodChannel.Result;
public class FlutterWebView implements PlatformView, MethodCallHandler {
static final String LOG_TAG = "IAWFlutterWebView"; static final String LOG_TAG = "IAWFlutterWebView";
public InAppWebView webView; public InAppWebView webView;
public final MethodChannel channel; public final MethodChannel channel;
public InAppWebViewMethodHandler methodCallDelegate;
public FlutterWebView(BinaryMessenger messenger, final Context context, Object id, HashMap<String, Object> params, View containerView) { 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 = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_" + id);
channel.setMethodCallHandler(this);
DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy(); DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
...@@ -56,6 +45,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { ...@@ -56,6 +45,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
Map<String, Object> initialOptions = (Map<String, Object>) params.get("initialOptions"); Map<String, Object> initialOptions = (Map<String, Object>) params.get("initialOptions");
Map<String, Object> contextMenu = (Map<String, Object>) params.get("contextMenu"); Map<String, Object> contextMenu = (Map<String, Object>) params.get("contextMenu");
Integer windowId = (Integer) params.get("windowId"); Integer windowId = (Integer) params.get("windowId");
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) params.get("initialUserScripts");
InAppWebViewOptions options = new InAppWebViewOptions(); InAppWebViewOptions options = new InAppWebViewOptions();
options.parse(initialOptions); options.parse(initialOptions);
...@@ -67,9 +57,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { ...@@ -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"); "- 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); displayListenerProxy.onPostWebViewInitialization(displayManager);
methodCallDelegate = new InAppWebViewMethodHandler(webView);
channel.setMethodCallHandler(methodCallDelegate);
webView.prepare(); webView.prepare();
if (windowId != null) { if (windowId != null) {
...@@ -114,392 +107,13 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { ...@@ -114,392 +107,13 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
return webView; 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 @Override
public void dispose() { public void dispose() {
channel.setMethodCallHandler(null); channel.setMethodCallHandler(null);
if (methodCallDelegate != null) {
methodCallDelegate.dispose();
methodCallDelegate = null;
}
if (webView != null) { if (webView != null) {
webView.inAppWebViewChromeClient.dispose(); webView.inAppWebViewChromeClient.dispose();
webView.inAppWebViewClient.dispose(); webView.inAppWebViewClient.dispose();
......
...@@ -104,6 +104,7 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -104,6 +104,7 @@ final public class InAppWebView extends InputAwareWebView {
public Map<String, Object> contextMenu = null; public Map<String, Object> contextMenu = null;
public Handler headlessHandler = new Handler(Looper.getMainLooper()); public Handler headlessHandler = new Handler(Looper.getMainLooper());
static Handler mHandler = new Handler(); static Handler mHandler = new Handler();
public List<Map<String, Object>> userScripts = new ArrayList<>();
public Runnable checkScrollStoppedTask; public Runnable checkScrollStoppedTask;
public int initialPositionScrollStoppedTask; public int initialPositionScrollStoppedTask;
...@@ -632,7 +633,10 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -632,7 +633,10 @@ final public class InAppWebView extends InputAwareWebView {
super(context, attrs, defaultStyle); 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); super(context, containerView);
if (obj instanceof InAppBrowserActivity) if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj; this.inAppBrowserActivity = (InAppBrowserActivity) obj;
...@@ -643,6 +647,7 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -643,6 +647,7 @@ final public class InAppWebView extends InputAwareWebView {
this.windowId = windowId; this.windowId = windowId;
this.options = options; this.options = options;
this.contextMenu = contextMenu; this.contextMenu = contextMenu;
this.userScripts = userScripts;
Shared.activity.registerForContextMenu(this); Shared.activity.registerForContextMenu(this);
} }
...@@ -1975,6 +1980,18 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -1975,6 +1980,18 @@ final public class InAppWebView extends InputAwareWebView {
return null; 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 @Override
public void dispose() { public void dispose() {
if (windowId != null && InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) { if (windowId != null && InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
......
...@@ -162,44 +162,99 @@ public class InAppWebViewClient extends WebViewClient { ...@@ -162,44 +162,99 @@ public class InAppWebViewClient extends WebViewClient {
}); });
} }
private void loadCustomJavaScript(WebView view) { private void loadCustomJavaScriptOnPageStarted(WebView view) {
InAppWebView webView = (InAppWebView) view; InAppWebView webView = (InAppWebView) view;
String js = InAppWebView.consoleLogJS.replaceAll("[\r\n]+", ""); String js = preparePluginUserScripts(webView);
js += JavaScriptBridgeInterface.flutterInAppBroserJSClass.replaceAll("[\r\n]+", ""); 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) { if (webView.options.useShouldInterceptAjaxRequest) {
js += InAppWebView.interceptAjaxRequestsJS.replaceAll("[\r\n]+", ""); js += InAppWebView.interceptAjaxRequestsJS;
} }
if (webView.options.useShouldInterceptFetchRequest) { if (webView.options.useShouldInterceptFetchRequest) {
js += InAppWebView.interceptFetchRequestsJS.replaceAll("[\r\n]+", ""); js += InAppWebView.interceptFetchRequestsJS;
} }
if (webView.options.useOnLoadResource) { if (webView.options.useOnLoadResource) {
js += InAppWebView.resourceObserverJS.replaceAll("[\r\n]+", ""); js += InAppWebView.resourceObserverJS;
} }
if (!webView.options.useHybridComposition) { if (!webView.options.useHybridComposition) {
js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS.replaceAll("[\r\n]+", ""); js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS;
} }
js += InAppWebView.onWindowFocusEventJS.replaceAll("[\r\n]+", ""); js += InAppWebView.onWindowFocusEventJS;
js += InAppWebView.onWindowBlurEventJS.replaceAll("[\r\n]+", ""); js += InAppWebView.onWindowBlurEventJS;
js += InAppWebView.printJS.replaceAll("[\r\n]+", ""); js += InAppWebView.printJS;
js = InAppWebView.scriptsWrapperJS return js;
.replace("$PLACEHOLDER_VALUE", js) }
.replaceAll("[\r\n]+", "");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { private String prepareUserScriptsAtDocumentStart(InAppWebView webView) {
webView.evaluateJavascript(js, (ValueCallback<String>) null); StringBuilder js = new StringBuilder();
} else {
webView.loadUrl("javascript:" + js); 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 @Override
public void onPageStarted(WebView view, String url, Bitmap favicon) { public void onPageStarted(WebView view, String url, Bitmap favicon) {
final InAppWebView webView = (InAppWebView) view;
InAppWebView webView = (InAppWebView) view; loadCustomJavaScriptOnPageStarted(webView);
loadCustomJavaScript(webView);
super.onPageStarted(view, url, favicon); super.onPageStarted(view, url, favicon);
...@@ -219,8 +274,7 @@ public class InAppWebViewClient extends WebViewClient { ...@@ -219,8 +274,7 @@ public class InAppWebViewClient extends WebViewClient {
public void onPageFinished(WebView view, String url) { public void onPageFinished(WebView view, String url) {
final InAppWebView webView = (InAppWebView) view; final InAppWebView webView = (InAppWebView) view;
// try to reload custom javascript scripts if they were not loaded during the onPageStarted event loadCustomJavaScriptOnPageFinished(webView);
loadCustomJavaScript(webView);
super.onPageFinished(view, url); super.onPageFinished(view, url);
...@@ -228,19 +282,19 @@ public class InAppWebViewClient extends WebViewClient { ...@@ -228,19 +282,19 @@ public class InAppWebViewClient extends WebViewClient {
previousAuthRequestFailureCount = 0; previousAuthRequestFailureCount = 0;
credentialsProposed = null; 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush(); CookieManager.getInstance().flush();
} else { } else {
CookieSyncManager.getInstance().sync(); CookieSyncManager.getInstance().sync();
} }
String js = InAppWebView.platformReadyJS.replaceAll("[\r\n]+", ""); String js = InAppWebView.platformReadyJS;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null); webView.evaluateJavascript(js, (ValueCallback<String>) null);
} else { } else {
webView.loadUrl("javascript:" + js); webView.loadUrl("javascript:" + js.replaceAll("[\r\n]+", ""));
} }
Map<String, Object> obj = new HashMap<>(); Map<String, Object> obj = new HashMap<>();
...@@ -251,7 +305,7 @@ public class InAppWebViewClient extends WebViewClient { ...@@ -251,7 +305,7 @@ public class InAppWebViewClient extends WebViewClient {
} }
@Override @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<>(); Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null) if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid); 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"} {"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 \ No newline at end of file
import 'dart:async'; import 'dart:async';
import 'dart:collection';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
...@@ -6,6 +7,9 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart'; ...@@ -6,6 +7,9 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main.dart'; import 'main.dart';
class MyInAppBrowser extends InAppBrowser { class MyInAppBrowser extends InAppBrowser {
MyInAppBrowser({int? windowId, UnmodifiableListView<UserScript>? initialUserScripts}) : super(windowId: windowId, initialUserScripts: initialUserScripts);
@override @override
Future onBrowserCreated() async { Future onBrowserCreated() async {
print("\n\nBrowser Created!\n\n"); print("\n\nBrowser Created!\n\n");
...@@ -18,6 +22,7 @@ class MyInAppBrowser extends InAppBrowser { ...@@ -18,6 +22,7 @@ class MyInAppBrowser extends InAppBrowser {
@override @override
Future onLoadStop(url) async { Future onLoadStop(url) async {
print(await this.webViewController.getTitle());
print("\n\nStopped $url\n\n"); print("\n\nStopped $url\n\n");
} }
......
import 'dart:collection';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
......
...@@ -41,9 +41,10 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin { ...@@ -41,9 +41,10 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
let optionsFallback = arguments!["optionsFallback"] as? [String: Any?] let optionsFallback = arguments!["optionsFallback"] as? [String: Any?]
let contextMenuFallback = arguments!["contextMenuFallback"] as? [String: Any] let contextMenuFallback = arguments!["contextMenuFallback"] as? [String: Any]
let windowIdFallback = arguments!["windowIdFallback"] as? Int64 let windowIdFallback = arguments!["windowIdFallback"] as? Int64
let initialUserScriptsFallback = arguments!["initialUserScriptsFallback"] as? [[String: Any]]
open(uuid: uuid, url: url, options: options, menuItemList: menuItemList, uuidFallback: uuidFallback, open(uuid: uuid, url: url, options: options, menuItemList: menuItemList, uuidFallback: uuidFallback,
headersFallback: headersFallback, optionsFallback: optionsFallback, contextMenuFallback: contextMenuFallback, headersFallback: headersFallback, optionsFallback: optionsFallback, contextMenuFallback: contextMenuFallback,
windowIdFallback: windowIdFallback, result: result) windowIdFallback: windowIdFallback, initialUserScriptsFallback: initialUserScriptsFallback, result: result)
break break
case "isAvailable": case "isAvailable":
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
...@@ -60,7 +61,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin { ...@@ -60,7 +61,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
public func open(uuid: String, url: String, options: [String: Any?], menuItemList: [[String: Any]], uuidFallback: String?, public func open(uuid: String, url: String, options: [String: Any?], menuItemList: [[String: Any]], uuidFallback: String?,
headersFallback: [String: String]?, optionsFallback: [String: Any?]?, contextMenuFallback: [String: Any]?, 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 let absoluteUrl = URL(string: url)!.absoluteURL
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
...@@ -104,7 +105,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin { ...@@ -104,7 +105,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
return 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 @@ ...@@ -8,13 +8,14 @@
import Foundation import Foundation
import WebKit import WebKit
public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatformView { public class FlutterWebViewController: NSObject, FlutterPlatformView {
private weak var registrar: FlutterPluginRegistrar? private weak var registrar: FlutterPluginRegistrar?
var webView: InAppWebView? var webView: InAppWebView?
var viewId: Any = 0 var viewId: Any = 0
var channel: FlutterMethodChannel? var channel: FlutterMethodChannel?
var myView: UIView? var myView: UIView?
var methodCallDelegate: InAppWebViewMethodHandler?
init(registrar: FlutterPluginRegistrar, withFrame frame: CGRect, viewIdentifier viewId: Any, arguments args: NSDictionary) { init(registrar: FlutterPluginRegistrar, withFrame frame: CGRect, viewIdentifier viewId: Any, arguments args: NSDictionary) {
super.init() super.init()
...@@ -29,7 +30,6 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor ...@@ -29,7 +30,6 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
channelName = "com.pichillilorenzo/flutter_inappwebview_" + id channelName = "com.pichillilorenzo/flutter_inappwebview_" + id
} }
channel = FlutterMethodChannel(name: channelName, binaryMessenger: registrar.messenger()) channel = FlutterMethodChannel(name: channelName, binaryMessenger: registrar.messenger())
channel!.setMethodCallHandler(LeakAvoider(delegate: self).handle)
myView = UIView(frame: frame) myView = UIView(frame: frame)
...@@ -40,6 +40,7 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor ...@@ -40,6 +40,7 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
let initialOptions = args["initialOptions"] as! [String: Any?] let initialOptions = args["initialOptions"] as! [String: Any?]
let contextMenu = args["contextMenu"] as? [String: Any] let contextMenu = args["contextMenu"] as? [String: Any]
let windowId = args["windowId"] as? Int64 let windowId = args["windowId"] as? Int64
let initialUserScripts = args["initialUserScripts"] as? [[String: Any]]
let options = InAppWebViewOptions() let options = InAppWebViewOptions()
let _ = options.parse(options: initialOptions) let _ = options.parse(options: initialOptions)
...@@ -55,12 +56,18 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor ...@@ -55,12 +56,18 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
webView = InAppWebView(frame: myView!.bounds, configuration: preWebviewConfiguration, IABController: nil, contextMenu: contextMenu, channel: channel!) 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] webView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
myView!.autoresizesSubviews = true myView!.autoresizesSubviews = true
myView!.autoresizingMask = [.flexibleWidth, .flexibleHeight] myView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
myView!.addSubview(webView!) myView!.addSubview(webView!)
webView!.options = options webView!.options = options
if let userScripts = initialUserScripts {
webView!.appendUserScripts(userScripts: userScripts)
}
webView!.prepare() webView!.prepare()
if windowId == nil { if windowId == nil {
...@@ -114,6 +121,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor ...@@ -114,6 +121,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
deinit { deinit {
print("FlutterWebViewController - dealloc") print("FlutterWebViewController - dealloc")
channel?.setMethodCallHandler(nil) channel?.setMethodCallHandler(nil)
methodCallDelegate?.webView = nil
methodCallDelegate = nil
webView?.dispose() webView?.dispose()
webView = nil webView = nil
myView = nil myView = nil
...@@ -145,377 +154,4 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor ...@@ -145,377 +154,4 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
webView!.loadUrl(url: url, headers: initialHeaders) 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 { ...@@ -43,7 +43,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let headers = arguments!["headers"] as! [String: String] let headers = arguments!["headers"] as! [String: String]
let contextMenu = arguments!["contextMenu"] as! [String: Any] let contextMenu = arguments!["contextMenu"] as! [String: Any]
let windowId = arguments!["windowId"] as? Int64 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) result(true)
break break
case "openFile": case "openFile":
...@@ -61,7 +62,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin { ...@@ -61,7 +62,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let headers = arguments!["headers"] as! [String: String] let headers = arguments!["headers"] as! [String: String]
let contextMenu = arguments!["contextMenu"] as! [String: Any] let contextMenu = arguments!["contextMenu"] as! [String: Any]
let windowId = arguments!["windowId"] as? Int64 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) result(true)
break break
case "openData": case "openData":
...@@ -73,7 +75,9 @@ public class InAppBrowserManager: NSObject, FlutterPlugin { ...@@ -73,7 +75,9 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let baseUrl = arguments!["baseUrl"] as! String let baseUrl = arguments!["baseUrl"] as! String
let contextMenu = arguments!["contextMenu"] as! [String: Any] let contextMenu = arguments!["contextMenu"] as! [String: Any]
let windowId = arguments!["windowId"] as? Int64 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) result(true)
break break
case "openWithSystemBrowser": case "openWithSystemBrowser":
...@@ -118,16 +122,16 @@ public class InAppBrowserManager: NSObject, FlutterPlugin { ...@@ -118,16 +122,16 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
} }
public func openUrl(uuid: String, url: String, options: [String: Any?], headers: [String: String], 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 absoluteUrl = URL(string: url)!.absoluteURL
let webViewController = prepareInAppBrowserWebViewController(options: options) let webViewController = prepareInAppBrowserWebViewController(options: options)
webViewController.uuid = uuid webViewController.uuid = uuid
webViewController.prepareMethodChannel()
webViewController.initURL = absoluteUrl webViewController.initURL = absoluteUrl
webViewController.initHeaders = headers webViewController.initHeaders = headers
webViewController.contextMenu = contextMenu webViewController.contextMenu = contextMenu
webViewController.windowId = windowId webViewController.windowId = windowId
webViewController.initUserScripts = initialUserScripts ?? []
if webViewController.isHidden { if webViewController.isHidden {
webViewController.view.isHidden = true webViewController.view.isHidden = true
...@@ -147,17 +151,17 @@ public class InAppBrowserManager: NSObject, FlutterPlugin { ...@@ -147,17 +151,17 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
} }
public func openData(uuid: String, options: [String: Any?], data: String, mimeType: String, encoding: String, 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) let webViewController = prepareInAppBrowserWebViewController(options: options)
webViewController.uuid = uuid webViewController.uuid = uuid
webViewController.prepareMethodChannel()
webViewController.initData = data webViewController.initData = data
webViewController.initMimeType = mimeType webViewController.initMimeType = mimeType
webViewController.initEncoding = encoding webViewController.initEncoding = encoding
webViewController.initBaseUrl = baseUrl webViewController.initBaseUrl = baseUrl
webViewController.contextMenu = contextMenu webViewController.contextMenu = contextMenu
webViewController.windowId = windowId webViewController.windowId = windowId
webViewController.initUserScripts = initialUserScripts ?? []
if webViewController.isHidden { if webViewController.isHidden {
webViewController.view.isHidden = true webViewController.view.isHidden = true
......
...@@ -21,7 +21,7 @@ public class InAppWebView_IBWrapper: UIView { ...@@ -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 containerWebView: InAppWebView_IBWrapper!
@IBOutlet var closeButton: UIButton! @IBOutlet var closeButton: UIButton!
...@@ -58,333 +58,17 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS ...@@ -58,333 +58,17 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
var isHidden = false var isHidden = false
var viewPrepared = false var viewPrepared = false
var previousStatusBarStyle = -1 var previousStatusBarStyle = -1
var initUserScripts: [[String: Any]] = []
var methodCallDelegate: InAppWebViewMethodHandler?
required init(coder aDecoder: NSCoder) { required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)! 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) { public override func viewWillAppear(_ animated: Bool) {
if !viewPrepared { if !viewPrepared {
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser_" + uuid, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(options: webViewOptions) let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(options: webViewOptions)
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] { if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
self.webView = webViewTransport.webView self.webView = webViewTransport.webView
...@@ -398,6 +82,11 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS ...@@ -398,6 +82,11 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
contextMenu: contextMenu, contextMenu: contextMenu,
channel: channel!) channel: channel!)
} }
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
self.webView.appendUserScripts(userScripts: initUserScripts)
self.containerWebView.addSubview(self.webView) self.containerWebView.addSubview(self.webView)
prepareConstraints() prepareConstraints()
prepareWebView() prepareWebView()
...@@ -456,6 +145,11 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS ...@@ -456,6 +145,11 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
urlField.delegate = self urlField.delegate = self
urlField.text = self.initURL?.absoluteString 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) closeButton.addTarget(self, action: #selector(self.close), for: .touchUpInside)
...@@ -799,6 +493,8 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS ...@@ -799,6 +493,8 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
onExit() onExit()
channel?.setMethodCallHandler(nil) channel?.setMethodCallHandler(nil)
channel = nil channel = nil
methodCallDelegate?.webView = nil
methodCallDelegate = nil
} }
public func onBrowserCreated() { public func onBrowserCreated() {
......
...@@ -858,6 +858,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -858,6 +858,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
// in order to have the same behavior as Android // in order to have the same behavior as Android
var activateShouldOverrideUrlLoading = false var activateShouldOverrideUrlLoading = false
var contextMenu: [String: Any]? var contextMenu: [String: Any]?
var userScripts: [WKUserScript] = []
// https://github.com/mozilla-mobile/firefox-ios/blob/50531a7e9e4d459fb11d4fcb7d4322e08103501f/Client/Frontend/Browser/ContextMenuHelper.swift // https://github.com/mozilla-mobile/firefox-ios/blob/50531a7e9e4d459fb11d4fcb7d4322e08103501f/Client/Frontend/Browser/ContextMenuHelper.swift
fileprivate var nativeHighlightLongPressRecognizer: UILongPressGestureRecognizer? fileprivate var nativeHighlightLongPressRecognizer: UILongPressGestureRecognizer?
...@@ -1161,17 +1162,52 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -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) prepareAndAddUserScripts()
configuration.userContentController.addUserScript(userScript)
if windowId != nil { if windowId != nil {
// the new created window webview has the same WKWebViewConfiguration variable reference // the new created window webview has the same WKWebViewConfiguration variable reference
return return
} }
configuration.userContentController = WKUserContentController()
configuration.preferences = WKPreferences() 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 { if let options = options {
let originalViewPortMetaTagContentJSScript = WKUserScript(source: originalViewPortMetaTagContentJS, injectionTime: .atDocumentEnd, forMainFrameOnly: true) let originalViewPortMetaTagContentJSScript = WKUserScript(source: originalViewPortMetaTagContentJS, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
...@@ -1192,14 +1228,20 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1192,14 +1228,20 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let javaScriptBridgeJSScript = WKUserScript(source: javaScriptBridgeJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) let javaScriptBridgeJSScript = WKUserScript(source: javaScriptBridgeJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(javaScriptBridgeJSScript) configuration.userContentController.addUserScript(javaScriptBridgeJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
configuration.userContentController.add(self, name: "callHandler") configuration.userContentController.add(self, name: "callHandler")
let consoleLogJSScript = WKUserScript(source: consoleLogJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) let consoleLogJSScript = WKUserScript(source: consoleLogJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(consoleLogJSScript) configuration.userContentController.addUserScript(consoleLogJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog")
configuration.userContentController.add(self, name: "consoleLog") configuration.userContentController.add(self, name: "consoleLog")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleDebug")
configuration.userContentController.add(self, name: "consoleDebug") configuration.userContentController.add(self, name: "consoleDebug")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleError")
configuration.userContentController.add(self, name: "consoleError") configuration.userContentController.add(self, name: "consoleError")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleInfo")
configuration.userContentController.add(self, name: "consoleInfo") configuration.userContentController.add(self, name: "consoleInfo")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
configuration.userContentController.add(self, name: "consoleWarn") configuration.userContentController.add(self, name: "consoleWarn")
let findElementsAtPointJSScript = WKUserScript(source: findElementsAtPointJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) let findElementsAtPointJSScript = WKUserScript(source: findElementsAtPointJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
...@@ -1218,6 +1260,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1218,6 +1260,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let findTextHighlightJSScript = WKUserScript(source: findTextHighlightJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) let findTextHighlightJSScript = WKUserScript(source: findTextHighlightJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(findTextHighlightJSScript) configuration.userContentController.addUserScript(findTextHighlightJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
configuration.userContentController.add(self, name: "onFindResultReceived") configuration.userContentController.add(self, name: "onFindResultReceived")
let onWindowFocusEventJSScript = WKUserScript(source: onWindowFocusEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) let onWindowFocusEventJSScript = WKUserScript(source: onWindowFocusEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
...@@ -1235,24 +1278,55 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1235,24 +1278,55 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let interceptFetchRequestsJSScript = WKUserScript(source: interceptFetchRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) let interceptFetchRequestsJSScript = WKUserScript(source: interceptFetchRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript) configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
} }
}
}
if #available(iOS 9.0, *) { func addAllUserScripts() -> Void {
configuration.allowsAirPlayForMediaPlayback = options.allowsAirPlayForMediaPlayback for userScript in userScripts {
configuration.allowsPictureInPictureMediaPlayback = options.allowsPictureInPictureMediaPlayback configuration.userContentController.addUserScript(userScript)
if !options.applicationNameForUserAgent.isEmpty {
configuration.applicationNameForUserAgent = options.applicationNameForUserAgent
} }
} }
configuration.preferences.javaScriptCanOpenWindowsAutomatically = options.javaScriptCanOpenWindowsAutomatically public func addUserScript(wkUserScript: WKUserScript) -> Void {
configuration.preferences.javaScriptEnabled = options.javaScriptEnabled userScripts.append(wkUserScript)
configuration.preferences.minimumFontSize = CGFloat(options.minimumFontSize) configuration.userContentController.addUserScript(wkUserScript)
}
if #available(iOS 13.0, *) { public func appendUserScript(userScript: [String: Any]) -> Void {
configuration.preferences.isFraudulentWebsiteWarningEnabled = options.isFraudulentWebsiteWarningEnabled let wkUserScript = WKUserScript(source: userScript["source"] as! String,
configuration.defaultWebpagePreferences.preferredContentMode = WKWebpagePreferences.ContentMode(rawValue: options.preferredContentMode)! 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, *) @available(iOS 10.0, *)
...@@ -2257,8 +2331,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -2257,8 +2331,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
_ in completionHandler()} _ in completionHandler()}
); );
let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window!.rootViewController!) if let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window?.rootViewController!) {
presentingViewController.present(alertController, animated: true, completion: {}) presentingViewController.present(alertController, animated: true, completion: {})
} else {
completionHandler()
}
} }
public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String,
...@@ -2320,8 +2397,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -2320,8 +2397,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
completionHandler(false) completionHandler(false)
})) }))
let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window!.rootViewController!) if let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window?.rootViewController!) {
presentingViewController.present(alertController, animated: true, completion: nil) presentingViewController.present(alertController, animated: true, completion: nil)
} else {
completionHandler(false)
}
} }
public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo,
...@@ -2393,8 +2473,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -2393,8 +2473,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
completionHandler(nil) completionHandler(nil)
})) }))
let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window!.rootViewController!) if let presentingViewController = ((self.IABController != nil) ? self.IABController! : self.window?.rootViewController!) {
presentingViewController.present(alertController, animated: true, completion: nil) presentingViewController.present(alertController, animated: true, completion: nil)
} else {
completionHandler(nil)
}
} }
public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt message: String, defaultText defaultValue: String?, initiatedByFrame frame: WKFrameInfo, 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) { ...@@ -3158,6 +3241,9 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn") configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler") configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived") configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
if #available(iOS 14.0, *) {
configuration.userContentController.removeAllScriptMessageHandlers()
}
configuration.userContentController.removeAllUserScripts() configuration.userContentController.removeAllUserScripts()
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
configuration.userContentController.removeAllContentRuleLists() 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 { ...@@ -90,6 +90,7 @@ class ChromeSafariBrowser {
args.putIfAbsent('optionsFallback', () => optionsFallback?.toMap() ?? {}); args.putIfAbsent('optionsFallback', () => optionsFallback?.toMap() ?? {});
args.putIfAbsent('contextMenuFallback', args.putIfAbsent('contextMenuFallback',
() => browserFallback?.contextMenu?.toMap() ?? {}); () => browserFallback?.contextMenu?.toMap() ?? {});
args.putIfAbsent('initialUserScriptsFallback', () => browserFallback?.initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
await _sharedChannel.invokeMethod('open', args); await _sharedChannel.invokeMethod('open', args);
this._isOpened = true; this._isOpened = true;
} }
......
...@@ -72,10 +72,41 @@ class CookieManager { ...@@ -72,10 +72,41 @@ class CookieManager {
if (Platform.isIOS) { if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo; IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion); var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) { if (version != null && version < 11.0) {
await _setCookieWithJavaScript(url: url, name: name, value: value, domain: domain,
path: path, expiresDate: expiresDate, maxAge: maxAge, isSecure: isSecure,
sameSite: sameSite, webViewController: iosBelow11WebViewController);
return;
}
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
args.putIfAbsent('name', () => name);
args.putIfAbsent('value', () => value);
args.putIfAbsent('domain', () => domain);
args.putIfAbsent('path', () => path);
args.putIfAbsent('expiresDate', () => expiresDate?.toString());
args.putIfAbsent('maxAge', () => maxAge);
args.putIfAbsent('isSecure', () => isSecure);
args.putIfAbsent('isHttpOnly', () => isHttpOnly);
args.putIfAbsent('sameSite', () => sameSite?.toValue());
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; var cookieValue = name + "=" + value + "; Domain=" + domain + "; Path=" + path;
if (expiresDate != null) if (expiresDate != null)
...@@ -92,11 +123,11 @@ class CookieManager { ...@@ -92,11 +123,11 @@ class CookieManager {
cookieValue += ";"; cookieValue += ";";
if (iosBelow11WebViewController != null) { if (webViewController != null) {
InAppWebViewGroupOptions? options = await iosBelow11WebViewController.getOptions(); InAppWebViewGroupOptions? options = await webViewController.getOptions();
if (options != null && options.crossPlatform != null && if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled == true) { options.crossPlatform!.javaScriptEnabled) {
await iosBelow11WebViewController.evaluateJavascript( await webViewController.evaluateJavascript(
source: 'document.cookie="$cookieValue"'); source: 'document.cookie="$cookieValue"');
return; return;
} }
...@@ -114,24 +145,6 @@ class CookieManager { ...@@ -114,24 +145,6 @@ class CookieManager {
await headlessWebView.run(); await headlessWebView.run();
await setCookieCompleter.future; await setCookieCompleter.future;
await headlessWebView.dispose(); await headlessWebView.dispose();
return;
}
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
args.putIfAbsent('name', () => name);
args.putIfAbsent('value', () => value);
args.putIfAbsent('domain', () => domain);
args.putIfAbsent('path', () => path);
args.putIfAbsent('expiresDate', () => expiresDate?.toString());
args.putIfAbsent('maxAge', () => maxAge);
args.putIfAbsent('isSecure', () => isSecure);
args.putIfAbsent('isHttpOnly', () => isHttpOnly);
args.putIfAbsent('sameSite', () => sameSite?.toValue());
await _channel.invokeMethod('setCookie', args);
} }
///Gets all the cookies for the given [url]. ///Gets all the cookies for the given [url].
...@@ -146,21 +159,49 @@ class CookieManager { ...@@ -146,21 +159,49 @@ class CookieManager {
Future<List<Cookie>> getCookies({required String url, InAppWebViewController? iosBelow11WebViewController}) async { Future<List<Cookie>> getCookies({required String url, InAppWebViewController? iosBelow11WebViewController}) async {
assert(url.isNotEmpty); assert(url.isNotEmpty);
List<Cookie> cookies = [];
if (Platform.isIOS) { if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo; IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion); var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) { if (version != null && version < 11.0) {
return await _getCookiesWithJavaScript(url: url, webViewController: iosBelow11WebViewController);
}
}
List<Cookie> cookies = [];
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
List<dynamic> cookieListMap =
await _channel.invokeMethod('getCookies', args);
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
cookieListMap.forEach((cookieMap) {
cookies.add(Cookie(
name: cookieMap["name"],
value: cookieMap["value"],
expiresDate: cookieMap["expiresDate"],
isSessionOnly: cookieMap["isSessionOnly"],
domain: cookieMap["domain"],
sameSite:
HTTPCookieSameSitePolicy.fromValue(cookieMap["sameSite"]),
isSecure: cookieMap["isSecure"],
isHttpOnly: cookieMap["isHttpOnly"],
path: cookieMap["path"]));
});
return cookies;
}
Future<List<Cookie>> _getCookiesWithJavaScript({required String url, InAppWebViewController? webViewController}) async {
assert(url.isNotEmpty);
List<Cookie> cookies = [];
if (iosBelow11WebViewController != null) { if (webViewController != null) {
InAppWebViewGroupOptions? options = await iosBelow11WebViewController.getOptions(); InAppWebViewGroupOptions? options = await webViewController.getOptions();
if (options != null && options.crossPlatform != null && if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled == true) { options.crossPlatform!.javaScriptEnabled) {
List<String> documentCookies = (await iosBelow11WebViewController.evaluateJavascript(source: 'document.cookie') as String) List<String> documentCookies = (await webViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList(); .split(';').map((documentCookie) => documentCookie.trim()).toList();
documentCookies.forEach((documentCookie) { documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('='); List<String> cookie = documentCookie.split('=');
...@@ -197,29 +238,6 @@ class CookieManager { ...@@ -197,29 +238,6 @@ class CookieManager {
await headlessWebView.dispose(); await headlessWebView.dispose();
return cookies; return cookies;
} }
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url);
List<dynamic> cookieListMap =
await _channel.invokeMethod('getCookies', args);
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
cookieListMap.forEach((cookieMap) {
cookies.add(Cookie(
name: cookieMap["name"],
value: cookieMap["value"],
expiresDate: cookieMap["expiresDate"],
isSessionOnly: cookieMap["isSessionOnly"],
domain: cookieMap["domain"],
sameSite:
HTTPCookieSameSitePolicy.fromValue(cookieMap["sameSite"]),
isSecure: cookieMap["isSecure"],
isHttpOnly: cookieMap["isHttpOnly"],
path: cookieMap["path"]));
});
return cookies;
}
///Gets a cookie by its [name] for the given [url]. ///Gets a cookie by its [name] for the given [url].
/// ///
...@@ -239,50 +257,10 @@ class CookieManager { ...@@ -239,50 +257,10 @@ class CookieManager {
if (Platform.isIOS) { if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo; IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion); var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) { if (version != null && version < 11.0) {
List<Cookie> cookies = await _getCookiesWithJavaScript(url: url, webViewController: iosBelow11WebViewController);
if (iosBelow11WebViewController != null) { return cookies.cast<Cookie?>().firstWhere((cookie) => cookie!.name == name, orElse: () => 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;
} }
} }
...@@ -335,7 +313,7 @@ class CookieManager { ...@@ -335,7 +313,7 @@ class CookieManager {
IosDeviceInfo iosInfo = await deviceInfo.iosInfo; IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion); var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) { 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; return;
} }
} }
...@@ -371,9 +349,9 @@ class CookieManager { ...@@ -371,9 +349,9 @@ class CookieManager {
IosDeviceInfo iosInfo = await deviceInfo.iosInfo; IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion); var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) { 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++) { 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; return;
} }
......
import 'dart:collection';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
...@@ -81,7 +82,8 @@ class HeadlessInAppWebView implements WebView { ...@@ -81,7 +82,8 @@ class HeadlessInAppWebView implements WebView {
this.initialData, this.initialData,
this.initialHeaders, this.initialHeaders,
this.initialOptions, this.initialOptions,
this.contextMenu}) { this.contextMenu,
this.initialUserScripts}) {
uuid = uuidGenerator.v4(); uuid = uuidGenerator.v4();
webViewController = new InAppWebViewController(uuid, this); webViewController = new InAppWebViewController(uuid, this);
} }
...@@ -115,7 +117,8 @@ class HeadlessInAppWebView implements WebView { ...@@ -115,7 +117,8 @@ class HeadlessInAppWebView implements WebView {
'initialHeaders': this.initialHeaders, 'initialHeaders': this.initialHeaders,
'initialOptions': this.initialOptions?.toMap() ?? {}, 'initialOptions': this.initialOptions?.toMap() ?? {},
'contextMenu': this.contextMenu?.toMap() ?? {}, 'contextMenu': this.contextMenu?.toMap() ?? {},
'windowId': this.windowId 'windowId': this.windowId,
'initialUserScripts': this.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
}); });
await _sharedChannel.invokeMethod('createHeadlessWebView', args); await _sharedChannel.invokeMethod('createHeadlessWebView', args);
} }
...@@ -168,6 +171,9 @@ class HeadlessInAppWebView implements WebView { ...@@ -168,6 +171,9 @@ class HeadlessInAppWebView implements WebView {
@override @override
final String? initialUrl; final String? initialUrl;
@override
final UnmodifiableListView<UserScript>? initialUserScripts;
@override @override
final void Function(InAppWebViewController controller, String? url)? final void Function(InAppWebViewController controller, String? url)?
onPageCommitVisible; onPageCommitVisible;
......
...@@ -19,8 +19,9 @@ class InAppBrowser { ...@@ -19,8 +19,9 @@ class InAppBrowser {
///Context menu used by the browser. It should be set before opening the browser. ///Context menu used by the browser. It should be set before opening the browser.
ContextMenu? contextMenu; ContextMenu? contextMenu;
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap = ///Initial list of user scripts to be loaded at start or end of a page loading.
HashMap<String, JavaScriptHandlerCallback>(); UnmodifiableListView<UserScript>? initialUserScripts;
bool _isOpened = false; bool _isOpened = false;
late MethodChannel _channel; late MethodChannel _channel;
static const MethodChannel _sharedChannel = static const MethodChannel _sharedChannel =
...@@ -33,14 +34,14 @@ class InAppBrowser { ...@@ -33,14 +34,14 @@ class InAppBrowser {
final int? windowId; final int? windowId;
/// ///
InAppBrowser({this.windowId}) { InAppBrowser({this.windowId, this.initialUserScripts}) {
uuid = uuidGenerator.v4(); uuid = uuidGenerator.v4();
this._channel = this._channel =
MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$uuid'); MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$uuid');
this._channel.setMethodCallHandler(handleMethod); this._channel.setMethodCallHandler(handleMethod);
_isOpened = false; _isOpened = false;
webViewController = webViewController =
new InAppWebViewController.fromInAppBrowser(uuid, this._channel, this); new InAppWebViewController.fromInAppBrowser(uuid, this._channel, this, this.initialUserScripts);
} }
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> handleMethod(MethodCall call) async {
...@@ -79,6 +80,7 @@ class InAppBrowser { ...@@ -79,6 +80,7 @@ class InAppBrowser {
args.putIfAbsent('options', () => options?.toMap() ?? {}); args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId); args.putIfAbsent('windowId', () => windowId);
args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
await _sharedChannel.invokeMethod('openUrl', args); await _sharedChannel.invokeMethod('openUrl', args);
} }
...@@ -129,6 +131,7 @@ class InAppBrowser { ...@@ -129,6 +131,7 @@ class InAppBrowser {
args.putIfAbsent('options', () => options?.toMap() ?? {}); args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId); args.putIfAbsent('windowId', () => windowId);
args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
await _sharedChannel.invokeMethod('openFile', args); await _sharedChannel.invokeMethod('openFile', args);
} }
...@@ -158,6 +161,7 @@ class InAppBrowser { ...@@ -158,6 +161,7 @@ class InAppBrowser {
args.putIfAbsent('historyUrl', () => androidHistoryUrl); args.putIfAbsent('historyUrl', () => androidHistoryUrl);
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId); args.putIfAbsent('windowId', () => windowId);
args.putIfAbsent('initialUserScripts', () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
await _sharedChannel.invokeMethod('openData', args); await _sharedChannel.invokeMethod('openData', args);
} }
......
import 'dart:async'; import 'dart:async';
import 'dart:collection';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -35,6 +36,7 @@ class InAppWebView extends StatefulWidget implements WebView { ...@@ -35,6 +36,7 @@ class InAppWebView extends StatefulWidget implements WebView {
this.initialData, this.initialData,
this.initialHeaders = const {}, this.initialHeaders = const {},
this.initialOptions, this.initialOptions,
this.initialUserScripts,
this.contextMenu, this.contextMenu,
this.onWebViewCreated, this.onWebViewCreated,
this.onLoadStart, this.onLoadStart,
...@@ -127,6 +129,9 @@ class InAppWebView extends StatefulWidget implements WebView { ...@@ -127,6 +129,9 @@ class InAppWebView extends StatefulWidget implements WebView {
@override @override
final String? initialUrl; final String? initialUrl;
@override
final UnmodifiableListView<UserScript>? initialUserScripts;
@override @override
final ContextMenu? contextMenu; final ContextMenu? contextMenu;
...@@ -365,7 +370,8 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -365,7 +370,8 @@ class _InAppWebViewState extends State<InAppWebView> {
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions?.toMap() ?? {}, 'initialOptions': widget.initialOptions?.toMap() ?? {},
'contextMenu': widget.contextMenu?.toMap() ?? {}, 'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId 'windowId': widget.windowId,
'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
}, },
creationParamsCodec: const StandardMessageCodec(), creationParamsCodec: const StandardMessageCodec(),
) )
...@@ -387,7 +393,8 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -387,7 +393,8 @@ class _InAppWebViewState extends State<InAppWebView> {
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions?.toMap() ?? {}, 'initialOptions': widget.initialOptions?.toMap() ?? {},
'contextMenu': widget.contextMenu?.toMap() ?? {}, 'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId 'windowId': widget.windowId,
'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
}, },
creationParamsCodec: const StandardMessageCodec(), creationParamsCodec: const StandardMessageCodec(),
); );
...@@ -404,7 +411,8 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -404,7 +411,8 @@ class _InAppWebViewState extends State<InAppWebView> {
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions?.toMap() ?? {}, 'initialOptions': widget.initialOptions?.toMap() ?? {},
'contextMenu': widget.contextMenu?.toMap() ?? {}, 'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId 'windowId': widget.windowId,
'initialUserScripts': widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
}, },
creationParamsCodec: const StandardMessageCodec(), creationParamsCodec: const StandardMessageCodec(),
); );
......
...@@ -45,6 +45,7 @@ class InAppWebViewController { ...@@ -45,6 +45,7 @@ class InAppWebViewController {
MethodChannel('com.pichillilorenzo/flutter_inappwebview_static'); MethodChannel('com.pichillilorenzo/flutter_inappwebview_static');
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap = Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
HashMap<String, JavaScriptHandlerCallback>(); HashMap<String, JavaScriptHandlerCallback>();
List<UserScript> _userScripts = [];
// ignore: unused_field // ignore: unused_field
bool _isOpened = false; bool _isOpened = false;
...@@ -72,14 +73,16 @@ class InAppWebViewController { ...@@ -72,14 +73,16 @@ class InAppWebViewController {
MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id'); MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id');
this._channel.setMethodCallHandler(handleMethod); this._channel.setMethodCallHandler(handleMethod);
this._webview = webview; this._webview = webview;
this._userScripts = List<UserScript>.from(webview.initialUserScripts ?? []);
this._init(); this._init();
} }
InAppWebViewController.fromInAppBrowser( InAppWebViewController.fromInAppBrowser(
String uuid, MethodChannel channel, InAppBrowser inAppBrowser) { String uuid, MethodChannel channel, InAppBrowser inAppBrowser, UnmodifiableListView<UserScript>? initialUserScripts) {
this._inAppBrowserUuid = uuid; this._inAppBrowserUuid = uuid;
this._channel = channel; this._channel = channel;
this._inAppBrowser = inAppBrowser; this._inAppBrowser = inAppBrowser;
this._userScripts = List<UserScript>.from(initialUserScripts ?? []);
this._init(); this._init();
} }
...@@ -1972,6 +1975,59 @@ class InAppWebViewController { ...@@ -1972,6 +1975,59 @@ class InAppWebViewController {
return null; 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. ///Gets the default user agent.
/// ///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context) ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context)
......
...@@ -4503,3 +4503,86 @@ class LoginRequest { ...@@ -4503,3 +4503,86 @@ class LoginRequest {
return toMap().toString(); 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 'dart:typed_data';
import 'context_menu.dart'; import 'context_menu.dart';
...@@ -621,6 +622,11 @@ abstract class WebView { ...@@ -621,6 +622,11 @@ abstract class WebView {
///Context menu which contains custom menu items to be shown when [ContextMenu] is presented. ///Context menu which contains custom menu items to be shown when [ContextMenu] is presented.
final ContextMenu? contextMenu; 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( WebView(
{this.windowId, {this.windowId,
this.onWebViewCreated, this.onWebViewCreated,
...@@ -679,5 +685,6 @@ abstract class WebView { ...@@ -679,5 +685,6 @@ abstract class WebView {
this.initialData, this.initialData,
this.initialHeaders, this.initialHeaders,
this.initialOptions, 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