diff --git a/packages/webview_flutter/webview_flutter/android/build.gradle b/packages/webview_flutter/webview_flutter/android/build.gradle
deleted file mode 100644
index 4a164317c60f..000000000000
--- a/packages/webview_flutter/webview_flutter/android/build.gradle
+++ /dev/null
@@ -1,57 +0,0 @@
-group 'io.flutter.plugins.webviewflutter'
-version '1.0-SNAPSHOT'
-
-buildscript {
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:3.3.0'
- }
-}
-
-rootProject.allprojects {
- repositories {
- google()
- mavenCentral()
- }
-}
-
-apply plugin: 'com.android.library'
-
-android {
- compileSdkVersion 29
-
- defaultConfig {
- minSdkVersion 19
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
-
- lintOptions {
- disable 'InvalidPackage'
- disable 'GradleDependency'
- }
-
- dependencies {
- implementation 'androidx.annotation:annotation:1.0.0'
- implementation 'androidx.webkit:webkit:1.0.0'
- testImplementation 'junit:junit:4.12'
- testImplementation 'org.mockito:mockito-inline:3.11.1'
- testImplementation 'androidx.test:core:1.3.0'
- }
-
-
- testOptions {
- unitTests.includeAndroidResources = true
- unitTests.returnDefaultValues = true
- unitTests.all {
- testLogging {
- events "passed", "skipped", "failed", "standardOut", "standardError"
- outputs.upToDateWhen {false}
- showStandardStreams = true
- }
- }
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/settings.gradle b/packages/webview_flutter/webview_flutter/android/settings.gradle
deleted file mode 100644
index 5be7a4b4c692..000000000000
--- a/packages/webview_flutter/webview_flutter/android/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-rootProject.name = 'webview_flutter'
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml b/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml
deleted file mode 100644
index a087f2c75c24..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/DisplayListenerProxy.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/DisplayListenerProxy.java
deleted file mode 100644
index 31e3fe08c057..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/DisplayListenerProxy.java
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import static android.hardware.display.DisplayManager.DisplayListener;
-
-import android.annotation.TargetApi;
-import android.hardware.display.DisplayManager;
-import android.os.Build;
-import android.util.Log;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-
-/**
- * Works around an Android WebView bug by filtering some DisplayListener invocations.
- *
- *
Older Android WebView versions had assumed that when {@link DisplayListener#onDisplayChanged}
- * is invoked, the display ID it is provided is of a valid display. However it turns out that when a
- * display is removed Android may call onDisplayChanged with the ID of the removed display, in this
- * case the Android WebView code tries to fetch and use the display with this ID and crashes with an
- * NPE.
- *
- *
This issue was fixed in the Android WebView code in
- * https://chromium-review.googlesource.com/517913 which is available starting WebView version
- * 58.0.3029.125 however older webviews in the wild still have this issue.
- *
- *
Since Flutter removes virtual displays whenever a platform view is resized the webview crash
- * is more likely to happen than other apps. And users were reporting this issue see:
- * https://github.com/flutter/flutter/issues/30420
- *
- *
This class works around the webview bug by unregistering the WebView's DisplayListener, and
- * instead registering its own DisplayListener which delegates the callbacks to the WebView's
- * listener unless it's a onDisplayChanged for an invalid display.
- *
- *
I did not find a clean way to get a handle of the WebView's DisplayListener so I'm using
- * reflection to fetch all registered listeners before and after initializing a webview. In the
- * first initialization of a webview within the process the difference between the lists is the
- * webview's display listener.
- */
-@TargetApi(Build.VERSION_CODES.KITKAT)
-class DisplayListenerProxy {
- private static final String TAG = "DisplayListenerProxy";
-
- private ArrayList listenersBeforeWebView;
-
- /** Should be called prior to the webview's initialization. */
- void onPreWebViewInitialization(DisplayManager displayManager) {
- listenersBeforeWebView = yoinkDisplayListeners(displayManager);
- }
-
- /** Should be called after the webview's initialization. */
- void onPostWebViewInitialization(final DisplayManager displayManager) {
- final ArrayList webViewListeners = yoinkDisplayListeners(displayManager);
- // We recorded the list of listeners prior to initializing webview, any new listeners we see
- // after initializing the webview are listeners added by the webview.
- webViewListeners.removeAll(listenersBeforeWebView);
-
- if (webViewListeners.isEmpty()) {
- // The Android WebView registers a single display listener per process (even if there
- // are multiple WebView instances) so this list is expected to be non-empty only the
- // first time a webview is initialized.
- // Note that in an add2app scenario if the application had instantiated a non Flutter
- // WebView prior to instantiating the Flutter WebView we are not able to get a reference
- // to the WebView's display listener and can't work around the bug.
- //
- // This means that webview resizes in add2app Flutter apps with a non Flutter WebView
- // running on a system with a webview prior to 58.0.3029.125 may crash (the Android's
- // behavior seems to be racy so it doesn't always happen).
- return;
- }
-
- for (DisplayListener webViewListener : webViewListeners) {
- // Note that while DisplayManager.unregisterDisplayListener throws when given an
- // unregistered listener, this isn't an issue as the WebView code never calls
- // unregisterDisplayListener.
- displayManager.unregisterDisplayListener(webViewListener);
-
- // We never explicitly unregister this listener as the webview's listener is never
- // unregistered (it's released when the process is terminated).
- displayManager.registerDisplayListener(
- new DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) {
- for (DisplayListener webViewListener : webViewListeners) {
- webViewListener.onDisplayAdded(displayId);
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- for (DisplayListener webViewListener : webViewListeners) {
- webViewListener.onDisplayRemoved(displayId);
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (displayManager.getDisplay(displayId) == null) {
- return;
- }
- for (DisplayListener webViewListener : webViewListeners) {
- webViewListener.onDisplayChanged(displayId);
- }
- }
- },
- null);
- }
- }
-
- @SuppressWarnings({"unchecked", "PrivateApi"})
- private static ArrayList yoinkDisplayListeners(DisplayManager displayManager) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- // We cannot use reflection on Android P, but it shouldn't matter as it shipped
- // with WebView 66.0.3359.158 and the WebView version the bug this code is working around was
- // fixed in 61.0.3116.0.
- return new ArrayList<>();
- }
- try {
- Field displayManagerGlobalField = DisplayManager.class.getDeclaredField("mGlobal");
- displayManagerGlobalField.setAccessible(true);
- Object displayManagerGlobal = displayManagerGlobalField.get(displayManager);
- Field displayListenersField =
- displayManagerGlobal.getClass().getDeclaredField("mDisplayListeners");
- displayListenersField.setAccessible(true);
- ArrayList delegates =
- (ArrayList) displayListenersField.get(displayManagerGlobal);
-
- Field listenerField = null;
- ArrayList listeners = new ArrayList<>();
- for (Object delegate : delegates) {
- if (listenerField == null) {
- listenerField = delegate.getClass().getField("mListener");
- listenerField.setAccessible(true);
- }
- DisplayManager.DisplayListener listener =
- (DisplayManager.DisplayListener) listenerField.get(delegate);
- listeners.add(listener);
- }
- return listeners;
- } catch (NoSuchFieldException | IllegalAccessException e) {
- Log.w(TAG, "Could not extract WebView's display listeners. " + e);
- return new ArrayList<>();
- }
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java
deleted file mode 100644
index df3f21daadeb..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.webkit.CookieManager;
-import android.webkit.ValueCallback;
-import io.flutter.plugin.common.BinaryMessenger;
-import io.flutter.plugin.common.MethodCall;
-import io.flutter.plugin.common.MethodChannel;
-import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
-import io.flutter.plugin.common.MethodChannel.Result;
-
-class FlutterCookieManager implements MethodCallHandler {
- private final MethodChannel methodChannel;
-
- FlutterCookieManager(BinaryMessenger messenger) {
- methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager");
- methodChannel.setMethodCallHandler(this);
- }
-
- @Override
- public void onMethodCall(MethodCall methodCall, Result result) {
- switch (methodCall.method) {
- case "clearCookies":
- clearCookies(result);
- break;
- default:
- result.notImplemented();
- }
- }
-
- void dispose() {
- methodChannel.setMethodCallHandler(null);
- }
-
- private static void clearCookies(final Result result) {
- CookieManager cookieManager = CookieManager.getInstance();
- final boolean hasCookies = cookieManager.hasCookies();
- if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- cookieManager.removeAllCookies(
- new ValueCallback() {
- @Override
- public void onReceiveValue(Boolean value) {
- result.success(hasCookies);
- }
- });
- } else {
- cookieManager.removeAllCookie();
- result.success(hasCookies);
- }
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterDownloadListener.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterDownloadListener.java
deleted file mode 100644
index cfad4e315514..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterDownloadListener.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import android.webkit.DownloadListener;
-import android.webkit.WebView;
-
-/** DownloadListener to notify the {@link FlutterWebViewClient} of download starts */
-public class FlutterDownloadListener implements DownloadListener {
- private final FlutterWebViewClient webViewClient;
- private WebView webView;
-
- public FlutterDownloadListener(FlutterWebViewClient webViewClient) {
- this.webViewClient = webViewClient;
- }
-
- /** Sets the {@link WebView} that the result of the navigation delegate will be send to. */
- public void setWebView(WebView webView) {
- this.webView = webView;
- }
-
- @Override
- public void onDownloadStart(
- String url,
- String userAgent,
- String contentDisposition,
- String mimetype,
- long contentLength) {
- webViewClient.notifyDownload(webView, url);
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
deleted file mode 100644
index 4651a5f5ae22..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
+++ /dev/null
@@ -1,498 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Message;
-import android.view.View;
-import android.webkit.DownloadListener;
-import android.webkit.WebChromeClient;
-import android.webkit.WebResourceRequest;
-import android.webkit.WebStorage;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import io.flutter.plugin.common.MethodCall;
-import io.flutter.plugin.common.MethodChannel;
-import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
-import io.flutter.plugin.common.MethodChannel.Result;
-import io.flutter.plugin.platform.PlatformView;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-public class FlutterWebView implements PlatformView, MethodCallHandler {
-
- private static final String JS_CHANNEL_NAMES_FIELD = "javascriptChannelNames";
- private final WebView webView;
- private final MethodChannel methodChannel;
- private final FlutterWebViewClient flutterWebViewClient;
- private final Handler platformThreadHandler;
-
- // Verifies that a url opened by `Window.open` has a secure url.
- private class FlutterWebChromeClient extends WebChromeClient {
-
- @Override
- public boolean onCreateWindow(
- final WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
- final WebViewClient webViewClient =
- new WebViewClient() {
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- @Override
- public boolean shouldOverrideUrlLoading(
- @NonNull WebView view, @NonNull WebResourceRequest request) {
- final String url = request.getUrl().toString();
- if (!flutterWebViewClient.shouldOverrideUrlLoading(
- FlutterWebView.this.webView, request)) {
- webView.loadUrl(url);
- }
- return true;
- }
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (!flutterWebViewClient.shouldOverrideUrlLoading(
- FlutterWebView.this.webView, url)) {
- webView.loadUrl(url);
- }
- return true;
- }
- };
-
- final WebView newWebView = new WebView(view.getContext());
- newWebView.setWebViewClient(webViewClient);
-
- final WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
- transport.setWebView(newWebView);
- resultMsg.sendToTarget();
-
- return true;
- }
-
- @Override
- public void onProgressChanged(WebView view, int progress) {
- flutterWebViewClient.onLoadingProgress(progress);
- }
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- @SuppressWarnings("unchecked")
- FlutterWebView(
- final Context context,
- MethodChannel methodChannel,
- Map params,
- View containerView) {
-
- DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
- DisplayManager displayManager =
- (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
- displayListenerProxy.onPreWebViewInitialization(displayManager);
-
- this.methodChannel = methodChannel;
- this.methodChannel.setMethodCallHandler(this);
-
- flutterWebViewClient = new FlutterWebViewClient(methodChannel);
-
- FlutterDownloadListener flutterDownloadListener =
- new FlutterDownloadListener(flutterWebViewClient);
- webView =
- createWebView(
- new WebViewBuilder(context, containerView),
- params,
- new FlutterWebChromeClient(),
- flutterDownloadListener);
- flutterDownloadListener.setWebView(webView);
-
- displayListenerProxy.onPostWebViewInitialization(displayManager);
-
- platformThreadHandler = new Handler(context.getMainLooper());
-
- Map settings = (Map) params.get("settings");
- if (settings != null) {
- applySettings(settings);
- }
-
- if (params.containsKey(JS_CHANNEL_NAMES_FIELD)) {
- List names = (List) params.get(JS_CHANNEL_NAMES_FIELD);
- if (names != null) {
- registerJavaScriptChannelNames(names);
- }
- }
-
- Integer autoMediaPlaybackPolicy = (Integer) params.get("autoMediaPlaybackPolicy");
- if (autoMediaPlaybackPolicy != null) {
- updateAutoMediaPlaybackPolicy(autoMediaPlaybackPolicy);
- }
- if (params.containsKey("userAgent")) {
- String userAgent = (String) params.get("userAgent");
- updateUserAgent(userAgent);
- }
- if (params.containsKey("initialUrl")) {
- String url = (String) params.get("initialUrl");
- webView.loadUrl(url);
- }
- }
-
- /**
- * Creates a {@link android.webkit.WebView} and configures it according to the supplied
- * parameters.
- *
- * The {@link WebView} is configured with the following predefined settings:
- *
- *
- * always enable the DOM storage API;
- * always allow JavaScript to automatically open windows;
- * always allow support for multiple windows;
- * always use the {@link FlutterWebChromeClient} as web Chrome client.
- *
- *
- * Important: This method is visible for testing purposes only and should
- * never be called from outside this class.
- *
- * @param webViewBuilder a {@link WebViewBuilder} which is responsible for building the {@link
- * WebView}.
- * @param params creation parameters received over the method channel.
- * @param webChromeClient an implementation of WebChromeClient This value may be null.
- * @return The new {@link android.webkit.WebView} object.
- */
- @VisibleForTesting
- static WebView createWebView(
- WebViewBuilder webViewBuilder,
- Map params,
- WebChromeClient webChromeClient,
- @Nullable DownloadListener downloadListener) {
- boolean usesHybridComposition = Boolean.TRUE.equals(params.get("usesHybridComposition"));
- webViewBuilder
- .setUsesHybridComposition(usesHybridComposition)
- .setDomStorageEnabled(true) // Always enable DOM storage API.
- .setJavaScriptCanOpenWindowsAutomatically(
- true) // Always allow automatically opening of windows.
- .setSupportMultipleWindows(true) // Always support multiple windows.
- .setWebChromeClient(webChromeClient)
- .setDownloadListener(
- downloadListener); // Always use {@link FlutterWebChromeClient} as web Chrome client.
-
- return webViewBuilder.build();
- }
-
- @Override
- public View getView() {
- return webView;
- }
-
- // @Override
- // This is overriding a method that hasn't rolled into stable Flutter yet. Including the
- // annotation would cause compile time failures in versions of Flutter too old to include the new
- // method. However leaving it raw like this means that the method will be ignored in old versions
- // of Flutter but used as an override anyway wherever it's actually defined.
- // TODO(mklim): Add the @Override annotation once flutter/engine#9727 rolls to stable.
- public void onInputConnectionUnlocked() {
- if (webView instanceof InputAwareWebView) {
- ((InputAwareWebView) webView).unlockInputConnection();
- }
- }
-
- // @Override
- // This is overriding a method that hasn't rolled into stable Flutter yet. Including the
- // annotation would cause compile time failures in versions of Flutter too old to include the new
- // method. However leaving it raw like this means that the method will be ignored in old versions
- // of Flutter but used as an override anyway wherever it's actually defined.
- // TODO(mklim): Add the @Override annotation once flutter/engine#9727 rolls to stable.
- public void onInputConnectionLocked() {
- if (webView instanceof InputAwareWebView) {
- ((InputAwareWebView) webView).lockInputConnection();
- }
- }
-
- // @Override
- // This is overriding a method that hasn't rolled into stable Flutter yet. Including the
- // annotation would cause compile time failures in versions of Flutter too old to include the new
- // method. However leaving it raw like this means that the method will be ignored in old versions
- // of Flutter but used as an override anyway wherever it's actually defined.
- // TODO(mklim): Add the @Override annotation once stable passes v1.10.9.
- public void onFlutterViewAttached(View flutterView) {
- if (webView instanceof InputAwareWebView) {
- ((InputAwareWebView) webView).setContainerView(flutterView);
- }
- }
-
- // @Override
- // This is overriding a method that hasn't rolled into stable Flutter yet. Including the
- // annotation would cause compile time failures in versions of Flutter too old to include the new
- // method. However leaving it raw like this means that the method will be ignored in old versions
- // of Flutter but used as an override anyway wherever it's actually defined.
- // TODO(mklim): Add the @Override annotation once stable passes v1.10.9.
- public void onFlutterViewDetached() {
- if (webView instanceof InputAwareWebView) {
- ((InputAwareWebView) webView).setContainerView(null);
- }
- }
-
- @Override
- public void onMethodCall(MethodCall methodCall, Result result) {
- switch (methodCall.method) {
- case "loadUrl":
- loadUrl(methodCall, result);
- break;
- case "updateSettings":
- updateSettings(methodCall, result);
- break;
- case "canGoBack":
- canGoBack(result);
- break;
- case "canGoForward":
- canGoForward(result);
- break;
- case "goBack":
- goBack(result);
- break;
- case "goForward":
- goForward(result);
- break;
- case "reload":
- reload(result);
- break;
- case "currentUrl":
- currentUrl(result);
- break;
- case "evaluateJavascript":
- evaluateJavaScript(methodCall, result);
- break;
- case "addJavascriptChannels":
- addJavaScriptChannels(methodCall, result);
- break;
- case "removeJavascriptChannels":
- removeJavaScriptChannels(methodCall, result);
- break;
- case "clearCache":
- clearCache(result);
- break;
- case "getTitle":
- getTitle(result);
- break;
- case "scrollTo":
- scrollTo(methodCall, result);
- break;
- case "scrollBy":
- scrollBy(methodCall, result);
- break;
- case "getScrollX":
- getScrollX(result);
- break;
- case "getScrollY":
- getScrollY(result);
- break;
- default:
- result.notImplemented();
- }
- }
-
- @SuppressWarnings("unchecked")
- private void loadUrl(MethodCall methodCall, Result result) {
- Map request = (Map) methodCall.arguments;
- String url = (String) request.get("url");
- Map headers = (Map) request.get("headers");
- if (headers == null) {
- headers = Collections.emptyMap();
- }
- webView.loadUrl(url, headers);
- result.success(null);
- }
-
- private void canGoBack(Result result) {
- result.success(webView.canGoBack());
- }
-
- private void canGoForward(Result result) {
- result.success(webView.canGoForward());
- }
-
- private void goBack(Result result) {
- if (webView.canGoBack()) {
- webView.goBack();
- }
- result.success(null);
- }
-
- private void goForward(Result result) {
- if (webView.canGoForward()) {
- webView.goForward();
- }
- result.success(null);
- }
-
- private void reload(Result result) {
- webView.reload();
- result.success(null);
- }
-
- private void currentUrl(Result result) {
- result.success(webView.getUrl());
- }
-
- @SuppressWarnings("unchecked")
- private void updateSettings(MethodCall methodCall, Result result) {
- applySettings((Map) methodCall.arguments);
- result.success(null);
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private void evaluateJavaScript(MethodCall methodCall, final Result result) {
- String jsString = (String) methodCall.arguments;
- if (jsString == null) {
- throw new UnsupportedOperationException("JavaScript string cannot be null");
- }
- webView.evaluateJavascript(
- jsString,
- new android.webkit.ValueCallback() {
- @Override
- public void onReceiveValue(String value) {
- result.success(value);
- }
- });
- }
-
- @SuppressWarnings("unchecked")
- private void addJavaScriptChannels(MethodCall methodCall, Result result) {
- List channelNames = (List) methodCall.arguments;
- registerJavaScriptChannelNames(channelNames);
- result.success(null);
- }
-
- @SuppressWarnings("unchecked")
- private void removeJavaScriptChannels(MethodCall methodCall, Result result) {
- List channelNames = (List) methodCall.arguments;
- for (String channelName : channelNames) {
- webView.removeJavascriptInterface(channelName);
- }
- result.success(null);
- }
-
- private void clearCache(Result result) {
- webView.clearCache(true);
- WebStorage.getInstance().deleteAllData();
- result.success(null);
- }
-
- private void getTitle(Result result) {
- result.success(webView.getTitle());
- }
-
- private void scrollTo(MethodCall methodCall, Result result) {
- Map request = methodCall.arguments();
- int x = (int) request.get("x");
- int y = (int) request.get("y");
-
- webView.scrollTo(x, y);
-
- result.success(null);
- }
-
- private void scrollBy(MethodCall methodCall, Result result) {
- Map request = methodCall.arguments();
- int x = (int) request.get("x");
- int y = (int) request.get("y");
-
- webView.scrollBy(x, y);
- result.success(null);
- }
-
- private void getScrollX(Result result) {
- result.success(webView.getScrollX());
- }
-
- private void getScrollY(Result result) {
- result.success(webView.getScrollY());
- }
-
- private void applySettings(Map settings) {
- for (String key : settings.keySet()) {
- switch (key) {
- case "jsMode":
- Integer mode = (Integer) settings.get(key);
- if (mode != null) {
- updateJsMode(mode);
- }
- break;
- case "hasNavigationDelegate":
- final boolean hasNavigationDelegate = (boolean) settings.get(key);
-
- final WebViewClient webViewClient =
- flutterWebViewClient.createWebViewClient(hasNavigationDelegate);
-
- webView.setWebViewClient(webViewClient);
- break;
- case "debuggingEnabled":
- final boolean debuggingEnabled = (boolean) settings.get(key);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- webView.setWebContentsDebuggingEnabled(debuggingEnabled);
- }
- break;
- case "hasProgressTracking":
- flutterWebViewClient.hasProgressTracking = (boolean) settings.get(key);
- break;
- case "gestureNavigationEnabled":
- break;
- case "userAgent":
- updateUserAgent((String) settings.get(key));
- break;
- case "allowsInlineMediaPlayback":
- // no-op inline media playback is always allowed on Android.
- break;
- default:
- throw new IllegalArgumentException("Unknown WebView setting: " + key);
- }
- }
- }
-
- private void updateJsMode(int mode) {
- switch (mode) {
- case 0: // disabled
- webView.getSettings().setJavaScriptEnabled(false);
- break;
- case 1: // unrestricted
- webView.getSettings().setJavaScriptEnabled(true);
- break;
- default:
- throw new IllegalArgumentException("Trying to set unknown JavaScript mode: " + mode);
- }
- }
-
- private void updateAutoMediaPlaybackPolicy(int mode) {
- // This is the index of the AutoMediaPlaybackPolicy enum, index 1 is always_allow, for all
- // other values we require a user gesture.
- boolean requireUserGesture = mode != 1;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- webView.getSettings().setMediaPlaybackRequiresUserGesture(requireUserGesture);
- }
- }
-
- private void registerJavaScriptChannelNames(List channelNames) {
- for (String channelName : channelNames) {
- webView.addJavascriptInterface(
- new JavaScriptChannel(methodChannel, channelName, platformThreadHandler), channelName);
- }
- }
-
- private void updateUserAgent(String userAgent) {
- webView.getSettings().setUserAgentString(userAgent);
- }
-
- @Override
- public void dispose() {
- methodChannel.setMethodCallHandler(null);
- if (webView instanceof InputAwareWebView) {
- ((InputAwareWebView) webView).dispose();
- }
- webView.destroy();
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewClient.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewClient.java
deleted file mode 100644
index 260ef8e8b15d..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewClient.java
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.graphics.Bitmap;
-import android.os.Build;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.webkit.WebResourceError;
-import android.webkit.WebResourceRequest;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-import androidx.webkit.WebResourceErrorCompat;
-import androidx.webkit.WebViewClientCompat;
-import io.flutter.plugin.common.MethodChannel;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-// We need to use WebViewClientCompat to get
-// shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
-// invoked by the webview on older Android devices, without it pages that use iframes will
-// be broken when a navigationDelegate is set on Android version earlier than N.
-class FlutterWebViewClient {
- private static final String TAG = "FlutterWebViewClient";
- private final MethodChannel methodChannel;
- private boolean hasNavigationDelegate;
- boolean hasProgressTracking;
-
- FlutterWebViewClient(MethodChannel methodChannel) {
- this.methodChannel = methodChannel;
- }
-
- static String errorCodeToString(int errorCode) {
- switch (errorCode) {
- case WebViewClient.ERROR_AUTHENTICATION:
- return "authentication";
- case WebViewClient.ERROR_BAD_URL:
- return "badUrl";
- case WebViewClient.ERROR_CONNECT:
- return "connect";
- case WebViewClient.ERROR_FAILED_SSL_HANDSHAKE:
- return "failedSslHandshake";
- case WebViewClient.ERROR_FILE:
- return "file";
- case WebViewClient.ERROR_FILE_NOT_FOUND:
- return "fileNotFound";
- case WebViewClient.ERROR_HOST_LOOKUP:
- return "hostLookup";
- case WebViewClient.ERROR_IO:
- return "io";
- case WebViewClient.ERROR_PROXY_AUTHENTICATION:
- return "proxyAuthentication";
- case WebViewClient.ERROR_REDIRECT_LOOP:
- return "redirectLoop";
- case WebViewClient.ERROR_TIMEOUT:
- return "timeout";
- case WebViewClient.ERROR_TOO_MANY_REQUESTS:
- return "tooManyRequests";
- case WebViewClient.ERROR_UNKNOWN:
- return "unknown";
- case WebViewClient.ERROR_UNSAFE_RESOURCE:
- return "unsafeResource";
- case WebViewClient.ERROR_UNSUPPORTED_AUTH_SCHEME:
- return "unsupportedAuthScheme";
- case WebViewClient.ERROR_UNSUPPORTED_SCHEME:
- return "unsupportedScheme";
- }
-
- final String message =
- String.format(Locale.getDefault(), "Could not find a string for errorCode: %d", errorCode);
- throw new IllegalArgumentException(message);
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
- if (!hasNavigationDelegate) {
- return false;
- }
- notifyOnNavigationRequest(
- request.getUrl().toString(), request.getRequestHeaders(), view, request.isForMainFrame());
- // We must make a synchronous decision here whether to allow the navigation or not,
- // if the Dart code has set a navigation delegate we want that delegate to decide whether
- // to navigate or not, and as we cannot get a response from the Dart delegate synchronously we
- // return true here to block the navigation, if the Dart delegate decides to allow the
- // navigation the plugin will later make an addition loadUrl call for this url.
- //
- // Since we cannot call loadUrl for a subframe, we currently only allow the delegate to stop
- // navigations that target the main frame, if the request is not for the main frame
- // we just return false to allow the navigation.
- //
- // For more details see: https://github.com/flutter/flutter/issues/25329#issuecomment-464863209
- return request.isForMainFrame();
- }
-
- boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (!hasNavigationDelegate) {
- return false;
- }
- // This version of shouldOverrideUrlLoading is only invoked by the webview on devices with
- // webview versions earlier than 67(it is also invoked when hasNavigationDelegate is false).
- // On these devices we cannot tell whether the navigation is targeted to the main frame or not.
- // We proceed assuming that the navigation is targeted to the main frame. If the page had any
- // frames they will be loaded in the main frame instead.
- Log.w(
- TAG,
- "Using a navigationDelegate with an old webview implementation, pages with frames or iframes will not work");
- notifyOnNavigationRequest(url, null, view, true);
- return true;
- }
-
- /**
- * Notifies the Flutter code that a download should start when a navigation delegate is set.
- *
- * @param view the webView the result of the navigation delegate will be send to.
- * @param url the download url
- * @return A boolean whether or not the request is forwarded to the Flutter code.
- */
- boolean notifyDownload(WebView view, String url) {
- if (!hasNavigationDelegate) {
- return false;
- }
-
- notifyOnNavigationRequest(url, null, view, true);
- return true;
- }
-
- private void onPageStarted(WebView view, String url) {
- Map args = new HashMap<>();
- args.put("url", url);
- methodChannel.invokeMethod("onPageStarted", args);
- }
-
- private void onPageFinished(WebView view, String url) {
- Map args = new HashMap<>();
- args.put("url", url);
- methodChannel.invokeMethod("onPageFinished", args);
- }
-
- void onLoadingProgress(int progress) {
- if (hasProgressTracking) {
- Map args = new HashMap<>();
- args.put("progress", progress);
- methodChannel.invokeMethod("onProgress", args);
- }
- }
-
- private void onWebResourceError(
- final int errorCode, final String description, final String failingUrl) {
- final Map args = new HashMap<>();
- args.put("errorCode", errorCode);
- args.put("description", description);
- args.put("errorType", FlutterWebViewClient.errorCodeToString(errorCode));
- args.put("failingUrl", failingUrl);
- methodChannel.invokeMethod("onWebResourceError", args);
- }
-
- private void notifyOnNavigationRequest(
- String url, Map headers, WebView webview, boolean isMainFrame) {
- HashMap args = new HashMap<>();
- args.put("url", url);
- args.put("isForMainFrame", isMainFrame);
- if (isMainFrame) {
- methodChannel.invokeMethod(
- "navigationRequest", args, new OnNavigationRequestResult(url, headers, webview));
- } else {
- methodChannel.invokeMethod("navigationRequest", args);
- }
- }
-
- // This method attempts to avoid using WebViewClientCompat due to bug
- // https://bugs.chromium.org/p/chromium/issues/detail?id=925887. Also, see
- // https://github.com/flutter/flutter/issues/29446.
- WebViewClient createWebViewClient(boolean hasNavigationDelegate) {
- this.hasNavigationDelegate = hasNavigationDelegate;
-
- if (!hasNavigationDelegate || android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- return internalCreateWebViewClient();
- }
-
- return internalCreateWebViewClientCompat();
- }
-
- private WebViewClient internalCreateWebViewClient() {
- return new WebViewClient() {
- @TargetApi(Build.VERSION_CODES.N)
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
- return FlutterWebViewClient.this.shouldOverrideUrlLoading(view, request);
- }
-
- @Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {
- FlutterWebViewClient.this.onPageStarted(view, url);
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- FlutterWebViewClient.this.onPageFinished(view, url);
- }
-
- @TargetApi(Build.VERSION_CODES.M)
- @Override
- public void onReceivedError(
- WebView view, WebResourceRequest request, WebResourceError error) {
- if (request.isForMainFrame()) {
- FlutterWebViewClient.this.onWebResourceError(
- error.getErrorCode(), error.getDescription().toString(), request.getUrl().toString());
- }
- }
-
- @Override
- public void onReceivedError(
- WebView view, int errorCode, String description, String failingUrl) {
- FlutterWebViewClient.this.onWebResourceError(errorCode, description, failingUrl);
- }
-
- @Override
- public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
- // Deliberately empty. Occasionally the webview will mark events as having failed to be
- // handled even though they were handled. We don't want to propagate those as they're not
- // truly lost.
- }
- };
- }
-
- private WebViewClientCompat internalCreateWebViewClientCompat() {
- return new WebViewClientCompat() {
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
- return FlutterWebViewClient.this.shouldOverrideUrlLoading(view, request);
- }
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- return FlutterWebViewClient.this.shouldOverrideUrlLoading(view, url);
- }
-
- @Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {
- FlutterWebViewClient.this.onPageStarted(view, url);
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- FlutterWebViewClient.this.onPageFinished(view, url);
- }
-
- // This method is only called when the WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR feature is
- // enabled. The deprecated method is called when a device doesn't support this.
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- @SuppressLint("RequiresFeature")
- @Override
- public void onReceivedError(
- @NonNull WebView view,
- @NonNull WebResourceRequest request,
- @NonNull WebResourceErrorCompat error) {
- if (request.isForMainFrame()) {
- FlutterWebViewClient.this.onWebResourceError(
- error.getErrorCode(), error.getDescription().toString(), request.getUrl().toString());
- }
- }
-
- @Override
- public void onReceivedError(
- WebView view, int errorCode, String description, String failingUrl) {
- FlutterWebViewClient.this.onWebResourceError(errorCode, description, failingUrl);
- }
-
- @Override
- public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
- // Deliberately empty. Occasionally the webview will mark events as having failed to be
- // handled even though they were handled. We don't want to propagate those as they're not
- // truly lost.
- }
- };
- }
-
- private static class OnNavigationRequestResult implements MethodChannel.Result {
- private final String url;
- private final Map headers;
- private final WebView webView;
-
- private OnNavigationRequestResult(String url, Map headers, WebView webView) {
- this.url = url;
- this.headers = headers;
- this.webView = webView;
- }
-
- @Override
- public void success(Object shouldLoad) {
- Boolean typedShouldLoad = (Boolean) shouldLoad;
- if (typedShouldLoad) {
- loadUrl();
- }
- }
-
- @Override
- public void error(String errorCode, String s1, Object o) {
- throw new IllegalStateException("navigationRequest calls must succeed");
- }
-
- @Override
- public void notImplemented() {
- throw new IllegalStateException(
- "navigationRequest must be implemented by the webview method channel");
- }
-
- private void loadUrl() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- webView.loadUrl(url, headers);
- } else {
- webView.loadUrl(url);
- }
- }
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewFactory.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewFactory.java
deleted file mode 100644
index 8fe58104a0fb..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewFactory.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import android.content.Context;
-import android.view.View;
-import io.flutter.plugin.common.BinaryMessenger;
-import io.flutter.plugin.common.MethodChannel;
-import io.flutter.plugin.common.StandardMessageCodec;
-import io.flutter.plugin.platform.PlatformView;
-import io.flutter.plugin.platform.PlatformViewFactory;
-import java.util.Map;
-
-public final class FlutterWebViewFactory extends PlatformViewFactory {
- private final BinaryMessenger messenger;
- private final View containerView;
-
- FlutterWebViewFactory(BinaryMessenger messenger, View containerView) {
- super(StandardMessageCodec.INSTANCE);
- this.messenger = messenger;
- this.containerView = containerView;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public PlatformView create(Context context, int id, Object args) {
- Map params = (Map) args;
- MethodChannel methodChannel = new MethodChannel(messenger, "plugins.flutter.io/webview_" + id);
- return new FlutterWebView(context, methodChannel, params, containerView);
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
deleted file mode 100644
index 51b2a3809fff..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import static android.content.Context.INPUT_METHOD_SERVICE;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.Log;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.webkit.WebView;
-import android.widget.ListPopupWindow;
-
-/**
- * A WebView subclass that mirrors the same implementation hacks that the system WebView does in
- * order to correctly create an InputConnection.
- *
- * These hacks are only needed in Android versions below N and exist to create an InputConnection
- * on the WebView's dedicated input, or IME, thread. The majority of this proxying logic is in
- * {@link #checkInputConnectionProxy}.
- *
- *
See also {@link ThreadedInputConnectionProxyAdapterView}.
- */
-final class InputAwareWebView extends WebView {
- private static final String TAG = "InputAwareWebView";
- private View threadedInputConnectionProxyView;
- private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
- private View containerView;
-
- InputAwareWebView(Context context, View containerView) {
- super(context);
- this.containerView = containerView;
- }
-
- void setContainerView(View containerView) {
- this.containerView = containerView;
-
- if (proxyAdapterView == null) {
- return;
- }
-
- Log.w(TAG, "The containerView has changed while the proxyAdapterView exists.");
- if (containerView != null) {
- setInputConnectionTarget(proxyAdapterView);
- }
- }
-
- /**
- * Set our proxy adapter view to use its cached input connection instead of creating new ones.
- *
- *
This is used to avoid losing our input connection when the virtual display is resized.
- */
- void lockInputConnection() {
- if (proxyAdapterView == null) {
- return;
- }
-
- proxyAdapterView.setLocked(true);
- }
-
- /** Sets the proxy adapter view back to its default behavior. */
- void unlockInputConnection() {
- if (proxyAdapterView == null) {
- return;
- }
-
- proxyAdapterView.setLocked(false);
- }
-
- /** Restore the original InputConnection, if needed. */
- void dispose() {
- resetInputConnection();
- }
-
- /**
- * Creates an InputConnection from the IME thread when needed.
- *
- *
We only need to create a {@link ThreadedInputConnectionProxyAdapterView} and create an
- * InputConnectionProxy on the IME thread when WebView is doing the same thing. So we rely on the
- * system calling this method for WebView's proxy view in order to know when we need to create our
- * own.
- *
- *
This method would normally be called for any View that used the InputMethodManager. We rely
- * on flutter/engine filtering the calls we receive down to the ones in our hierarchy and the
- * system WebView in order to know whether or not the system WebView expects an InputConnection on
- * the IME thread.
- */
- @Override
- public boolean checkInputConnectionProxy(final View view) {
- // Check to see if the view param is WebView's ThreadedInputConnectionProxyView.
- View previousProxy = threadedInputConnectionProxyView;
- threadedInputConnectionProxyView = view;
- if (previousProxy == view) {
- // This isn't a new ThreadedInputConnectionProxyView. Ignore it.
- return super.checkInputConnectionProxy(view);
- }
- if (containerView == null) {
- Log.e(
- TAG,
- "Can't create a proxy view because there's no container view. Text input may not work.");
- return super.checkInputConnectionProxy(view);
- }
-
- // We've never seen this before, so we make the assumption that this is WebView's
- // ThreadedInputConnectionProxyView. We are making the assumption that the only view that could
- // possibly be interacting with the IMM here is WebView's ThreadedInputConnectionProxyView.
- proxyAdapterView =
- new ThreadedInputConnectionProxyAdapterView(
- /*containerView=*/ containerView,
- /*targetView=*/ view,
- /*imeHandler=*/ view.getHandler());
- setInputConnectionTarget(/*targetView=*/ proxyAdapterView);
- return super.checkInputConnectionProxy(view);
- }
-
- /**
- * Ensure that input creation happens back on {@link #containerView}'s thread once this view no
- * longer has focus.
- *
- *
The logic in {@link #checkInputConnectionProxy} forces input creation to happen on Webview's
- * thread for all connections. We undo it here so users will be able to go back to typing in
- * Flutter UIs as expected.
- */
- @Override
- public void clearFocus() {
- super.clearFocus();
- resetInputConnection();
- }
-
- /**
- * Ensure that input creation happens back on {@link #containerView}.
- *
- *
The logic in {@link #checkInputConnectionProxy} forces input creation to happen on Webview's
- * thread for all connections. We undo it here so users will be able to go back to typing in
- * Flutter UIs as expected.
- */
- private void resetInputConnection() {
- if (proxyAdapterView == null) {
- // No need to reset the InputConnection to the default thread if we've never changed it.
- return;
- }
- if (containerView == null) {
- Log.e(TAG, "Can't reset the input connection to the container view because there is none.");
- return;
- }
- setInputConnectionTarget(/*targetView=*/ containerView);
- }
-
- /**
- * This is the crucial trick that gets the InputConnection creation to happen on the correct
- * thread pre Android N.
- * https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java?l=169&rcl=f0698ee3e4483fad5b0c34159276f71cfaf81f3a
- *
- *
{@code targetView} should have a {@link View#getHandler} method with the thread that future
- * InputConnections should be created on.
- */
- private void setInputConnectionTarget(final View targetView) {
- if (containerView == null) {
- Log.e(
- TAG,
- "Can't set the input connection target because there is no containerView to use as a handler.");
- return;
- }
-
- targetView.requestFocus();
- containerView.post(
- new Runnable() {
- @Override
- public void run() {
- InputMethodManager imm =
- (InputMethodManager) getContext().getSystemService(INPUT_METHOD_SERVICE);
- // This is a hack to make InputMethodManager believe that the target view now has focus.
- // As a result, InputMethodManager will think that targetView is focused, and will call
- // getHandler() of the view when creating input connection.
-
- // Step 1: Set targetView as InputMethodManager#mNextServedView. This does not affect
- // the real window focus.
- targetView.onWindowFocusChanged(true);
-
- // Step 2: Have InputMethodManager focus in on targetView. As a result, IMM will call
- // onCreateInputConnection() on targetView on the same thread as
- // targetView.getHandler(). It will also call subsequent InputConnection methods on this
- // thread. This is the IME thread in cases where targetView is our proxyAdapterView.
- imm.isActive(containerView);
- }
- });
- }
-
- @Override
- protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
- // This works around a crash when old (<67.0.3367.0) Chromium versions are used.
-
- // Prior to Chromium 67.0.3367 the following sequence happens when a select drop down is shown
- // on tablets:
- //
- // - WebView is calling ListPopupWindow#show
- // - buildDropDown is invoked, which sets mDropDownList to a DropDownListView.
- // - showAsDropDown is invoked - resulting in mDropDownList being added to the window and is
- // also synchronously performing the following sequence:
- // - WebView's focus change listener is loosing focus (as mDropDownList got it)
- // - WebView is hiding all popups (as it lost focus)
- // - WebView's SelectPopupDropDown#hide is invoked.
- // - DropDownPopupWindow#dismiss is invoked setting mDropDownList to null.
- // - mDropDownList#setSelection is invoked and is throwing a NullPointerException (as we just set mDropDownList to null).
- //
- // To workaround this, we drop the problematic focus lost call.
- // See more details on: https://github.com/flutter/flutter/issues/54164
- //
- // We don't do this after Android P as it shipped with a new enough WebView version, and it's
- // better to not do this on all future Android versions in case DropDownListView's code changes.
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P
- && isCalledFromListPopupWindowShow()
- && !focused) {
- return;
- }
- super.onFocusChanged(focused, direction, previouslyFocusedRect);
- }
-
- private boolean isCalledFromListPopupWindowShow() {
- StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
- for (StackTraceElement stackTraceElement : stackTraceElements) {
- if (stackTraceElement.getClassName().equals(ListPopupWindow.class.getCanonicalName())
- && stackTraceElement.getMethodName().equals("show")) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java
deleted file mode 100644
index 4d596351b3d0..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.webkit.JavascriptInterface;
-import io.flutter.plugin.common.MethodChannel;
-import java.util.HashMap;
-
-/**
- * Added as a JavaScript interface to the WebView for any JavaScript channel that the Dart code sets
- * up.
- *
- *
Exposes a single method named `postMessage` to JavaScript, which sends a message over a method
- * channel to the Dart code.
- */
-class JavaScriptChannel {
- private final MethodChannel methodChannel;
- private final String javaScriptChannelName;
- private final Handler platformThreadHandler;
-
- /**
- * @param methodChannel the Flutter WebView method channel to which JS messages are sent
- * @param javaScriptChannelName the name of the JavaScript channel, this is sent over the method
- * channel with each message to let the Dart code know which JavaScript channel the message
- * was sent through
- */
- JavaScriptChannel(
- MethodChannel methodChannel, String javaScriptChannelName, Handler platformThreadHandler) {
- this.methodChannel = methodChannel;
- this.javaScriptChannelName = javaScriptChannelName;
- this.platformThreadHandler = platformThreadHandler;
- }
-
- // Suppressing unused warning as this is invoked from JavaScript.
- @SuppressWarnings("unused")
- @JavascriptInterface
- public void postMessage(final String message) {
- Runnable postMessageRunnable =
- new Runnable() {
- @Override
- public void run() {
- HashMap arguments = new HashMap<>();
- arguments.put("channel", javaScriptChannelName);
- arguments.put("message", message);
- methodChannel.invokeMethod("javascriptChannelMessage", arguments);
- }
- };
- if (platformThreadHandler.getLooper() == Looper.myLooper()) {
- postMessageRunnable.run();
- } else {
- platformThreadHandler.post(postMessageRunnable);
- }
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/ThreadedInputConnectionProxyAdapterView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/ThreadedInputConnectionProxyAdapterView.java
deleted file mode 100644
index 1c865c9444e2..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/ThreadedInputConnectionProxyAdapterView.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import android.os.Handler;
-import android.os.IBinder;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-/**
- * A fake View only exposed to InputMethodManager.
- *
- * This follows a similar flow to Chromium's WebView (see
- * https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java).
- * WebView itself bounces its InputConnection around several different threads. We follow its logic
- * here to get the same working connection.
- *
- *
This exists solely to forward input creation to WebView's ThreadedInputConnectionProxyView on
- * the IME thread. The way that this is created in {@link
- * InputAwareWebView#checkInputConnectionProxy} guarantees that we have a handle to
- * ThreadedInputConnectionProxyView and {@link #onCreateInputConnection} is always called on the IME
- * thread. We delegate to ThreadedInputConnectionProxyView there to get WebView's input connection.
- */
-final class ThreadedInputConnectionProxyAdapterView extends View {
- final Handler imeHandler;
- final IBinder windowToken;
- final View containerView;
- final View rootView;
- final View targetView;
-
- private boolean triggerDelayed = true;
- private boolean isLocked = false;
- private InputConnection cachedConnection;
-
- ThreadedInputConnectionProxyAdapterView(View containerView, View targetView, Handler imeHandler) {
- super(containerView.getContext());
- this.imeHandler = imeHandler;
- this.containerView = containerView;
- this.targetView = targetView;
- windowToken = containerView.getWindowToken();
- rootView = containerView.getRootView();
- setFocusable(true);
- setFocusableInTouchMode(true);
- setVisibility(VISIBLE);
- }
-
- /** Returns whether or not this is currently asynchronously acquiring an input connection. */
- boolean isTriggerDelayed() {
- return triggerDelayed;
- }
-
- /** Sets whether or not this should use its previously cached input connection. */
- void setLocked(boolean locked) {
- isLocked = locked;
- }
-
- /**
- * This is expected to be called on the IME thread. See the setup required for this in {@link
- * InputAwareWebView#checkInputConnectionProxy(View)}.
- *
- *
Delegates to ThreadedInputConnectionProxyView to get WebView's input connection.
- */
- @Override
- public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
- triggerDelayed = false;
- InputConnection inputConnection =
- (isLocked) ? cachedConnection : targetView.onCreateInputConnection(outAttrs);
- triggerDelayed = true;
- cachedConnection = inputConnection;
- return inputConnection;
- }
-
- @Override
- public boolean checkInputConnectionProxy(View view) {
- return true;
- }
-
- @Override
- public boolean hasWindowFocus() {
- // None of our views here correctly report they have window focus because of how we're embedding
- // the platform view inside of a virtual display.
- return true;
- }
-
- @Override
- public View getRootView() {
- return rootView;
- }
-
- @Override
- public boolean onCheckIsTextEditor() {
- return true;
- }
-
- @Override
- public boolean isFocused() {
- return true;
- }
-
- @Override
- public IBinder getWindowToken() {
- return windowToken;
- }
-
- @Override
- public Handler getHandler() {
- return imeHandler;
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewBuilder.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewBuilder.java
deleted file mode 100644
index d3cd1d57cdae..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewBuilder.java
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import android.content.Context;
-import android.view.View;
-import android.webkit.DownloadListener;
-import android.webkit.WebChromeClient;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/** Builder used to create {@link android.webkit.WebView} objects. */
-public class WebViewBuilder {
-
- /** Factory used to create a new {@link android.webkit.WebView} instance. */
- static class WebViewFactory {
-
- /**
- * Creates a new {@link android.webkit.WebView} instance.
- *
- * @param context an Activity Context to access application assets. This value cannot be null.
- * @param usesHybridComposition If {@code false} a {@link InputAwareWebView} instance is
- * returned.
- * @param containerView must be supplied when the {@code useHybridComposition} parameter is set
- * to {@code false}. Used to create an InputConnection on the WebView's dedicated input, or
- * IME, thread (see also {@link InputAwareWebView})
- * @return A new instance of the {@link android.webkit.WebView} object.
- */
- static WebView create(Context context, boolean usesHybridComposition, View containerView) {
- return usesHybridComposition
- ? new WebView(context)
- : new InputAwareWebView(context, containerView);
- }
- }
-
- private final Context context;
- private final View containerView;
-
- private boolean enableDomStorage;
- private boolean javaScriptCanOpenWindowsAutomatically;
- private boolean supportMultipleWindows;
- private boolean usesHybridComposition;
- private WebChromeClient webChromeClient;
- private DownloadListener downloadListener;
-
- /**
- * Constructs a new {@link WebViewBuilder} object with a custom implementation of the {@link
- * WebViewFactory} object.
- *
- * @param context an Activity Context to access application assets. This value cannot be null.
- * @param containerView must be supplied when the {@code useHybridComposition} parameter is set to
- * {@code false}. Used to create an InputConnection on the WebView's dedicated input, or IME,
- * thread (see also {@link InputAwareWebView})
- */
- WebViewBuilder(@NonNull final Context context, View containerView) {
- this.context = context;
- this.containerView = containerView;
- }
-
- /**
- * Sets whether the DOM storage API is enabled. The default value is {@code false}.
- *
- * @param flag {@code true} is {@link android.webkit.WebView} should use the DOM storage API.
- * @return This builder. This value cannot be {@code null}.
- */
- public WebViewBuilder setDomStorageEnabled(boolean flag) {
- this.enableDomStorage = flag;
- return this;
- }
-
- /**
- * Sets whether JavaScript is allowed to open windows automatically. This applies to the
- * JavaScript function {@code window.open()}. The default value is {@code false}.
- *
- * @param flag {@code true} if JavaScript is allowed to open windows automatically.
- * @return This builder. This value cannot be {@code null}.
- */
- public WebViewBuilder setJavaScriptCanOpenWindowsAutomatically(boolean flag) {
- this.javaScriptCanOpenWindowsAutomatically = flag;
- return this;
- }
-
- /**
- * Sets whether the {@link WebView} supports multiple windows. If set to {@code true}, {@link
- * WebChromeClient#onCreateWindow} must be implemented by the host application. The default is
- * {@code false}.
- *
- * @param flag {@code true} if multiple windows are supported.
- * @return This builder. This value cannot be {@code null}.
- */
- public WebViewBuilder setSupportMultipleWindows(boolean flag) {
- this.supportMultipleWindows = flag;
- return this;
- }
-
- /**
- * Sets whether the hybrid composition should be used.
- *
- *
If set to {@code true} a standard {@link WebView} is created. If set to {@code false} the
- * {@link WebViewBuilder} will create a {@link InputAwareWebView} to workaround issues using the
- * {@link WebView} on Android versions below N.
- *
- * @param flag {@code true} if uses hybrid composition. The default is {@code false}.
- * @return This builder. This value cannot be {@code null}
- */
- public WebViewBuilder setUsesHybridComposition(boolean flag) {
- this.usesHybridComposition = flag;
- return this;
- }
-
- /**
- * Sets the chrome handler. This is an implementation of WebChromeClient for use in handling
- * JavaScript dialogs, favicons, titles, and the progress. This will replace the current handler.
- *
- * @param webChromeClient an implementation of WebChromeClient This value may be null.
- * @return This builder. This value cannot be {@code null}.
- */
- public WebViewBuilder setWebChromeClient(@Nullable WebChromeClient webChromeClient) {
- this.webChromeClient = webChromeClient;
- return this;
- }
-
- /**
- * Registers the interface to be used when content can not be handled by the rendering engine, and
- * should be downloaded instead. This will replace the current handler.
- *
- * @param downloadListener an implementation of DownloadListener This value may be null.
- * @return This builder. This value cannot be {@code null}.
- */
- public WebViewBuilder setDownloadListener(@Nullable DownloadListener downloadListener) {
- this.downloadListener = downloadListener;
- return this;
- }
-
- /**
- * Build the {@link android.webkit.WebView} using the current settings.
- *
- * @return The {@link android.webkit.WebView} using the current settings.
- */
- public WebView build() {
- WebView webView = WebViewFactory.create(context, usesHybridComposition, containerView);
-
- WebSettings webSettings = webView.getSettings();
- webSettings.setDomStorageEnabled(enableDomStorage);
- webSettings.setJavaScriptCanOpenWindowsAutomatically(javaScriptCanOpenWindowsAutomatically);
- webSettings.setSupportMultipleWindows(supportMultipleWindows);
- webView.setWebChromeClient(webChromeClient);
- webView.setDownloadListener(downloadListener);
- return webView;
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
deleted file mode 100644
index 268d35a1e04c..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import io.flutter.embedding.engine.plugins.FlutterPlugin;
-import io.flutter.plugin.common.BinaryMessenger;
-
-/**
- * Java platform implementation of the webview_flutter plugin.
- *
- *
Register this in an add to app scenario to gracefully handle activity and context changes.
- *
- *
Call {@link #registerWith(Registrar)} to use the stable {@code io.flutter.plugin.common}
- * package instead.
- */
-public class WebViewFlutterPlugin implements FlutterPlugin {
-
- private FlutterCookieManager flutterCookieManager;
-
- /**
- * Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to
- * register it.
- *
- *
THIS PLUGIN CODE PATH DEPENDS ON A NEWER VERSION OF FLUTTER THAN THE ONE DEFINED IN THE
- * PUBSPEC.YAML. Text input will fail on some Android devices unless this is used with at least
- * flutter/flutter@1d4d63ace1f801a022ea9ec737bf8c15395588b9. Use the V1 embedding with {@link
- * #registerWith(Registrar)} to use this plugin with older Flutter versions.
- *
- *
Registration should eventually be handled automatically by v2 of the
- * GeneratedPluginRegistrant. https://github.com/flutter/flutter/issues/42694
- */
- public WebViewFlutterPlugin() {}
-
- /**
- * Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
- * package.
- *
- *
Calling this automatically initializes the plugin. However plugins initialized this way
- * won't react to changes in activity or context, unlike {@link CameraPlugin}.
- */
- @SuppressWarnings("deprecation")
- public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
- registrar
- .platformViewRegistry()
- .registerViewFactory(
- "plugins.flutter.io/webview",
- new FlutterWebViewFactory(registrar.messenger(), registrar.view()));
- new FlutterCookieManager(registrar.messenger());
- }
-
- @Override
- public void onAttachedToEngine(FlutterPluginBinding binding) {
- BinaryMessenger messenger = binding.getBinaryMessenger();
- binding
- .getPlatformViewRegistry()
- .registerViewFactory(
- "plugins.flutter.io/webview",
- new FlutterWebViewFactory(messenger, /*containerView=*/ null));
- flutterCookieManager = new FlutterCookieManager(messenger);
- }
-
- @Override
- public void onDetachedFromEngine(FlutterPluginBinding binding) {
- if (flutterCookieManager == null) {
- return;
- }
-
- flutterCookieManager.dispose();
- flutterCookieManager = null;
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterDownloadListenerTest.java b/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterDownloadListenerTest.java
deleted file mode 100644
index 2c918584ba83..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterDownloadListenerTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.webkit.WebView;
-import org.junit.Before;
-import org.junit.Test;
-
-public class FlutterDownloadListenerTest {
- private FlutterWebViewClient webViewClient;
- private WebView webView;
-
- @Before
- public void before() {
- webViewClient = mock(FlutterWebViewClient.class);
- webView = mock(WebView.class);
- }
-
- @Test
- public void onDownloadStart_should_notify_webViewClient() {
- String url = "testurl.com";
- FlutterDownloadListener downloadListener = new FlutterDownloadListener(webViewClient);
- downloadListener.onDownloadStart(url, "test", "inline", "data/text", 0);
- verify(webViewClient).notifyDownload(nullable(WebView.class), eq(url));
- }
-
- @Test
- public void onDownloadStart_should_pass_webView() {
- FlutterDownloadListener downloadListener = new FlutterDownloadListener(webViewClient);
- downloadListener.setWebView(webView);
- downloadListener.onDownloadStart("testurl.com", "test", "inline", "data/text", 0);
- verify(webViewClient).notifyDownload(eq(webView), anyString());
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewClientTest.java b/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewClientTest.java
deleted file mode 100644
index 86346ac08f16..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewClientTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-
-import android.webkit.WebView;
-import io.flutter.plugin.common.MethodChannel;
-import java.util.HashMap;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-public class FlutterWebViewClientTest {
-
- MethodChannel mockMethodChannel;
- WebView mockWebView;
-
- @Before
- public void before() {
- mockMethodChannel = mock(MethodChannel.class);
- mockWebView = mock(WebView.class);
- }
-
- @Test
- public void notify_download_should_notifyOnNavigationRequest_when_navigationDelegate_is_set() {
- final String url = "testurl.com";
-
- FlutterWebViewClient client = new FlutterWebViewClient(mockMethodChannel);
- client.createWebViewClient(true);
-
- client.notifyDownload(mockWebView, url);
- ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Object.class);
- verify(mockMethodChannel)
- .invokeMethod(
- eq("navigationRequest"), argumentCaptor.capture(), any(MethodChannel.Result.class));
- HashMap map = (HashMap) argumentCaptor.getValue();
- assertEquals(map.get("url"), url);
- assertEquals(map.get("isForMainFrame"), true);
- }
-
- @Test
- public void
- notify_download_should_not_notifyOnNavigationRequest_when_navigationDelegate_is_not_set() {
- final String url = "testurl.com";
-
- FlutterWebViewClient client = new FlutterWebViewClient(mockMethodChannel);
- client.createWebViewClient(false);
-
- client.notifyDownload(mockWebView, url);
- verifyNoInteractions(mockMethodChannel);
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewTest.java b/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewTest.java
deleted file mode 100644
index 56d9db1ee493..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.webkit.DownloadListener;
-import android.webkit.WebChromeClient;
-import android.webkit.WebView;
-import java.util.HashMap;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-
-public class FlutterWebViewTest {
- private WebChromeClient mockWebChromeClient;
- private DownloadListener mockDownloadListener;
- private WebViewBuilder mockWebViewBuilder;
- private WebView mockWebView;
-
- @Before
- public void before() {
- mockWebChromeClient = mock(WebChromeClient.class);
- mockWebViewBuilder = mock(WebViewBuilder.class);
- mockWebView = mock(WebView.class);
- mockDownloadListener = mock(DownloadListener.class);
-
- when(mockWebViewBuilder.setDomStorageEnabled(anyBoolean())).thenReturn(mockWebViewBuilder);
- when(mockWebViewBuilder.setJavaScriptCanOpenWindowsAutomatically(anyBoolean()))
- .thenReturn(mockWebViewBuilder);
- when(mockWebViewBuilder.setSupportMultipleWindows(anyBoolean())).thenReturn(mockWebViewBuilder);
- when(mockWebViewBuilder.setUsesHybridComposition(anyBoolean())).thenReturn(mockWebViewBuilder);
- when(mockWebViewBuilder.setWebChromeClient(any(WebChromeClient.class)))
- .thenReturn(mockWebViewBuilder);
- when(mockWebViewBuilder.setDownloadListener(any(DownloadListener.class)))
- .thenReturn(mockWebViewBuilder);
-
- when(mockWebViewBuilder.build()).thenReturn(mockWebView);
- }
-
- @Test
- public void createWebView_should_create_webview_with_default_configuration() {
- FlutterWebView.createWebView(
- mockWebViewBuilder, createParameterMap(false), mockWebChromeClient, mockDownloadListener);
-
- verify(mockWebViewBuilder, times(1)).setDomStorageEnabled(true);
- verify(mockWebViewBuilder, times(1)).setJavaScriptCanOpenWindowsAutomatically(true);
- verify(mockWebViewBuilder, times(1)).setSupportMultipleWindows(true);
- verify(mockWebViewBuilder, times(1)).setUsesHybridComposition(false);
- verify(mockWebViewBuilder, times(1)).setWebChromeClient(mockWebChromeClient);
- }
-
- private Map createParameterMap(boolean usesHybridComposition) {
- Map params = new HashMap<>();
- params.put("usesHybridComposition", usesHybridComposition);
-
- return params;
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewBuilderTest.java b/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewBuilderTest.java
deleted file mode 100644
index 423cb210c392..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewBuilderTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.*;
-
-import android.content.Context;
-import android.view.View;
-import android.webkit.DownloadListener;
-import android.webkit.WebChromeClient;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import io.flutter.plugins.webviewflutter.WebViewBuilder.WebViewFactory;
-import java.io.IOException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.MockedStatic;
-import org.mockito.MockedStatic.Verification;
-
-public class WebViewBuilderTest {
- private Context mockContext;
- private View mockContainerView;
- private WebView mockWebView;
- private MockedStatic mockedStaticWebViewFactory;
-
- @Before
- public void before() {
- mockContext = mock(Context.class);
- mockContainerView = mock(View.class);
- mockWebView = mock(WebView.class);
- mockedStaticWebViewFactory = mockStatic(WebViewFactory.class);
-
- mockedStaticWebViewFactory
- .when(
- new Verification() {
- @Override
- public void apply() {
- WebViewFactory.create(mockContext, false, mockContainerView);
- }
- })
- .thenReturn(mockWebView);
- }
-
- @After
- public void after() {
- mockedStaticWebViewFactory.close();
- }
-
- @Test
- public void ctor_test() {
- WebViewBuilder builder = new WebViewBuilder(mockContext, mockContainerView);
-
- assertNotNull(builder);
- }
-
- @Test
- public void build_should_set_values() throws IOException {
- WebSettings mockWebSettings = mock(WebSettings.class);
- WebChromeClient mockWebChromeClient = mock(WebChromeClient.class);
- DownloadListener mockDownloadListener = mock(DownloadListener.class);
-
- when(mockWebView.getSettings()).thenReturn(mockWebSettings);
-
- WebViewBuilder builder =
- new WebViewBuilder(mockContext, mockContainerView)
- .setDomStorageEnabled(true)
- .setJavaScriptCanOpenWindowsAutomatically(true)
- .setSupportMultipleWindows(true)
- .setWebChromeClient(mockWebChromeClient)
- .setDownloadListener(mockDownloadListener);
-
- WebView webView = builder.build();
-
- assertNotNull(webView);
- verify(mockWebSettings).setDomStorageEnabled(true);
- verify(mockWebSettings).setJavaScriptCanOpenWindowsAutomatically(true);
- verify(mockWebSettings).setSupportMultipleWindows(true);
- verify(mockWebView).setWebChromeClient(mockWebChromeClient);
- verify(mockWebView).setDownloadListener(mockDownloadListener);
- }
-
- @Test
- public void build_should_use_default_values() throws IOException {
- WebSettings mockWebSettings = mock(WebSettings.class);
- WebChromeClient mockWebChromeClient = mock(WebChromeClient.class);
-
- when(mockWebView.getSettings()).thenReturn(mockWebSettings);
-
- WebViewBuilder builder = new WebViewBuilder(mockContext, mockContainerView);
-
- WebView webView = builder.build();
-
- assertNotNull(webView);
- verify(mockWebSettings).setDomStorageEnabled(false);
- verify(mockWebSettings).setJavaScriptCanOpenWindowsAutomatically(false);
- verify(mockWebSettings).setSupportMultipleWindows(false);
- verify(mockWebView).setWebChromeClient(null);
- verify(mockWebView).setDownloadListener(null);
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java b/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
deleted file mode 100644
index 131a5a3eb53a..000000000000
--- a/packages/webview_flutter/webview_flutter/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-import static org.junit.Assert.assertEquals;
-
-import android.webkit.WebViewClient;
-import org.junit.Test;
-
-public class WebViewTest {
- @Test
- public void errorCodes() {
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_AUTHENTICATION),
- "authentication");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_BAD_URL), "badUrl");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_CONNECT), "connect");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE),
- "failedSslHandshake");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_FILE), "file");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_FILE_NOT_FOUND), "fileNotFound");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_HOST_LOOKUP), "hostLookup");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_IO), "io");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_PROXY_AUTHENTICATION),
- "proxyAuthentication");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_REDIRECT_LOOP), "redirectLoop");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_TIMEOUT), "timeout");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_TOO_MANY_REQUESTS),
- "tooManyRequests");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_UNKNOWN), "unknown");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_UNSAFE_RESOURCE),
- "unsafeResource");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_UNSUPPORTED_AUTH_SCHEME),
- "unsupportedAuthScheme");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_UNSUPPORTED_SCHEME),
- "unsupportedScheme");
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart
index 0e128caa8f32..a6211b2dae75 100644
--- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart
+++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart
@@ -18,6 +18,8 @@ import 'package:integration_test/integration_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+ const bool _skipDueToIssue86757 = true;
+
// TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757.
testWidgets('initialUrl', (WidgetTester tester) async {
final Completer controllerCompleter =
@@ -37,7 +39,7 @@ void main() {
final WebViewController controller = await controllerCompleter.future;
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, 'https://flutter.dev/');
- }, skip: true);
+ }, skip: _skipDueToIssue86757);
// TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757.
testWidgets('loadUrl', (WidgetTester tester) async {
@@ -59,7 +61,7 @@ void main() {
await controller.loadUrl('https://www.google.com/');
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, 'https://www.google.com/');
- }, skip: true);
+ }, skip: _skipDueToIssue86757);
// TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757.
testWidgets('loadUrl with headers', (WidgetTester tester) async {
@@ -101,7 +103,7 @@ void main() {
final String content = await controller
.evaluateJavascript('document.documentElement.innerText');
expect(content.contains('flutter_test_header'), isTrue);
- }, skip: Platform.isAndroid);
+ }, skip: Platform.isAndroid && _skipDueToIssue86757);
// TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757.
testWidgets('JavaScriptChannel', (WidgetTester tester) async {
@@ -150,7 +152,7 @@ void main() {
// https://github.com/flutter/flutter/issues/66318
await controller.evaluateJavascript('Echo.postMessage("hello");1;');
expect(messagesReceived, equals(['hello']));
- }, skip: Platform.isAndroid);
+ }, skip: Platform.isAndroid && _skipDueToIssue86757);
testWidgets('resize webview', (WidgetTester tester) async {
final String resizeTest = '''
@@ -328,7 +330,7 @@ void main() {
final String customUserAgent2 = await _getUserAgent(controller);
expect(customUserAgent2, defaultPlatformUserAgent);
- }, skip: Platform.isAndroid);
+ }, skip: Platform.isAndroid && _skipDueToIssue86757);
group('Video playback policy', () {
late String videoTestBase64;
@@ -877,7 +879,7 @@ void main() {
scrollPosY = await controller.getScrollY();
expect(scrollPosX, X_SCROLL * 2);
expect(scrollPosY, Y_SCROLL * 2);
- }, skip: Platform.isAndroid);
+ }, skip: Platform.isAndroid && _skipDueToIssue86757);
});
group('SurfaceAndroidWebView', () {
@@ -956,7 +958,7 @@ void main() {
scrollPosY = await controller.getScrollY();
expect(X_SCROLL * 2, scrollPosX);
expect(Y_SCROLL * 2, scrollPosY);
- }, skip: true);
+ }, skip: !Platform.isAndroid || _skipDueToIssue86757);
// TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757.
testWidgets('inputs are scrolled into view when focused',
@@ -1062,7 +1064,7 @@ void main() {
lastInputClientRectRelativeToViewport['right'] <=
viewportRectRelativeToViewport['right'],
isTrue);
- }, skip: true);
+ }, skip: !Platform.isAndroid || _skipDueToIssue86757);
});
group('NavigationDelegate', () {
@@ -1332,7 +1334,7 @@ void main() {
expect(currentUrl, 'https://flutter.dev/');
},
// Flaky on Android: https://github.com/flutter/flutter/issues/86757
- skip: Platform.isAndroid);
+ skip: Platform.isAndroid && _skipDueToIssue86757);
// TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757.
testWidgets(
@@ -1373,7 +1375,7 @@ void main() {
await pageLoaded.future;
expect(controller.currentUrl(), completion('https://flutter.dev/'));
},
- skip: true,
+ skip: _skipDueToIssue86757,
);
testWidgets(
diff --git a/packages/webview_flutter/webview_flutter/ios/Assets/.gitkeep b/packages/webview_flutter/webview_flutter/ios/Assets/.gitkeep
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FLTCookieManager.h b/packages/webview_flutter/webview_flutter/ios/Classes/FLTCookieManager.h
deleted file mode 100644
index 8fe331875250..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FLTCookieManager.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FLTCookieManager : NSObject
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FLTCookieManager.m b/packages/webview_flutter/webview_flutter/ios/Classes/FLTCookieManager.m
deleted file mode 100644
index eb7c856b250d..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FLTCookieManager.m
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "FLTCookieManager.h"
-
-@implementation FLTCookieManager {
-}
-
-+ (void)registerWithRegistrar:(NSObject *)registrar {
- FLTCookieManager *instance = [[FLTCookieManager alloc] init];
-
- FlutterMethodChannel *channel =
- [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/cookie_manager"
- binaryMessenger:[registrar messenger]];
- [registrar addMethodCallDelegate:instance channel:channel];
-}
-
-- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
- if ([[call method] isEqualToString:@"clearCookies"]) {
- [self clearCookies:result];
- } else {
- result(FlutterMethodNotImplemented);
- }
-}
-
-- (void)clearCookies:(FlutterResult)result {
- NSSet *websiteDataTypes = [NSSet setWithObject:WKWebsiteDataTypeCookies];
- WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
-
- void (^deleteAndNotify)(NSArray *) =
- ^(NSArray *cookies) {
- BOOL hasCookies = cookies.count > 0;
- [dataStore removeDataOfTypes:websiteDataTypes
- forDataRecords:cookies
- completionHandler:^{
- result(@(hasCookies));
- }];
- };
-
- [dataStore fetchDataRecordsOfTypes:websiteDataTypes completionHandler:deleteAndNotify];
-}
-
-@end
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKNavigationDelegate.h b/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKNavigationDelegate.h
deleted file mode 100644
index 31edadc8cc05..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKNavigationDelegate.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FLTWKNavigationDelegate : NSObject
-
-- (instancetype)initWithChannel:(FlutterMethodChannel*)channel;
-
-/**
- * Whether to delegate navigation decisions over the method channel.
- */
-@property(nonatomic, assign) BOOL hasDartNavigationDelegate;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKNavigationDelegate.m b/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKNavigationDelegate.m
deleted file mode 100644
index 8b7ee7d0cfb7..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKNavigationDelegate.m
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "FLTWKNavigationDelegate.h"
-
-@implementation FLTWKNavigationDelegate {
- FlutterMethodChannel *_methodChannel;
-}
-
-- (instancetype)initWithChannel:(FlutterMethodChannel *)channel {
- self = [super init];
- if (self) {
- _methodChannel = channel;
- }
- return self;
-}
-
-#pragma mark - WKNavigationDelegate conformance
-
-- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
- [_methodChannel invokeMethod:@"onPageStarted" arguments:@{@"url" : webView.URL.absoluteString}];
-}
-
-- (void)webView:(WKWebView *)webView
- decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
- decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
- if (!self.hasDartNavigationDelegate) {
- decisionHandler(WKNavigationActionPolicyAllow);
- return;
- }
- NSDictionary *arguments = @{
- @"url" : navigationAction.request.URL.absoluteString,
- @"isForMainFrame" : @(navigationAction.targetFrame.isMainFrame)
- };
- [_methodChannel invokeMethod:@"navigationRequest"
- arguments:arguments
- result:^(id _Nullable result) {
- if ([result isKindOfClass:[FlutterError class]]) {
- NSLog(@"navigationRequest has unexpectedly completed with an error, "
- @"allowing navigation.");
- decisionHandler(WKNavigationActionPolicyAllow);
- return;
- }
- if (result == FlutterMethodNotImplemented) {
- NSLog(@"navigationRequest was unexepectedly not implemented: %@, "
- @"allowing navigation.",
- result);
- decisionHandler(WKNavigationActionPolicyAllow);
- return;
- }
- if (![result isKindOfClass:[NSNumber class]]) {
- NSLog(@"navigationRequest unexpectedly returned a non boolean value: "
- @"%@, allowing navigation.",
- result);
- decisionHandler(WKNavigationActionPolicyAllow);
- return;
- }
- NSNumber *typedResult = result;
- decisionHandler([typedResult boolValue] ? WKNavigationActionPolicyAllow
- : WKNavigationActionPolicyCancel);
- }];
-}
-
-- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
- [_methodChannel invokeMethod:@"onPageFinished" arguments:@{@"url" : webView.URL.absoluteString}];
-}
-
-+ (id)errorCodeToString:(NSUInteger)code {
- switch (code) {
- case WKErrorUnknown:
- return @"unknown";
- case WKErrorWebContentProcessTerminated:
- return @"webContentProcessTerminated";
- case WKErrorWebViewInvalidated:
- return @"webViewInvalidated";
- case WKErrorJavaScriptExceptionOccurred:
- return @"javaScriptExceptionOccurred";
- case WKErrorJavaScriptResultTypeIsUnsupported:
- return @"javaScriptResultTypeIsUnsupported";
- }
-
- return [NSNull null];
-}
-
-- (void)onWebResourceError:(NSError *)error {
- [_methodChannel invokeMethod:@"onWebResourceError"
- arguments:@{
- @"errorCode" : @(error.code),
- @"domain" : error.domain,
- @"description" : error.description,
- @"errorType" : [FLTWKNavigationDelegate errorCodeToString:error.code],
- }];
-}
-
-- (void)webView:(WKWebView *)webView
- didFailNavigation:(WKNavigation *)navigation
- withError:(NSError *)error {
- [self onWebResourceError:error];
-}
-
-- (void)webView:(WKWebView *)webView
- didFailProvisionalNavigation:(WKNavigation *)navigation
- withError:(NSError *)error {
- [self onWebResourceError:error];
-}
-
-- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
- NSError *contentProcessTerminatedError =
- [[NSError alloc] initWithDomain:WKErrorDomain
- code:WKErrorWebContentProcessTerminated
- userInfo:nil];
- [self onWebResourceError:contentProcessTerminatedError];
-}
-
-@end
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKProgressionDelegate.h b/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKProgressionDelegate.h
deleted file mode 100644
index 96af4ef6c578..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKProgressionDelegate.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FLTWKProgressionDelegate : NSObject
-
-- (instancetype)initWithWebView:(WKWebView *)webView channel:(FlutterMethodChannel *)channel;
-
-- (void)stopObservingProgress:(WKWebView *)webView;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKProgressionDelegate.m b/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKProgressionDelegate.m
deleted file mode 100644
index 8e7af4649aa0..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWKProgressionDelegate.m
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "FLTWKProgressionDelegate.h"
-
-NSString *const FLTWKEstimatedProgressKeyPath = @"estimatedProgress";
-
-@implementation FLTWKProgressionDelegate {
- FlutterMethodChannel *_methodChannel;
-}
-
-- (instancetype)initWithWebView:(WKWebView *)webView channel:(FlutterMethodChannel *)channel {
- self = [super init];
- if (self) {
- _methodChannel = channel;
- [webView addObserver:self
- forKeyPath:FLTWKEstimatedProgressKeyPath
- options:NSKeyValueObservingOptionNew
- context:nil];
- }
- return self;
-}
-
-- (void)stopObservingProgress:(WKWebView *)webView {
- [webView removeObserver:self forKeyPath:FLTWKEstimatedProgressKeyPath];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath
- ofObject:(id)object
- change:(NSDictionary *)change
- context:(void *)context {
- if ([keyPath isEqualToString:FLTWKEstimatedProgressKeyPath]) {
- NSNumber *newValue =
- change[NSKeyValueChangeNewKey] ?: 0; // newValue is anywhere between 0.0 and 1.0
- int newValueAsInt = [newValue floatValue] * 100; // Anywhere between 0 and 100
- [_methodChannel invokeMethod:@"onProgress" arguments:@{@"progress" : @(newValueAsInt)}];
- }
-}
-
-@end
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWebViewFlutterPlugin.h b/packages/webview_flutter/webview_flutter/ios/Classes/FLTWebViewFlutterPlugin.h
deleted file mode 100644
index 2a80c7d886f2..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWebViewFlutterPlugin.h
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import
-
-@interface FLTWebViewFlutterPlugin : NSObject
-@end
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWebViewFlutterPlugin.m b/packages/webview_flutter/webview_flutter/ios/Classes/FLTWebViewFlutterPlugin.m
deleted file mode 100644
index 9f01416acc6a..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FLTWebViewFlutterPlugin.m
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "FLTWebViewFlutterPlugin.h"
-#import "FLTCookieManager.h"
-#import "FlutterWebView.h"
-
-@implementation FLTWebViewFlutterPlugin
-
-+ (void)registerWithRegistrar:(NSObject*)registrar {
- FLTWebViewFactory* webviewFactory =
- [[FLTWebViewFactory alloc] initWithMessenger:registrar.messenger];
- [registrar registerViewFactory:webviewFactory withId:@"plugins.flutter.io/webview"];
- [FLTCookieManager registerWithRegistrar:registrar];
-}
-
-@end
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FlutterWebView.h b/packages/webview_flutter/webview_flutter/ios/Classes/FlutterWebView.h
deleted file mode 100644
index 6e795f7d1528..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FlutterWebView.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FLTWebViewController : NSObject
-
-- (instancetype)initWithFrame:(CGRect)frame
- viewIdentifier:(int64_t)viewId
- arguments:(id _Nullable)args
- binaryMessenger:(NSObject*)messenger;
-
-- (UIView*)view;
-@end
-
-@interface FLTWebViewFactory : NSObject
-- (instancetype)initWithMessenger:(NSObject*)messenger;
-@end
-
-/**
- * The WkWebView used for the plugin.
- *
- * This class overrides some methods in `WKWebView` to serve the needs for the plugin.
- */
-@interface FLTWKWebView : WKWebView
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter/ios/Classes/FlutterWebView.m
deleted file mode 100644
index 1604f2756f31..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/FlutterWebView.m
+++ /dev/null
@@ -1,475 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "FlutterWebView.h"
-#import "FLTWKNavigationDelegate.h"
-#import "FLTWKProgressionDelegate.h"
-#import "JavaScriptChannelHandler.h"
-
-@implementation FLTWebViewFactory {
- NSObject* _messenger;
-}
-
-- (instancetype)initWithMessenger:(NSObject*)messenger {
- self = [super init];
- if (self) {
- _messenger = messenger;
- }
- return self;
-}
-
-- (NSObject*)createArgsCodec {
- return [FlutterStandardMessageCodec sharedInstance];
-}
-
-- (NSObject*)createWithFrame:(CGRect)frame
- viewIdentifier:(int64_t)viewId
- arguments:(id _Nullable)args {
- FLTWebViewController* webviewController = [[FLTWebViewController alloc] initWithFrame:frame
- viewIdentifier:viewId
- arguments:args
- binaryMessenger:_messenger];
- return webviewController;
-}
-
-@end
-
-@implementation FLTWKWebView
-
-- (void)setFrame:(CGRect)frame {
- [super setFrame:frame];
- self.scrollView.contentInset = UIEdgeInsetsZero;
- // We don't want the contentInsets to be adjusted by iOS, flutter should always take control of
- // webview's contentInsets.
- // self.scrollView.contentInset = UIEdgeInsetsZero;
- if (@available(iOS 11, *)) {
- // Above iOS 11, adjust contentInset to compensate the adjustedContentInset so the sum will
- // always be 0.
- if (UIEdgeInsetsEqualToEdgeInsets(self.scrollView.adjustedContentInset, UIEdgeInsetsZero)) {
- return;
- }
- UIEdgeInsets insetToAdjust = self.scrollView.adjustedContentInset;
- self.scrollView.contentInset = UIEdgeInsetsMake(-insetToAdjust.top, -insetToAdjust.left,
- -insetToAdjust.bottom, -insetToAdjust.right);
- }
-}
-
-@end
-
-@implementation FLTWebViewController {
- FLTWKWebView* _webView;
- int64_t _viewId;
- FlutterMethodChannel* _channel;
- NSString* _currentUrl;
- // The set of registered JavaScript channel names.
- NSMutableSet* _javaScriptChannelNames;
- FLTWKNavigationDelegate* _navigationDelegate;
- FLTWKProgressionDelegate* _progressionDelegate;
-}
-
-- (instancetype)initWithFrame:(CGRect)frame
- viewIdentifier:(int64_t)viewId
- arguments:(id _Nullable)args
- binaryMessenger:(NSObject*)messenger {
- if (self = [super init]) {
- _viewId = viewId;
-
- NSString* channelName = [NSString stringWithFormat:@"plugins.flutter.io/webview_%lld", viewId];
- _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger];
- _javaScriptChannelNames = [[NSMutableSet alloc] init];
-
- WKUserContentController* userContentController = [[WKUserContentController alloc] init];
- if ([args[@"javascriptChannelNames"] isKindOfClass:[NSArray class]]) {
- NSArray* javaScriptChannelNames = args[@"javascriptChannelNames"];
- [_javaScriptChannelNames addObjectsFromArray:javaScriptChannelNames];
- [self registerJavaScriptChannels:_javaScriptChannelNames controller:userContentController];
- }
-
- NSDictionary* settings = args[@"settings"];
-
- WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
- [self applyConfigurationSettings:settings toConfiguration:configuration];
- configuration.userContentController = userContentController;
- [self updateAutoMediaPlaybackPolicy:args[@"autoMediaPlaybackPolicy"]
- inConfiguration:configuration];
-
- _webView = [[FLTWKWebView alloc] initWithFrame:frame configuration:configuration];
- _navigationDelegate = [[FLTWKNavigationDelegate alloc] initWithChannel:_channel];
- _webView.UIDelegate = self;
- _webView.navigationDelegate = _navigationDelegate;
- __weak __typeof__(self) weakSelf = self;
- [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
- [weakSelf onMethodCall:call result:result];
- }];
-
- if (@available(iOS 11.0, *)) {
- _webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
- if (@available(iOS 13.0, *)) {
- _webView.scrollView.automaticallyAdjustsScrollIndicatorInsets = NO;
- }
- }
-
- [self applySettings:settings];
- // TODO(amirh): return an error if apply settings failed once it's possible to do so.
- // https://github.com/flutter/flutter/issues/36228
-
- NSString* initialUrl = args[@"initialUrl"];
- if ([initialUrl isKindOfClass:[NSString class]]) {
- [self loadUrl:initialUrl];
- }
- }
- return self;
-}
-
-- (void)dealloc {
- if (_progressionDelegate != nil) {
- [_progressionDelegate stopObservingProgress:_webView];
- }
-}
-
-- (UIView*)view {
- return _webView;
-}
-
-- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
- if ([[call method] isEqualToString:@"updateSettings"]) {
- [self onUpdateSettings:call result:result];
- } else if ([[call method] isEqualToString:@"loadUrl"]) {
- [self onLoadUrl:call result:result];
- } else if ([[call method] isEqualToString:@"canGoBack"]) {
- [self onCanGoBack:call result:result];
- } else if ([[call method] isEqualToString:@"canGoForward"]) {
- [self onCanGoForward:call result:result];
- } else if ([[call method] isEqualToString:@"goBack"]) {
- [self onGoBack:call result:result];
- } else if ([[call method] isEqualToString:@"goForward"]) {
- [self onGoForward:call result:result];
- } else if ([[call method] isEqualToString:@"reload"]) {
- [self onReload:call result:result];
- } else if ([[call method] isEqualToString:@"currentUrl"]) {
- [self onCurrentUrl:call result:result];
- } else if ([[call method] isEqualToString:@"evaluateJavascript"]) {
- [self onEvaluateJavaScript:call result:result];
- } else if ([[call method] isEqualToString:@"addJavascriptChannels"]) {
- [self onAddJavaScriptChannels:call result:result];
- } else if ([[call method] isEqualToString:@"removeJavascriptChannels"]) {
- [self onRemoveJavaScriptChannels:call result:result];
- } else if ([[call method] isEqualToString:@"clearCache"]) {
- [self clearCache:result];
- } else if ([[call method] isEqualToString:@"getTitle"]) {
- [self onGetTitle:result];
- } else if ([[call method] isEqualToString:@"scrollTo"]) {
- [self onScrollTo:call result:result];
- } else if ([[call method] isEqualToString:@"scrollBy"]) {
- [self onScrollBy:call result:result];
- } else if ([[call method] isEqualToString:@"getScrollX"]) {
- [self getScrollX:call result:result];
- } else if ([[call method] isEqualToString:@"getScrollY"]) {
- [self getScrollY:call result:result];
- } else {
- result(FlutterMethodNotImplemented);
- }
-}
-
-- (void)onUpdateSettings:(FlutterMethodCall*)call result:(FlutterResult)result {
- NSString* error = [self applySettings:[call arguments]];
- if (error == nil) {
- result(nil);
- return;
- }
- result([FlutterError errorWithCode:@"updateSettings_failed" message:error details:nil]);
-}
-
-- (void)onLoadUrl:(FlutterMethodCall*)call result:(FlutterResult)result {
- if (![self loadRequest:[call arguments]]) {
- result([FlutterError
- errorWithCode:@"loadUrl_failed"
- message:@"Failed parsing the URL"
- details:[NSString stringWithFormat:@"Request was: '%@'", [call arguments]]]);
- } else {
- result(nil);
- }
-}
-
-- (void)onCanGoBack:(FlutterMethodCall*)call result:(FlutterResult)result {
- BOOL canGoBack = [_webView canGoBack];
- result(@(canGoBack));
-}
-
-- (void)onCanGoForward:(FlutterMethodCall*)call result:(FlutterResult)result {
- BOOL canGoForward = [_webView canGoForward];
- result(@(canGoForward));
-}
-
-- (void)onGoBack:(FlutterMethodCall*)call result:(FlutterResult)result {
- [_webView goBack];
- result(nil);
-}
-
-- (void)onGoForward:(FlutterMethodCall*)call result:(FlutterResult)result {
- [_webView goForward];
- result(nil);
-}
-
-- (void)onReload:(FlutterMethodCall*)call result:(FlutterResult)result {
- [_webView reload];
- result(nil);
-}
-
-- (void)onCurrentUrl:(FlutterMethodCall*)call result:(FlutterResult)result {
- _currentUrl = [[_webView URL] absoluteString];
- result(_currentUrl);
-}
-
-- (void)onEvaluateJavaScript:(FlutterMethodCall*)call result:(FlutterResult)result {
- NSString* jsString = [call arguments];
- if (!jsString) {
- result([FlutterError errorWithCode:@"evaluateJavaScript_failed"
- message:@"JavaScript String cannot be null"
- details:nil]);
- return;
- }
- [_webView evaluateJavaScript:jsString
- completionHandler:^(_Nullable id evaluateResult, NSError* _Nullable error) {
- if (error) {
- result([FlutterError
- errorWithCode:@"evaluateJavaScript_failed"
- message:@"Failed evaluating JavaScript"
- details:[NSString stringWithFormat:@"JavaScript string was: '%@'\n%@",
- jsString, error]]);
- } else {
- result([NSString stringWithFormat:@"%@", evaluateResult]);
- }
- }];
-}
-
-- (void)onAddJavaScriptChannels:(FlutterMethodCall*)call result:(FlutterResult)result {
- NSArray* channelNames = [call arguments];
- NSSet* channelNamesSet = [[NSSet alloc] initWithArray:channelNames];
- [_javaScriptChannelNames addObjectsFromArray:channelNames];
- [self registerJavaScriptChannels:channelNamesSet
- controller:_webView.configuration.userContentController];
- result(nil);
-}
-
-- (void)onRemoveJavaScriptChannels:(FlutterMethodCall*)call result:(FlutterResult)result {
- // WkWebView does not support removing a single user script, so instead we remove all
- // user scripts, all message handlers. And re-register channels that shouldn't be removed.
- [_webView.configuration.userContentController removeAllUserScripts];
- for (NSString* channelName in _javaScriptChannelNames) {
- [_webView.configuration.userContentController removeScriptMessageHandlerForName:channelName];
- }
-
- NSArray* channelNamesToRemove = [call arguments];
- for (NSString* channelName in channelNamesToRemove) {
- [_javaScriptChannelNames removeObject:channelName];
- }
-
- [self registerJavaScriptChannels:_javaScriptChannelNames
- controller:_webView.configuration.userContentController];
- result(nil);
-}
-
-- (void)clearCache:(FlutterResult)result {
- NSSet* cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
- WKWebsiteDataStore* dataStore = [WKWebsiteDataStore defaultDataStore];
- NSDate* dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
- [dataStore removeDataOfTypes:cacheDataTypes
- modifiedSince:dateFrom
- completionHandler:^{
- result(nil);
- }];
-}
-
-- (void)onGetTitle:(FlutterResult)result {
- NSString* title = _webView.title;
- result(title);
-}
-
-- (void)onScrollTo:(FlutterMethodCall*)call result:(FlutterResult)result {
- NSDictionary* arguments = [call arguments];
- int x = [arguments[@"x"] intValue];
- int y = [arguments[@"y"] intValue];
-
- _webView.scrollView.contentOffset = CGPointMake(x, y);
- result(nil);
-}
-
-- (void)onScrollBy:(FlutterMethodCall*)call result:(FlutterResult)result {
- CGPoint contentOffset = _webView.scrollView.contentOffset;
-
- NSDictionary* arguments = [call arguments];
- int x = [arguments[@"x"] intValue] + contentOffset.x;
- int y = [arguments[@"y"] intValue] + contentOffset.y;
-
- _webView.scrollView.contentOffset = CGPointMake(x, y);
- result(nil);
-}
-
-- (void)getScrollX:(FlutterMethodCall*)call result:(FlutterResult)result {
- int offsetX = _webView.scrollView.contentOffset.x;
- result(@(offsetX));
-}
-
-- (void)getScrollY:(FlutterMethodCall*)call result:(FlutterResult)result {
- int offsetY = _webView.scrollView.contentOffset.y;
- result(@(offsetY));
-}
-
-// Returns nil when successful, or an error message when one or more keys are unknown.
-- (NSString*)applySettings:(NSDictionary*)settings {
- NSMutableArray* unknownKeys = [[NSMutableArray alloc] init];
- for (NSString* key in settings) {
- if ([key isEqualToString:@"jsMode"]) {
- NSNumber* mode = settings[key];
- [self updateJsMode:mode];
- } else if ([key isEqualToString:@"hasNavigationDelegate"]) {
- NSNumber* hasDartNavigationDelegate = settings[key];
- _navigationDelegate.hasDartNavigationDelegate = [hasDartNavigationDelegate boolValue];
- } else if ([key isEqualToString:@"hasProgressTracking"]) {
- NSNumber* hasProgressTrackingValue = settings[key];
- bool hasProgressTracking = [hasProgressTrackingValue boolValue];
- if (hasProgressTracking) {
- _progressionDelegate = [[FLTWKProgressionDelegate alloc] initWithWebView:_webView
- channel:_channel];
- }
- } else if ([key isEqualToString:@"debuggingEnabled"]) {
- // no-op debugging is always enabled on iOS.
- } else if ([key isEqualToString:@"gestureNavigationEnabled"]) {
- NSNumber* allowsBackForwardNavigationGestures = settings[key];
- _webView.allowsBackForwardNavigationGestures =
- [allowsBackForwardNavigationGestures boolValue];
- } else if ([key isEqualToString:@"userAgent"]) {
- NSString* userAgent = settings[key];
- [self updateUserAgent:[userAgent isEqual:[NSNull null]] ? nil : userAgent];
- } else {
- [unknownKeys addObject:key];
- }
- }
- if ([unknownKeys count] == 0) {
- return nil;
- }
- return [NSString stringWithFormat:@"webview_flutter: unknown setting keys: {%@}",
- [unknownKeys componentsJoinedByString:@", "]];
-}
-
-- (void)applyConfigurationSettings:(NSDictionary*)settings
- toConfiguration:(WKWebViewConfiguration*)configuration {
- NSAssert(configuration != _webView.configuration,
- @"configuration needs to be updated before webView.configuration.");
- for (NSString* key in settings) {
- if ([key isEqualToString:@"allowsInlineMediaPlayback"]) {
- NSNumber* allowsInlineMediaPlayback = settings[key];
- configuration.allowsInlineMediaPlayback = [allowsInlineMediaPlayback boolValue];
- }
- }
-}
-
-- (void)updateJsMode:(NSNumber*)mode {
- WKPreferences* preferences = [[_webView configuration] preferences];
- switch ([mode integerValue]) {
- case 0: // disabled
- [preferences setJavaScriptEnabled:NO];
- break;
- case 1: // unrestricted
- [preferences setJavaScriptEnabled:YES];
- break;
- default:
- NSLog(@"webview_flutter: unknown JavaScript mode: %@", mode);
- }
-}
-
-- (void)updateAutoMediaPlaybackPolicy:(NSNumber*)policy
- inConfiguration:(WKWebViewConfiguration*)configuration {
- switch ([policy integerValue]) {
- case 0: // require_user_action_for_all_media_types
- if (@available(iOS 10.0, *)) {
- configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll;
- } else {
- configuration.requiresUserActionForMediaPlayback = true;
- }
- break;
- case 1: // always_allow
- if (@available(iOS 10.0, *)) {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
-#pragma clang diagnostic pop
- } else {
- configuration.requiresUserActionForMediaPlayback = false;
- }
- break;
- default:
- NSLog(@"webview_flutter: unknown auto media playback policy: %@", policy);
- }
-}
-
-- (bool)loadRequest:(NSDictionary*)request {
- if (!request) {
- return false;
- }
-
- NSString* url = request[@"url"];
- if ([url isKindOfClass:[NSString class]]) {
- id headers = request[@"headers"];
- if ([headers isKindOfClass:[NSDictionary class]]) {
- return [self loadUrl:url withHeaders:headers];
- } else {
- return [self loadUrl:url];
- }
- }
-
- return false;
-}
-
-- (bool)loadUrl:(NSString*)url {
- return [self loadUrl:url withHeaders:[NSMutableDictionary dictionary]];
-}
-
-- (bool)loadUrl:(NSString*)url withHeaders:(NSDictionary*)headers {
- NSURL* nsUrl = [NSURL URLWithString:url];
- if (!nsUrl) {
- return false;
- }
- NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:nsUrl];
- [request setAllHTTPHeaderFields:headers];
- [_webView loadRequest:request];
- return true;
-}
-
-- (void)registerJavaScriptChannels:(NSSet*)channelNames
- controller:(WKUserContentController*)userContentController {
- for (NSString* channelName in channelNames) {
- FLTJavaScriptChannel* channel =
- [[FLTJavaScriptChannel alloc] initWithMethodChannel:_channel
- javaScriptChannelName:channelName];
- [userContentController addScriptMessageHandler:channel name:channelName];
- NSString* wrapperSource = [NSString
- stringWithFormat:@"window.%@ = webkit.messageHandlers.%@;", channelName, channelName];
- WKUserScript* wrapperScript =
- [[WKUserScript alloc] initWithSource:wrapperSource
- injectionTime:WKUserScriptInjectionTimeAtDocumentStart
- forMainFrameOnly:NO];
- [userContentController addUserScript:wrapperScript];
- }
-}
-
-- (void)updateUserAgent:(NSString*)userAgent {
- [_webView setCustomUserAgent:userAgent];
-}
-
-#pragma mark WKUIDelegate
-
-- (WKWebView*)webView:(WKWebView*)webView
- createWebViewWithConfiguration:(WKWebViewConfiguration*)configuration
- forNavigationAction:(WKNavigationAction*)navigationAction
- windowFeatures:(WKWindowFeatures*)windowFeatures {
- if (!navigationAction.targetFrame.isMainFrame) {
- [webView loadRequest:navigationAction.request];
- }
-
- return nil;
-}
-
-@end
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/JavaScriptChannelHandler.h b/packages/webview_flutter/webview_flutter/ios/Classes/JavaScriptChannelHandler.h
deleted file mode 100644
index a0a5ec657295..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/JavaScriptChannelHandler.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FLTJavaScriptChannel : NSObject
-
-- (instancetype)initWithMethodChannel:(FlutterMethodChannel*)methodChannel
- javaScriptChannelName:(NSString*)javaScriptChannelName;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter/ios/Classes/JavaScriptChannelHandler.m b/packages/webview_flutter/webview_flutter/ios/Classes/JavaScriptChannelHandler.m
deleted file mode 100644
index ec9a363a4b2e..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/Classes/JavaScriptChannelHandler.m
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "JavaScriptChannelHandler.h"
-
-@implementation FLTJavaScriptChannel {
- FlutterMethodChannel* _methodChannel;
- NSString* _javaScriptChannelName;
-}
-
-- (instancetype)initWithMethodChannel:(FlutterMethodChannel*)methodChannel
- javaScriptChannelName:(NSString*)javaScriptChannelName {
- self = [super init];
- NSAssert(methodChannel != nil, @"methodChannel must not be null.");
- NSAssert(javaScriptChannelName != nil, @"javaScriptChannelName must not be null.");
- if (self) {
- _methodChannel = methodChannel;
- _javaScriptChannelName = javaScriptChannelName;
- }
- return self;
-}
-
-- (void)userContentController:(WKUserContentController*)userContentController
- didReceiveScriptMessage:(WKScriptMessage*)message {
- NSAssert(_methodChannel != nil, @"Can't send a message to an unitialized JavaScript channel.");
- NSAssert(_javaScriptChannelName != nil,
- @"Can't send a message to an unitialized JavaScript channel.");
- NSDictionary* arguments = @{
- @"channel" : _javaScriptChannelName,
- @"message" : [NSString stringWithFormat:@"%@", message.body]
- };
- [_methodChannel invokeMethod:@"javascriptChannelMessage" arguments:arguments];
-}
-
-@end
diff --git a/packages/webview_flutter/webview_flutter/ios/webview_flutter.podspec b/packages/webview_flutter/webview_flutter/ios/webview_flutter.podspec
deleted file mode 100644
index 2e021994b8f4..000000000000
--- a/packages/webview_flutter/webview_flutter/ios/webview_flutter.podspec
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
-#
-Pod::Spec.new do |s|
- s.name = 'webview_flutter'
- s.version = '0.0.1'
- s.summary = 'A WebView Plugin for Flutter.'
- s.description = <<-DESC
-A Flutter plugin that provides a WebView widget.
-Downloaded by pub (not CocoaPods).
- DESC
- s.homepage = 'https://github.com/flutter/plugins'
- s.license = { :type => 'BSD', :file => '../LICENSE' }
- s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
- s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/webview_flutter' }
- s.documentation_url = 'https://pub.dev/packages/webview_flutter'
- s.source_files = 'Classes/**/*'
- s.public_header_files = 'Classes/**/*.h'
- s.dependency 'Flutter'
-
- s.platform = :ios, '9.0'
- s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
-end
diff --git a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart b/packages/webview_flutter/webview_flutter/lib/platform_interface.dart
index 92aa87b7480f..aa7b3a0931e8 100644
--- a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart
+++ b/packages/webview_flutter/webview_flutter/lib/platform_interface.dart
@@ -2,547 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:async';
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/widgets.dart';
-
-import 'webview_flutter.dart';
-
-/// Interface for callbacks made by [WebViewPlatformController].
-///
-/// The webview plugin implements this class, and passes an instance to the [WebViewPlatformController].
-/// [WebViewPlatformController] is notifying this handler on events that happened on the platform's webview.
-abstract class WebViewPlatformCallbacksHandler {
- /// Invoked by [WebViewPlatformController] when a JavaScript channel message is received.
- void onJavaScriptChannelMessage(String channel, String message);
-
- /// Invoked by [WebViewPlatformController] when a navigation request is pending.
- ///
- /// If true is returned the navigation is allowed, otherwise it is blocked.
- FutureOr onNavigationRequest(
- {required String url, required bool isForMainFrame});
-
- /// Invoked by [WebViewPlatformController] when a page has started loading.
- void onPageStarted(String url);
-
- /// Invoked by [WebViewPlatformController] when a page has finished loading.
- void onPageFinished(String url);
-
- /// Invoked by [WebViewPlatformController] when a page is loading.
- /// /// Only works when [WebSettings.hasProgressTracking] is set to `true`.
- void onProgress(int progress);
-
- /// Report web resource loading error to the host application.
- void onWebResourceError(WebResourceError error);
-}
-
-/// Possible error type categorizations used by [WebResourceError].
-enum WebResourceErrorType {
- /// User authentication failed on server.
- authentication,
-
- /// Malformed URL.
- badUrl,
-
- /// Failed to connect to the server.
- connect,
-
- /// Failed to perform SSL handshake.
- failedSslHandshake,
-
- /// Generic file error.
- file,
-
- /// File not found.
- fileNotFound,
-
- /// Server or proxy hostname lookup failed.
- hostLookup,
-
- /// Failed to read or write to the server.
- io,
-
- /// User authentication failed on proxy.
- proxyAuthentication,
-
- /// Too many redirects.
- redirectLoop,
-
- /// Connection timed out.
- timeout,
-
- /// Too many requests during this load.
- tooManyRequests,
-
- /// Generic error.
- unknown,
-
- /// Resource load was canceled by Safe Browsing.
- unsafeResource,
-
- /// Unsupported authentication scheme (not basic or digest).
- unsupportedAuthScheme,
-
- /// Unsupported URI scheme.
- unsupportedScheme,
-
- /// The web content process was terminated.
- webContentProcessTerminated,
-
- /// The web view was invalidated.
- webViewInvalidated,
-
- /// A JavaScript exception occurred.
- javaScriptExceptionOccurred,
-
- /// The result of JavaScript execution could not be returned.
- javaScriptResultTypeIsUnsupported,
-}
-
-/// Error returned in `WebView.onWebResourceError` when a web resource loading error has occurred.
-class WebResourceError {
- /// Creates a new [WebResourceError]
- ///
- /// A user should not need to instantiate this class, but will receive one in
- /// [WebResourceErrorCallback].
- WebResourceError({
- required this.errorCode,
- required this.description,
- this.domain,
- this.errorType,
- this.failingUrl,
- }) : assert(errorCode != null),
- assert(description != null);
-
- /// Raw code of the error from the respective platform.
- ///
- /// On Android, the error code will be a constant from a
- /// [WebViewClient](https://developer.android.com/reference/android/webkit/WebViewClient#summary) and
- /// will have a corresponding [errorType].
- ///
- /// On iOS, the error code will be a constant from `NSError.code` in
- /// Objective-C. See
- /// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorObjectsDomains/ErrorObjectsDomains.html
- /// for more information on error handling on iOS. Some possible error codes
- /// can be found at https://developer.apple.com/documentation/webkit/wkerrorcode?language=objc.
- final int errorCode;
-
- /// The domain of where to find the error code.
- ///
- /// This field is only available on iOS and represents a "domain" from where
- /// the [errorCode] is from. This value is taken directly from an `NSError`
- /// in Objective-C. See
- /// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorObjectsDomains/ErrorObjectsDomains.html
- /// for more information on error handling on iOS.
- final String? domain;
-
- /// Description of the error that can be used to communicate the problem to the user.
- final String description;
-
- /// The type this error can be categorized as.
- ///
- /// This will never be `null` on Android, but can be `null` on iOS.
- final WebResourceErrorType? errorType;
-
- /// Gets the URL for which the resource request was made.
- ///
- /// This value is not provided on iOS. Alternatively, you can keep track of
- /// the last values provided to [WebViewPlatformController.loadUrl].
- final String? failingUrl;
-}
-
-/// Interface for talking to the webview's platform implementation.
-///
-/// An instance implementing this interface is passed to the `onWebViewPlatformCreated` callback that is
-/// passed to [WebViewPlatformBuilder#onWebViewPlatformCreated].
-///
-/// Platform implementations that live in a separate package should extend this class rather than
-/// implement it as webview_flutter does not consider newly added methods to be breaking changes.
-/// Extending this class (using `extends`) ensures that the subclass will get the default
-/// implementation, while platform implementations that `implements` this interface will be broken
-/// by newly added [WebViewPlatformController] methods.
-abstract class WebViewPlatformController {
- /// Creates a new WebViewPlatform.
- ///
- /// Callbacks made by the WebView will be delegated to `handler`.
- ///
- /// The `handler` parameter must not be null.
- WebViewPlatformController(WebViewPlatformCallbacksHandler handler);
-
- /// Loads the specified URL.
- ///
- /// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will
- /// be added as key value pairs of HTTP headers for the request.
- ///
- /// `url` must not be null.
- ///
- /// Throws an ArgumentError if `url` is not a valid URL string.
- Future loadUrl(
- String url,
- Map? headers,
- ) {
- throw UnimplementedError(
- "WebView loadUrl is not implemented on the current platform");
- }
-
- /// Updates the webview settings.
- ///
- /// Any non null field in `settings` will be set as the new setting value.
- /// All null fields in `settings` are ignored.
- Future updateSettings(WebSettings setting) {
- throw UnimplementedError(
- "WebView updateSettings is not implemented on the current platform");
- }
-
- /// Accessor to the current URL that the WebView is displaying.
- ///
- /// If no URL was ever loaded, returns `null`.
- Future currentUrl() {
- throw UnimplementedError(
- "WebView currentUrl is not implemented on the current platform");
- }
-
- /// Checks whether there's a back history item.
- Future canGoBack() {
- throw UnimplementedError(
- "WebView canGoBack is not implemented on the current platform");
- }
-
- /// Checks whether there's a forward history item.
- Future canGoForward() {
- throw UnimplementedError(
- "WebView canGoForward is not implemented on the current platform");
- }
-
- /// Goes back in the history of this WebView.
- ///
- /// If there is no back history item this is a no-op.
- Future goBack() {
- throw UnimplementedError(
- "WebView goBack is not implemented on the current platform");
- }
-
- /// Goes forward in the history of this WebView.
- ///
- /// If there is no forward history item this is a no-op.
- Future goForward() {
- throw UnimplementedError(
- "WebView goForward is not implemented on the current platform");
- }
-
- /// Reloads the current URL.
- Future reload() {
- throw UnimplementedError(
- "WebView reload is not implemented on the current platform");
- }
-
- /// Clears all caches used by the [WebView].
- ///
- /// The following caches are cleared:
- /// 1. Browser HTTP Cache.
- /// 2. [Cache API](https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api) caches.
- /// These are not yet supported in iOS WkWebView. Service workers tend to use this cache.
- /// 3. Application cache.
- /// 4. Local Storage.
- Future clearCache() {
- throw UnimplementedError(
- "WebView clearCache is not implemented on the current platform");
- }
-
- /// Evaluates a JavaScript expression in the context of the current page.
- ///
- /// The Future completes with an error if a JavaScript error occurred, or if the type of the
- /// evaluated expression is not supported(e.g on iOS not all non primitive type can be evaluated).
- Future evaluateJavascript(String javascriptString) {
- throw UnimplementedError(
- "WebView evaluateJavascript is not implemented on the current platform");
- }
-
- /// Adds new JavaScript channels to the set of enabled channels.
- ///
- /// For each value in this list the platform's webview should make sure that a corresponding
- /// property with a postMessage method is set on `window`. For example for a JavaScript channel
- /// named `Foo` it should be possible for JavaScript code executing in the webview to do
- ///
- /// ```javascript
- /// Foo.postMessage('hello');
- /// ```
- ///
- /// See also: [CreationParams.javascriptChannelNames].
- Future addJavascriptChannels(Set javascriptChannelNames) {
- throw UnimplementedError(
- "WebView addJavascriptChannels is not implemented on the current platform");
- }
-
- /// Removes JavaScript channel names from the set of enabled channels.
- ///
- /// This disables channels that were previously enabled by [addJavaScriptChannels] or through
- /// [CreationParams.javascriptChannelNames].
- Future removeJavascriptChannels(Set javascriptChannelNames) {
- throw UnimplementedError(
- "WebView removeJavascriptChannels is not implemented on the current platform");
- }
-
- /// Returns the title of the currently loaded page.
- Future getTitle() {
- throw UnimplementedError(
- "WebView getTitle is not implemented on the current platform");
- }
-
- /// Set the scrolled position of this view.
- ///
- /// The parameters `x` and `y` specify the position to scroll to in WebView pixels.
- Future scrollTo(int x, int y) {
- throw UnimplementedError(
- "WebView scrollTo is not implemented on the current platform");
- }
-
- /// Move the scrolled position of this view.
- ///
- /// The parameters `x` and `y` specify the amount of WebView pixels to scroll by.
- Future scrollBy(int x, int y) {
- throw UnimplementedError(
- "WebView scrollBy is not implemented on the current platform");
- }
-
- /// Return the horizontal scroll position of this view.
- ///
- /// Scroll position is measured from left.
- Future getScrollX() {
- throw UnimplementedError(
- "WebView getScrollX is not implemented on the current platform");
- }
-
- /// Return the vertical scroll position of this view.
- ///
- /// Scroll position is measured from top.
- Future getScrollY() {
- throw UnimplementedError(
- "WebView getScrollY is not implemented on the current platform");
- }
-}
-
-/// A single setting for configuring a WebViewPlatform which may be absent.
-class WebSetting {
- /// Constructs an absent setting instance.
- ///
- /// The [isPresent] field for the instance will be false.
- ///
- /// Accessing [value] for an absent instance will throw.
- WebSetting.absent()
- : _value = null,
- isPresent = false;
-
- /// Constructs a setting of the given `value`.
- ///
- /// The [isPresent] field for the instance will be true.
- WebSetting.of(T value)
- : _value = value,
- isPresent = true;
-
- final T? _value;
-
- /// The setting's value.
- ///
- /// Throws if [WebSetting.isPresent] is false.
- T get value {
- if (!isPresent) {
- throw StateError('Cannot access a value of an absent WebSetting');
- }
- assert(isPresent);
- // The intention of this getter is to return T whether it is nullable or
- // not whereas _value is of type T? since _value can be null even when
- // T is not nullable (when isPresent == false).
- //
- // We promote _value to T using `as T` instead of `!` operator to handle
- // the case when _value is legitimately null (and T is a nullable type).
- // `!` operator would always throw if _value is null.
- return _value as T;
- }
-
- /// True when this web setting instance contains a value.
- ///
- /// When false the [WebSetting.value] getter throws.
- final bool isPresent;
-
- @override
- bool operator ==(Object other) {
- if (other.runtimeType != runtimeType) return false;
- final WebSetting typedOther = other as WebSetting;
- return typedOther.isPresent == isPresent && typedOther._value == _value;
- }
-
- @override
- int get hashCode => hashValues(_value, isPresent);
-}
-
-/// Settings for configuring a WebViewPlatform.
-///
-/// Initial settings are passed as part of [CreationParams], settings updates are sent with
-/// [WebViewPlatform#updateSettings].
-///
-/// The `userAgent` parameter must not be null.
-class WebSettings {
- /// Construct an instance with initial settings. Future setting changes can be
- /// sent with [WebviewPlatform#updateSettings].
- ///
- /// The `userAgent` parameter must not be null.
- WebSettings({
- this.javascriptMode,
- this.hasNavigationDelegate,
- this.hasProgressTracking,
- this.debuggingEnabled,
- this.gestureNavigationEnabled,
- this.allowsInlineMediaPlayback,
- required this.userAgent,
- }) : assert(userAgent != null);
-
- /// The JavaScript execution mode to be used by the webview.
- final JavascriptMode? javascriptMode;
-
- /// Whether the [WebView] has a [NavigationDelegate] set.
- final bool? hasNavigationDelegate;
-
- /// Whether the [WebView] should track page loading progress.
- /// See also: [WebViewPlatformCallbacksHandler.onProgress] to get the progress.
- final bool? hasProgressTracking;
-
- /// Whether to enable the platform's webview content debugging tools.
- ///
- /// See also: [WebView.debuggingEnabled].
- final bool? debuggingEnabled;
-
- /// Whether to play HTML5 videos inline or use the native full-screen controller on iOS.
- ///
- /// This will have no effect on Android.
- final bool? allowsInlineMediaPlayback;
-
- /// The value used for the HTTP `User-Agent:` request header.
- ///
- /// If [userAgent.value] is null the platform's default user agent should be used.
- ///
- /// An absent value ([userAgent.isPresent] is false) represents no change to this setting from the
- /// last time it was set.
- ///
- /// See also [WebView.userAgent].
- final WebSetting userAgent;
-
- /// Whether to allow swipe based navigation in iOS.
- ///
- /// See also: [WebView.gestureNavigationEnabled]
- final bool? gestureNavigationEnabled;
-
- @override
- String toString() {
- return 'WebSettings(javascriptMode: $javascriptMode, hasNavigationDelegate: $hasNavigationDelegate, hasProgressTracking: $hasProgressTracking, debuggingEnabled: $debuggingEnabled, gestureNavigationEnabled: $gestureNavigationEnabled, userAgent: $userAgent, allowsInlineMediaPlayback: $allowsInlineMediaPlayback)';
- }
-}
-
-/// Configuration to use when creating a new [WebViewPlatformController].
-///
-/// The `autoMediaPlaybackPolicy` parameter must not be null.
-class CreationParams {
- /// Constructs an instance to use when creating a new
- /// [WebViewPlatformController].
- ///
- /// The `autoMediaPlaybackPolicy` parameter must not be null.
- CreationParams({
- this.initialUrl,
- this.webSettings,
- this.javascriptChannelNames = const {},
- this.userAgent,
- this.autoMediaPlaybackPolicy =
- AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
- }) : assert(autoMediaPlaybackPolicy != null);
-
- /// The initialUrl to load in the webview.
- ///
- /// When null the webview will be created without loading any page.
- final String? initialUrl;
-
- /// The initial [WebSettings] for the new webview.
- ///
- /// This can later be updated with [WebViewPlatformController.updateSettings].
- final WebSettings? webSettings;
-
- /// The initial set of JavaScript channels that are configured for this webview.
- ///
- /// For each value in this set the platform's webview should make sure that a corresponding
- /// property with a postMessage method is set on `window`. For example for a JavaScript channel
- /// named `Foo` it should be possible for JavaScript code executing in the webview to do
- ///
- /// ```javascript
- /// Foo.postMessage('hello');
- /// ```
- // TODO(amirh): describe what should happen when postMessage is called once that code is migrated
- // to PlatformWebView.
- final Set javascriptChannelNames;
-
- /// The value used for the HTTP User-Agent: request header.
- ///
- /// When null the platform's webview default is used for the User-Agent header.
- final String? userAgent;
-
- /// Which restrictions apply on automatic media playback.
- final AutoMediaPlaybackPolicy autoMediaPlaybackPolicy;
-
- @override
- String toString() {
- return '$runtimeType(initialUrl: $initialUrl, settings: $webSettings, javascriptChannelNames: $javascriptChannelNames, UserAgent: $userAgent)';
- }
-}
-
-/// Signature for callbacks reporting that a [WebViewPlatformController] was created.
-///
-/// See also the `onWebViewPlatformCreated` argument for [WebViewPlatform.build].
-typedef WebViewPlatformCreatedCallback = void Function(
- WebViewPlatformController? webViewPlatformController);
-
-/// Interface for a platform implementation of a WebView.
-///
-/// [WebView.platform] controls the builder that is used by [WebView].
-/// [AndroidWebViewPlatform] and [CupertinoWebViewPlatform] are the default implementations
-/// for Android and iOS respectively.
-abstract class WebViewPlatform {
- /// Builds a new WebView.
- ///
- /// Returns a Widget tree that embeds the created webview.
- ///
- /// `creationParams` are the initial parameters used to setup the webview.
- ///
- /// `webViewPlatformHandler` will be used for handling callbacks that are made by the created
- /// [WebViewPlatformController].
- ///
- /// `onWebViewPlatformCreated` will be invoked after the platform specific [WebViewPlatformController]
- /// implementation is created with the [WebViewPlatformController] instance as a parameter.
- ///
- /// `gestureRecognizers` specifies which gestures should be consumed by the web view.
- /// It is possible for other gesture recognizers to be competing with the web view on pointer
- /// events, e.g if the web view is inside a [ListView] the [ListView] will want to handle
- /// vertical drags. The web view will claim gestures that are recognized by any of the
- /// recognizers on this list.
- /// When `gestureRecognizers` is empty or null, the web view will only handle pointer events for gestures that
- /// were not claimed by any other gesture recognizer.
- ///
- /// `webViewPlatformHandler` must not be null.
- Widget build({
- required BuildContext context,
- // TODO(amirh): convert this to be the actual parameters.
- // I'm starting without it as the PR is starting to become pretty big.
- // I'll followup with the conversion PR.
- required CreationParams creationParams,
- required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
- WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
- Set>? gestureRecognizers,
- });
-
- /// Clears all cookies for all [WebView] instances.
- ///
- /// Returns true if cookies were present before clearing, else false.
- Future clearCookies() {
- throw UnimplementedError(
- "WebView clearCookies is not implemented on the current platform");
- }
-}
+/// Re-export the classes from the webview_flutter_platform_interface through
+/// the `platform_interface.dart` file so we don't accidentally break any
+/// non-endorsed existing implementations of the interface.
+export 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'
+ show
+ AutoMediaPlaybackPolicy,
+ CreationParams,
+ JavascriptChannel,
+ JavascriptChannelRegistry,
+ JavascriptMessage,
+ JavascriptMode,
+ JavascriptMessageHandler,
+ WebViewPlatform,
+ WebViewPlatformCallbacksHandler,
+ WebViewPlatformController,
+ WebViewPlatformCreatedCallback,
+ WebSetting,
+ WebSettings,
+ WebResourceError,
+ WebResourceErrorType;
diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart
new file mode 100644
index 000000000000..7699cc46c5d3
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart
@@ -0,0 +1,681 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter/widgets.dart';
+import 'package:webview_flutter_android/webview_android.dart';
+import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
+
+import '../platform_interface.dart';
+
+/// Optional callback invoked when a web view is first created. [controller] is
+/// the [WebViewController] for the created web view.
+typedef void WebViewCreatedCallback(WebViewController controller);
+
+/// Information about a navigation action that is about to be executed.
+class NavigationRequest {
+ NavigationRequest._({required this.url, required this.isForMainFrame});
+
+ /// The URL that will be loaded if the navigation is executed.
+ final String url;
+
+ /// Whether the navigation request is to be loaded as the main frame.
+ final bool isForMainFrame;
+
+ @override
+ String toString() {
+ return '$runtimeType(url: $url, isForMainFrame: $isForMainFrame)';
+ }
+}
+
+/// A decision on how to handle a navigation request.
+enum NavigationDecision {
+ /// Prevent the navigation from taking place.
+ prevent,
+
+ /// Allow the navigation to take place.
+ navigate,
+}
+
+/// Decides how to handle a specific navigation request.
+///
+/// The returned [NavigationDecision] determines how the navigation described by
+/// `navigation` should be handled.
+///
+/// See also: [WebView.navigationDelegate].
+typedef FutureOr NavigationDelegate(
+ NavigationRequest navigation);
+
+/// Signature for when a [WebView] has started loading a page.
+typedef void PageStartedCallback(String url);
+
+/// Signature for when a [WebView] has finished loading a page.
+typedef void PageFinishedCallback(String url);
+
+/// Signature for when a [WebView] is loading a page.
+typedef void PageLoadingCallback(int progress);
+
+/// Signature for when a [WebView] has failed to load a resource.
+typedef void WebResourceErrorCallback(WebResourceError error);
+
+/// A web view widget for showing html content.
+///
+/// There is a known issue that on iOS 13.4 and 13.5, other flutter widgets covering
+/// the `WebView` is not able to block the `WebView` from receiving touch events.
+/// See https://github.com/flutter/flutter/issues/53490.
+class WebView extends StatefulWidget {
+ /// Creates a new web view.
+ ///
+ /// The web view can be controlled using a `WebViewController` that is passed to the
+ /// `onWebViewCreated` callback once the web view is created.
+ ///
+ /// The `javascriptMode` and `autoMediaPlaybackPolicy` parameters must not be null.
+ const WebView({
+ Key? key,
+ this.onWebViewCreated,
+ this.initialUrl,
+ this.javascriptMode = JavascriptMode.disabled,
+ this.javascriptChannels,
+ this.navigationDelegate,
+ this.gestureRecognizers,
+ this.onPageStarted,
+ this.onPageFinished,
+ this.onProgress,
+ this.onWebResourceError,
+ this.debuggingEnabled = false,
+ this.gestureNavigationEnabled = false,
+ this.userAgent,
+ this.initialMediaPlaybackPolicy =
+ AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
+ this.allowsInlineMediaPlayback = false,
+ }) : assert(javascriptMode != null),
+ assert(initialMediaPlaybackPolicy != null),
+ assert(allowsInlineMediaPlayback != null),
+ super(key: key);
+
+ static WebViewPlatform? _platform;
+
+ /// Sets a custom [WebViewPlatform].
+ ///
+ /// This property can be set to use a custom platform implementation for WebViews.
+ ///
+ /// Setting `platform` doesn't affect [WebView]s that were already created.
+ ///
+ /// The default value is [AndroidWebView] on Android and [CupertinoWebView] on iOS.
+ static set platform(WebViewPlatform? platform) {
+ _platform = platform;
+ }
+
+ /// The WebView platform that's used by this WebView.
+ ///
+ /// The default value is [AndroidWebView] on Android and [CupertinoWebView] on iOS.
+ static WebViewPlatform get platform {
+ if (_platform == null) {
+ switch (defaultTargetPlatform) {
+ case TargetPlatform.android:
+ _platform = AndroidWebView();
+ break;
+ case TargetPlatform.iOS:
+ _platform = CupertinoWebView();
+ break;
+ default:
+ throw UnsupportedError(
+ "Trying to use the default webview implementation for $defaultTargetPlatform but there isn't a default one");
+ }
+ }
+ return _platform!;
+ }
+
+ /// If not null invoked once the web view is created.
+ final WebViewCreatedCallback? onWebViewCreated;
+
+ /// Which gestures should be consumed by the web view.
+ ///
+ /// It is possible for other gesture recognizers to be competing with the web view on pointer
+ /// events, e.g if the web view is inside a [ListView] the [ListView] will want to handle
+ /// vertical drags. The web view will claim gestures that are recognized by any of the
+ /// recognizers on this list.
+ ///
+ /// When this set is empty or null, the web view will only handle pointer events for gestures that
+ /// were not claimed by any other gesture recognizer.
+ final Set>? gestureRecognizers;
+
+ /// The initial URL to load.
+ final String? initialUrl;
+
+ /// Whether Javascript execution is enabled.
+ final JavascriptMode javascriptMode;
+
+ /// The set of [JavascriptChannel]s available to JavaScript code running in the web view.
+ ///
+ /// For each [JavascriptChannel] in the set, a channel object is made available for the
+ /// JavaScript code in a window property named [JavascriptChannel.name].
+ /// The JavaScript code can then call `postMessage` on that object to send a message that will be
+ /// passed to [JavascriptChannel.onMessageReceived].
+ ///
+ /// For example for the following JavascriptChannel:
+ ///
+ /// ```dart
+ /// JavascriptChannel(name: 'Print', onMessageReceived: (JavascriptMessage message) { print(message.message); });
+ /// ```
+ ///
+ /// JavaScript code can call:
+ ///
+ /// ```javascript
+ /// Print.postMessage('Hello');
+ /// ```
+ ///
+ /// To asynchronously invoke the message handler which will print the message to standard output.
+ ///
+ /// Adding a new JavaScript channel only takes affect after the next page is loaded.
+ ///
+ /// Set values must not be null. A [JavascriptChannel.name] cannot be the same for multiple
+ /// channels in the list.
+ ///
+ /// A null value is equivalent to an empty set.
+ final Set? javascriptChannels;
+
+ /// A delegate function that decides how to handle navigation actions.
+ ///
+ /// When a navigation is initiated by the WebView (e.g when a user clicks a link)
+ /// this delegate is called and has to decide how to proceed with the navigation.
+ ///
+ /// See [NavigationDecision] for possible decisions the delegate can take.
+ ///
+ /// When null all navigation actions are allowed.
+ ///
+ /// Caveats on Android:
+ ///
+ /// * Navigation actions targeted to the main frame can be intercepted,
+ /// navigation actions targeted to subframes are allowed regardless of the value
+ /// returned by this delegate.
+ /// * Setting a navigationDelegate makes the WebView treat all navigations as if they were
+ /// triggered by a user gesture, this disables some of Chromium's security mechanisms.
+ /// A navigationDelegate should only be set when loading trusted content.
+ /// * On Android WebView versions earlier than 67(most devices running at least Android L+ should have
+ /// a later version):
+ /// * When a navigationDelegate is set pages with frames are not properly handled by the
+ /// webview, and frames will be opened in the main frame.
+ /// * When a navigationDelegate is set HTTP requests do not include the HTTP referer header.
+ final NavigationDelegate? navigationDelegate;
+
+ /// Controls whether inline playback of HTML5 videos is allowed on iOS.
+ ///
+ /// This field is ignored on Android because Android allows it by default.
+ ///
+ /// By default `allowsInlineMediaPlayback` is false.
+ final bool allowsInlineMediaPlayback;
+
+ /// Invoked when a page starts loading.
+ final PageStartedCallback? onPageStarted;
+
+ /// Invoked when a page has finished loading.
+ ///
+ /// This is invoked only for the main frame.
+ ///
+ /// When [onPageFinished] is invoked on Android, the page being rendered may
+ /// not be updated yet.
+ ///
+ /// When invoked on iOS or Android, any Javascript code that is embedded
+ /// directly in the HTML has been loaded and code injected with
+ /// [WebViewController.evaluateJavascript] can assume this.
+ final PageFinishedCallback? onPageFinished;
+
+ /// Invoked when a page is loading.
+ final PageLoadingCallback? onProgress;
+
+ /// Invoked when a web resource has failed to load.
+ ///
+ /// This callback is only called for the main page.
+ final WebResourceErrorCallback? onWebResourceError;
+
+ /// Controls whether WebView debugging is enabled.
+ ///
+ /// Setting this to true enables [WebView debugging on Android](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/).
+ ///
+ /// WebView debugging is enabled by default in dev builds on iOS.
+ ///
+ /// To debug WebViews on iOS:
+ /// - Enable developer options (Open Safari, go to Preferences -> Advanced and make sure "Show Develop Menu in Menubar" is on.)
+ /// - From the Menu-bar (of Safari) select Develop -> iPhone Simulator ->
+ ///
+ /// By default `debuggingEnabled` is false.
+ final bool debuggingEnabled;
+
+ /// A Boolean value indicating whether horizontal swipe gestures will trigger back-forward list navigations.
+ ///
+ /// This only works on iOS.
+ ///
+ /// By default `gestureNavigationEnabled` is false.
+ final bool gestureNavigationEnabled;
+
+ /// The value used for the HTTP User-Agent: request header.
+ ///
+ /// When null the platform's webview default is used for the User-Agent header.
+ ///
+ /// When the [WebView] is rebuilt with a different `userAgent`, the page reloads and the request uses the new User Agent.
+ ///
+ /// When [WebViewController.goBack] is called after changing `userAgent` the previous `userAgent` value is used until the page is reloaded.
+ ///
+ /// This field is ignored on iOS versions prior to 9 as the platform does not support a custom
+ /// user agent.
+ ///
+ /// By default `userAgent` is null.
+ final String? userAgent;
+
+ /// Which restrictions apply on automatic media playback.
+ ///
+ /// This initial value is applied to the platform's webview upon creation. Any following
+ /// changes to this parameter are ignored (as long as the state of the [WebView] is preserved).
+ ///
+ /// The default policy is [AutoMediaPlaybackPolicy.require_user_action_for_all_media_types].
+ final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy;
+
+ @override
+ State createState() => _WebViewState();
+}
+
+class _WebViewState extends State {
+ final Completer _controller =
+ Completer();
+
+ late JavascriptChannelRegistry _javascriptChannelRegistry;
+ late _PlatformCallbacksHandler _platformCallbacksHandler;
+
+ @override
+ Widget build(BuildContext context) {
+ return WebView.platform.build(
+ context: context,
+ onWebViewPlatformCreated: _onWebViewPlatformCreated,
+ webViewPlatformCallbacksHandler: _platformCallbacksHandler,
+ javascriptChannelRegistry: _javascriptChannelRegistry,
+ gestureRecognizers: widget.gestureRecognizers,
+ creationParams: _creationParamsfromWidget(widget),
+ );
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ _assertJavascriptChannelNamesAreUnique();
+ _platformCallbacksHandler = _PlatformCallbacksHandler(widget);
+ _javascriptChannelRegistry =
+ JavascriptChannelRegistry(widget.javascriptChannels);
+ }
+
+ @override
+ void didUpdateWidget(WebView oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ _assertJavascriptChannelNamesAreUnique();
+ _controller.future.then((WebViewController controller) {
+ _platformCallbacksHandler._widget = widget;
+ controller._updateWidget(widget);
+ });
+ }
+
+ void _onWebViewPlatformCreated(WebViewPlatformController? webViewPlatform) {
+ final WebViewController controller = WebViewController._(
+ widget,
+ webViewPlatform!,
+ _javascriptChannelRegistry,
+ );
+ _controller.complete(controller);
+ if (widget.onWebViewCreated != null) {
+ widget.onWebViewCreated!(controller);
+ }
+ }
+
+ void _assertJavascriptChannelNamesAreUnique() {
+ if (widget.javascriptChannels == null ||
+ widget.javascriptChannels!.isEmpty) {
+ return;
+ }
+ assert(_extractChannelNames(widget.javascriptChannels).length ==
+ widget.javascriptChannels!.length);
+ }
+}
+
+CreationParams _creationParamsfromWidget(WebView widget) {
+ return CreationParams(
+ initialUrl: widget.initialUrl,
+ webSettings: _webSettingsFromWidget(widget),
+ javascriptChannelNames: _extractChannelNames(widget.javascriptChannels),
+ userAgent: widget.userAgent,
+ autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy,
+ );
+}
+
+WebSettings _webSettingsFromWidget(WebView widget) {
+ return WebSettings(
+ javascriptMode: widget.javascriptMode,
+ hasNavigationDelegate: widget.navigationDelegate != null,
+ hasProgressTracking: widget.onProgress != null,
+ debuggingEnabled: widget.debuggingEnabled,
+ gestureNavigationEnabled: widget.gestureNavigationEnabled,
+ allowsInlineMediaPlayback: widget.allowsInlineMediaPlayback,
+ userAgent: WebSetting.of(widget.userAgent),
+ );
+}
+
+// This method assumes that no fields in `currentValue` are null.
+WebSettings _clearUnchangedWebSettings(
+ WebSettings currentValue, WebSettings newValue) {
+ assert(currentValue.javascriptMode != null);
+ assert(currentValue.hasNavigationDelegate != null);
+ assert(currentValue.hasProgressTracking != null);
+ assert(currentValue.debuggingEnabled != null);
+ assert(currentValue.userAgent != null);
+ assert(newValue.javascriptMode != null);
+ assert(newValue.hasNavigationDelegate != null);
+ assert(newValue.debuggingEnabled != null);
+ assert(newValue.userAgent != null);
+
+ JavascriptMode? javascriptMode;
+ bool? hasNavigationDelegate;
+ bool? hasProgressTracking;
+ bool? debuggingEnabled;
+ WebSetting userAgent = WebSetting.absent();
+ if (currentValue.javascriptMode != newValue.javascriptMode) {
+ javascriptMode = newValue.javascriptMode;
+ }
+ if (currentValue.hasNavigationDelegate != newValue.hasNavigationDelegate) {
+ hasNavigationDelegate = newValue.hasNavigationDelegate;
+ }
+ if (currentValue.hasProgressTracking != newValue.hasProgressTracking) {
+ hasProgressTracking = newValue.hasProgressTracking;
+ }
+ if (currentValue.debuggingEnabled != newValue.debuggingEnabled) {
+ debuggingEnabled = newValue.debuggingEnabled;
+ }
+ if (currentValue.userAgent != newValue.userAgent) {
+ userAgent = newValue.userAgent;
+ }
+
+ return WebSettings(
+ javascriptMode: javascriptMode,
+ hasNavigationDelegate: hasNavigationDelegate,
+ hasProgressTracking: hasProgressTracking,
+ debuggingEnabled: debuggingEnabled,
+ userAgent: userAgent,
+ );
+}
+
+Set _extractChannelNames(Set? channels) {
+ final Set channelNames = channels == null
+ ? {}
+ : channels.map((JavascriptChannel channel) => channel.name).toSet();
+ return channelNames;
+}
+
+class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler {
+ _PlatformCallbacksHandler(this._widget);
+
+ WebView _widget;
+
+ @override
+ FutureOr onNavigationRequest({
+ required String url,
+ required bool isForMainFrame,
+ }) async {
+ final NavigationRequest request =
+ NavigationRequest._(url: url, isForMainFrame: isForMainFrame);
+ final bool allowNavigation = _widget.navigationDelegate == null ||
+ await _widget.navigationDelegate!(request) ==
+ NavigationDecision.navigate;
+ return allowNavigation;
+ }
+
+ @override
+ void onPageStarted(String url) {
+ if (_widget.onPageStarted != null) {
+ _widget.onPageStarted!(url);
+ }
+ }
+
+ @override
+ void onPageFinished(String url) {
+ if (_widget.onPageFinished != null) {
+ _widget.onPageFinished!(url);
+ }
+ }
+
+ @override
+ void onProgress(int progress) {
+ if (_widget.onProgress != null) {
+ _widget.onProgress!(progress);
+ }
+ }
+
+ void onWebResourceError(WebResourceError error) {
+ if (_widget.onWebResourceError != null) {
+ _widget.onWebResourceError!(error);
+ }
+ }
+}
+
+/// Controls a [WebView].
+///
+/// A [WebViewController] instance can be obtained by setting the [WebView.onWebViewCreated]
+/// callback for a [WebView] widget.
+class WebViewController {
+ WebViewController._(
+ this._widget,
+ this._webViewPlatformController,
+ this._javascriptChannelRegistry,
+ ) : assert(_webViewPlatformController != null) {
+ _settings = _webSettingsFromWidget(_widget);
+ }
+
+ final WebViewPlatformController _webViewPlatformController;
+ final JavascriptChannelRegistry _javascriptChannelRegistry;
+
+ late WebSettings _settings;
+
+ WebView _widget;
+
+ /// Loads the specified URL.
+ ///
+ /// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will
+ /// be added as key value pairs of HTTP headers for the request.
+ ///
+ /// `url` must not be null.
+ ///
+ /// Throws an ArgumentError if `url` is not a valid URL string.
+ Future loadUrl(
+ String url, {
+ Map? headers,
+ }) async {
+ assert(url != null);
+ _validateUrlString(url);
+ return _webViewPlatformController.loadUrl(url, headers);
+ }
+
+ /// Accessor to the current URL that the WebView is displaying.
+ ///
+ /// If [WebView.initialUrl] was never specified, returns `null`.
+ /// Note that this operation is asynchronous, and it is possible that the
+ /// current URL changes again by the time this function returns (in other
+ /// words, by the time this future completes, the WebView may be displaying a
+ /// different URL).
+ Future currentUrl() {
+ return _webViewPlatformController.currentUrl();
+ }
+
+ /// Checks whether there's a back history item.
+ ///
+ /// Note that this operation is asynchronous, and it is possible that the "canGoBack" state has
+ /// changed by the time the future completed.
+ Future canGoBack() {
+ return _webViewPlatformController.canGoBack();
+ }
+
+ /// Checks whether there's a forward history item.
+ ///
+ /// Note that this operation is asynchronous, and it is possible that the "canGoForward" state has
+ /// changed by the time the future completed.
+ Future canGoForward() {
+ return _webViewPlatformController.canGoForward();
+ }
+
+ /// Goes back in the history of this WebView.
+ ///
+ /// If there is no back history item this is a no-op.
+ Future goBack() {
+ return _webViewPlatformController.goBack();
+ }
+
+ /// Goes forward in the history of this WebView.
+ ///
+ /// If there is no forward history item this is a no-op.
+ Future goForward() {
+ return _webViewPlatformController.goForward();
+ }
+
+ /// Reloads the current URL.
+ Future reload() {
+ return _webViewPlatformController.reload();
+ }
+
+ /// Clears all caches used by the [WebView].
+ ///
+ /// The following caches are cleared:
+ /// 1. Browser HTTP Cache.
+ /// 2. [Cache API](https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api) caches.
+ /// These are not yet supported in iOS WkWebView. Service workers tend to use this cache.
+ /// 3. Application cache.
+ /// 4. Local Storage.
+ ///
+ /// Note: Calling this method also triggers a reload.
+ Future clearCache() async {
+ await _webViewPlatformController.clearCache();
+ return reload();
+ }
+
+ Future _updateJavascriptChannels(
+ Set? newChannels) async {
+ final Set currentChannels =
+ _javascriptChannelRegistry.channels.keys.toSet();
+ final Set newChannelNames = _extractChannelNames(newChannels);
+ final Set channelsToAdd =
+ newChannelNames.difference(currentChannels);
+ final Set channelsToRemove =
+ currentChannels.difference(newChannelNames);
+ if (channelsToRemove.isNotEmpty) {
+ await _webViewPlatformController
+ .removeJavascriptChannels(channelsToRemove);
+ }
+ if (channelsToAdd.isNotEmpty) {
+ await _webViewPlatformController.addJavascriptChannels(channelsToAdd);
+ }
+ _javascriptChannelRegistry.updateJavascriptChannelsFromSet(newChannels);
+ }
+
+ Future _updateWidget(WebView widget) async {
+ _widget = widget;
+ await _updateSettings(_webSettingsFromWidget(widget));
+ await _updateJavascriptChannels(widget.javascriptChannels);
+ }
+
+ Future _updateSettings(WebSettings newSettings) {
+ final WebSettings update =
+ _clearUnchangedWebSettings(_settings, newSettings);
+ _settings = newSettings;
+ return _webViewPlatformController.updateSettings(update);
+ }
+
+ /// Evaluates a JavaScript expression in the context of the current page.
+ ///
+ /// On Android returns the evaluation result as a JSON formatted string.
+ ///
+ /// On iOS depending on the value type the return value would be one of:
+ ///
+ /// - For primitive JavaScript types: the value string formatted (e.g JavaScript 100 returns '100').
+ /// - For JavaScript arrays of supported types: a string formatted NSArray(e.g '(1,2,3), note that the string for NSArray is formatted and might contain newlines and extra spaces.').
+ /// - Other non-primitive types are not supported on iOS and will complete the Future with an error.
+ ///
+ /// The Future completes with an error if a JavaScript error occurred, or on iOS, if the type of the
+ /// evaluated expression is not supported as described above.
+ ///
+ /// When evaluating Javascript in a [WebView], it is best practice to wait for
+ /// the [WebView.onPageFinished] callback. This guarantees all the Javascript
+ /// embedded in the main frame HTML has been loaded.
+ Future evaluateJavascript(String javascriptString) {
+ if (_settings.javascriptMode == JavascriptMode.disabled) {
+ return Future.error(FlutterError(
+ 'JavaScript mode must be enabled/unrestricted when calling evaluateJavascript.'));
+ }
+ // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
+ // https://github.com/flutter/flutter/issues/26431
+ // ignore: strong_mode_implicit_dynamic_method
+ return _webViewPlatformController.evaluateJavascript(javascriptString);
+ }
+
+ /// Returns the title of the currently loaded page.
+ Future getTitle() {
+ return _webViewPlatformController.getTitle();
+ }
+
+ /// Sets the WebView's content scroll position.
+ ///
+ /// The parameters `x` and `y` specify the scroll position in WebView pixels.
+ Future scrollTo(int x, int y) {
+ return _webViewPlatformController.scrollTo(x, y);
+ }
+
+ /// Move the scrolled position of this view.
+ ///
+ /// The parameters `x` and `y` specify the amount of WebView pixels to scroll by horizontally and vertically respectively.
+ Future scrollBy(int x, int y) {
+ return _webViewPlatformController.scrollBy(x, y);
+ }
+
+ /// Return the horizontal scroll position, in WebView pixels, of this view.
+ ///
+ /// Scroll position is measured from left.
+ Future getScrollX() {
+ return _webViewPlatformController.getScrollX();
+ }
+
+ /// Return the vertical scroll position, in WebView pixels, of this view.
+ ///
+ /// Scroll position is measured from top.
+ Future getScrollY() {
+ return _webViewPlatformController.getScrollY();
+ }
+}
+
+/// Manages cookies pertaining to all [WebView]s.
+class CookieManager {
+ /// Creates a [CookieManager] -- returns the instance if it's already been called.
+ factory CookieManager() {
+ return _instance ??= CookieManager._();
+ }
+
+ CookieManager._();
+
+ static CookieManager? _instance;
+
+ /// Clears all cookies for all [WebView] instances.
+ ///
+ /// This is a no op on iOS version smaller than 9.
+ ///
+ /// Returns true if cookies were present before clearing, else false.
+ Future clearCookies() => WebView.platform.clearCookies();
+}
+
+// Throws an ArgumentError if `url` is not a valid URL string.
+void _validateUrlString(String url) {
+ try {
+ final Uri uri = Uri.parse(url);
+ if (uri.scheme.isEmpty) {
+ throw ArgumentError('Missing scheme in URL string: "$url"');
+ }
+ } on FormatException catch (e) {
+ throw ArgumentError(e);
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview_android.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_android.dart
deleted file mode 100644
index ca1440d69929..000000000000
--- a/packages/webview_flutter/webview_flutter/lib/src/webview_android.dart
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter/widgets.dart';
-
-import '../platform_interface.dart';
-import 'webview_method_channel.dart';
-
-/// Builds an Android webview.
-///
-/// This is used as the default implementation for [WebView.platform] on Android. It uses
-/// an [AndroidView] to embed the webview in the widget hierarchy, and uses a method channel to
-/// communicate with the platform code.
-class AndroidWebView implements WebViewPlatform {
- @override
- Widget build({
- required BuildContext context,
- required CreationParams creationParams,
- required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
- WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
- Set>? gestureRecognizers,
- }) {
- assert(webViewPlatformCallbacksHandler != null);
- return GestureDetector(
- // We prevent text selection by intercepting the long press event.
- // This is a temporary stop gap due to issues with text selection on Android:
- // https://github.com/flutter/flutter/issues/24585 - the text selection
- // dialog is not responding to touch events.
- // https://github.com/flutter/flutter/issues/24584 - the text selection
- // handles are not showing.
- // TODO(amirh): remove this when the issues above are fixed.
- onLongPress: () {},
- excludeFromSemantics: true,
- child: AndroidView(
- viewType: 'plugins.flutter.io/webview',
- onPlatformViewCreated: (int id) {
- if (onWebViewPlatformCreated == null) {
- return;
- }
- onWebViewPlatformCreated(MethodChannelWebViewPlatform(
- id, webViewPlatformCallbacksHandler));
- },
- gestureRecognizers: gestureRecognizers,
- layoutDirection: Directionality.maybeOf(context) ?? TextDirection.rtl,
- creationParams:
- MethodChannelWebViewPlatform.creationParamsToMap(creationParams),
- creationParamsCodec: const StandardMessageCodec(),
- ),
- );
- }
-
- @override
- Future clearCookies() => MethodChannelWebViewPlatform.clearCookies();
-}
diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview_cupertino.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_cupertino.dart
deleted file mode 100644
index 8d4be3800a28..000000000000
--- a/packages/webview_flutter/webview_flutter/lib/src/webview_cupertino.dart
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter/widgets.dart';
-
-import '../platform_interface.dart';
-import 'webview_method_channel.dart';
-
-/// Builds an iOS webview.
-///
-/// This is used as the default implementation for [WebView.platform] on iOS. It uses
-/// a [UiKitView] to embed the webview in the widget hierarchy, and uses a method channel to
-/// communicate with the platform code.
-class CupertinoWebView implements WebViewPlatform {
- @override
- Widget build({
- required BuildContext context,
- required CreationParams creationParams,
- required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
- WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
- Set>? gestureRecognizers,
- }) {
- return UiKitView(
- viewType: 'plugins.flutter.io/webview',
- onPlatformViewCreated: (int id) {
- if (onWebViewPlatformCreated == null) {
- return;
- }
- onWebViewPlatformCreated(
- MethodChannelWebViewPlatform(id, webViewPlatformCallbacksHandler));
- },
- gestureRecognizers: gestureRecognizers,
- creationParams:
- MethodChannelWebViewPlatform.creationParamsToMap(creationParams),
- creationParamsCodec: const StandardMessageCodec(),
- );
- }
-
- @override
- Future clearCookies() => MethodChannelWebViewPlatform.clearCookies();
-}
diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview_method_channel.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_method_channel.dart
deleted file mode 100644
index 05831a9d8794..000000000000
--- a/packages/webview_flutter/webview_flutter/lib/src/webview_method_channel.dart
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:flutter/services.dart';
-
-import '../platform_interface.dart';
-
-/// A [WebViewPlatformController] that uses a method channel to control the webview.
-class MethodChannelWebViewPlatform implements WebViewPlatformController {
- /// Constructs an instance that will listen for webviews broadcasting to the
- /// given [id], using the given [WebViewPlatformCallbacksHandler].
- MethodChannelWebViewPlatform(int id, this._platformCallbacksHandler)
- : assert(_platformCallbacksHandler != null),
- _channel = MethodChannel('plugins.flutter.io/webview_$id') {
- _channel.setMethodCallHandler(_onMethodCall);
- }
-
- final WebViewPlatformCallbacksHandler _platformCallbacksHandler;
-
- final MethodChannel _channel;
-
- static const MethodChannel _cookieManagerChannel =
- MethodChannel('plugins.flutter.io/cookie_manager');
-
- Future _onMethodCall(MethodCall call) async {
- switch (call.method) {
- case 'javascriptChannelMessage':
- final String channel = call.arguments['channel']!;
- final String message = call.arguments['message']!;
- _platformCallbacksHandler.onJavaScriptChannelMessage(channel, message);
- return true;
- case 'navigationRequest':
- return await _platformCallbacksHandler.onNavigationRequest(
- url: call.arguments['url']!,
- isForMainFrame: call.arguments['isForMainFrame']!,
- );
- case 'onPageFinished':
- _platformCallbacksHandler.onPageFinished(call.arguments['url']!);
- return null;
- case 'onProgress':
- _platformCallbacksHandler.onProgress(call.arguments['progress']);
- return null;
- case 'onPageStarted':
- _platformCallbacksHandler.onPageStarted(call.arguments['url']!);
- return null;
- case 'onWebResourceError':
- _platformCallbacksHandler.onWebResourceError(
- WebResourceError(
- errorCode: call.arguments['errorCode']!,
- description: call.arguments['description']!,
- // iOS doesn't support `failingUrl`.
- failingUrl: call.arguments['failingUrl'],
- domain: call.arguments['domain'],
- errorType: call.arguments['errorType'] == null
- ? null
- : WebResourceErrorType.values.firstWhere(
- (WebResourceErrorType type) {
- return type.toString() ==
- '$WebResourceErrorType.${call.arguments['errorType']}';
- },
- ),
- ),
- );
- return null;
- }
-
- throw MissingPluginException(
- '${call.method} was invoked but has no handler',
- );
- }
-
- @override
- Future loadUrl(
- String url,
- Map? headers,
- ) async {
- assert(url != null);
- return _channel.invokeMethod('loadUrl', {
- 'url': url,
- 'headers': headers,
- });
- }
-
- @override
- Future currentUrl() => _channel.invokeMethod('currentUrl');
-
- @override
- Future canGoBack() =>
- _channel.invokeMethod("canGoBack").then((result) => result!);
-
- @override
- Future canGoForward() =>
- _channel.invokeMethod("canGoForward").then((result) => result!);
-
- @override
- Future goBack() => _channel.invokeMethod("goBack");
-
- @override
- Future goForward() => _channel.invokeMethod("goForward");
-
- @override
- Future reload() => _channel.invokeMethod("reload");
-
- @override
- Future clearCache() => _channel.invokeMethod("clearCache");
-
- @override
- Future updateSettings(WebSettings settings) async {
- final Map updatesMap = _webSettingsToMap(settings);
- if (updatesMap.isNotEmpty) {
- await _channel.invokeMethod('updateSettings', updatesMap);
- }
- }
-
- @override
- Future evaluateJavascript(String javascriptString) {
- return _channel
- .invokeMethod('evaluateJavascript', javascriptString)
- .then((result) => result!);
- }
-
- @override
- Future addJavascriptChannels(Set javascriptChannelNames) {
- return _channel.invokeMethod(
- 'addJavascriptChannels', javascriptChannelNames.toList());
- }
-
- @override
- Future removeJavascriptChannels(Set javascriptChannelNames) {
- return _channel.invokeMethod(
- 'removeJavascriptChannels', javascriptChannelNames.toList());
- }
-
- @override
- Future getTitle() => _channel.invokeMethod("getTitle");
-
- @override
- Future scrollTo(int x, int y) {
- return _channel.invokeMethod('scrollTo', {
- 'x': x,
- 'y': y,
- });
- }
-
- @override
- Future scrollBy(int x, int y) {
- return _channel.invokeMethod('scrollBy', {
- 'x': x,
- 'y': y,
- });
- }
-
- @override
- Future getScrollX() =>
- _channel.invokeMethod("getScrollX").then((result) => result!);
-
- @override
- Future getScrollY() =>
- _channel.invokeMethod("getScrollY").then((result) => result!);
-
- /// Method channel implementation for [WebViewPlatform.clearCookies].
- static Future clearCookies() {
- return _cookieManagerChannel
- .invokeMethod('clearCookies')
- .then((dynamic result) => result!);
- }
-
- static Map _webSettingsToMap(WebSettings? settings) {
- final Map map = {};
- void _addIfNonNull(String key, dynamic value) {
- if (value == null) {
- return;
- }
- map[key] = value;
- }
-
- void _addSettingIfPresent(String key, WebSetting setting) {
- if (!setting.isPresent) {
- return;
- }
- map[key] = setting.value;
- }
-
- _addIfNonNull('jsMode', settings!.javascriptMode?.index);
- _addIfNonNull('hasNavigationDelegate', settings.hasNavigationDelegate);
- _addIfNonNull('hasProgressTracking', settings.hasProgressTracking);
- _addIfNonNull('debuggingEnabled', settings.debuggingEnabled);
- _addIfNonNull(
- 'gestureNavigationEnabled', settings.gestureNavigationEnabled);
- _addIfNonNull(
- 'allowsInlineMediaPlayback', settings.allowsInlineMediaPlayback);
- _addSettingIfPresent('userAgent', settings.userAgent);
- return map;
- }
-
- /// Converts a [CreationParams] object to a map as expected by `platform_views` channel.
- ///
- /// This is used for the `creationParams` argument of the platform views created by
- /// [AndroidWebViewBuilder] and [CupertinoWebViewBuilder].
- static Map creationParamsToMap(
- CreationParams creationParams, {
- bool usesHybridComposition = false,
- }) {
- return {
- 'initialUrl': creationParams.initialUrl,
- 'settings': _webSettingsToMap(creationParams.webSettings),
- 'javascriptChannelNames': creationParams.javascriptChannelNames.toList(),
- 'userAgent': creationParams.userAgent,
- 'autoMediaPlaybackPolicy': creationParams.autoMediaPlaybackPolicy.index,
- 'usesHybridComposition': usesHybridComposition,
- };
- }
-}
diff --git a/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart
index 398ac876bf3e..ba38771e5107 100644
--- a/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart
+++ b/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart
@@ -2,833 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:async';
-import 'dart:io';
+export 'package:webview_flutter_android/webview_android.dart';
+export 'package:webview_flutter_android/webview_surface_android.dart';
+export 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/rendering.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter/widgets.dart';
-
-import 'platform_interface.dart';
-import 'src/webview_android.dart';
-import 'src/webview_cupertino.dart';
-import 'src/webview_method_channel.dart';
-
-/// Optional callback invoked when a web view is first created. [controller] is
-/// the [WebViewController] for the created web view.
-typedef void WebViewCreatedCallback(WebViewController controller);
-
-/// Describes the state of JavaScript support in a given web view.
-enum JavascriptMode {
- /// JavaScript execution is disabled.
- disabled,
-
- /// JavaScript execution is not restricted.
- unrestricted,
-}
-
-/// A message that was sent by JavaScript code running in a [WebView].
-class JavascriptMessage {
- /// Constructs a JavaScript message object.
- ///
- /// The `message` parameter must not be null.
- const JavascriptMessage(this.message) : assert(message != null);
-
- /// The contents of the message that was sent by the JavaScript code.
- final String message;
-}
-
-/// Callback type for handling messages sent from Javascript running in a web view.
-typedef void JavascriptMessageHandler(JavascriptMessage message);
-
-/// Information about a navigation action that is about to be executed.
-class NavigationRequest {
- NavigationRequest._({required this.url, required this.isForMainFrame});
-
- /// The URL that will be loaded if the navigation is executed.
- final String url;
-
- /// Whether the navigation request is to be loaded as the main frame.
- final bool isForMainFrame;
-
- @override
- String toString() {
- return '$runtimeType(url: $url, isForMainFrame: $isForMainFrame)';
- }
-}
-
-/// A decision on how to handle a navigation request.
-enum NavigationDecision {
- /// Prevent the navigation from taking place.
- prevent,
-
- /// Allow the navigation to take place.
- navigate,
-}
-
-/// Android [WebViewPlatform] that uses [AndroidViewSurface] to build the [WebView] widget.
-///
-/// To use this, set [WebView.platform] to an instance of this class.
-///
-/// This implementation uses hybrid composition to render the [WebView] on
-/// Android. It solves multiple issues related to accessibility and interaction
-/// with the [WebView] at the cost of some performance on Android versions below
-/// 10. See https://github.com/flutter/flutter/wiki/Hybrid-Composition for more
-/// information.
-class SurfaceAndroidWebView extends AndroidWebView {
- @override
- Widget build({
- required BuildContext context,
- required CreationParams creationParams,
- WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
- Set>? gestureRecognizers,
- required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
- }) {
- assert(Platform.isAndroid);
- assert(webViewPlatformCallbacksHandler != null);
- return PlatformViewLink(
- viewType: 'plugins.flutter.io/webview',
- surfaceFactory: (
- BuildContext context,
- PlatformViewController controller,
- ) {
- return AndroidViewSurface(
- controller: controller as AndroidViewController,
- gestureRecognizers: gestureRecognizers ??
- const >{},
- hitTestBehavior: PlatformViewHitTestBehavior.opaque,
- );
- },
- onCreatePlatformView: (PlatformViewCreationParams params) {
- return PlatformViewsService.initSurfaceAndroidView(
- id: params.id,
- viewType: 'plugins.flutter.io/webview',
- // WebView content is not affected by the Android view's layout direction,
- // we explicitly set it here so that the widget doesn't require an ambient
- // directionality.
- layoutDirection: TextDirection.rtl,
- creationParams: MethodChannelWebViewPlatform.creationParamsToMap(
- creationParams,
- usesHybridComposition: true,
- ),
- creationParamsCodec: const StandardMessageCodec(),
- )
- ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
- ..addOnPlatformViewCreatedListener((int id) {
- if (onWebViewPlatformCreated == null) {
- return;
- }
- onWebViewPlatformCreated(
- MethodChannelWebViewPlatform(id, webViewPlatformCallbacksHandler),
- );
- })
- ..create();
- },
- );
- }
-}
-
-/// Decides how to handle a specific navigation request.
-///
-/// The returned [NavigationDecision] determines how the navigation described by
-/// `navigation` should be handled.
-///
-/// See also: [WebView.navigationDelegate].
-typedef FutureOr NavigationDelegate(
- NavigationRequest navigation);
-
-/// Signature for when a [WebView] has started loading a page.
-typedef void PageStartedCallback(String url);
-
-/// Signature for when a [WebView] has finished loading a page.
-typedef void PageFinishedCallback(String url);
-
-/// Signature for when a [WebView] is loading a page.
-typedef void PageLoadingCallback(int progress);
-
-/// Signature for when a [WebView] has failed to load a resource.
-typedef void WebResourceErrorCallback(WebResourceError error);
-
-/// Specifies possible restrictions on automatic media playback.
-///
-/// This is typically used in [WebView.initialMediaPlaybackPolicy].
-// The method channel implementation is marshalling this enum to the value's index, so the order
-// is important.
-enum AutoMediaPlaybackPolicy {
- /// Starting any kind of media playback requires a user action.
- ///
- /// For example: JavaScript code cannot start playing media unless the code was executed
- /// as a result of a user action (like a touch event).
- require_user_action_for_all_media_types,
-
- /// Starting any kind of media playback is always allowed.
- ///
- /// For example: JavaScript code that's triggered when the page is loaded can start playing
- /// video or audio.
- always_allow,
-}
-
-final RegExp _validChannelNames = RegExp('^[a-zA-Z_][a-zA-Z0-9_]*\$');
-
-/// A named channel for receiving messaged from JavaScript code running inside a web view.
-class JavascriptChannel {
- /// Constructs a Javascript channel.
- ///
- /// The parameters `name` and `onMessageReceived` must not be null.
- JavascriptChannel({
- required this.name,
- required this.onMessageReceived,
- }) : assert(name != null),
- assert(onMessageReceived != null),
- assert(_validChannelNames.hasMatch(name));
-
- /// The channel's name.
- ///
- /// Passing this channel object as part of a [WebView.javascriptChannels] adds a channel object to
- /// the Javascript window object's property named `name`.
- ///
- /// The name must start with a letter or underscore(_), followed by any combination of those
- /// characters plus digits.
- ///
- /// Note that any JavaScript existing `window` property with this name will be overriden.
- ///
- /// See also [WebView.javascriptChannels] for more details on the channel registration mechanism.
- final String name;
-
- /// A callback that's invoked when a message is received through the channel.
- final JavascriptMessageHandler onMessageReceived;
-}
-
-/// A web view widget for showing html content.
-///
-/// There is a known issue that on iOS 13.4 and 13.5, other flutter widgets covering
-/// the `WebView` is not able to block the `WebView` from receiving touch events.
-/// See https://github.com/flutter/flutter/issues/53490.
-class WebView extends StatefulWidget {
- /// Creates a new web view.
- ///
- /// The web view can be controlled using a `WebViewController` that is passed to the
- /// `onWebViewCreated` callback once the web view is created.
- ///
- /// The `javascriptMode` and `autoMediaPlaybackPolicy` parameters must not be null.
- const WebView({
- Key? key,
- this.onWebViewCreated,
- this.initialUrl,
- this.javascriptMode = JavascriptMode.disabled,
- this.javascriptChannels,
- this.navigationDelegate,
- this.gestureRecognizers,
- this.onPageStarted,
- this.onPageFinished,
- this.onProgress,
- this.onWebResourceError,
- this.debuggingEnabled = false,
- this.gestureNavigationEnabled = false,
- this.userAgent,
- this.initialMediaPlaybackPolicy =
- AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
- this.allowsInlineMediaPlayback = false,
- }) : assert(javascriptMode != null),
- assert(initialMediaPlaybackPolicy != null),
- assert(allowsInlineMediaPlayback != null),
- super(key: key);
-
- static WebViewPlatform? _platform;
-
- /// Sets a custom [WebViewPlatform].
- ///
- /// This property can be set to use a custom platform implementation for WebViews.
- ///
- /// Setting `platform` doesn't affect [WebView]s that were already created.
- ///
- /// The default value is [AndroidWebView] on Android and [CupertinoWebView] on iOS.
- static set platform(WebViewPlatform? platform) {
- _platform = platform;
- }
-
- /// The WebView platform that's used by this WebView.
- ///
- /// The default value is [AndroidWebView] on Android and [CupertinoWebView] on iOS.
- static WebViewPlatform get platform {
- if (_platform == null) {
- switch (defaultTargetPlatform) {
- case TargetPlatform.android:
- _platform = AndroidWebView();
- break;
- case TargetPlatform.iOS:
- _platform = CupertinoWebView();
- break;
- default:
- throw UnsupportedError(
- "Trying to use the default webview implementation for $defaultTargetPlatform but there isn't a default one");
- }
- }
- return _platform!;
- }
-
- /// If not null invoked once the web view is created.
- final WebViewCreatedCallback? onWebViewCreated;
-
- /// Which gestures should be consumed by the web view.
- ///
- /// It is possible for other gesture recognizers to be competing with the web view on pointer
- /// events, e.g if the web view is inside a [ListView] the [ListView] will want to handle
- /// vertical drags. The web view will claim gestures that are recognized by any of the
- /// recognizers on this list.
- ///
- /// When this set is empty or null, the web view will only handle pointer events for gestures that
- /// were not claimed by any other gesture recognizer.
- final Set>? gestureRecognizers;
-
- /// The initial URL to load.
- final String? initialUrl;
-
- /// Whether Javascript execution is enabled.
- final JavascriptMode javascriptMode;
-
- /// The set of [JavascriptChannel]s available to JavaScript code running in the web view.
- ///
- /// For each [JavascriptChannel] in the set, a channel object is made available for the
- /// JavaScript code in a window property named [JavascriptChannel.name].
- /// The JavaScript code can then call `postMessage` on that object to send a message that will be
- /// passed to [JavascriptChannel.onMessageReceived].
- ///
- /// For example for the following JavascriptChannel:
- ///
- /// ```dart
- /// JavascriptChannel(name: 'Print', onMessageReceived: (JavascriptMessage message) { print(message.message); });
- /// ```
- ///
- /// JavaScript code can call:
- ///
- /// ```javascript
- /// Print.postMessage('Hello');
- /// ```
- ///
- /// To asynchronously invoke the message handler which will print the message to standard output.
- ///
- /// Adding a new JavaScript channel only takes affect after the next page is loaded.
- ///
- /// Set values must not be null. A [JavascriptChannel.name] cannot be the same for multiple
- /// channels in the list.
- ///
- /// A null value is equivalent to an empty set.
- final Set? javascriptChannels;
-
- /// A delegate function that decides how to handle navigation actions.
- ///
- /// When a navigation is initiated by the WebView (e.g when a user clicks a link)
- /// this delegate is called and has to decide how to proceed with the navigation.
- ///
- /// See [NavigationDecision] for possible decisions the delegate can take.
- ///
- /// When null all navigation actions are allowed.
- ///
- /// Caveats on Android:
- ///
- /// * Navigation actions targeted to the main frame can be intercepted,
- /// navigation actions targeted to subframes are allowed regardless of the value
- /// returned by this delegate.
- /// * Setting a navigationDelegate makes the WebView treat all navigations as if they were
- /// triggered by a user gesture, this disables some of Chromium's security mechanisms.
- /// A navigationDelegate should only be set when loading trusted content.
- /// * On Android WebView versions earlier than 67(most devices running at least Android L+ should have
- /// a later version):
- /// * When a navigationDelegate is set pages with frames are not properly handled by the
- /// webview, and frames will be opened in the main frame.
- /// * When a navigationDelegate is set HTTP requests do not include the HTTP referer header.
- final NavigationDelegate? navigationDelegate;
-
- /// Controls whether inline playback of HTML5 videos is allowed on iOS.
- ///
- /// This field is ignored on Android because Android allows it by default.
- ///
- /// By default `allowsInlineMediaPlayback` is false.
- final bool allowsInlineMediaPlayback;
-
- /// Invoked when a page starts loading.
- final PageStartedCallback? onPageStarted;
-
- /// Invoked when a page has finished loading.
- ///
- /// This is invoked only for the main frame.
- ///
- /// When [onPageFinished] is invoked on Android, the page being rendered may
- /// not be updated yet.
- ///
- /// When invoked on iOS or Android, any Javascript code that is embedded
- /// directly in the HTML has been loaded and code injected with
- /// [WebViewController.evaluateJavascript] can assume this.
- final PageFinishedCallback? onPageFinished;
-
- /// Invoked when a page is loading.
- final PageLoadingCallback? onProgress;
-
- /// Invoked when a web resource has failed to load.
- ///
- /// This callback is only called for the main page.
- final WebResourceErrorCallback? onWebResourceError;
-
- /// Controls whether WebView debugging is enabled.
- ///
- /// Setting this to true enables [WebView debugging on Android](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/).
- ///
- /// WebView debugging is enabled by default in dev builds on iOS.
- ///
- /// To debug WebViews on iOS:
- /// - Enable developer options (Open Safari, go to Preferences -> Advanced and make sure "Show Develop Menu in Menubar" is on.)
- /// - From the Menu-bar (of Safari) select Develop -> iPhone Simulator ->
- ///
- /// By default `debuggingEnabled` is false.
- final bool debuggingEnabled;
-
- /// A Boolean value indicating whether horizontal swipe gestures will trigger back-forward list navigations.
- ///
- /// This only works on iOS.
- ///
- /// By default `gestureNavigationEnabled` is false.
- final bool gestureNavigationEnabled;
-
- /// The value used for the HTTP User-Agent: request header.
- ///
- /// When null the platform's webview default is used for the User-Agent header.
- ///
- /// When the [WebView] is rebuilt with a different `userAgent`, the page reloads and the request uses the new User Agent.
- ///
- /// When [WebViewController.goBack] is called after changing `userAgent` the previous `userAgent` value is used until the page is reloaded.
- ///
- /// This field is ignored on iOS versions prior to 9 as the platform does not support a custom
- /// user agent.
- ///
- /// By default `userAgent` is null.
- final String? userAgent;
-
- /// Which restrictions apply on automatic media playback.
- ///
- /// This initial value is applied to the platform's webview upon creation. Any following
- /// changes to this parameter are ignored (as long as the state of the [WebView] is preserved).
- ///
- /// The default policy is [AutoMediaPlaybackPolicy.require_user_action_for_all_media_types].
- final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy;
-
- @override
- State createState() => _WebViewState();
-}
-
-class _WebViewState extends State {
- final Completer _controller =
- Completer();
-
- late _PlatformCallbacksHandler _platformCallbacksHandler;
-
- @override
- Widget build(BuildContext context) {
- return WebView.platform.build(
- context: context,
- onWebViewPlatformCreated: _onWebViewPlatformCreated,
- webViewPlatformCallbacksHandler: _platformCallbacksHandler,
- gestureRecognizers: widget.gestureRecognizers,
- creationParams: _creationParamsfromWidget(widget),
- );
- }
-
- @override
- void initState() {
- super.initState();
- _assertJavascriptChannelNamesAreUnique();
- _platformCallbacksHandler = _PlatformCallbacksHandler(widget);
- }
-
- @override
- void didUpdateWidget(WebView oldWidget) {
- super.didUpdateWidget(oldWidget);
- _assertJavascriptChannelNamesAreUnique();
- _controller.future.then((WebViewController controller) {
- _platformCallbacksHandler._widget = widget;
- controller._updateWidget(widget);
- });
- }
-
- void _onWebViewPlatformCreated(WebViewPlatformController? webViewPlatform) {
- final WebViewController controller = WebViewController._(
- widget, webViewPlatform!, _platformCallbacksHandler);
- _controller.complete(controller);
- if (widget.onWebViewCreated != null) {
- widget.onWebViewCreated!(controller);
- }
- }
-
- void _assertJavascriptChannelNamesAreUnique() {
- if (widget.javascriptChannels == null ||
- widget.javascriptChannels!.isEmpty) {
- return;
- }
- assert(_extractChannelNames(widget.javascriptChannels).length ==
- widget.javascriptChannels!.length);
- }
-}
-
-CreationParams _creationParamsfromWidget(WebView widget) {
- return CreationParams(
- initialUrl: widget.initialUrl,
- webSettings: _webSettingsFromWidget(widget),
- javascriptChannelNames: _extractChannelNames(widget.javascriptChannels),
- userAgent: widget.userAgent,
- autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy,
- );
-}
-
-WebSettings _webSettingsFromWidget(WebView widget) {
- return WebSettings(
- javascriptMode: widget.javascriptMode,
- hasNavigationDelegate: widget.navigationDelegate != null,
- hasProgressTracking: widget.onProgress != null,
- debuggingEnabled: widget.debuggingEnabled,
- gestureNavigationEnabled: widget.gestureNavigationEnabled,
- allowsInlineMediaPlayback: widget.allowsInlineMediaPlayback,
- userAgent: WebSetting.of(widget.userAgent),
- );
-}
-
-// This method assumes that no fields in `currentValue` are null.
-WebSettings _clearUnchangedWebSettings(
- WebSettings currentValue, WebSettings newValue) {
- assert(currentValue.javascriptMode != null);
- assert(currentValue.hasNavigationDelegate != null);
- assert(currentValue.hasProgressTracking != null);
- assert(currentValue.debuggingEnabled != null);
- assert(currentValue.userAgent != null);
- assert(newValue.javascriptMode != null);
- assert(newValue.hasNavigationDelegate != null);
- assert(newValue.debuggingEnabled != null);
- assert(newValue.userAgent != null);
-
- JavascriptMode? javascriptMode;
- bool? hasNavigationDelegate;
- bool? hasProgressTracking;
- bool? debuggingEnabled;
- WebSetting userAgent = WebSetting.absent();
- if (currentValue.javascriptMode != newValue.javascriptMode) {
- javascriptMode = newValue.javascriptMode;
- }
- if (currentValue.hasNavigationDelegate != newValue.hasNavigationDelegate) {
- hasNavigationDelegate = newValue.hasNavigationDelegate;
- }
- if (currentValue.hasProgressTracking != newValue.hasProgressTracking) {
- hasProgressTracking = newValue.hasProgressTracking;
- }
- if (currentValue.debuggingEnabled != newValue.debuggingEnabled) {
- debuggingEnabled = newValue.debuggingEnabled;
- }
- if (currentValue.userAgent != newValue.userAgent) {
- userAgent = newValue.userAgent;
- }
-
- return WebSettings(
- javascriptMode: javascriptMode,
- hasNavigationDelegate: hasNavigationDelegate,
- hasProgressTracking: hasProgressTracking,
- debuggingEnabled: debuggingEnabled,
- userAgent: userAgent,
- );
-}
-
-Set _extractChannelNames(Set? channels) {
- final Set channelNames = channels == null
- ? {}
- : channels.map((JavascriptChannel channel) => channel.name).toSet();
- return channelNames;
-}
-
-class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler {
- _PlatformCallbacksHandler(this._widget) {
- _updateJavascriptChannelsFromSet(_widget.javascriptChannels);
- }
-
- WebView _widget;
-
- // Maps a channel name to a channel.
- final Map _javascriptChannels =
- {};
-
- @override
- void onJavaScriptChannelMessage(String channel, String message) {
- _javascriptChannels[channel]!.onMessageReceived(JavascriptMessage(message));
- }
-
- @override
- FutureOr onNavigationRequest({
- required String url,
- required bool isForMainFrame,
- }) async {
- final NavigationRequest request =
- NavigationRequest._(url: url, isForMainFrame: isForMainFrame);
- final bool allowNavigation = _widget.navigationDelegate == null ||
- await _widget.navigationDelegate!(request) ==
- NavigationDecision.navigate;
- return allowNavigation;
- }
-
- @override
- void onPageStarted(String url) {
- if (_widget.onPageStarted != null) {
- _widget.onPageStarted!(url);
- }
- }
-
- @override
- void onPageFinished(String url) {
- if (_widget.onPageFinished != null) {
- _widget.onPageFinished!(url);
- }
- }
-
- @override
- void onProgress(int progress) {
- if (_widget.onProgress != null) {
- _widget.onProgress!(progress);
- }
- }
-
- void onWebResourceError(WebResourceError error) {
- if (_widget.onWebResourceError != null) {
- _widget.onWebResourceError!(error);
- }
- }
-
- void _updateJavascriptChannelsFromSet(Set? channels) {
- _javascriptChannels.clear();
- if (channels == null) {
- return;
- }
- for (JavascriptChannel channel in channels) {
- _javascriptChannels[channel.name] = channel;
- }
- }
-}
-
-/// Controls a [WebView].
-///
-/// A [WebViewController] instance can be obtained by setting the [WebView.onWebViewCreated]
-/// callback for a [WebView] widget.
-class WebViewController {
- WebViewController._(
- this._widget,
- this._webViewPlatformController,
- this._platformCallbacksHandler,
- ) : assert(_webViewPlatformController != null) {
- _settings = _webSettingsFromWidget(_widget);
- }
-
- final WebViewPlatformController _webViewPlatformController;
-
- final _PlatformCallbacksHandler _platformCallbacksHandler;
-
- late WebSettings _settings;
-
- WebView _widget;
-
- /// Loads the specified URL.
- ///
- /// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will
- /// be added as key value pairs of HTTP headers for the request.
- ///
- /// `url` must not be null.
- ///
- /// Throws an ArgumentError if `url` is not a valid URL string.
- Future loadUrl(
- String url, {
- Map? headers,
- }) async {
- assert(url != null);
- _validateUrlString(url);
- return _webViewPlatformController.loadUrl(url, headers);
- }
-
- /// Accessor to the current URL that the WebView is displaying.
- ///
- /// If [WebView.initialUrl] was never specified, returns `null`.
- /// Note that this operation is asynchronous, and it is possible that the
- /// current URL changes again by the time this function returns (in other
- /// words, by the time this future completes, the WebView may be displaying a
- /// different URL).
- Future currentUrl() {
- return _webViewPlatformController.currentUrl();
- }
-
- /// Checks whether there's a back history item.
- ///
- /// Note that this operation is asynchronous, and it is possible that the "canGoBack" state has
- /// changed by the time the future completed.
- Future canGoBack() {
- return _webViewPlatformController.canGoBack();
- }
-
- /// Checks whether there's a forward history item.
- ///
- /// Note that this operation is asynchronous, and it is possible that the "canGoForward" state has
- /// changed by the time the future completed.
- Future canGoForward() {
- return _webViewPlatformController.canGoForward();
- }
-
- /// Goes back in the history of this WebView.
- ///
- /// If there is no back history item this is a no-op.
- Future goBack() {
- return _webViewPlatformController.goBack();
- }
-
- /// Goes forward in the history of this WebView.
- ///
- /// If there is no forward history item this is a no-op.
- Future goForward() {
- return _webViewPlatformController.goForward();
- }
-
- /// Reloads the current URL.
- Future reload() {
- return _webViewPlatformController.reload();
- }
-
- /// Clears all caches used by the [WebView].
- ///
- /// The following caches are cleared:
- /// 1. Browser HTTP Cache.
- /// 2. [Cache API](https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api) caches.
- /// These are not yet supported in iOS WkWebView. Service workers tend to use this cache.
- /// 3. Application cache.
- /// 4. Local Storage.
- ///
- /// Note: Calling this method also triggers a reload.
- Future clearCache() async {
- await _webViewPlatformController.clearCache();
- return reload();
- }
-
- Future _updateWidget(WebView widget) async {
- _widget = widget;
- await _updateSettings(_webSettingsFromWidget(widget));
- await _updateJavascriptChannels(widget.javascriptChannels);
- }
-
- Future _updateSettings(WebSettings newSettings) {
- final WebSettings update =
- _clearUnchangedWebSettings(_settings, newSettings);
- _settings = newSettings;
- return _webViewPlatformController.updateSettings(update);
- }
-
- Future _updateJavascriptChannels(
- Set? newChannels) async {
- final Set currentChannels =
- _platformCallbacksHandler._javascriptChannels.keys.toSet();
- final Set newChannelNames = _extractChannelNames(newChannels);
- final Set channelsToAdd =
- newChannelNames.difference(currentChannels);
- final Set channelsToRemove =
- currentChannels.difference(newChannelNames);
- if (channelsToRemove.isNotEmpty) {
- await _webViewPlatformController
- .removeJavascriptChannels(channelsToRemove);
- }
- if (channelsToAdd.isNotEmpty) {
- await _webViewPlatformController.addJavascriptChannels(channelsToAdd);
- }
- _platformCallbacksHandler._updateJavascriptChannelsFromSet(newChannels);
- }
-
- /// Evaluates a JavaScript expression in the context of the current page.
- ///
- /// On Android returns the evaluation result as a JSON formatted string.
- ///
- /// On iOS depending on the value type the return value would be one of:
- ///
- /// - For primitive JavaScript types: the value string formatted (e.g JavaScript 100 returns '100').
- /// - For JavaScript arrays of supported types: a string formatted NSArray(e.g '(1,2,3), note that the string for NSArray is formatted and might contain newlines and extra spaces.').
- /// - Other non-primitive types are not supported on iOS and will complete the Future with an error.
- ///
- /// The Future completes with an error if a JavaScript error occurred, or on iOS, if the type of the
- /// evaluated expression is not supported as described above.
- ///
- /// When evaluating Javascript in a [WebView], it is best practice to wait for
- /// the [WebView.onPageFinished] callback. This guarantees all the Javascript
- /// embedded in the main frame HTML has been loaded.
- Future evaluateJavascript(String javascriptString) {
- if (_settings.javascriptMode == JavascriptMode.disabled) {
- return Future.error(FlutterError(
- 'JavaScript mode must be enabled/unrestricted when calling evaluateJavascript.'));
- }
- // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
- // https://github.com/flutter/flutter/issues/26431
- // ignore: strong_mode_implicit_dynamic_method
- return _webViewPlatformController.evaluateJavascript(javascriptString);
- }
-
- /// Returns the title of the currently loaded page.
- Future getTitle() {
- return _webViewPlatformController.getTitle();
- }
-
- /// Sets the WebView's content scroll position.
- ///
- /// The parameters `x` and `y` specify the scroll position in WebView pixels.
- Future scrollTo(int x, int y) {
- return _webViewPlatformController.scrollTo(x, y);
- }
-
- /// Move the scrolled position of this view.
- ///
- /// The parameters `x` and `y` specify the amount of WebView pixels to scroll by horizontally and vertically respectively.
- Future