Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403

This commit is contained in:
Lorenzo Pichilli 2020-06-29 16:34:08 +02:00
parent 9d92911ab2
commit eea691664b
26 changed files with 1590 additions and 413 deletions

View File

@ -1,12 +1,24 @@
## 4.0.0
- Updated `onCreateWindow`, `onJsAlert`, `onJsConfirm`, `onJsPrompt` webview events
- Added `onCloseWindow`, `onTitleChanged`, `onWindowFocus`, `onWindowBlur` webview events
- Added `androidOnRequestFocus`, `androidOnReceivedIcon`, `androidOnReceivedTouchIconUrl`, `androidOnJsBeforeUnload`, `androidOnReceivedLoginRequest` Android-specific webview events
- Fixed "SFSafariViewController doesn't open like a native iOS modal" [#403](https://github.com/pichillilorenzo/flutter_inappwebview/issues/403)
### BREAKING CHANGES
- Updated `onCreateWindow`, `onJsAlert`, `onJsConfirm`, `onJsPrompt` webview event
- Renamed `OnCreateWindowRequest` class to `CreateWindowRequest`
## 3.4.0+2
- Revert default `InAppWebView.gestureRecognizers` value to null on Android
- Reverted default `InAppWebView.gestureRecognizers` value to null on Android
## 3.4.0+1
- Update README.md
- Update missing docs
- Fix pub.dev Health suggestions and Analysis suggestions
- Updated README.md
- Updated missing docs
- Fixed pub.dev Health suggestions and Analysis suggestions
## 3.4.0

View File

@ -628,6 +628,7 @@ Event names that starts with `android` or `ios` are events platform-specific.
* `onDownloadStart`: Event fired when InAppWebView recognizes a downloadable file (to use this event, the `useOnDownloadStart` option must be `true`). To download the file, you can use the [flutter_downloader](https://pub.dev/packages/flutter_downloader) plugin.
* `onLoadResourceCustomScheme`: Event fired when the InAppWebView finds the `custom-scheme` while loading a resource. Here you can handle the url request and return a CustomSchemeResponse to load a specific resource encoded to `base64`.
* `onCreateWindow`: Event fired when the InAppWebView requests the host application to create a new window, for example when trying to open a link with `target="_blank"` or when `window.open()` is called by JavaScript side.
* `onCloseWindow`: Event fired when the host application should close the given WebView and remove it from the view system if necessary.
* `onJsAlert`: Event fired when javascript calls the `alert()` method to display an alert dialog.
* `onJsConfirm`: Event fired when javascript calls the `confirm()` method to display a confirm dialog.
* `onJsPrompt`: Event fired when javascript calls the `prompt()` method to display a prompt dialog.
@ -644,6 +645,9 @@ Event names that starts with `android` or `ios` are events platform-specific.
* `onEnterFullscreen`: Event fired when the current page has entered full screen mode.
* `onExitFullscreen`: Event fired when the current page has exited full screen mode.
* `onPageCommitVisible`: Called when the web view begins to receive web content.
* `onTitleChanged`: Event fired when a change in the document title occurred.
* `onWindowFocus`: Event fired when the JavaScript `window` object of the WebView has received focus. This is the result of the `focus` JavaScript event applied to the `window` object.
* `onWindowBlur`: Event fired when the JavaScript `window` object of the WebView has lost focus. This is the result of the `blur` JavaScript event applied to the `window` object.
* `androidOnSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android).
* `androidOnPermissionRequest`: Event fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android).
* `androidOnGeolocationPermissionsShowPrompt`: Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin (available only on Android).
@ -654,6 +658,11 @@ Event names that starts with `android` or `ios` are events platform-specific.
* `androidOnRenderProcessUnresponsive`: Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript (available only on Android).
* `androidOnFormResubmission`: As the host application if the browser should resend data as the requested page was a result of a POST. The default is to not resend the data (available only on Android).
* `androidOnScaleChanged`: Event fired when the scale applied to the WebView has changed (available only on Android).
* `androidOnRequestFocus`: Event fired when there is a request to display and focus for this WebView (available only on Android).
* `androidOnReceivedIcon`: Event fired when there is new favicon for the current page (available only on Android).
* `androidOnReceivedTouchIconUrl`: Event fired when there is an url for an apple-touch-icon (available only on Android).
* `androidOnJsBeforeUnload`: Event fired when the client should display a dialog to confirm navigation away from the current page. This is the result of the `onbeforeunload` javascript event (available only on Android).
* `androidOnReceivedLoginRequest`: Event fired when a request to automatically log in the user has been processed (available only on Android).
* `iosOnWebContentProcessDidTerminate`: Invoked when the web view's web content process is terminated (available only on iOS).
* `iosOnDidReceiveServerRedirectForProvisionalNavigation`: Called when a web view receives a server redirect (available only on iOS).

View File

@ -45,7 +45,8 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl
Map<String, String> headersFallback = (Map<String, String>) call.argument("headersFallback");
HashMap<String, Object> optionsFallback = (HashMap<String, Object>) call.argument("optionsFallback");
HashMap<String, Object> contextMenuFallback = (HashMap<String, Object>) call.argument("contextMenuFallback");
open(activity, uuid, url, options, menuItemList, uuidFallback, headersFallback, optionsFallback, contextMenuFallback, result);
Integer windowIdFallback = (Integer) call.argument("windowIdFallback");
open(activity, uuid, url, options, menuItemList, uuidFallback, headersFallback, optionsFallback, contextMenuFallback, windowIdFallback, result);
}
break;
default:
@ -54,7 +55,8 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl
}
public void open(Activity activity, String uuid, String url, HashMap<String, Object> options, List<HashMap<String, Object>> menuItemList, String uuidFallback,
Map<String, String> headersFallback, HashMap<String, Object> optionsFallback, HashMap<String, Object> contextMenuFallback, MethodChannel.Result result) {
Map<String, String> headersFallback, HashMap<String, Object> optionsFallback, HashMap<String, Object> contextMenuFallback, Integer windowIdFallback,
MethodChannel.Result result) {
Intent intent = null;
Bundle extras = new Bundle();
@ -68,6 +70,8 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl
extras.putSerializable("headers", (Serializable) headersFallback);
extras.putSerializable("contextMenu", (Serializable) contextMenuFallback);
extras.putInt("windowId", windowIdFallback != null ? windowIdFallback : -1);
if (CustomTabActivityHelper.isAvailable(activity)) {
intent = new Intent(activity, ChromeCustomTabsActivity.class);
}

View File

@ -5,12 +5,14 @@ import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
@ -25,6 +27,7 @@ import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewChromeClient;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;
@ -41,6 +44,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
static final String LOG_TAG = "InAppBrowserActivity";
public MethodChannel channel;
public Integer windowId;
public String uuid;
public InAppWebView webView;
public ActionBar actionBar;
@ -63,6 +67,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
Bundle b = getIntent().getExtras();
uuid = b.getString("uuid");
windowId = b.getInt("windowId");
channel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappbrowser_" + uuid);
channel.setMethodCallHandler(this);
@ -70,6 +75,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
setContentView(R.layout.activity_web_view);
webView = findViewById(R.id.webView);
webView.windowId = windowId;
webView.inAppBrowserActivity = this;
webView.channel = channel;
@ -90,19 +96,27 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
prepareView();
Boolean isData = b.getBoolean("isData");
if (!isData) {
headers = (HashMap<String, String>) b.getSerializable("headers");
String url = b.getString("url");
webView.loadUrl(url, headers);
}
else {
String data = b.getString("data");
String mimeType = b.getString("mimeType");
String encoding = b.getString("encoding");
String baseUrl = b.getString("baseUrl");
String historyUrl = b.getString("historyUrl");
webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
if (windowId != -1) {
Message resultMsg = InAppWebViewChromeClient.windowWebViewMessages.get(windowId);
if (resultMsg != null) {
((WebView.WebViewTransport) resultMsg.obj).setWebView(webView);
resultMsg.sendToTarget();
}
} else {
Boolean isData = b.getBoolean("isData");
if (!isData) {
headers = (HashMap<String, String>) b.getSerializable("headers");
String url = b.getString("url");
webView.loadUrl(url, headers);
}
else {
String data = b.getString("data");
String mimeType = b.getString("mimeType");
String encoding = b.getString("encoding");
String baseUrl = b.getString("baseUrl");
String historyUrl = b.getString("historyUrl");
webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
}
Map<String, Object> obj = new HashMap<>();
@ -565,30 +579,14 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
}
public void close(final MethodChannel.Result result) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Map<String, Object> obj = new HashMap<>();
channel.invokeMethod("onExit", obj);
Map<String, Object> obj = new HashMap<>();
channel.invokeMethod("onExit", obj);
webView.setWebViewClient(new WebViewClient() {
// NB: wait for about:blank before dismissing
public void onPageFinished(WebView view, String url) {
hide();
finish();
}
});
// NB: From SDK 19: "If you call methods on WebView from any thread
// other than your app's UI thread, it can cause unexpected results."
// http://developer.android.com/guide/webapps/migrating.html#Threads
webView.loadUrl("about:blank");
if (result != null) {
result.success(true);
}
}
});
dispose();
if (result != null) {
result.success(true);
}
}
public void reload() {
@ -984,6 +982,8 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
if (Shared.activityPluginBinding != null) {
Shared.activityPluginBinding.removeActivityResultListener(webView.inAppWebViewChromeClient);
}
ViewGroup vg = (ViewGroup) (webView.getParent());
vg.removeView(webView);
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
@ -993,6 +993,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
}
});
webView.loadUrl("about:blank");
finish();
}
}

View File

@ -71,7 +71,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
Map<String, String> headers = (Map<String, String>) call.argument("headers");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
openUrl(activity, uuid, url, options, headers, contextMenu);
Integer windowId = (Integer) call.argument("windowId");
openUrl(activity, uuid, url, options, headers, contextMenu, windowId);
}
result.success(true);
break;
@ -88,7 +89,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
Map<String, String> headers = (Map<String, String>) call.argument("headers");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
openUrl(activity, uuid, url, options, headers, contextMenu);
Integer windowId = (Integer) call.argument("windowId");
openUrl(activity, uuid, url, options, headers, contextMenu, windowId);
}
result.success(true);
break;
@ -101,7 +103,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
String baseUrl = (String) call.argument("baseUrl");
String historyUrl = (String) call.argument("historyUrl");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) call.argument("contextMenu");
openData(activity, uuid, options, data, mimeType, encoding, baseUrl, historyUrl, contextMenu);
Integer windowId = (Integer) call.argument("windowId");
openData(activity, uuid, options, data, mimeType, encoding, baseUrl, historyUrl, contextMenu, windowId);
}
result.success(true);
break;
@ -192,7 +195,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
}
}
public void openUrl(Activity activity, String uuid, String url, HashMap<String, Object> options, Map<String, String> headers, HashMap<String, Object> contextMenu) {
public void openUrl(Activity activity, String uuid, String url, HashMap<String, Object> options, Map<String, String> headers,
HashMap<String, Object> contextMenu, Integer windowId) {
Bundle extras = new Bundle();
extras.putString("fromActivity", activity.getClass().getName());
extras.putString("url", url);
@ -201,10 +205,12 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
extras.putSerializable("options", options);
extras.putSerializable("headers", (Serializable) headers);
extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1);
startInAppBrowserActivity(activity, extras);
}
public void openData(Activity activity, String uuid, HashMap<String, Object> options, String data, String mimeType, String encoding, String baseUrl, String historyUrl, HashMap<String, Object> contextMenu) {
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) {
Bundle extras = new Bundle();
extras.putBoolean("isData", true);
extras.putString("uuid", uuid);
@ -215,6 +221,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
extras.putString("baseUrl", baseUrl);
extras.putString("historyUrl", historyUrl);
extras.putSerializable("contextMenu", (Serializable) contextMenu);
extras.putInt("windowId", windowId != null ? windowId : -1);
startInAppBrowserActivity(activity, extras);
}

View File

@ -1,11 +1,11 @@
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.content.Context;
import android.content.MutableContextWrapper;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.webkit.ValueCallback;
@ -25,6 +25,7 @@ import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import io.flutter.embedding.android.FlutterView;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
@ -54,6 +55,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
final Map<String, String> initialHeaders = (Map<String, String>) params.get("initialHeaders");
Map<String, Object> initialOptions = (Map<String, Object>) params.get("initialOptions");
Map<String, Object> contextMenu = (Map<String, Object>) params.get("contextMenu");
Integer windowId = (Integer) params.get("windowId");
InAppWebViewOptions options = new InAppWebViewOptions();
options.parse(initialOptions);
@ -70,7 +72,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
// displayListenerProxy.onPostWebViewInitialization(displayManager);
// mMutableContext.setBaseContext(context);
webView = new InAppWebView(Shared.activity, this, id, options, contextMenu, containerView);
webView = new InAppWebView(Shared.activity, this, id, windowId, options, contextMenu, containerView);
displayListenerProxy.onPostWebViewInitialization(displayManager);
// fix https://github.com/pichillilorenzo/flutter_inappwebview/issues/182
@ -89,33 +91,41 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
webView.prepare();
if (initialFile != null) {
try {
initialUrl = Util.getUrlAsset(initialFile);
} catch (IOException e) {
e.printStackTrace();
Log.e(LOG_TAG, initialFile + " asset file cannot be found!", e);
return;
if (windowId != null) {
Message resultMsg = InAppWebViewChromeClient.windowWebViewMessages.get(windowId);
if (resultMsg != null) {
((WebView.WebViewTransport) resultMsg.obj).setWebView(webView);
resultMsg.sendToTarget();
}
}
final String finalInitialUrl = initialUrl;
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
if (initialData != null) {
String data = initialData.get("data");
String mimeType = initialData.get("mimeType");
String encoding = initialData.get("encoding");
String baseUrl = initialData.get("baseUrl");
String historyUrl = initialData.get("historyUrl");
webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
} else {
if (initialFile != null) {
try {
initialUrl = Util.getUrlAsset(initialFile);
} catch (IOException e) {
e.printStackTrace();
Log.e(LOG_TAG, initialFile + " asset file cannot be found!", e);
return;
}
else
webView.loadUrl(finalInitialUrl, initialHeaders);
}
});
final String finalInitialUrl = initialUrl;
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
if (initialData != null) {
String data = initialData.get("data");
String mimeType = initialData.get("mimeType");
String encoding = initialData.get("encoding");
String baseUrl = initialData.get("baseUrl");
String historyUrl = initialData.get("historyUrl");
webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
else
webView.loadUrl(finalInitialUrl, initialHeaders);
}
});
}
if (containerView == null && id instanceof String) {
Map<String, Object> obj = new HashMap<>();
@ -513,6 +523,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
}
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
webView.dispose();
webView.destroy();

View File

@ -35,9 +35,11 @@ import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
import android.webkit.WebHistoryItem;
import android.webkit.WebSettings;
import android.webkit.WebStorage;
import android.webkit.WebViewClient;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
@ -46,6 +48,7 @@ import androidx.annotation.Keep;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import androidx.webkit.WebViewRenderProcessClient;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlocker;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerAction;
@ -84,6 +87,7 @@ final public class InAppWebView extends InputAwareWebView {
public FlutterWebView flutterWebView;
public MethodChannel channel;
public Object id;
public Integer windowId;
public InAppWebViewClient inAppWebViewClient;
public InAppWebViewChromeClient inAppWebViewChromeClient;
public InAppWebViewRenderProcessClient inAppWebViewRenderProcessClient;
@ -108,6 +112,13 @@ final public class InAppWebView extends InputAwareWebView {
public Runnable checkContextMenuShouldBeClosedTask;
public int newCheckContextMenuShouldBeClosedTaskTask = 100; // ms
static final String scriptsWrapperJS = "(function(){" +
" if (window." + JavaScriptBridgeInterface.name + "._scriptsLoaded == null) {" +
" $PLACEHOLDER_VALUE" +
" window." + JavaScriptBridgeInterface.name + "._scriptsLoaded = true;" +
" }" +
"})();";
static final String consoleLogJS = "(function(console) {" +
" var oldLogs = {" +
" 'log': console.log," +
@ -138,7 +149,12 @@ final public class InAppWebView extends InputAwareWebView {
" window." + JavaScriptBridgeInterface.name + ".callHandler('onPrint', window.location.href);" +
"};";
static final String platformReadyJS = "window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));";
static final String platformReadyJS = "(function() {" +
" if (window." + JavaScriptBridgeInterface.name + "._platformReady == null) {" +
" window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));" +
" window." + JavaScriptBridgeInterface.name + "._platformReady = true;" +
" }" +
"})();";
static final String variableForOnLoadResourceJS = "window._flutter_inappwebview_useOnLoadResource";
static final String enableVariableForOnLoadResourceJS = variableForOnLoadResourceJS + " = $PLACEHOLDER_VALUE;";
@ -592,6 +608,18 @@ final public class InAppWebView extends InputAwareWebView {
" });" +
"})();";
static final String onWindowFocusEventJS = "(function(){" +
" window.addEventListener('focus', function(e) {" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onWindowFocus');" +
" });" +
"})();";
static final String onWindowBlurEventJS = "(function(){" +
" window.addEventListener('blur', function(e) {" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onWindowBlur');" +
" });" +
"})();";
public InAppWebView(Context context) {
super(context);
}
@ -604,7 +632,7 @@ final public class InAppWebView extends InputAwareWebView {
super(context, attrs, defaultStyle);
}
public InAppWebView(Context context, Object obj, Object id, 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) {
super(context, containerView);
if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
@ -612,6 +640,7 @@ final public class InAppWebView extends InputAwareWebView {
this.flutterWebView = (FlutterWebView) obj;
this.channel = (this.inAppBrowserActivity != null) ? this.inAppBrowserActivity.channel : this.flutterWebView.channel;
this.id = id;
this.windowId = windowId;
this.options = options;
this.contextMenu = contextMenu;
Shared.activity.registerForContextMenu(this);
@ -1949,12 +1978,22 @@ final public class InAppWebView extends InputAwareWebView {
@Override
public void dispose() {
if (windowId != null && InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
}
headlessHandler.removeCallbacksAndMessages(null);
mHandler.removeCallbacksAndMessages(null);
removeJavascriptInterface(JavaScriptBridgeInterface.name);
removeAllViews();
if (checkContextMenuShouldBeClosedTask != null)
removeCallbacks(checkContextMenuShouldBeClosedTask);
if (checkScrollStoppedTask != null)
removeCallbacks(checkScrollStoppedTask);
super.dispose();
}
@Override
public void destroy() {
headlessHandler.removeCallbacksAndMessages(null);
super.destroy();
}
}

View File

@ -3,7 +3,6 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
@ -30,11 +29,11 @@ import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
@ -45,6 +44,7 @@ import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -64,6 +64,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
public MethodChannel channel;
public static Map<Integer, Message> windowWebViewMessages = new HashMap<>();
private static int windowAutoincrementId = 0;
private static final String fileProviderAuthorityExtension = "flutter_inappwebview.fileprovider";
@ -132,7 +134,6 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
this.mCustomViewCallback.onCustomViewHidden();
this.mCustomViewCallback = null;
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
@ -175,7 +176,9 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
obj.put("message", message);
obj.put("iosIsMainFrame", null);
channel.invokeMethod("onJsAlert", obj, new MethodChannel.Result() {
@Override
@ -260,7 +263,9 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
obj.put("message", message);
obj.put("iosIsMainFrame", null);
channel.invokeMethod("onJsConfirm", obj, new MethodChannel.Result() {
@Override
@ -358,8 +363,10 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
obj.put("message", message);
obj.put("defaultValue", defaultValue);
obj.put("iosIsMainFrame", null);
channel.invokeMethod("onJsPrompt", obj, new MethodChannel.Result() {
@Override
@ -472,47 +479,172 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
alertDialog.show();
}
@Override
public boolean onJsBeforeUnload(final WebView view, String url, final String message,
final JsResult result) {
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
obj.put("message", message);
obj.put("iosIsMainFrame", null);
channel.invokeMethod("onJsBeforeUnload", obj, new MethodChannel.Result() {
@Override
public void success(Object response) {
String responseMessage = null;
String confirmButtonTitle = null;
String cancelButtonTitle = null;
if (response != null) {
Map<String, Object> responseMap = (Map<String, Object>) response;
responseMessage = (String) responseMap.get("message");
confirmButtonTitle = (String) responseMap.get("confirmButtonTitle");
cancelButtonTitle = (String) responseMap.get("cancelButtonTitle");
Boolean handledByClient = (Boolean) responseMap.get("handledByClient");
if (handledByClient != null && handledByClient) {
Integer action = (Integer) responseMap.get("action");
action = action != null ? action : 1;
switch (action) {
case 0:
result.confirm();
break;
case 1:
default:
result.cancel();
}
return;
}
}
createBeforeUnloadDialog(view, message, result, responseMessage, confirmButtonTitle, cancelButtonTitle);
}
@Override
public void error(String s, String s1, Object o) {
Log.e(LOG_TAG, s + ", " + s1);
result.cancel();
}
@Override
public void notImplemented() {
createConfirmDialog(view, message, result, null, null, null);
}
});
return true;
}
public void createBeforeUnloadDialog(WebView view, String message, final JsResult result, String responseMessage, String confirmButtonTitle, String cancelButtonTitle) {
String alertMessage = (responseMessage != null && !responseMessage.isEmpty()) ? responseMessage : message;
DialogInterface.OnClickListener confirmClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
dialog.dismiss();
}
};
DialogInterface.OnClickListener cancelClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
dialog.dismiss();
}
};
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage);
if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) {
alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener);
} else {
alertDialogBuilder.setPositiveButton(android.R.string.ok, confirmClickListener);
}
if (cancelButtonTitle != null && !cancelButtonTitle.isEmpty()) {
alertDialogBuilder.setNegativeButton(cancelButtonTitle, cancelClickListener);
} else {
alertDialogBuilder.setNegativeButton(android.R.string.cancel, cancelClickListener);
}
alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
result.cancel();
dialog.dismiss();
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
alertDialog.show();
}
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, final Message resultMsg) {
windowAutoincrementId++;
final int windowId = windowAutoincrementId;
WebView.HitTestResult result = view.getHitTestResult();
String url = result.getExtra();
final Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
obj.put("windowId", windowId);
obj.put("androidIsDialog", isDialog);
obj.put("androidIsUserGesture", isUserGesture);
obj.put("iosWKNavigationType", null);
obj.put("iosIsForMainFrame", null);
WebView.HitTestResult result = view.getHitTestResult();
String data = result.getExtra();
windowWebViewMessages.put(windowId, resultMsg);
if (data == null) {
// to get the URL, create a temp weview
final WebView tempWebView = new WebView(view.getContext());
// disable javascript
tempWebView.getSettings().setJavaScriptEnabled(false);
tempWebView.setWebViewClient(new WebViewClient(){
@Override
public void onPageStarted(WebView v, String url, Bitmap favicon) {
super.onPageStarted(v, url, favicon);
obj.put("url", url);
channel.invokeMethod("onCreateWindow", obj);
// stop webview loading
v.stopLoading();
// this will throw the error "Application attempted to call on a destroyed AwAutofillManager" that will kill the webview.
// that's ok.
v.destroy();
channel.invokeMethod("onCreateWindow", obj, new MethodChannel.Result() {
@Override
public void success(@Nullable Object result) {
if (result == null && InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
}
});
((WebView.WebViewTransport) resultMsg.obj).setWebView(tempWebView);
resultMsg.sendToTarget();
return true;
}
}
obj.put("url", data);
channel.invokeMethod("onCreateWindow", obj);
return false;
@Override
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
if (InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
}
}
@Override
public void notImplemented() {
if (InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
}
}
});
return true;
}
@Override
public void onCloseWindow(WebView window) {
final Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
channel.invokeMethod("onCloseWindow", obj);
super.onCloseWindow(window);
}
@Override
public void onRequestFocus(WebView view) {
final Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
channel.invokeMethod("onRequestFocus", obj);
super.onCloseWindow(view);
}
@Override
@ -588,13 +720,53 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (inAppBrowserActivity != null && inAppBrowserActivity.actionBar != null && inAppBrowserActivity.options.toolbarTopFixedTitle.isEmpty())
if (inAppBrowserActivity != null && inAppBrowserActivity.actionBar != null && inAppBrowserActivity.options.toolbarTopFixedTitle.isEmpty()) {
inAppBrowserActivity.actionBar.setTitle(title);
}
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("title", title);
channel.invokeMethod("onTitleChanged", obj);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
icon.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
String errorMessage = e.getMessage();
if (errorMessage != null) {
Log.e(LOG_TAG, errorMessage);
}
}
icon.recycle();
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("icon", byteArrayOutputStream.toByteArray());
channel.invokeMethod("onReceivedIcon", obj);
}
@Override
public void onReceivedTouchIconUrl(WebView view,
String url,
boolean precomposed) {
super.onReceivedTouchIconUrl(view, url, precomposed);
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
obj.put("precomposed", precomposed);
channel.invokeMethod("onReceivedTouchIconUrl", obj);
}
protected ViewGroup getRootView() {

View File

@ -2,10 +2,8 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
@ -29,20 +27,13 @@ import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.Credential;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -185,8 +176,14 @@ public class InAppWebViewClient extends WebViewClient {
js += InAppWebView.resourceObserverJS.replaceAll("[\r\n]+", "");
}
js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS.replaceAll("[\r\n]+", "");
js += InAppWebView.onWindowFocusEventJS.replaceAll("[\r\n]+", "");
js += InAppWebView.onWindowBlurEventJS.replaceAll("[\r\n]+", "");
js += InAppWebView.printJS.replaceAll("[\r\n]+", "");
js = InAppWebView.scriptsWrapperJS
.replace("$PLACEHOLDER_VALUE", js)
.replaceAll("[\r\n]+", "");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
} else {
@ -817,6 +814,18 @@ public class InAppWebViewClient extends WebViewClient {
return super.onRenderProcessGone(view, detail);
}
@Override
public void onReceivedLoginRequest(WebView view, String realm, String account, String args) {
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("realm", realm);
obj.put("account", account);
obj.put("args", args);
channel.invokeMethod("onReceivedLoginRequest", obj);
}
@Override
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {

View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.10/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.10/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+1/","dependencies":[]}],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-06-23 10:38:00.775824","version":"1.17.4"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.10/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.10/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+1/","dependencies":[]}],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-06-29 16:24:39.876196","version":"1.17.4"}

View File

@ -48,8 +48,9 @@ class InAppWebViewOnCreateWindowTestState extends WidgetTestState {
});
}
},
onCreateWindow: (InAppWebViewController controller, OnCreateWindowRequest onCreateWindowRequest) {
controller.loadUrl(url: onCreateWindowRequest.url);
onCreateWindow: (InAppWebViewController controller, CreateWindowRequest createWindowRequest) async {
controller.loadUrl(url: createWindowRequest.url);
return null;
},
),
),

View File

@ -66,24 +66,23 @@ class InAppWebViewOnJsDialogTestState extends WidgetTestState {
});
},
onJsAlert:
(InAppWebViewController controller, String message) async {
(InAppWebViewController controller, JsAlertRequest jsAlertRequest) async {
JsAlertResponseAction action =
await createAlertDialog(context, message);
await createAlertDialog(context, jsAlertRequest.message);
return JsAlertResponse(
handledByClient: true, action: action);
},
onJsConfirm:
(InAppWebViewController controller, String message) async {
(InAppWebViewController controller, JsConfirmRequest jsConfirmRequest) async {
JsConfirmResponseAction action =
await createConfirmDialog(context, message);
await createConfirmDialog(context, jsConfirmRequest.message);
return JsConfirmResponse(
handledByClient: true, action: action);
},
onJsPrompt: (InAppWebViewController controller, String message,
String defaultValue) async {
_textFieldController.text = defaultValue;
onJsPrompt: (InAppWebViewController controller, JsPromptRequest jsPromptRequest) async {
_textFieldController.text = jsPromptRequest.defaultValue;
JsPromptResponseAction action =
await createPromptDialog(context, message);
await createPromptDialog(context, jsPromptRequest.message);
return JsPromptResponse(
handledByClient: true,
action: action,

View File

@ -16,9 +16,6 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
static var registrar: FlutterPluginRegistrar?
static var channel: FlutterMethodChannel?
var tmpWindow: UIWindow?
private var previousStatusBarStyle = -1
public static func register(with registrar: FlutterPluginRegistrar) {
}
@ -43,7 +40,10 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
let headersFallback = arguments!["headersFallback"] as? [String: String]
let optionsFallback = arguments!["optionsFallback"] as? [String: Any?]
let contextMenuFallback = arguments!["contextMenuFallback"] as? [String: Any]
open(uuid: uuid, url: url, options: options, menuItemList: menuItemList, uuidFallback: uuidFallback, headersFallback: headersFallback, optionsFallback: optionsFallback, contextMenuFallback: contextMenuFallback, result: result)
let windowIdFallback = arguments!["windowIdFallback"] as? Int64
open(uuid: uuid, url: url, options: options, menuItemList: menuItemList, uuidFallback: uuidFallback,
headersFallback: headersFallback, optionsFallback: optionsFallback, contextMenuFallback: contextMenuFallback,
windowIdFallback: windowIdFallback, result: result)
break
default:
result(FlutterMethodNotImplemented)
@ -51,51 +51,40 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
}
}
public func open(uuid: String, url: String, options: [String: Any?], menuItemList: [[String: Any]], uuidFallback: String?, headersFallback: [String: String]?, optionsFallback: [String: Any?]?, contextMenuFallback: [String: Any]?, result: @escaping FlutterResult) {
public func open(uuid: String, url: String, options: [String: Any?], menuItemList: [[String: Any]], uuidFallback: String?,
headersFallback: [String: String]?, optionsFallback: [String: Any?]?, contextMenuFallback: [String: Any]?,
windowIdFallback: Int64?, result: @escaping FlutterResult) {
let absoluteUrl = URL(string: url)!.absoluteURL
if self.previousStatusBarStyle == -1 {
self.previousStatusBarStyle = UIApplication.shared.statusBarStyle.rawValue
}
if !(self.tmpWindow != nil) {
let frame: CGRect = UIScreen.main.bounds
self.tmpWindow = UIWindow(frame: frame)
}
let tmpController = UIViewController()
let baseWindowLevel = UIApplication.shared.keyWindow?.windowLevel
self.tmpWindow!.rootViewController = tmpController
self.tmpWindow!.windowLevel = UIWindow.Level(baseWindowLevel!.rawValue + 1.0)
self.tmpWindow!.makeKeyAndVisible()
if #available(iOS 9.0, *) {
let safariOptions = SafariBrowserOptions()
let _ = safariOptions.parse(options: options)
let safari: SafariViewController
if #available(iOS 11.0, *) {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = safariOptions.entersReaderIfAvailable
config.barCollapsingEnabled = safariOptions.barCollapsingEnabled
if let flutterViewController = UIApplication.shared.delegate?.window.unsafelyUnwrapped?.rootViewController as? FlutterViewController {
let safariOptions = SafariBrowserOptions()
let _ = safariOptions.parse(options: options)
safari = SafariViewController(url: absoluteUrl, configuration: config)
} else {
// Fallback on earlier versions
safari = SafariViewController(url: absoluteUrl)
}
safari.uuid = uuid
safari.menuItemList = menuItemList
safari.prepareMethodChannel()
safari.delegate = safari
safari.tmpWindow = tmpWindow
safari.safariOptions = safariOptions
safari.prepareSafariBrowser()
tmpController.present(safari, animated: true) {
result(true)
let safari: SafariViewController
if #available(iOS 11.0, *) {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = safariOptions.entersReaderIfAvailable
config.barCollapsingEnabled = safariOptions.barCollapsingEnabled
safari = SafariViewController(url: absoluteUrl, configuration: config)
} else {
// Fallback on earlier versions
safari = SafariViewController(url: absoluteUrl)
}
safari.uuid = uuid
safari.menuItemList = menuItemList
safari.prepareMethodChannel()
safari.delegate = safari
safari.safariOptions = safariOptions
safari.prepareSafariBrowser()
flutterViewController.present(safari, animated: true) {
result(true)
}
}
return
}
@ -106,7 +95,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
return
}
SwiftFlutterPlugin.instance!.inAppBrowserManager!.openUrl(uuid: uuidFallback!, url: url, options: optionsFallback ?? [:], headers: headersFallback ?? [:], contextMenu: contextMenuFallback ?? [:])
SwiftFlutterPlugin.instance!.inAppBrowserManager!.openUrl(uuid: uuidFallback!, url: url, options: optionsFallback ?? [:], headers: headersFallback ?? [:], contextMenu: contextMenuFallback ?? [:], windowId: windowIdFallback)
}
}
}

View File

@ -39,47 +39,62 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
let initialHeaders = args["initialHeaders"] as? [String: String]
let initialOptions = args["initialOptions"] as! [String: Any?]
let contextMenu = args["contextMenu"] as? [String: Any]
let windowId = args["windowId"] as? Int64
let options = InAppWebViewOptions()
let _ = options.parse(options: initialOptions)
let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(options: options)
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
webView = webViewTransport.webView
webView!.frame = myView!.bounds
webView!.IABController = nil
webView!.contextMenu = contextMenu
webView!.channel = channel!
} else {
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!)
webView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
myView!.autoresizesSubviews = true
myView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
myView!.addSubview(webView!)
webView!.options = options
webView!.prepare()
if windowId == nil {
if #available(iOS 11.0, *) {
self.webView!.configuration.userContentController.removeAllContentRuleLists()
if let contentBlockers = webView!.options?.contentBlockers, contentBlockers.count > 0 {
do {
let jsonData = try JSONSerialization.data(withJSONObject: contentBlockers, options: [])
let blockRules = String(data: jsonData, encoding: String.Encoding.utf8)
WKContentRuleListStore.default().compileContentRuleList(
forIdentifier: "ContentBlockingRules",
encodedContentRuleList: blockRules) { (contentRuleList, error) in
if #available(iOS 11.0, *) {
self.webView!.configuration.userContentController.removeAllContentRuleLists()
if let contentBlockers = webView!.options?.contentBlockers, contentBlockers.count > 0 {
do {
let jsonData = try JSONSerialization.data(withJSONObject: contentBlockers, options: [])
let blockRules = String(data: jsonData, encoding: String.Encoding.utf8)
WKContentRuleListStore.default().compileContentRuleList(
forIdentifier: "ContentBlockingRules",
encodedContentRuleList: blockRules) { (contentRuleList, error) in
if let error = error {
print(error.localizedDescription)
return
}
if let error = error {
print(error.localizedDescription)
return
}
let configuration = self.webView!.configuration
configuration.userContentController.add(contentRuleList!)
let configuration = self.webView!.configuration
configuration.userContentController.add(contentRuleList!)
self.load(initialUrl: initialUrl, initialFile: initialFile, initialData: initialData, initialHeaders: initialHeaders)
self.load(initialUrl: initialUrl, initialFile: initialFile, initialData: initialData, initialHeaders: initialHeaders)
}
return
} catch {
print(error.localizedDescription)
}
return
} catch {
print(error.localizedDescription)
}
}
load(initialUrl: initialUrl, initialFile: initialFile, initialData: initialData, initialHeaders: initialHeaders)
}
else if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
webView!.load(webViewTransport.request)
}
load(initialUrl: initialUrl, initialFile: initialFile, initialData: initialData, initialHeaders: initialHeaders)
if (frame.isEmpty && viewId is String) {
/// Note: The WKWebView behaves very unreliable when rendering offscreen

View File

@ -18,7 +18,7 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
static var registrar: FlutterPluginRegistrar?
static var channel: FlutterMethodChannel?
var tmpWindow: UIWindow?
// var tmpWindow: UIWindow?
private var previousStatusBarStyle = -1
public static func register(with registrar: FlutterPluginRegistrar) {
@ -42,7 +42,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let options = arguments!["options"] as! [String: Any?]
let headers = arguments!["headers"] as! [String: String]
let contextMenu = arguments!["contextMenu"] as! [String: Any]
openUrl(uuid: uuid, url: url, options: options, headers: headers, contextMenu: contextMenu)
let windowId = arguments!["windowId"] as? Int64
openUrl(uuid: uuid, url: url, options: options, headers: headers, contextMenu: contextMenu, windowId: windowId)
result(true)
break
case "openFile":
@ -59,7 +60,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let options = arguments!["options"] as! [String: Any?]
let headers = arguments!["headers"] as! [String: String]
let contextMenu = arguments!["contextMenu"] as! [String: Any]
openUrl(uuid: uuid, url: url, options: options, headers: headers, contextMenu: contextMenu)
let windowId = arguments!["windowId"] as? Int64
openUrl(uuid: uuid, url: url, options: options, headers: headers, contextMenu: contextMenu, windowId: windowId)
result(true)
break
case "openData":
@ -70,7 +72,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let encoding = arguments!["encoding"] as! String
let baseUrl = arguments!["baseUrl"] as! String
let contextMenu = arguments!["contextMenu"] as! [String: Any]
openData(uuid: uuid, options: options, data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl, contextMenu: contextMenu)
let windowId = arguments!["windowId"] as? Int64
openData(uuid: uuid, options: options, data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl, contextMenu: contextMenu, windowId: windowId)
result(true)
break
case "openWithSystemBrowser":
@ -88,16 +91,14 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
self.previousStatusBarStyle = UIApplication.shared.statusBarStyle.rawValue
}
if !(self.tmpWindow != nil) {
let frame: CGRect = UIScreen.main.bounds
self.tmpWindow = UIWindow(frame: frame)
}
let frame: CGRect = UIScreen.main.bounds
let tmpWindow = UIWindow(frame: frame)
let tmpController = UIViewController()
let baseWindowLevel = UIApplication.shared.keyWindow?.windowLevel
self.tmpWindow!.rootViewController = tmpController
self.tmpWindow!.windowLevel = UIWindow.Level(baseWindowLevel!.rawValue + 1.0)
self.tmpWindow!.makeKeyAndVisible()
tmpWindow.rootViewController = tmpController
tmpWindow.windowLevel = UIWindow.Level(baseWindowLevel!.rawValue + 1.0)
tmpWindow.makeKeyAndVisible()
let browserOptions = InAppBrowserOptions()
let _ = browserOptions.parse(options: options)
@ -107,6 +108,7 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
let storyboard = UIStoryboard(name: WEBVIEW_STORYBOARD, bundle: Bundle(for: InAppWebViewFlutterPlugin.self))
let webViewController = storyboard.instantiateViewController(withIdentifier: WEBVIEW_STORYBOARD_CONTROLLER_ID) as! InAppBrowserWebViewController
webViewController.tmpWindow = tmpWindow
webViewController.browserOptions = browserOptions
webViewController.webViewOptions = webViewOptions
webViewController.isHidden = browserOptions.hidden
@ -115,57 +117,60 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
return webViewController
}
public func openUrl(uuid: String, url: String, options: [String: Any?], headers: [String: String], contextMenu: [String: Any]) {
public func openUrl(uuid: String, url: String, options: [String: Any?], headers: [String: String],
contextMenu: [String: Any], windowId: Int64?) {
let absoluteUrl = URL(string: url)!.absoluteURL
let webViewController = prepareInAppBrowserWebViewController(options: options)
webViewController.uuid = uuid
webViewController.prepareMethodChannel()
webViewController.tmpWindow = tmpWindow
webViewController.initURL = absoluteUrl
webViewController.initHeaders = headers
webViewController.contextMenu = contextMenu
webViewController.windowId = windowId
if webViewController.isHidden {
webViewController.view.isHidden = true
tmpWindow!.rootViewController!.present(webViewController, animated: false, completion: {() -> Void in
webViewController.tmpWindow!.rootViewController!.present(webViewController, animated: false, completion: {() -> Void in
})
webViewController.presentingViewController?.dismiss(animated: false, completion: {() -> Void in
self.tmpWindow?.windowLevel = UIWindow.Level(rawValue: 0.0)
webViewController.tmpWindow?.windowLevel = UIWindow.Level(rawValue: 0.0)
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
})
}
else {
tmpWindow!.rootViewController!.present(webViewController, animated: true, completion: {() -> Void in
webViewController.tmpWindow!.rootViewController!.present(webViewController, animated: true, completion: {() -> Void in
})
}
}
public func openData(uuid: String, options: [String: Any?], data: String, mimeType: String, encoding: String, baseUrl: String, contextMenu: [String: Any]) {
public func openData(uuid: String, options: [String: Any?], data: String, mimeType: String, encoding: String,
baseUrl: String, contextMenu: [String: Any], windowId: Int64?) {
let webViewController = prepareInAppBrowserWebViewController(options: options)
webViewController.uuid = uuid
webViewController.tmpWindow = tmpWindow
webViewController.prepareMethodChannel()
webViewController.initData = data
webViewController.initMimeType = mimeType
webViewController.initEncoding = encoding
webViewController.initBaseUrl = baseUrl
webViewController.contextMenu = contextMenu
webViewController.windowId = windowId
if webViewController.isHidden {
webViewController.view.isHidden = true
tmpWindow!.rootViewController!.present(webViewController, animated: false, completion: {() -> Void in
webViewController.tmpWindow!.rootViewController!.present(webViewController, animated: false, completion: {() -> Void in
webViewController.webView.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl)
})
webViewController.presentingViewController?.dismiss(animated: false, completion: {() -> Void in
self.tmpWindow?.windowLevel = UIWindow.Level(rawValue: 0.0)
webViewController.tmpWindow?.windowLevel = UIWindow.Level(rawValue: 0.0)
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
})
}
else {
tmpWindow!.rootViewController!.present(webViewController, animated: true, completion: {() -> Void in
webViewController.tmpWindow!.rootViewController!.present(webViewController, animated: true, completion: {() -> Void in
webViewController.webView.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl)
})
}

View File

@ -14,17 +14,16 @@ import AVFoundation
typealias OlderClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void
typealias NewerClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void
public class InAppWebView_IBWrapper: InAppWebView {
required init(coder: NSCoder) {
let config = WKWebViewConfiguration()
super.init(frame: .zero, configuration: config, IABController: nil, contextMenu: nil, channel: nil)
public class InAppWebView_IBWrapper: UIView {
required init?(coder: NSCoder) {
super.init(coder: coder)
self.translatesAutoresizingMaskIntoConstraints = false
}
}
public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIScrollViewDelegate, WKUIDelegate, UITextFieldDelegate {
@IBOutlet var containerWebView: UIView!
@IBOutlet var containerWebView: InAppWebView_IBWrapper!
@IBOutlet var closeButton: UIButton!
@IBOutlet var reloadButton: UIBarButtonItem!
@IBOutlet var backButton: UIBarButtonItem!
@ -43,6 +42,7 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
@IBOutlet var webView_TopFullScreenConstraint: NSLayoutConstraint!
var uuid: String = ""
var windowId: Int64?
var webView: InAppWebView!
var channel: FlutterMethodChannel?
var initURL: URL?
@ -385,42 +385,58 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
public override func viewWillAppear(_ animated: Bool) {
if !viewPrepared {
print(containerWebView)
let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(options: webViewOptions)
self.webView = InAppWebView(frame: .zero, configuration: preWebviewConfiguration, IABController: self, contextMenu: contextMenu, channel: channel!)
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
self.webView = webViewTransport.webView
self.webView.IABController = self
self.webView.contextMenu = contextMenu
self.webView.channel = channel!
} else {
self.webView = InAppWebView(frame: .zero,
configuration: preWebviewConfiguration,
IABController: self,
contextMenu: contextMenu,
channel: channel!)
}
self.containerWebView.addSubview(self.webView)
prepareConstraints()
prepareWebView()
if #available(iOS 11.0, *) {
if let contentBlockers = webView.options?.contentBlockers, contentBlockers.count > 0 {
do {
let jsonData = try JSONSerialization.data(withJSONObject: contentBlockers, options: [])
let blockRules = String(data: jsonData, encoding: String.Encoding.utf8)
WKContentRuleListStore.default().compileContentRuleList(
forIdentifier: "ContentBlockingRules",
encodedContentRuleList: blockRules) { (contentRuleList, error) in
if let error = error {
print(error.localizedDescription)
return
}
let configuration = self.webView!.configuration
configuration.userContentController.add(contentRuleList!)
self.initLoad(initURL: self.initURL, initData: self.initData, initMimeType: self.initMimeType, initEncoding: self.initEncoding, initBaseUrl: self.initBaseUrl, initHeaders: self.initHeaders)
self.onBrowserCreated()
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
self.webView.load(webViewTransport.request)
} else {
if #available(iOS 11.0, *) {
if let contentBlockers = webView.options?.contentBlockers, contentBlockers.count > 0 {
do {
let jsonData = try JSONSerialization.data(withJSONObject: contentBlockers, options: [])
let blockRules = String(data: jsonData, encoding: String.Encoding.utf8)
WKContentRuleListStore.default().compileContentRuleList(
forIdentifier: "ContentBlockingRules",
encodedContentRuleList: blockRules) { (contentRuleList, error) in
if let error = error {
print(error.localizedDescription)
return
}
let configuration = self.webView!.configuration
configuration.userContentController.add(contentRuleList!)
self.initLoad(initURL: self.initURL, initData: self.initData, initMimeType: self.initMimeType, initEncoding: self.initEncoding, initBaseUrl: self.initBaseUrl, initHeaders: self.initHeaders)
self.onBrowserCreated()
}
return
} catch {
print(error.localizedDescription)
}
return
} catch {
print(error.localizedDescription)
}
}
initLoad(initURL: initURL, initData: initData, initMimeType: initMimeType, initEncoding: initEncoding, initBaseUrl: initBaseUrl, initHeaders: initHeaders)
}
initLoad(initURL: initURL, initData: initData, initMimeType: initMimeType, initEncoding: initEncoding, initBaseUrl: initBaseUrl, initHeaders: initHeaders)
onBrowserCreated()
}
viewPrepared = true

View File

@ -791,10 +791,37 @@ window.\(JAVASCRIPT_BRIDGE_NAME)._originalViewPortMetaTagContent = "";
})();
"""
let onWindowFocusEventJS = """
(function(){
window.addEventListener('focus', function(e) {
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onWindowFocus');
});
})();
"""
let onWindowBlurEventJS = """
(function(){
window.addEventListener('blur', function(e) {
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onWindowBlur');
});
})();
"""
var SharedLastTouchPointTimestamp: [InAppWebView: Int64] = [:]
public class WebViewTransport: NSObject {
var webView: InAppWebView
var request: URLRequest
init(webView: InAppWebView, request: URLRequest) {
self.webView = webView
self.request = request
}
}
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate {
var windowId: Int64?
var IABController: InAppBrowserWebViewController?
var channel: FlutterMethodChannel?
var options: InAppWebViewOptions?
@ -826,6 +853,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
var customIMPs: [IMP] = []
static var windowWebViews: [Int64:WebViewTransport] = [:]
static var windowAutoincrementId: Int64 = 0;
init(frame: CGRect, configuration: WKWebViewConfiguration, IABController: InAppBrowserWebViewController?, contextMenu: [String: Any]?, channel: FlutterMethodChannel?) {
super.init(frame: frame, configuration: configuration)
self.channel = channel
@ -1000,6 +1030,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
forKeyPath: #keyPath(WKWebView.url),
options: [.new, .old],
context: nil)
addObserver(self,
forKeyPath: #keyPath(WKWebView.title),
options: [.new, .old],
context: nil)
NotificationCenter.default.addObserver(
self,
@ -1026,9 +1061,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
name: UIWindow.didBecomeHiddenNotification,
object: window)
configuration.userContentController = WKUserContentController()
configuration.preferences = WKPreferences()
if let options = options {
if options.transparentBackground {
isOpaque = false
@ -1050,6 +1082,55 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
}
if #available(iOS 11.0, *) {
accessibilityIgnoresInvertColors = options.accessibilityIgnoresInvertColors
scrollView.contentInsetAdjustmentBehavior =
UIScrollView.ContentInsetAdjustmentBehavior.init(rawValue: options.contentInsetAdjustmentBehavior)!
}
allowsBackForwardNavigationGestures = options.allowsBackForwardNavigationGestures
if #available(iOS 9.0, *) {
allowsLinkPreview = options.allowsLinkPreview
if !options.userAgent.isEmpty {
customUserAgent = options.userAgent
}
}
if #available(iOS 13.0, *) {
scrollView.automaticallyAdjustsScrollIndicatorInsets = options.automaticallyAdjustsScrollIndicatorInsets
}
scrollView.showsVerticalScrollIndicator = !options.disableVerticalScroll
scrollView.showsHorizontalScrollIndicator = !options.disableHorizontalScroll
scrollView.showsVerticalScrollIndicator = options.verticalScrollBarEnabled
scrollView.showsHorizontalScrollIndicator = options.horizontalScrollBarEnabled
scrollView.decelerationRate = InAppWebView.getDecelerationRate(type: options.decelerationRate)
scrollView.alwaysBounceVertical = options.alwaysBounceVertical
scrollView.alwaysBounceHorizontal = options.alwaysBounceHorizontal
scrollView.scrollsToTop = options.scrollsToTop
scrollView.isPagingEnabled = options.isPagingEnabled
scrollView.maximumZoomScale = CGFloat(options.maximumZoomScale)
scrollView.minimumZoomScale = CGFloat(options.minimumZoomScale)
// options.debuggingEnabled is always enabled for iOS,
// there isn't any option to set about it such as on Android.
if options.clearCache {
clearCache()
}
}
if windowId != nil {
// the new created window webview has the same WKWebViewConfiguration variable reference
return
}
configuration.userContentController = WKUserContentController()
configuration.preferences = WKPreferences()
if let options = options {
let originalViewPortMetaTagContentJSScript = WKUserScript(source: originalViewPortMetaTagContentJS, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
configuration.userContentController.addUserScript(originalViewPortMetaTagContentJSScript)
@ -1096,6 +1177,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.userContentController.addUserScript(findTextHighlightJSScript)
configuration.userContentController.add(self, name: "onFindResultReceived")
let onWindowFocusEventJSScript = WKUserScript(source: onWindowFocusEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(onWindowFocusEventJSScript)
let onWindowBlurEventJSScript = WKUserScript(source: onWindowBlurEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(onWindowBlurEventJSScript)
if options.useShouldInterceptAjaxRequest {
let interceptAjaxRequestsJSScript = WKUserScript(source: interceptAjaxRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(interceptAjaxRequestsJSScript)
@ -1106,23 +1193,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
}
if #available(iOS 11.0, *) {
accessibilityIgnoresInvertColors = options.accessibilityIgnoresInvertColors
scrollView.contentInsetAdjustmentBehavior =
UIScrollView.ContentInsetAdjustmentBehavior.init(rawValue: options.contentInsetAdjustmentBehavior)!
}
allowsBackForwardNavigationGestures = options.allowsBackForwardNavigationGestures
if #available(iOS 9.0, *) {
allowsLinkPreview = options.allowsLinkPreview
configuration.allowsAirPlayForMediaPlayback = options.allowsAirPlayForMediaPlayback
configuration.allowsPictureInPictureMediaPlayback = options.allowsPictureInPictureMediaPlayback
if !options.applicationNameForUserAgent.isEmpty {
configuration.applicationNameForUserAgent = options.applicationNameForUserAgent
}
if !options.userAgent.isEmpty {
customUserAgent = options.userAgent
}
}
configuration.preferences.javaScriptCanOpenWindowsAutomatically = options.javaScriptCanOpenWindowsAutomatically
@ -1132,26 +1208,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if #available(iOS 13.0, *) {
configuration.preferences.isFraudulentWebsiteWarningEnabled = options.isFraudulentWebsiteWarningEnabled
configuration.defaultWebpagePreferences.preferredContentMode = WKWebpagePreferences.ContentMode(rawValue: options.preferredContentMode)!
scrollView.automaticallyAdjustsScrollIndicatorInsets = options.automaticallyAdjustsScrollIndicatorInsets
}
scrollView.showsVerticalScrollIndicator = !options.disableVerticalScroll
scrollView.showsHorizontalScrollIndicator = !options.disableHorizontalScroll
scrollView.showsVerticalScrollIndicator = options.verticalScrollBarEnabled
scrollView.showsHorizontalScrollIndicator = options.horizontalScrollBarEnabled
scrollView.decelerationRate = InAppWebView.getDecelerationRate(type: options.decelerationRate)
scrollView.alwaysBounceVertical = options.alwaysBounceVertical
scrollView.alwaysBounceHorizontal = options.alwaysBounceHorizontal
scrollView.scrollsToTop = options.scrollsToTop
scrollView.isPagingEnabled = options.isPagingEnabled
scrollView.maximumZoomScale = CGFloat(options.maximumZoomScale)
scrollView.minimumZoomScale = CGFloat(options.minimumZoomScale)
// options.debuggingEnabled is always enabled for iOS.
if options.clearCache {
clearCache()
}
}
}
@ -1348,8 +1404,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
onProgressChanged(progress: progress)
} else if keyPath == #keyPath(WKWebView.url) && change?[NSKeyValueChangeKey.newKey] is URL {
let newUrl = change?[NSKeyValueChangeKey.newKey] as? URL
onUpdateVisitedHistory(url: newUrl!.absoluteString)
}
onUpdateVisitedHistory(url: newUrl?.absoluteString)
} else if keyPath == #keyPath(WKWebView.title) && change?[NSKeyValueChangeKey.newKey] is String {
let newTitle = change?[NSKeyValueChangeKey.newKey] as? String
onTitleChanged(title: newTitle)
}
replaceGestureHandlerIfNeeded()
}
@ -1831,7 +1890,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if navigationAction.navigationType == .linkActivated || navigationAction.navigationType == .backForward {
currentURL = url
if IABController != nil {
IABController!.updateUrlTextField(url: (currentURL?.absoluteString)!)
IABController!.updateUrlTextField(url: currentURL?.absoluteString ?? "")
}
}
}
@ -1841,7 +1900,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if navigationResponse.isForMainFrame, let response = navigationResponse.response as? HTTPURLResponse {
if response.statusCode >= 400 {
onLoadHttpError(url: response.url!.absoluteString, statusCode: response.statusCode, description: "")
onLoadHttpError(url: response.url?.absoluteString, statusCode: response.statusCode, description: "")
}
}
@ -1863,7 +1922,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
self.x509CertificateData = nil
self.startPageTime = currentTimeInMilliSeconds()
onLoadStart(url: (currentURL?.absoluteString)!)
onLoadStart(url: url?.absoluteString)
if IABController != nil {
// loading url, start spinner, update back/forward
@ -1880,10 +1940,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
currentURL = url
InAppWebView.credentialsProposed = []
evaluateJavaScript(platformReadyJS, completionHandler: nil)
onLoadStop(url: (currentURL?.absoluteString)!)
onLoadStop(url: url?.absoluteString)
if IABController != nil {
IABController!.updateUrlTextField(url: (currentURL?.absoluteString)!)
IABController!.updateUrlTextField(url: currentURL?.absoluteString ?? "")
IABController!.backButton.isEnabled = canGoBack
IABController!.forwardButton.isEnabled = canGoForward
IABController!.spinner.stopAnimating()
@ -1899,7 +1959,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
InAppWebView.credentialsProposed = []
onLoadError(url: (currentURL?.absoluteString)!, error: error)
onLoadError(url: url?.absoluteString, error: error)
if IABController != nil {
IABController!.backButton.isEnabled = canGoBack
@ -2138,7 +2198,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
return
}
onJsAlert(message: message, result: {(result) -> Void in
onJsAlert(frame: frame, message: message, result: {(result) -> Void in
if result is FlutterError {
print((result as! FlutterError).message ?? "")
}
@ -2195,8 +2255,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping (Bool) -> Void) {
onJsConfirm(message: message, result: {(result) -> Void in
onJsConfirm(frame: frame, message: message, result: {(result) -> Void in
if result is FlutterError {
print((result as! FlutterError).message ?? "")
}
@ -2268,7 +2328,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt message: String, defaultText defaultValue: String?, initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping (String?) -> Void) {
onJsPrompt(message: message, defaultValue: defaultValue, result: {(result) -> Void in
onJsPrompt(frame: frame, message: message, defaultValue: defaultValue, result: {(result) -> Void in
if result is FlutterError {
print((result as! FlutterError).message ?? "")
}
@ -2341,8 +2401,56 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures) -> WKWebView? {
onCreateWindow(url: navigationAction.request.url!, navigationType: navigationAction.navigationType)
return nil
InAppWebView.windowAutoincrementId += 1
let windowId = InAppWebView.windowAutoincrementId
let windowWebView = InAppWebView(frame: CGRect.zero, configuration: configuration, IABController: nil, contextMenu: nil, channel: nil)
windowWebView.windowId = windowId
let webViewTransport = WebViewTransport(
webView: windowWebView,
request: navigationAction.request
)
InAppWebView.windowWebViews[windowId] = webViewTransport
windowWebView.stopLoading()
let arguments: [String: Any?] = [
"url": navigationAction.request.url?.absoluteString,
"windowId": windowId,
"androidIsDialog": nil,
"androidIsUserGesture": nil,
"iosWKNavigationType": navigationAction.navigationType.rawValue,
"iosIsForMainFrame": navigationAction.targetFrame?.isMainFrame ?? false
]
channel?.invokeMethod("onCreateWindow", arguments: arguments, result: { (result) -> Void in
if result is FlutterError {
print((result as! FlutterError).message ?? "")
if InAppWebView.windowWebViews[windowId] != nil {
InAppWebView.windowWebViews.removeValue(forKey: windowId)
}
return
}
else if (result as? NSObject) == FlutterMethodNotImplemented {
self.updateUrlTextFieldForIABController(navigationAction: navigationAction)
if InAppWebView.windowWebViews[windowId] != nil {
InAppWebView.windowWebViews.removeValue(forKey: windowId)
}
return
}
else {
if result == nil, InAppWebView.windowWebViews[windowId] != nil {
InAppWebView.windowWebViews.removeValue(forKey: windowId)
}
}
})
return windowWebView
}
public func webViewDidClose(_ webView: WKWebView) {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onCloseWindow", arguments: arguments)
}
public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
@ -2461,23 +2569,23 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
// //onContextMenuWillPresentForElement(linkURL: elementInfo.linkURL?.absoluteString)
// }
public func onLoadStart(url: String) {
let arguments: [String: Any] = ["url": url]
public func onLoadStart(url: String?) {
let arguments: [String: Any?] = ["url": url]
channel?.invokeMethod("onLoadStart", arguments: arguments)
}
public func onLoadStop(url: String) {
let arguments: [String: Any] = ["url": url]
public func onLoadStop(url: String?) {
let arguments: [String: Any?] = ["url": url]
channel?.invokeMethod("onLoadStop", arguments: arguments)
}
public func onLoadError(url: String, error: Error) {
let arguments: [String: Any] = ["url": url, "code": error._code, "message": error.localizedDescription]
public func onLoadError(url: String?, error: Error) {
let arguments: [String: Any?] = ["url": url, "code": error._code, "message": error.localizedDescription]
channel?.invokeMethod("onLoadError", arguments: arguments)
}
public func onLoadHttpError(url: String, statusCode: Int, description: String) {
let arguments: [String: Any] = ["url": url, "statusCode": statusCode, "description": description]
public func onLoadHttpError(url: String?, statusCode: Int, description: String) {
let arguments: [String: Any?] = ["url": url, "statusCode": statusCode, "description": description]
channel?.invokeMethod("onLoadHttpError", arguments: arguments)
}
@ -2523,16 +2631,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
channel?.invokeMethod("shouldOverrideUrlLoading", arguments: arguments, result: result)
}
public func onCreateWindow(url: URL, navigationType: WKNavigationType) {
let arguments: [String: Any?] = [
"url": url.absoluteString,
"androidIsDialog": nil,
"androidIsUserGesture": nil,
"iosWKNavigationType": navigationType.rawValue
]
channel?.invokeMethod("onCreateWindow", arguments: arguments)
}
public func onReceivedHttpAuthRequest(challenge: URLAuthenticationChallenge, result: FlutterResult?) {
let arguments: [String: Any?] = [
"host": challenge.protectionSpace.host,
@ -2613,18 +2711,31 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
channel?.invokeMethod("onReceivedClientCertRequest", arguments: arguments, result: result)
}
public func onJsAlert(message: String, result: FlutterResult?) {
let arguments: [String: Any] = ["message": message]
public func onJsAlert(frame: WKFrameInfo, message: String, result: FlutterResult?) {
let arguments: [String: Any?] = [
"url": frame.request.url?.absoluteString,
"message": message,
"iosIsMainFrame": frame.isMainFrame
]
channel?.invokeMethod("onJsAlert", arguments: arguments, result: result)
}
public func onJsConfirm(message: String, result: FlutterResult?) {
let arguments: [String: Any] = ["message": message]
public func onJsConfirm(frame: WKFrameInfo, message: String, result: FlutterResult?) {
let arguments: [String: Any?] = [
"url": frame.request.url?.absoluteString,
"message": message,
"iosIsMainFrame": frame.isMainFrame
]
channel?.invokeMethod("onJsConfirm", arguments: arguments, result: result)
}
public func onJsPrompt(message: String, defaultValue: String?, result: FlutterResult?) {
let arguments: [String: Any] = ["message": message, "defaultValue": defaultValue as Any]
public func onJsPrompt(frame: WKFrameInfo, message: String, defaultValue: String?, result: FlutterResult?) {
let arguments: [String: Any?] = [
"url": frame.request.url?.absoluteString,
"message": message,
"defaultValue": defaultValue as Any,
"iosIsMainFrame": frame.isMainFrame
]
channel?.invokeMethod("onJsPrompt", arguments: arguments, result: result)
}
@ -2633,7 +2744,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
channel?.invokeMethod("onConsoleMessage", arguments: arguments)
}
public func onUpdateVisitedHistory(url: String) {
public func onUpdateVisitedHistory(url: String?) {
let arguments: [String: Any?] = [
"url": url,
"androidIsReload": nil
@ -2641,6 +2752,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
channel?.invokeMethod("onUpdateVisitedHistory", arguments: arguments)
}
public func onTitleChanged(title: String?) {
let arguments: [String: Any?] = [
"title": title
]
channel?.invokeMethod("onTitleChanged", arguments: arguments)
}
public func onLongPressHitTestResult(hitTestResult: [String: Any?]) {
let arguments: [String: Any?] = [
"hitTestResult": hitTestResult
@ -2929,19 +3047,22 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
completionHandler()
}
stopLoading()
configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleDebug")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleError")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleInfo")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
configuration.userContentController.removeAllUserScripts()
if windowId == nil {
configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleDebug")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleError")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleInfo")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
configuration.userContentController.removeAllUserScripts()
if #available(iOS 11.0, *) {
configuration.userContentController.removeAllContentRuleLists()
}
}
removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
removeObserver(self, forKeyPath: #keyPath(WKWebView.url))
if #available(iOS 11.0, *) {
configuration.userContentController.removeAllContentRuleLists()
}
removeObserver(self, forKeyPath: #keyPath(WKWebView.title))
NotificationCenter.default.removeObserver(self)
for imp in customIMPs {
imp_removeBlock(imp)
@ -2956,6 +3077,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
isPausedTimersCompletionHandler = nil
channel = nil
SharedLastTouchPointTimestamp.removeValue(forKey: self)
if let wId = windowId, InAppWebView.windowWebViews[wId] != nil {
InAppWebView.windowWebViews.removeValue(forKey: wId)
}
super.removeFromSuperview()
}

View File

@ -12,7 +12,6 @@ import SafariServices
public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafariViewControllerDelegate {
var channel: FlutterMethodChannel?
var tmpWindow: UIWindow?
var safariOptions: SafariBrowserOptions?
var uuid: String = ""
var menuItemList: [[String: Any]] = []
@ -48,6 +47,13 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
onChromeSafariBrowserOpened()
}
public override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.onChromeSafariBrowserClosed()
self.dispose()
}
func prepareSafariBrowser() {
if #available(iOS 11.0, *) {
self.dismissButtonStyle = SFSafariViewController.DismissButtonStyle(rawValue: (safariOptions?.dismissButtonStyle)!)!
@ -69,11 +75,8 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
func close(result: FlutterResult?) {
dismiss(animated: true)
// wait for the animation
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(400), execute: {() -> Void in
self.tmpWindow?.windowLevel = UIWindow.Level(rawValue: 0.0)
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
self.onChromeSafariBrowserClosed()
self.dispose()
if result != nil {
result!(true)
}

View File

@ -37,9 +37,6 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
var webViewControllers: [String: InAppBrowserWebViewController?] = [:]
var safariViewControllers: [String: Any?] = [:]
var tmpWindow: UIWindow?
private var previousStatusBarStyle = -1
public init(with registrar: FlutterPluginRegistrar) {
super.init()

View File

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'context_menu.dart';
@ -18,8 +20,12 @@ class HeadlessInAppWebView implements WebView {
///WebView Controller that can be used to access the [InAppWebViewController] API.
InAppWebViewController webViewController;
///The window id of a [CreateWindowRequest.windowId].
final int windowId;
HeadlessInAppWebView(
{this.onWebViewCreated,
{this.windowId,
this.onWebViewCreated,
this.onLoadStart,
this.onLoadStop,
this.onLoadError,
@ -32,6 +38,7 @@ class HeadlessInAppWebView implements WebView {
this.onDownloadStart,
this.onLoadResourceCustomScheme,
this.onCreateWindow,
this.onCloseWindow,
this.onJsAlert,
this.onJsConfirm,
this.onJsPrompt,
@ -49,6 +56,9 @@ class HeadlessInAppWebView implements WebView {
this.onEnterFullscreen,
this.onExitFullscreen,
this.onPageCommitVisible,
this.onTitleChanged,
this.onWindowFocus,
this.onWindowBlur,
this.androidOnSafeBrowsingHit,
this.androidOnPermissionRequest,
this.androidOnGeolocationPermissionsShowPrompt,
@ -59,6 +69,11 @@ class HeadlessInAppWebView implements WebView {
this.androidOnRenderProcessUnresponsive,
this.androidOnFormResubmission,
this.androidOnScaleChanged,
this.androidOnRequestFocus,
this.androidOnReceivedIcon,
this.androidOnReceivedTouchIconUrl,
this.androidOnJsBeforeUnload,
this.androidOnReceivedLoginRequest,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.initialUrl,
@ -114,7 +129,7 @@ class HeadlessInAppWebView implements WebView {
}
@override
final Future<void> Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)
androidOnGeolocationPermissionsHidePrompt;
@override
@ -151,15 +166,19 @@ class HeadlessInAppWebView implements WebView {
final String initialUrl;
@override
final Future<void> Function(InAppWebViewController controller, String url)
final void Function(InAppWebViewController controller, String url)
onPageCommitVisible;
@override
final Future<void> Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller, String title)
onTitleChanged;
@override
final void Function(InAppWebViewController controller)
iosOnDidReceiveServerRedirectForProvisionalNavigation;
@override
final Future<void> Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)
iosOnWebContentProcessDidTerminate;
@override
@ -178,8 +197,24 @@ class HeadlessInAppWebView implements WebView {
onConsoleMessage;
@override
final void Function(InAppWebViewController controller,
OnCreateWindowRequest onCreateWindowRequest) onCreateWindow;
final Future<WebView> Function(InAppWebViewController controller,
CreateWindowRequest onCreateWindowRequest) onCreateWindow;
@override
final void Function(InAppWebViewController controller)
onCloseWindow;
@override
final void Function(InAppWebViewController controller)
onWindowFocus;
@override
final void Function(InAppWebViewController controller)
onWindowBlur;
@override
final void Function(InAppWebViewController controller)
androidOnRequestFocus;
@override
final void Function(InAppWebViewController controller, String url)
@ -191,15 +226,15 @@ class HeadlessInAppWebView implements WebView {
@override
final Future<JsAlertResponse> Function(
InAppWebViewController controller, String message) onJsAlert;
InAppWebViewController controller, JsAlertRequest jsAlertRequest) onJsAlert;
@override
final Future<JsConfirmResponse> Function(
InAppWebViewController controller, String message) onJsConfirm;
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest) onJsConfirm;
@override
final Future<JsPromptResponse> Function(InAppWebViewController controller,
String message, String defaultValue) onJsPrompt;
JsPromptRequest jsPromptRequest) onJsPrompt;
@override
final void Function(InAppWebViewController controller, String url, int code,
@ -302,7 +337,7 @@ class HeadlessInAppWebView implements WebView {
androidOnRenderProcessResponsive;
@override
final Future<void> Function(
final void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail)
androidOnRenderProcessGone;
@ -311,7 +346,24 @@ class HeadlessInAppWebView implements WebView {
InAppWebViewController controller, String url) androidOnFormResubmission;
@override
final Future<void> Function(
final void Function(
InAppWebViewController controller, double oldScale, double newScale)
androidOnScaleChanged;
@override
final void Function(InAppWebViewController controller, Uint8List icon)
androidOnReceivedIcon;
@override
final void Function(InAppWebViewController controller, String url, bool precomposed)
androidOnReceivedTouchIconUrl;
@override
final Future<JsBeforeUnloadResponse> Function(
InAppWebViewController controller, JsBeforeUnloadRequest jsBeforeUnloadRequest)
androidOnJsBeforeUnload;
@override
final void Function(InAppWebViewController controller, LoginRequest loginRequest)
androidOnReceivedLoginRequest;
}

View File

@ -1,8 +1,10 @@
import 'dart:async';
import 'dart:collection';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'context_menu.dart';
import 'in_app_webview_controller.dart';
import 'webview_options.dart';
@ -28,8 +30,11 @@ class InAppBrowser {
/// WebView Controller that can be used to access the [InAppWebViewController] API.
InAppWebViewController webViewController;
///The window id of a [CreateWindowRequest.windowId].
final int windowId;
///
InAppBrowser() {
InAppBrowser({this.windowId}) {
uuid = uuidGenerator.v4();
this._channel =
MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$uuid');
@ -74,6 +79,7 @@ class InAppBrowser {
args.putIfAbsent('headers', () => headers);
args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId);
await _sharedChannel.invokeMethod('openUrl', args);
}
@ -123,6 +129,7 @@ class InAppBrowser {
args.putIfAbsent('headers', () => headers);
args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId);
await _sharedChannel.invokeMethod('openFile', args);
}
@ -153,6 +160,7 @@ class InAppBrowser {
args.putIfAbsent('baseUrl', () => baseUrl);
args.putIfAbsent('historyUrl', () => androidHistoryUrl);
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
args.putIfAbsent('windowId', () => windowId);
await _sharedChannel.invokeMethod('openData', args);
}
@ -231,18 +239,21 @@ class InAppBrowser {
///Event fired when the [InAppBrowser] starts to load an [url].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview
void onLoadStart(String url) {}
///Event fired when the [InAppBrowser] finishes loading an [url].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview
void onLoadStop(String url) {}
///Event fired when the [InAppBrowser] encounters an error loading an [url].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview
void onLoadError(String url, int code, String message) {}
@ -257,6 +268,7 @@ class InAppBrowser {
///**NOTE**: available on Android 23+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
void onLoadHttpError(String url, int statusCode, String description) {}
@ -283,6 +295,7 @@ class InAppBrowser {
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview
// ignore: missing_return
Future<ShouldOverrideUrlLoadingAction> shouldOverrideUrlLoading(
@ -300,6 +313,7 @@ class InAppBrowser {
///[y] represents the current vertical scroll origin in pixels.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#onScrollChanged(int,%20int,%20int,%20int)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll
void onScrollChanged(int x, int y) {}
@ -310,6 +324,7 @@ class InAppBrowser {
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnDownloadStart] option to `true`.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setDownloadListener(android.webkit.DownloadListener)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
void onDownloadStart(String url) {}
@ -326,51 +341,91 @@ class InAppBrowser {
///Event fired when the [InAppBrowser] webview requests the host application to create a new window,
///for example when trying to open a link with `target="_blank"` or when `window.open()` is called by JavaScript side.
///The return type should be an [InAppBrowser] instance, a [HeadlessInAppWebView] instance or `null`. If it returns `null`, then nothing will happen.
///If it returns an [InAppBrowser] instance, the new [InAppBrowser] will be immediately opened.
///If it returns a [HeadlessInAppWebView] instance, the [HeadlessInAppWebView.run] method will be immediately called.
///Remember to use the [CreateWindowRequest.windowId] to create the new WebView instance.
///
///[onCreateWindowRequest] represents the request.
///[createWindowRequest] represents the request.
///
///**NOTE**: on Android you need to set [AndroidInAppWebViewOptions.supportMultipleWindows] option to `true`.
///
///**NOTE**: on iOS, setting these initial options: [InAppWebViewOptions.supportZoom], [InAppWebViewOptions.useOnLoadResource], [InAppWebViewOptions.useShouldInterceptAjaxRequest],
///[InAppWebViewOptions.useShouldInterceptFetchRequest], [InAppWebViewOptions.applicationNameForUserAgent], [InAppWebViewOptions.javaScriptCanOpenWindowsAutomatically],
///[InAppWebViewOptions.javaScriptEnabled], [InAppWebViewOptions.minimumFontSize], [InAppWebViewOptions.preferredContentMode], [InAppWebViewOptions.incognito],
///[InAppWebViewOptions.cacheEnabled], [InAppWebViewOptions.mediaPlaybackRequiresUserGesture],
///[InAppWebViewOptions.resourceCustomSchemes], [IOSInAppWebViewOptions.sharedCookiesEnabled],
///[IOSInAppWebViewOptions.enableViewportScale], [IOSInAppWebViewOptions.allowsAirPlayForMediaPlayback],
///[IOSInAppWebViewOptions.allowsPictureInPictureMediaPlayback], [IOSInAppWebViewOptions.isFraudulentWebsiteWarningEnabled],
///[IOSInAppWebViewOptions.allowsInlineMediaPlayback], [IOSInAppWebViewOptions.suppressesIncrementalRendering], [IOSInAppWebViewOptions.selectionGranularity],
///[IOSInAppWebViewOptions.ignoresViewportScaleLimits],
///will have no effect due to a `WKWebView` limitation when creating a new window WebView: it's impossible to return a new `WKWebView`
///with a different `WKWebViewConfiguration` instance (see https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview).
///So, these options will be inherited from the caller WebView.
///Also, note that calling [InAppWebViewController.setOptions] method using the controller of the new created WebView,
///it will update also the WebView options of the caller WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview
void onCreateWindow(OnCreateWindowRequest onCreateWindowRequest) {}
// ignore: missing_return
Future<dynamic> onCreateWindow(CreateWindowRequest createWindowRequest) {}
///Event fired when the host application should close the given WebView and remove it from the view system if necessary.
///At this point, WebCore has stopped any loading in this window and has removed any cross-scripting ability in javascript.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCloseWindow(android.webkit.WebView)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537390-webviewdidclose
void onCloseWindow() {}
///Event fired when the JavaScript `window` object of the WebView has received focus.
///This is the result of the `focus` javascript event applied to the `window` object.
void onWindowFocus() {}
///Event fired when the JavaScript `window` object of the WebView has lost focus.
///This is the result of the `blur` javascript event applied to the `window` object.
void onWindowBlur() {}
///Event fired when javascript calls the `alert()` method to display an alert dialog.
///If [JsAlertResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
///[jsAlertRequest] contains the message to be displayed in the alert dialog and the of the page requesting the dialog.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview
// ignore: missing_return
Future<JsAlertResponse> onJsAlert(String message) {}
Future<JsAlertResponse> onJsAlert(JsAlertRequest jsAlertRequest) {}
///Event fired when javascript calls the `confirm()` method to display a confirm dialog.
///If [JsConfirmResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
///[jsConfirmRequest] contains the message to be displayed in the confirm dialog and the of the page requesting the dialog.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview
// ignore: missing_return
Future<JsConfirmResponse> onJsConfirm(String message) {}
Future<JsConfirmResponse> onJsConfirm(JsConfirmRequest jsConfirmRequest) {}
///Event fired when javascript calls the `prompt()` method to display a prompt dialog.
///If [JsPromptResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
///[defaultValue] represents the default value displayed in the prompt dialog.
///[jsPromptRequest] contains the message to be displayed in the prompt dialog, the default value displayed in the prompt dialog, and the of the page requesting the dialog.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview
// ignore: missing_return
Future<JsPromptResponse> onJsPrompt(String message, String defaultValue) {}
Future<JsPromptResponse> onJsPrompt(JsPromptRequest jsPromptRequest) {}
///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request.
///
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return
Future<HttpAuthResponse> onReceivedHttpAuthRequest(
@ -382,6 +437,7 @@ class InAppBrowser {
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ServerTrustChallenge].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return
Future<ServerTrustAuthResponse> onReceivedServerTrustAuthRequest(
@ -395,6 +451,7 @@ class InAppBrowser {
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ClientCertChallenge].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
// ignore: missing_return
Future<ClientCertResponse> onReceivedClientCertRequest(
@ -473,18 +530,21 @@ class InAppBrowser {
///[hitTestResult] represents the hit result for hitting an HTML elements.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#setOnLongClickListener(android.view.View.OnLongClickListener)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer
void onLongPressHitTestResult(InAppWebViewHitTestResult hitTestResult) {}
///Event fired when the current page has entered full screen mode.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621621-didbecomevisiblenotification
void onEnterFullscreen() {}
///Event fired when the current page has exited full screen mode.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onHideCustomView()
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621617-didbecomehiddennotification
void onExitFullscreen() {}
@ -496,9 +556,18 @@ class InAppBrowser {
///[url] represents the URL corresponding to the page navigation that triggered this callback.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview
void onPageCommitVisible(String url) {}
///Event fired when a change in the document title occurred.
///
///[title] represents the string containing the new title of the document.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String)
void onTitleChanged(String title) {}
///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing.
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
///
@ -636,6 +705,60 @@ class InAppBrowser {
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
void androidOnScaleChanged(double oldScale, double newScale) {}
///Event fired when there is a request to display and focus for this WebView.
///This may happen due to another WebView opening a link in this WebView and requesting that this WebView be displayed.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onRequestFocus(android.webkit.WebView)
void androidOnRequestFocus() {}
///Event fired when there is new favicon for the current page.
///
///[icon] represents the favicon for the current page.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedIcon(android.webkit.WebView,%20android.graphics.Bitmap)
void androidOnReceivedIcon(Uint8List icon) {}
///Event fired when there is an url for an apple-touch-icon.
///
///[url] represents the icon url.
///
///[precomposed] is `true` if the url is for a precomposed touch icon.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTouchIconUrl(android.webkit.WebView,%20java.lang.String,%20boolean)
void androidOnReceivedTouchIconUrl(String url, bool precomposed) {}
///Event fired when the client should display a dialog to confirm navigation away from the current page.
///This is the result of the `onbeforeunload` javascript event.
///If [JsBeforeUnloadResponse.handledByClient] is `true`, WebView will assume that the client will handle the confirm dialog.
///If [JsBeforeUnloadResponse.handledByClient] is `false`, a default value of `true` will be returned to javascript to accept navigation away from the current page.
///The default behavior is to return `false`.
///Setting the [JsBeforeUnloadResponse.action] to [JsBeforeUnloadResponseAction.CONFIRM] will navigate away from the current page,
///[JsBeforeUnloadResponseAction.CANCEL] will cancel the navigation.
///
///[jsBeforeUnloadRequest] contains the message to be displayed in the alert dialog and the of the page requesting the dialog.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsBeforeUnload(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
// ignore: missing_return
Future<JsBeforeUnloadResponse> androidOnJsBeforeUnload(
JsBeforeUnloadRequest jsBeforeUnloadRequest) {}
///Event fired when a request to automatically log in the user has been processed.
///
///[loginRequest] contains the realm, account and args of the login request.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedLoginRequest(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String)
void androidOnReceivedLoginRequest(LoginRequest loginRequest) {}
///Invoked when the web view's web content process is terminated.
///
///**NOTE**: available only on iOS.

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -22,8 +23,12 @@ class InAppWebView extends StatefulWidget implements WebView {
/// were not claimed by any other gesture recognizer.
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
///The window id of a [CreateWindowRequest.windowId].
final int windowId;
const InAppWebView({
Key key,
this.windowId,
this.initialUrl = "about:blank",
this.initialFile,
this.initialData,
@ -43,6 +48,7 @@ class InAppWebView extends StatefulWidget implements WebView {
this.onDownloadStart,
this.onLoadResourceCustomScheme,
this.onCreateWindow,
this.onCloseWindow,
this.onJsAlert,
this.onJsConfirm,
this.onJsPrompt,
@ -60,6 +66,9 @@ class InAppWebView extends StatefulWidget implements WebView {
this.onEnterFullscreen,
this.onExitFullscreen,
this.onPageCommitVisible,
this.onTitleChanged,
this.onWindowFocus,
this.onWindowBlur,
this.androidOnSafeBrowsingHit,
this.androidOnPermissionRequest,
this.androidOnGeolocationPermissionsShowPrompt,
@ -70,6 +79,11 @@ class InAppWebView extends StatefulWidget implements WebView {
this.androidOnRenderProcessUnresponsive,
this.androidOnFormResubmission,
this.androidOnScaleChanged,
this.androidOnRequestFocus,
this.androidOnReceivedIcon,
this.androidOnReceivedTouchIconUrl,
this.androidOnJsBeforeUnload,
this.androidOnReceivedLoginRequest,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.gestureRecognizers,
@ -79,7 +93,7 @@ class InAppWebView extends StatefulWidget implements WebView {
_InAppWebViewState createState() => _InAppWebViewState();
@override
final Future<void> Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)
androidOnGeolocationPermissionsHidePrompt;
@override
@ -116,15 +130,19 @@ class InAppWebView extends StatefulWidget implements WebView {
final ContextMenu contextMenu;
@override
final Future<void> Function(InAppWebViewController controller, String url)
final void Function(InAppWebViewController controller, String url)
onPageCommitVisible;
@override
final Future<void> Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller, String title)
onTitleChanged;
@override
final void Function(InAppWebViewController controller)
iosOnDidReceiveServerRedirectForProvisionalNavigation;
@override
final Future<void> Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)
iosOnWebContentProcessDidTerminate;
@override
@ -143,8 +161,32 @@ class InAppWebView extends StatefulWidget implements WebView {
onConsoleMessage;
@override
final void Function(InAppWebViewController controller,
OnCreateWindowRequest onCreateWindowRequest) onCreateWindow;
final Future<WebView> Function(InAppWebViewController controller,
CreateWindowRequest onCreateWindowRequest) onCreateWindow;
@override
final void Function(InAppWebViewController controller)
onCloseWindow;
@override
final void Function(InAppWebViewController controller)
onWindowFocus;
@override
final void Function(InAppWebViewController controller)
onWindowBlur;
@override
final void Function(InAppWebViewController controller)
androidOnRequestFocus;
@override
final void Function(InAppWebViewController controller, Uint8List icon)
androidOnReceivedIcon;
@override
final void Function(InAppWebViewController controller, String url, bool precomposed)
androidOnReceivedTouchIconUrl;
@override
final void Function(InAppWebViewController controller, String url)
@ -156,15 +198,15 @@ class InAppWebView extends StatefulWidget implements WebView {
@override
final Future<JsAlertResponse> Function(
InAppWebViewController controller, String message) onJsAlert;
InAppWebViewController controller, JsAlertRequest jsAlertRequest) onJsAlert;
@override
final Future<JsConfirmResponse> Function(
InAppWebViewController controller, String message) onJsConfirm;
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest) onJsConfirm;
@override
final Future<JsPromptResponse> Function(InAppWebViewController controller,
String message, String defaultValue) onJsPrompt;
JsPromptRequest jsPromptRequest) onJsPrompt;
@override
final void Function(InAppWebViewController controller, String url, int code,
@ -267,7 +309,7 @@ class InAppWebView extends StatefulWidget implements WebView {
androidOnRenderProcessResponsive;
@override
final Future<void> Function(
final void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail)
androidOnRenderProcessGone;
@ -276,9 +318,18 @@ class InAppWebView extends StatefulWidget implements WebView {
InAppWebViewController controller, String url) androidOnFormResubmission;
@override
final Future<void> Function(
final void Function(
InAppWebViewController controller, double oldScale, double newScale)
androidOnScaleChanged;
@override
final Future<JsBeforeUnloadResponse> Function(
InAppWebViewController controller, JsBeforeUnloadRequest jsBeforeUnloadRequest)
androidOnJsBeforeUnload;
@override
final void Function(InAppWebViewController controller, LoginRequest loginRequest)
androidOnReceivedLoginRequest;
}
class _InAppWebViewState extends State<InAppWebView> {
@ -298,7 +349,8 @@ class _InAppWebViewState extends State<InAppWebView> {
'initialData': widget.initialData?.toMap(),
'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions?.toMap() ?? {},
'contextMenu': widget.contextMenu?.toMap() ?? {}
'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId
},
creationParamsCodec: const StandardMessageCodec(),
);
@ -332,7 +384,8 @@ class _InAppWebViewState extends State<InAppWebView> {
'initialData': widget.initialData?.toMap(),
'initialHeaders': widget.initialHeaders,
'initialOptions': widget.initialOptions?.toMap() ?? {},
'contextMenu': widget.contextMenu?.toMap() ?? {}
'contextMenu': widget.contextMenu?.toMap() ?? {},
'windowId': widget.windowId
},
creationParamsCodec: const StandardMessageCodec(),
);

View File

@ -30,7 +30,8 @@ const javaScriptHandlerForbiddenNames = [
"onAjaxProgress",
"shouldInterceptFetchRequest",
"onPrint",
"androidKeyboardWorkaroundFocusoutEvent"
"onWindowFocus",
"onWindowBlur",
];
///Controls a WebView, such as an [InAppWebView] widget instance, a [HeadlessInAppWebView] instance or [InAppBrowser] WebView instance.
@ -206,21 +207,74 @@ class InAppWebViewController {
break;
case "onCreateWindow":
String url = call.arguments["url"];
int windowId = call.arguments["windowId"];
bool androidIsDialog = call.arguments["androidIsDialog"];
bool androidIsUserGesture = call.arguments["androidIsUserGesture"];
int iosWKNavigationType = call.arguments["iosWKNavigationType"];
bool iosIsForMainFrame = call.arguments["iosIsForMainFrame"];
OnCreateWindowRequest onCreateWindowRequest = OnCreateWindowRequest(
CreateWindowRequest createWindowRequest = CreateWindowRequest(
url: url,
windowId: windowId,
androidIsDialog: androidIsDialog,
androidIsUserGesture: androidIsUserGesture,
iosWKNavigationType:
IOSWKNavigationType.fromValue(iosWKNavigationType));
IOSWKNavigationType.fromValue(iosWKNavigationType),
iosIsForMainFrame: iosIsForMainFrame);
WebView webView;
dynamic inAppBrowserWindow;
if (_webview != null && _webview.onCreateWindow != null)
_webview.onCreateWindow(this, onCreateWindowRequest);
webView = await _webview.onCreateWindow(this, createWindowRequest);
else if (_inAppBrowser != null) {
inAppBrowserWindow = await _inAppBrowser.onCreateWindow(createWindowRequest);
assert(
inAppBrowserWindow is InAppBrowser || inAppBrowserWindow is HeadlessInAppWebView,
"InAppBrowser.onCreateWindow should return an \"InAppBrowser\" instance or a \"HeadlessInAppWebView\" instance."
);
}
int webViewWindowId;
if (webView != null) {
webViewWindowId = webView.windowId;
assert(webViewWindowId !=
null, "If you are returning a WebView, then WebView.windowId should be not null. To set the " +
"WebView.windowId property, you should use the CreateWindowRequest.windowId property.");
if (webView is HeadlessInAppWebView) {
webView.run();
}
} else if (inAppBrowserWindow != null) {
if (inAppBrowserWindow is InAppBrowser) {
webViewWindowId = inAppBrowserWindow.windowId;
assert(webViewWindowId !=
null, "If you are returning an InAppBrowser, then InAppBrowser.windowId should be not null. To set the " +
"InAppBrowser.windowId property, you should use the CreateWindowRequest.windowId property.");
inAppBrowserWindow.openUrl(url: "about:blank");
}
else if (inAppBrowserWindow is HeadlessInAppWebView) {
webViewWindowId = inAppBrowserWindow.windowId;
assert(webViewWindowId !=
null, "If you are returning a HeadlessInAppWebView, then HeadlessInAppWebView.windowId should be not null. To set the " +
"HeadlessInAppWebView.windowId property, you should use the CreateWindowRequest.windowId property.");
inAppBrowserWindow.run();
}
}
return webViewWindowId;
case "onCloseWindow":
if (_webview != null && _webview.onCloseWindow != null)
_webview.onCloseWindow(this);
else if (_inAppBrowser != null)
_inAppBrowser.onCreateWindow(onCreateWindowRequest);
_inAppBrowser.onCloseWindow();
break;
case "onTitleChanged":
String title = call.arguments["title"];
if (_webview != null && _webview.onTitleChanged != null)
_webview.onTitleChanged(this, title);
else if (_inAppBrowser != null)
_inAppBrowser.onTitleChanged(title);
break;
case "onGeolocationPermissionsShowPrompt":
String origin = call.arguments["origin"];
@ -312,30 +366,98 @@ class InAppWebViewController {
else if (_inAppBrowser != null)
_inAppBrowser.androidOnScaleChanged(oldScale, newScale);
break;
case "onJsAlert":
String message = call.arguments["message"];
if (_webview != null && _webview.onJsAlert != null)
return (await _webview.onJsAlert(this, message))?.toMap();
case "onRequestFocus":
if (_webview != null && _webview.androidOnRequestFocus != null)
_webview.androidOnRequestFocus(this);
else if (_inAppBrowser != null)
return (await _inAppBrowser.onJsAlert(message))?.toMap();
_inAppBrowser.androidOnRequestFocus();
break;
case "onReceivedIcon":
Uint8List icon = Uint8List.fromList(call.arguments["icon"].cast<int>());
if (_webview != null && _webview.androidOnReceivedIcon != null)
_webview.androidOnReceivedIcon(this, icon);
else if (_inAppBrowser != null)
_inAppBrowser.androidOnReceivedIcon(icon);
break;
case "onReceivedTouchIconUrl":
String url = call.arguments["url"];
bool precomposed = call.arguments["precomposed"];
if (_webview != null && _webview.androidOnReceivedTouchIconUrl != null)
_webview.androidOnReceivedTouchIconUrl(this, url, precomposed);
else if (_inAppBrowser != null)
_inAppBrowser.androidOnReceivedTouchIconUrl(url, precomposed);
break;
case "onJsAlert":
String url = call.arguments["url"];
String message = call.arguments["message"];
bool iosIsMainFrame = call.arguments["iosIsMainFrame"];
JsAlertRequest jsAlertRequest = JsAlertRequest(
url: url,
message: message,
iosIsMainFrame: iosIsMainFrame
);
if (_webview != null && _webview.onJsAlert != null)
return (await _webview.onJsAlert(this, jsAlertRequest))?.toMap();
else if (_inAppBrowser != null)
return (await _inAppBrowser.onJsAlert(jsAlertRequest))?.toMap();
break;
case "onJsConfirm":
String url = call.arguments["url"];
String message = call.arguments["message"];
bool iosIsMainFrame = call.arguments["iosIsMainFrame"];
JsConfirmRequest jsConfirmRequest = JsConfirmRequest(
url: url,
message: message,
iosIsMainFrame: iosIsMainFrame
);
if (_webview != null && _webview.onJsConfirm != null)
return (await _webview.onJsConfirm(this, message))?.toMap();
return (await _webview.onJsConfirm(this, jsConfirmRequest))?.toMap();
else if (_inAppBrowser != null)
return (await _inAppBrowser.onJsConfirm(message))?.toMap();
return (await _inAppBrowser.onJsConfirm(jsConfirmRequest))?.toMap();
break;
case "onJsPrompt":
String url = call.arguments["url"];
String message = call.arguments["message"];
String defaultValue = call.arguments["defaultValue"];
bool iosIsMainFrame = call.arguments["iosIsMainFrame"];
JsPromptRequest jsPromptRequest = JsPromptRequest(
url: url,
message: message,
defaultValue: defaultValue,
iosIsMainFrame: iosIsMainFrame
);
if (_webview != null && _webview.onJsPrompt != null)
return (await _webview.onJsPrompt(this, message, defaultValue))
return (await _webview.onJsPrompt(this, jsPromptRequest))
?.toMap();
else if (_inAppBrowser != null)
return (await _inAppBrowser.onJsPrompt(message, defaultValue))
return (await _inAppBrowser.onJsPrompt(jsPromptRequest))
?.toMap();
break;
case "onJsBeforeUnload":
String url = call.arguments["url"];
String message = call.arguments["message"];
bool iosIsMainFrame = call.arguments["iosIsMainFrame"];
JsBeforeUnloadRequest jsBeforeUnloadRequest = JsBeforeUnloadRequest(
url: url,
message: message,
iosIsMainFrame: iosIsMainFrame
);
print(jsBeforeUnloadRequest);
if (_webview != null && _webview.androidOnJsBeforeUnload != null)
return (await _webview.androidOnJsBeforeUnload(this, jsBeforeUnloadRequest))?.toMap();
else if (_inAppBrowser != null)
return (await _inAppBrowser.androidOnJsBeforeUnload(jsBeforeUnloadRequest))?.toMap();
break;
case "onSafeBrowsingHit":
String url = call.arguments["url"];
SafeBrowsingThreat threatType =
@ -348,6 +470,22 @@ class InAppWebViewController {
return (await _inAppBrowser.androidOnSafeBrowsingHit(url, threatType))
?.toMap();
break;
case "onReceivedLoginRequest":
String realm = call.arguments["realm"];
String account = call.arguments["account"];
String args = call.arguments["args"];
LoginRequest loginRequest = LoginRequest(
realm: realm,
account: account,
args: args
);
if (_webview != null && _webview.androidOnReceivedLoginRequest != null)
_webview.androidOnReceivedLoginRequest(this, loginRequest);
else if (_inAppBrowser != null)
_inAppBrowser.androidOnReceivedLoginRequest(loginRequest);
break;
case "onReceivedHttpAuthRequest":
String host = call.arguments["host"];
String protocol = call.arguments["protocol"];
@ -801,6 +939,18 @@ class InAppWebViewController {
_webview.onPrint(this, url);
else if (_inAppBrowser != null) _inAppBrowser.onPrint(url);
return null;
case "onWindowFocus":
if (_webview != null && _webview.onWindowFocus != null)
_webview.onWindowFocus(this);
else if (_inAppBrowser != null)
_inAppBrowser.onWindowFocus();
return null;
case "onWindowBlur":
if (_webview != null && _webview.onWindowBlur != null)
_webview.onWindowBlur(this);
else if (_inAppBrowser != null)
_inAppBrowser.onWindowBlur();
return null;
}
if (javaScriptHandlersMap.containsKey(handlerName)) {
@ -822,6 +972,7 @@ class InAppWebViewController {
///This is not always the same as the URL passed to [WebView.onLoadStart] because although the load for that URL has begun, the current page may not have changed.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getUrl()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1415005-url
Future<String> getUrl() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -831,6 +982,7 @@ class InAppWebViewController {
///Gets the title for the current page.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getTitle()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1415015-title
Future<String> getTitle() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -840,6 +992,7 @@ class InAppWebViewController {
///Gets the progress for the current page. The progress value is between 0 and 100.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getProgress()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress
Future<int> getProgress() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1039,6 +1192,7 @@ class InAppWebViewController {
///Loads the given [url] with optional [headers] specified as a map from name to value.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#loadUrl(java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414954-load
Future<void> loadUrl(
{@required String url, Map<String, String> headers = const {}}) async {
@ -1071,6 +1225,7 @@ class InAppWebViewController {
///The [androidHistoryUrl] parameter is the URL to use as the history entry. The default value is `about:blank`. If non-null, this must be a valid URL. This parameter is used only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#loadDataWithBaseURL(java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String)
///
///**Official iOS API**:
///- https://developer.apple.com/documentation/webkit/wkwebview/1415004-loadhtmlstring
///- https://developer.apple.com/documentation/webkit/wkwebview/1415011-load
@ -1132,6 +1287,7 @@ class InAppWebViewController {
///Reloads the WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#reload()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload
Future<void> reload() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1141,6 +1297,7 @@ class InAppWebViewController {
///Goes back in the history of the WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#goBack()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback
Future<void> goBack() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1150,6 +1307,7 @@ class InAppWebViewController {
///Returns a boolean value indicating whether the WebView can move backward.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#canGoBack()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback
Future<bool> canGoBack() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1159,6 +1317,7 @@ class InAppWebViewController {
///Goes forward in the history of the WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#goForward()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward
Future<void> goForward() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1168,6 +1327,7 @@ class InAppWebViewController {
///Returns a boolean value indicating whether the WebView can move forward.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#canGoForward()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward
Future<bool> canGoForward() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1177,6 +1337,7 @@ class InAppWebViewController {
///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.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#goBackOrForward(int)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414991-go
Future<void> goBackOrForward({@required int steps}) async {
assert(steps != null);
@ -1211,6 +1372,7 @@ class InAppWebViewController {
///Stops the WebView from loading.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#stopLoading()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading
Future<void> stopLoading() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1225,6 +1387,7 @@ class InAppWebViewController {
///where you know the page is ready "enough".
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript(java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.String%3E)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript
Future<dynamic> evaluateJavascript({@required String source}) async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1396,6 +1559,7 @@ class InAppWebViewController {
///The object returned from this method will not be updated to reflect any new state.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#copyBackForwardList()
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1414977-backforwardlist
Future<WebHistory> getCopyBackForwardList() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1488,6 +1652,7 @@ class InAppWebViewController {
///[animated] `true` to animate the scroll transition, `false` to make the scoll transition immediate.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#scrollTo(int,%20int)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619400-setcontentoffset
Future<void> scrollTo(
{@required int x, @required int y, bool animated = false}) async {
@ -1508,6 +1673,7 @@ class InAppWebViewController {
///[animated] `true` to animate the scroll transition, `false` to make the scoll transition immediate.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#scrollBy(int,%20int)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619400-setcontentoffset
Future<void> scrollBy(
{@required int x, @required int y, bool animated = false}) async {
@ -1545,6 +1711,7 @@ class InAppWebViewController {
///**NOTE**: available on Android 21+.
///
///**Official Android API**: https://developer.android.com/reference/android/print/PrintManager
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiprintinteractioncontroller
Future<void> printCurrentPage() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1554,6 +1721,7 @@ class InAppWebViewController {
///Gets the height of the HTML content.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#getContentHeight()
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619399-contentsize
Future<int> getContentHeight() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1567,6 +1735,7 @@ class InAppWebViewController {
///**NOTE**: available on Android 21+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#zoomBy(float)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619412-setzoomscale
Future<void> zoomBy(double zoomFactor) async {
assert(!Platform.isAndroid ||
@ -1582,6 +1751,7 @@ class InAppWebViewController {
///**Official Android API**:
///- https://developer.android.com/reference/android/util/DisplayMetrics#density
///- https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619419-zoomscale
Future<double> getScale() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1616,6 +1786,7 @@ class InAppWebViewController {
///Clears the current focus. It will clear also, for example, the current text selection.
///
///**Official Android API**: https://developer.android.com/reference/android/view/ViewGroup#clearFocus()
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiresponder/1621097-resignfirstresponder
Future<void> clearFocus() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1753,6 +1924,7 @@ class InAppWebViewController {
///Returns the scrolled left position of the current WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#getScrollX()
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset
Future<int> getScrollX() async {
Map<String, dynamic> args = <String, dynamic>{};
@ -1762,6 +1934,7 @@ class InAppWebViewController {
///Returns the scrolled top position of the current WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#getScrollY()
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset
Future<int> getScrollY() async {
Map<String, dynamic> args = <String, dynamic>{};

View File

@ -439,6 +439,37 @@ class GeolocationPermissionShowPromptResponse {
}
}
///Class that represents the request of the [WebView.onJsAlert] event.
class JsAlertRequest {
///The url of the page requesting the dialog.
String url;
///Message to be displayed in the window.
String message;
///Indicates whether the request was made for the main frame. Available only on iOS.
bool iosIsMainFrame;
JsAlertRequest({
this.url,
this.message,
this.iosIsMainFrame
});
Map<String, dynamic> toMap() {
return {"url": url, "message": message, "iosIsMainFrame": iosIsMainFrame};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
///Class used by [JsAlertResponse] class.
class JsAlertResponseAction {
final int _value;
@ -494,6 +525,37 @@ class JsAlertResponse {
}
}
///Class that represents the request of the [WebView.onJsConfirm] event.
class JsConfirmRequest {
///The url of the page requesting the dialog.
String url;
///Message to be displayed in the window.
String message;
///Indicates whether the request was made for the main frame. Available only on iOS.
bool iosIsMainFrame;
JsConfirmRequest({
this.url,
this.message,
this.iosIsMainFrame
});
Map<String, dynamic> toMap() {
return {"url": url, "message": message, "iosIsMainFrame": iosIsMainFrame};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
///Class used by [JsConfirmResponse] class.
class JsConfirmResponseAction {
final int _value;
@ -555,6 +617,41 @@ class JsConfirmResponse {
}
}
///Class that represents the request of the [WebView.onJsPrompt] event.
class JsPromptRequest {
///The url of the page requesting the dialog.
String url;
///Message to be displayed in the window.
String message;
///The default value displayed in the prompt dialog.
String defaultValue;
///Indicates whether the request was made for the main frame. Available only on iOS.
bool iosIsMainFrame;
JsPromptRequest({
this.url,
this.message,
this.defaultValue,
this.iosIsMainFrame
});
Map<String, dynamic> toMap() {
return {"url": url, "message": message, "defaultValue": defaultValue, "iosIsMainFrame": iosIsMainFrame};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
///Class used by [JsPromptResponse] class.
class JsPromptResponseAction {
final int _value;
@ -626,6 +723,98 @@ class JsPromptResponse {
}
}
///Class that represents the request of the [WebView.androidOnJsBeforeUnload] event.
class JsBeforeUnloadRequest {
///The url of the page requesting the dialog.
String url;
///Message to be displayed in the window.
String message;
///Indicates whether the request was made for the main frame. Available only on iOS.
bool iosIsMainFrame;
JsBeforeUnloadRequest({
this.url,
this.message,
this.iosIsMainFrame
});
Map<String, dynamic> toMap() {
return {"url": url, "message": message, "iosIsMainFrame": iosIsMainFrame};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
///Class used by [JsBeforeUnloadResponse] class.
class JsBeforeUnloadResponseAction {
final int _value;
const JsBeforeUnloadResponseAction._internal(this._value);
int toValue() => _value;
static const CONFIRM = const JsBeforeUnloadResponseAction._internal(0);
static const CANCEL = const JsBeforeUnloadResponseAction._internal(1);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///Class that represents the response used by the [WebView.androidOnJsBeforeUnload] event to control a JavaScript alert dialog.
class JsBeforeUnloadResponse {
///Message to be displayed in the window.
String message;
///Title of the confirm button.
String confirmButtonTitle;
///Title of the cancel button.
String cancelButtonTitle;
///Whether the client will handle the alert dialog.
bool handledByClient;
///Action used to confirm that the user hit confirm button.
JsBeforeUnloadResponseAction action;
JsBeforeUnloadResponse(
{this.message = "",
this.handledByClient = false,
this.confirmButtonTitle = "",
this.cancelButtonTitle = "",
this.action = JsBeforeUnloadResponseAction.CONFIRM});
Map<String, dynamic> toMap() {
return {
"message": message,
"confirmButtonTitle": confirmButtonTitle,
"cancelButtonTitle": cancelButtonTitle,
"handledByClient": handledByClient,
"action": action?.toValue()
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}
///Class that represents the reason the resource was caught by Safe Browsing.
class SafeBrowsingThreat {
final int _value;
@ -2954,10 +3143,15 @@ class ShouldOverrideUrlLoadingRequest {
}
///Class that represents the navigation request used by the [WebView.onCreateWindow] event.
class OnCreateWindowRequest {
///Represents the url of the navigation request.
class CreateWindowRequest {
///The URL of the request.
///
///**NOTE**: On Android, if the window has been created using JavaScript, this will be `null`.
String url;
///The window id. Used by [WebView] to create a new WebView.
int windowId;
///Indicates if the new window should be a dialog, rather than a full-size window. Available only on Android.
bool androidIsDialog;
@ -2967,18 +3161,24 @@ class OnCreateWindowRequest {
///The type of action triggering the navigation. Available only on iOS.
IOSWKNavigationType iosWKNavigationType;
OnCreateWindowRequest(
///Whether the request was made in order to fetch the main frame's document. Available only on iOS.
bool iosIsForMainFrame;
CreateWindowRequest(
{this.url,
this.windowId,
this.androidIsDialog,
this.androidIsUserGesture,
this.iosWKNavigationType});
this.iosWKNavigationType,
this.iosIsForMainFrame});
Map<String, dynamic> toMap() {
return {
"url": url,
"androidIsDialog": androidIsDialog,
"androidIsUserGesture": androidIsUserGesture,
"iosWKNavigationType": iosWKNavigationType?.toValue()
"iosWKNavigationType": iosWKNavigationType?.toValue(),
"iosUrl": url,
"windowId": windowId
};
}
@ -4163,3 +4363,39 @@ class SslCertificateDName {
return toMap().toString();
}
}
///Class used by [WebView.androidOnReceivedLoginRequest] event.
class LoginRequest {
///The account realm used to look up accounts.
String realm;
///An optional account. If not `null`, the account should be checked against accounts on the device.
///If it is a valid account, it should be used to log in the user. This value may be `null`.
String account;
///Authenticator specific arguments used to log in the user.
String args;
LoginRequest({
this.realm,
this.account,
this.args
});
Map<String, dynamic> toMap() {
return {
"realm": realm,
"account": account,
"args": args
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
@override
String toString() {
return toMap().toString();
}
}

View File

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'context_menu.dart';
import 'types.dart';
@ -7,12 +9,16 @@ import 'headless_in_app_webview.dart';
///Abstract class that represents a WebView. Used by [WebView] and [HeadlessInAppWebView].
abstract class WebView {
///The window id of a [CreateWindowRequest.windowId].
final int windowId;
///Event fired when the [WebView] is created.
final void Function(InAppWebViewController controller) onWebViewCreated;
///Event fired when the [WebView] starts to load an [url].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview
final void Function(InAppWebViewController controller, String url)
onLoadStart;
@ -20,12 +26,14 @@ abstract class WebView {
///Event fired when the [WebView] finishes loading an [url].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview
final void Function(InAppWebViewController controller, String url) onLoadStop;
///Event fired when the [WebView] encounters an error loading an [url].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview
final void Function(InAppWebViewController controller, String url, int code,
String message) onLoadError;
@ -41,6 +49,7 @@ abstract class WebView {
///**NOTE**: available on Android 23+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
final void Function(InAppWebViewController controller, String url,
int statusCode, String description) onLoadHttpError;
@ -71,6 +80,7 @@ abstract class WebView {
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview
final Future<ShouldOverrideUrlLoadingAction> Function(
InAppWebViewController controller,
@ -91,6 +101,7 @@ abstract class WebView {
///[y] represents the current vertical scroll origin in pixels.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#onScrollChanged(int,%20int,%20int,%20int)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll
final void Function(InAppWebViewController controller, int x, int y)
onScrollChanged;
@ -103,6 +114,7 @@ abstract class WebView {
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnDownloadStart] option to `true`.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setDownloadListener(android.webkit.DownloadListener)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview
final void Function(InAppWebViewController controller, String url)
onDownloadStart;
@ -120,53 +132,91 @@ abstract class WebView {
///Event fired when the [WebView] requests the host application to create a new window,
///for example when trying to open a link with `target="_blank"` or when `window.open()` is called by JavaScript side.
///The return value should be a [WebView] instance or `null`. If it returns `null`, then nothing will happen.
///If it returns an [InAppWebView] instance, when it will be added to the widget tree, it will load the request.
///If it returns a [HeadlessInAppWebView] instance, the [HeadlessInAppWebView.run] method will be immediately called.
///Remember to use the [CreateWindowRequest.windowId] to create the new WebView instance.
///
///[onCreateWindowRequest] represents the request.
///[createWindowRequest] represents the request.
///
///**NOTE**: on Android you need to set [AndroidInAppWebViewOptions.supportMultipleWindows] option to `true`.
///
///**NOTE**: on iOS, setting these initial options: [InAppWebViewOptions.supportZoom], [InAppWebViewOptions.useOnLoadResource], [InAppWebViewOptions.useShouldInterceptAjaxRequest],
///[InAppWebViewOptions.useShouldInterceptFetchRequest], [InAppWebViewOptions.applicationNameForUserAgent], [InAppWebViewOptions.javaScriptCanOpenWindowsAutomatically],
///[InAppWebViewOptions.javaScriptEnabled], [InAppWebViewOptions.minimumFontSize], [InAppWebViewOptions.preferredContentMode], [InAppWebViewOptions.incognito],
///[InAppWebViewOptions.cacheEnabled], [InAppWebViewOptions.mediaPlaybackRequiresUserGesture],
///[InAppWebViewOptions.resourceCustomSchemes], [IOSInAppWebViewOptions.sharedCookiesEnabled],
///[IOSInAppWebViewOptions.enableViewportScale], [IOSInAppWebViewOptions.allowsAirPlayForMediaPlayback],
///[IOSInAppWebViewOptions.allowsPictureInPictureMediaPlayback], [IOSInAppWebViewOptions.isFraudulentWebsiteWarningEnabled],
///[IOSInAppWebViewOptions.allowsInlineMediaPlayback], [IOSInAppWebViewOptions.suppressesIncrementalRendering], [IOSInAppWebViewOptions.selectionGranularity],
///[IOSInAppWebViewOptions.ignoresViewportScaleLimits],
///will have no effect due to a `WKWebView` limitation when creating a new window WebView: it's impossible to return a new `WKWebView`
///with a different `WKWebViewConfiguration` instance (see https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview).
///So, these options will be inherited from the caller WebView.
///Also, note that calling [InAppWebViewController.setOptions] method using the controller of the new created WebView,
///it will update also the WebView options of the caller WebView.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview
final void Function(InAppWebViewController controller,
OnCreateWindowRequest onCreateWindowRequest) onCreateWindow;
final Future<WebView> Function(InAppWebViewController controller,
CreateWindowRequest createWindowRequest) onCreateWindow;
///Event fired when the host application should close the given WebView and remove it from the view system if necessary.
///At this point, WebCore has stopped any loading in this window and has removed any cross-scripting ability in javascript.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCloseWindow(android.webkit.WebView)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537390-webviewdidclose
final void Function(InAppWebViewController controller) onCloseWindow;
///Event fired when the JavaScript `window` object of the WebView has received focus.
///This is the result of the `focus` JavaScript event applied to the `window` object.
final void Function(InAppWebViewController controller) onWindowFocus;
///Event fired when the JavaScript `window` object of the WebView has lost focus.
///This is the result of the `blur` JavaScript event applied to the `window` object.
final void Function(InAppWebViewController controller) onWindowBlur;
///Event fired when javascript calls the `alert()` method to display an alert dialog.
///If [JsAlertResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
///[jsAlertRequest] contains the message to be displayed in the alert dialog and the of the page requesting the dialog.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview
final Future<JsAlertResponse> Function(
InAppWebViewController controller, String message) onJsAlert;
InAppWebViewController controller, JsAlertRequest jsAlertRequest) onJsAlert;
///Event fired when javascript calls the `confirm()` method to display a confirm dialog.
///If [JsConfirmResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
///[jsConfirmRequest] contains the message to be displayed in the confirm dialog and the of the page requesting the dialog.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview
final Future<JsConfirmResponse> Function(
InAppWebViewController controller, String message) onJsConfirm;
InAppWebViewController controller, JsConfirmRequest jsConfirmRequest) onJsConfirm;
///Event fired when javascript calls the `prompt()` method to display a prompt dialog.
///If [JsPromptResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
///
///[defaultValue] represents the default value displayed in the prompt dialog.
///[jsPromptRequest] contains the message to be displayed in the prompt dialog, the default value displayed in the prompt dialog, and the of the page requesting the dialog.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview
final Future<JsPromptResponse> Function(InAppWebViewController controller,
String message, String defaultValue) onJsPrompt;
JsPromptRequest jsPromptRequest) onJsPrompt;
///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request.
///
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<HttpAuthResponse> Function(
InAppWebViewController controller, HttpAuthChallenge challenge)
@ -178,6 +228,7 @@ abstract class WebView {
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ServerTrustChallenge].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<ServerTrustAuthResponse> Function(
InAppWebViewController controller, ServerTrustChallenge challenge)
@ -191,6 +242,7 @@ abstract class WebView {
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ClientCertChallenge].
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview
final Future<ClientCertResponse> Function(
InAppWebViewController controller, ClientCertChallenge challenge)
@ -291,6 +343,7 @@ abstract class WebView {
///[hitTestResult] represents the hit result for hitting an HTML elements.
///
///**Official Android API**: https://developer.android.com/reference/android/view/View#setOnLongClickListener(android.view.View.OnLongClickListener)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer
final void Function(InAppWebViewController controller,
InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult;
@ -298,12 +351,14 @@ abstract class WebView {
///Event fired when the current page has entered full screen mode.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback)
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621621-didbecomevisiblenotification
final void Function(InAppWebViewController controller) onEnterFullscreen;
///Event fired when the current page has exited full screen mode.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onHideCustomView()
///
///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621617-didbecomehiddennotification
final void Function(InAppWebViewController controller) onExitFullscreen;
@ -315,10 +370,19 @@ abstract class WebView {
///[url] represents the URL corresponding to the page navigation that triggered this callback.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview
final Future<void> Function(InAppWebViewController controller, String url)
final void Function(InAppWebViewController controller, String url)
onPageCommitVisible;
///Event fired when a change in the document title occurred.
///
///[title] represents the string containing the new title of the document.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String)
final void Function(InAppWebViewController controller, String title)
onTitleChanged;
///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing.
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
///
@ -435,7 +499,7 @@ abstract class WebView {
///**NOTE**: available only on Android 26+.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail)
final Future<void> Function(
final void Function(
InAppWebViewController controller, RenderProcessGoneDetail detail)
androidOnRenderProcessGone;
@ -456,16 +520,69 @@ abstract class WebView {
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float)
final Future<void> Function(
final void Function(
InAppWebViewController controller, double oldScale, double newScale)
androidOnScaleChanged;
///Event fired when there is a request to display and focus for this WebView.
///This may happen due to another WebView opening a link in this WebView and requesting that this WebView be displayed.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onRequestFocus(android.webkit.WebView)
final void Function(InAppWebViewController controller) androidOnRequestFocus;
///Event fired when there is new favicon for the current page.
///
///[icon] represents the favicon for the current page.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedIcon(android.webkit.WebView,%20android.graphics.Bitmap)
final void Function(InAppWebViewController controller, Uint8List icon) androidOnReceivedIcon;
///Event fired when there is an url for an apple-touch-icon.
///
///[url] represents the icon url.
///
///[precomposed] is `true` if the url is for a precomposed touch icon.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTouchIconUrl(android.webkit.WebView,%20java.lang.String,%20boolean)
final void Function(InAppWebViewController controller, String url, bool precomposed) androidOnReceivedTouchIconUrl;
///Event fired when the client should display a dialog to confirm navigation away from the current page.
///This is the result of the `onbeforeunload` javascript event.
///If [JsBeforeUnloadResponse.handledByClient] is `true`, WebView will assume that the client will handle the confirm dialog.
///If [JsBeforeUnloadResponse.handledByClient] is `false`, a default value of `true` will be returned to javascript to accept navigation away from the current page.
///The default behavior is to return `false`.
///Setting the [JsBeforeUnloadResponse.action] to [JsBeforeUnloadResponseAction.CONFIRM] will navigate away from the current page,
///[JsBeforeUnloadResponseAction.CANCEL] will cancel the navigation.
///
///[jsBeforeUnloadRequest] contains the message to be displayed in the alert dialog and the of the page requesting the dialog.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsBeforeUnload(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)
final Future<JsBeforeUnloadResponse> Function(
InAppWebViewController controller, JsBeforeUnloadRequest jsBeforeUnloadRequest) androidOnJsBeforeUnload;
///Event fired when a request to automatically log in the user has been processed.
///
///[loginRequest] contains the realm, account and args of the login request.
///
///**NOTE**: available only on Android.
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedLoginRequest(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String)
final void Function(InAppWebViewController controller, LoginRequest loginRequest) androidOnReceivedLoginRequest;
///Invoked when the web view's web content process is terminated.
///
///**NOTE**: available only on iOS.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi
final Future<void> Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)
iosOnWebContentProcessDidTerminate;
///Called when a web view receives a server redirect.
@ -473,7 +590,7 @@ abstract class WebView {
///**NOTE**: available only on iOS.
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview
final Future<void> Function(InAppWebViewController controller)
final void Function(InAppWebViewController controller)
iosOnDidReceiveServerRedirectForProvisionalNavigation;
///Initial url that will be loaded.
@ -495,7 +612,8 @@ abstract class WebView {
final ContextMenu contextMenu;
WebView(
{this.onWebViewCreated,
{this.windowId,
this.onWebViewCreated,
this.onLoadStart,
this.onLoadStop,
this.onLoadError,
@ -508,6 +626,7 @@ abstract class WebView {
this.onDownloadStart,
this.onLoadResourceCustomScheme,
this.onCreateWindow,
this.onCloseWindow,
this.onJsAlert,
this.onJsConfirm,
this.onJsPrompt,
@ -525,6 +644,9 @@ abstract class WebView {
this.onEnterFullscreen,
this.onExitFullscreen,
this.onPageCommitVisible,
this.onTitleChanged,
this.onWindowFocus,
this.onWindowBlur,
this.androidOnSafeBrowsingHit,
this.androidOnPermissionRequest,
this.androidOnGeolocationPermissionsShowPrompt,
@ -535,6 +657,11 @@ abstract class WebView {
this.androidOnRenderProcessUnresponsive,
this.androidOnFormResubmission,
this.androidOnScaleChanged,
this.androidOnRequestFocus,
this.androidOnReceivedIcon,
this.androidOnReceivedTouchIconUrl,
this.androidOnJsBeforeUnload,
this.androidOnReceivedLoginRequest,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.initialUrl,

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
version: 3.4.0+2
version: 4.0.0
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: