blob: f2e305fe85c5924511aac559e1ae440763cd37a4 [file] [log] [blame]
// 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.
// TODO(bparrishMines): Replace unused callback methods in constructors with
// variables once automatic garbage collection is fully implemented. See
// https://github.com/flutter/flutter/issues/107199.
// ignore_for_file: avoid_unused_constructor_parameters
// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
// ignore: unnecessary_import
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart' show AndroidViewSurface;
import 'android_webview.pigeon.dart';
import 'android_webview_api_impls.dart';
import 'instance_manager.dart';
/// Root of the Java class hierarchy.
///
/// See https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html.
abstract class JavaObject with Copyable {
/// Constructs a [JavaObject] without creating the associated Java object.
///
/// This should only be used by subclasses created by this library or to
/// create copies.
JavaObject.detached();
/// Global instance of [InstanceManager].
static final InstanceManager globalInstanceManager = InstanceManager(
onWeakReferenceRemoved: (_) {},
);
}
/// An Android View that displays web pages.
///
/// **Basic usage**
/// In most cases, we recommend using a standard web browser, like Chrome, to
/// deliver content to the user. To learn more about web browsers, read the
/// guide on invoking a browser with
/// [url_launcher](https://pub.dev/packages/url_launcher).
///
/// WebView objects allow you to display web content as part of your widget
/// layout, but lack some of the features of fully-developed browsers. A WebView
/// is useful when you need increased control over the UI and advanced
/// configuration options that will allow you to embed web pages in a
/// specially-designed environment for your app.
///
/// To learn more about WebView and alternatives for serving web content, read
/// the documentation on
/// [Web-based content](https://developer.android.com/guide/webapps).
///
/// When a [WebView] is no longer needed [release] must be called.
class WebView extends JavaObject {
/// Constructs a new WebView.
WebView({this.useHybridComposition = false}) : super.detached() {
api.createFromInstance(this);
}
/// Constructs a [WebView] without creating the associated Java object.
///
/// This should only be used by subclasses created by this library or to
/// create copies.
WebView.detached({this.useHybridComposition = false}) : super.detached();
/// Pigeon Host Api implementation for [WebView].
@visibleForTesting
static WebViewHostApiImpl api = WebViewHostApiImpl();
WebViewClient? _currentWebViewClient;
/// Whether the [WebView] will be rendered with an [AndroidViewSurface].
///
/// This implementation uses hybrid composition to render the WebView Widget.
/// This comes at the cost of some performance on Android versions below 10.
/// See
/// https://flutter.dev/docs/development/platform-integration/platform-views#performance
/// for more information.
///
/// Defaults to false.
final bool useHybridComposition;
/// The [WebSettings] object used to control the settings for this WebView.
late final WebSettings settings = WebSettings(this);
/// Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application.
///
/// This flag can be enabled in order to facilitate debugging of web layouts
/// and JavaScript code running inside WebViews. Please refer to [WebView]
/// documentation for the debugging guide. The default is false.
static Future<void> setWebContentsDebuggingEnabled(bool enabled) {
return api.setWebContentsDebuggingEnabled(enabled);
}
/// Loads the given data into this WebView using a 'data' scheme URL.
///
/// Note that JavaScript's same origin policy means that script running in a
/// page loaded using this method will be unable to access content loaded
/// using any scheme other than 'data', including 'http(s)'. To avoid this
/// restriction, use [loadDataWithBaseURL()] with an appropriate base URL.
///
/// The [encoding] parameter specifies whether the data is base64 or URL
/// encoded. If the data is base64 encoded, the value of the encoding
/// parameter must be `'base64'`. HTML can be encoded with
/// `base64.encode(bytes)` like so:
/// ```dart
/// import 'dart:convert';
///
/// final unencodedHtml = '''
/// <html><body>'%28' is the code for '('</body></html>
/// ''';
/// final encodedHtml = base64.encode(utf8.encode(unencodedHtml));
/// print(encodedHtml);
/// ```
///
/// The [mimeType] parameter specifies the format of the data. If WebView
/// can't handle the specified MIME type, it will download the data. If
/// `null`, defaults to 'text/html'.
Future<void> loadData({
required String data,
String? mimeType,
String? encoding,
}) {
return api.loadDataFromInstance(
this,
data,
mimeType,
encoding,
);
}
/// Loads the given data into this WebView.
///
/// The [baseUrl] is used as base URL for the content. It is used both to
/// resolve relative URLs and when applying JavaScript's same origin policy.
///
/// The [historyUrl] is used for the history entry.
///
/// The [mimeType] parameter specifies the format of the data. If WebView
/// can't handle the specified MIME type, it will download the data. If
/// `null`, defaults to 'text/html'.
///
/// Note that content specified in this way can access local device files (via
/// 'file' scheme URLs) only if baseUrl specifies a scheme other than 'http',
/// 'https', 'ftp', 'ftps', 'about' or 'javascript'.
///
/// If the base URL uses the data scheme, this method is equivalent to calling
/// [loadData] and the [historyUrl] is ignored, and the data will be treated
/// as part of a data: URL, including the requirement that the content be
/// URL-encoded or base64 encoded. If the base URL uses any other scheme, then
/// the data will be loaded into the WebView as a plain string (i.e. not part
/// of a data URL) and any URL-encoded entities in the string will not be
/// decoded.
///
/// Note that the [baseUrl] is sent in the 'Referer' HTTP header when
/// requesting subresources (images, etc.) of the page loaded using this
/// method.
///
/// If a valid HTTP or HTTPS base URL is not specified in [baseUrl], then
/// content loaded using this method will have a `window.origin` value of
/// `"null"`. This must not be considered to be a trusted origin by the
/// application or by any JavaScript code running inside the WebView (for
/// example, event sources in DOM event handlers or web messages), because
/// malicious content can also create frames with a null origin. If you need
/// to identify the main frame's origin in a trustworthy way, you should use a
/// valid HTTP or HTTPS base URL to set the origin.
Future<void> loadDataWithBaseUrl({
String? baseUrl,
required String data,
String? mimeType,
String? encoding,
String? historyUrl,
}) {
return api.loadDataWithBaseUrlFromInstance(
this,
baseUrl,
data,
mimeType,
encoding,
historyUrl,
);
}
/// Loads the given URL with additional HTTP headers, specified as a map from name to value.
///
/// Note that if this map contains any of the headers that are set by default
/// by this WebView, such as those controlling caching, accept types or the
/// User-Agent, their values may be overridden by this WebView's defaults.
///
/// Also see compatibility note on [evaluateJavascript].
Future<void> loadUrl(String url, Map<String, String> headers) {
return api.loadUrlFromInstance(this, url, headers);
}
/// Loads the URL with postData using "POST" method into this WebView.
///
/// If url is not a network URL, it will be loaded with [loadUrl] instead, ignoring the postData param.
Future<void> postUrl(String url, Uint8List data) {
return api.postUrlFromInstance(this, url, data);
}
/// Gets the URL for the current page.
///
/// This is not always the same as the URL passed to
/// [WebViewClient.onPageStarted] because although the load for that URL has
/// begun, the current page may not have changed.
///
/// Returns null if no page has been loaded.
Future<String?> getUrl() {
return api.getUrlFromInstance(this);
}
/// Whether this WebView has a back history item.
Future<bool> canGoBack() {
return api.canGoBackFromInstance(this);
}
/// Whether this WebView has a forward history item.
Future<bool> canGoForward() {
return api.canGoForwardFromInstance(this);
}
/// Goes back in the history of this WebView.
Future<void> goBack() {
return api.goBackFromInstance(this);
}
/// Goes forward in the history of this WebView.
Future<void> goForward() {
return api.goForwardFromInstance(this);
}
/// Reloads the current URL.
Future<void> reload() {
return api.reloadFromInstance(this);
}
/// Clears the resource cache.
///
/// Note that the cache is per-application, so this will clear the cache for
/// all WebViews used.
Future<void> clearCache(bool includeDiskFiles) {
return api.clearCacheFromInstance(this, includeDiskFiles);
}
// TODO(bparrishMines): Update documentation once addJavascriptInterface is added.
/// Asynchronously evaluates JavaScript in the context of the currently displayed page.
///
/// If non-null, the returned value will be any result returned from that
/// execution.
///
/// Compatibility note. Applications targeting Android versions N or later,
/// JavaScript state from an empty WebView is no longer persisted across
/// navigations like [loadUrl]. For example, global variables and functions
/// defined before calling [loadUrl]) will not exist in the loaded page.
Future<String?> evaluateJavascript(String javascriptString) {
return api.evaluateJavascriptFromInstance(
this,
javascriptString,
);
}
// TODO(bparrishMines): Update documentation when WebViewClient.onReceivedTitle is added.
/// Gets the title for the current page.
///
/// Returns null if no page has been loaded.
Future<String?> getTitle() {
return api.getTitleFromInstance(this);
}
// TODO(bparrishMines): Update documentation when onScrollChanged is added.
/// Set the scrolled position of your view.
Future<void> scrollTo(int x, int y) {
return api.scrollToFromInstance(this, x, y);
}
// TODO(bparrishMines): Update documentation when onScrollChanged is added.
/// Move the scrolled position of your view.
Future<void> scrollBy(int x, int y) {
return api.scrollByFromInstance(this, x, y);
}
/// Return the scrolled left position of this view.
///
/// This is the left edge of the displayed part of your view. You do not
/// need to draw any pixels farther left, since those are outside of the frame
/// of your view on screen.
Future<int> getScrollX() {
return api.getScrollXFromInstance(this);
}
/// Return the scrolled top position of this view.
///
/// This is the top edge of the displayed part of your view. You do not need
/// to draw any pixels above it, since those are outside of the frame of your
/// view on screen.
Future<int> getScrollY() {
return api.getScrollYFromInstance(this);
}
/// Sets the [WebViewClient] that will receive various notifications and requests.
///
/// This will replace the current handler.
Future<void> setWebViewClient(WebViewClient webViewClient) {
_currentWebViewClient = webViewClient;
WebViewClient.api.createFromInstance(webViewClient);
return api.setWebViewClientFromInstance(this, webViewClient);
}
/// Injects the supplied [JavascriptChannel] into this WebView.
///
/// The object is injected into all frames of the web page, including all the
/// iframes, using the supplied name. This allows the object's methods to
/// be accessed from JavaScript.
///
/// Note that injected objects will not appear in JavaScript until the page is
/// next (re)loaded. JavaScript should be enabled before injecting the object.
/// For example:
///
/// ```dart
/// webview.settings.setJavaScriptEnabled(true);
/// webView.addJavascriptChannel(JavScriptChannel("injectedObject"));
/// webView.loadUrl("about:blank", <String, String>{});
/// webView.loadUrl("javascript:injectedObject.postMessage("Hello, World!")", <String, String>{});
/// ```
///
/// **Important**
/// * Because the object is exposed to all the frames, any frame could obtain
/// the object name and call methods on it. There is no way to tell the
/// calling frame's origin from the app side, so the app must not assume that
/// the caller is trustworthy unless the app can guarantee that no third party
/// content is ever loaded into the WebView even inside an iframe.
Future<void> addJavaScriptChannel(JavaScriptChannel javaScriptChannel) {
JavaScriptChannel.api.createFromInstance(javaScriptChannel);
return api.addJavaScriptChannelFromInstance(this, javaScriptChannel);
}
/// Removes a previously injected [JavaScriptChannel] from this WebView.
///
/// Note that the removal will not be reflected in JavaScript until the page
/// is next (re)loaded. See [addJavaScriptChannel].
Future<void> removeJavaScriptChannel(JavaScriptChannel javaScriptChannel) {
JavaScriptChannel.api.createFromInstance(javaScriptChannel);
return api.removeJavaScriptChannelFromInstance(this, javaScriptChannel);
}
/// 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.
Future<void> setDownloadListener(DownloadListener? listener) async {
await Future.wait(<Future<void>>[
if (listener != null) DownloadListener.api.createFromInstance(listener),
api.setDownloadListenerFromInstance(this, listener)
]);
}
/// 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.
Future<void> setWebChromeClient(WebChromeClient? client) async {
// WebView requires a WebViewClient because of a bug fix that makes
// calls to WebViewClient.requestLoading/WebViewClient.urlLoading when a new
// window is opened. This is to make sure a url opened by `Window.open` has
// a secure url.
assert(
_currentWebViewClient != null,
"Can't set a WebChromeClient without setting a WebViewClient first.",
);
await Future.wait(<Future<void>>[
if (client != null)
WebChromeClient.api.createFromInstance(client, _currentWebViewClient!),
api.setWebChromeClientFromInstance(this, client),
]);
}
/// Sets the background color of this WebView.
Future<void> setBackgroundColor(Color color) {
return api.setBackgroundColorFromInstance(this, color.value);
}
/// Releases all resources used by the [WebView].
///
/// Any methods called after [release] will throw an exception.
Future<void> release() {
_currentWebViewClient = null;
WebSettings.api.disposeFromInstance(settings);
return api.disposeFromInstance(this);
}
@override
WebView copy() {
return WebView.detached(useHybridComposition: useHybridComposition);
}
}
/// Manages cookies globally for all webviews.
class CookieManager {
CookieManager._();
static CookieManager? _instance;
/// Gets the globally set CookieManager instance.
static CookieManager get instance => _instance ??= CookieManager._();
/// Setter for the singleton value, for testing purposes only.
@visibleForTesting
static set instance(CookieManager value) => _instance = value;
/// Pigeon Host Api implementation for [CookieManager].
@visibleForTesting
static CookieManagerHostApi api = CookieManagerHostApi();
/// Sets a single cookie (key-value pair) for the given URL. Any existing
/// cookie with the same host, path and name will be replaced with the new
/// cookie. The cookie being set will be ignored if it is expired. To set
/// multiple cookies, your application should invoke this method multiple
/// times.
///
/// The value parameter must follow the format of the Set-Cookie HTTP
/// response header defined by RFC6265bis. This is a key-value pair of the
/// form "key=value", optionally followed by a list of cookie attributes
/// delimited with semicolons (ex. "key=value; Max-Age=123"). Please consult
/// the RFC specification for a list of valid attributes.
///
/// Note: if specifying a value containing the "Secure" attribute, url must
/// use the "https://" scheme.
///
/// Params:
/// url – the URL for which the cookie is to be set
/// value – the cookie as a string, using the format of the 'Set-Cookie' HTTP response header
Future<void> setCookie(String url, String value) => api.setCookie(url, value);
/// Removes all cookies.
///
/// The returned future resolves to true if any cookies were removed.
Future<bool> clearCookies() => api.clearCookies();
}
/// Manages settings state for a [WebView].
///
/// When a WebView is first created, it obtains a set of default settings. These
/// default settings will be returned from any getter call. A WebSettings object
/// obtained from [WebView.settings] is tied to the life of the WebView. If a
/// WebView has been destroyed, any method call on [WebSettings] will throw an
/// Exception.
class WebSettings extends JavaObject {
/// Constructs a [WebSettings].
///
/// This constructor is only used for testing. An instance should be obtained
/// with [WebView.settings].
@visibleForTesting
WebSettings(WebView webView) : super.detached() {
api.createFromInstance(this, webView);
}
/// Constructs a [WebSettings] without creating the associated Java object.
///
/// This should only be used by subclasses created by this library or to
/// create copies.
WebSettings.detached() : super.detached();
/// Pigeon Host Api implementation for [WebSettings].
@visibleForTesting
static WebSettingsHostApiImpl api = WebSettingsHostApiImpl();
/// Sets whether the DOM storage API is enabled.
///
/// The default value is false.
Future<void> setDomStorageEnabled(bool flag) {
return api.setDomStorageEnabledFromInstance(this, flag);
}
/// Tells JavaScript to open windows automatically.
///
/// This applies to the JavaScript function `window.open()`. The default is
/// false.
Future<void> setJavaScriptCanOpenWindowsAutomatically(bool flag) {
return api.setJavaScriptCanOpenWindowsAutomaticallyFromInstance(
this,
flag,
);
}
// TODO(bparrishMines): Update documentation when WebChromeClient.onCreateWindow is added.
/// Sets whether the WebView should supports multiple windows.
///
/// The default is false.
Future<void> setSupportMultipleWindows(bool support) {
return api.setSupportMultipleWindowsFromInstance(this, support);
}
/// Tells the WebView to enable JavaScript execution.
///
/// The default is false.
Future<void> setJavaScriptEnabled(bool flag) {
return api.setJavaScriptEnabledFromInstance(this, flag);
}
/// Sets the WebView's user-agent string.
///
/// If the string is empty, the system default value will be used. Note that
/// starting from KITKAT Android version, changing the user-agent while
/// loading a web page causes WebView to initiate loading once again.
Future<void> setUserAgentString(String? userAgentString) {
return api.setUserAgentStringFromInstance(this, userAgentString);
}
/// Sets whether the WebView requires a user gesture to play media.
///
/// The default is true.
Future<void> setMediaPlaybackRequiresUserGesture(bool require) {
return api.setMediaPlaybackRequiresUserGestureFromInstance(this, require);
}
// TODO(bparrishMines): Update documentation when WebView.zoomIn and WebView.zoomOut are added.
/// Sets whether the WebView should support zooming using its on-screen zoom controls and gestures.
///
/// The particular zoom mechanisms that should be used can be set with
/// [setBuiltInZoomControls].
///
/// The default is true.
Future<void> setSupportZoom(bool support) {
return api.setSupportZoomFromInstance(this, support);
}
/// Sets whether the WebView loads pages in overview mode, that is, zooms out the content to fit on screen by width.
///
/// This setting is taken into account when the content width is greater than
/// the width of the WebView control, for example, when [setUseWideViewPort]
/// is enabled.
///
/// The default is false.
Future<void> setLoadWithOverviewMode(bool overview) {
return api.setLoadWithOverviewModeFromInstance(this, overview);
}
/// Sets whether the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport.
///
/// When the value of the setting is false, the layout width is always set to
/// the width of the WebView control in device-independent (CSS) pixels. When
/// the value is true and the page contains the viewport meta tag, the value
/// of the width specified in the tag is used. If the page does not contain
/// the tag or does not provide a width, then a wide viewport will be used.
Future<void> setUseWideViewPort(bool use) {
return api.setUseWideViewPortFromInstance(this, use);
}
// TODO(bparrishMines): Update documentation when ZoomButtonsController is added.
/// Sets whether the WebView should display on-screen zoom controls when using the built-in zoom mechanisms.
///
/// See [setBuiltInZoomControls]. The default is true. However, on-screen zoom
/// controls are deprecated in Android so it's recommended to set this to
/// false.
Future<void> setDisplayZoomControls(bool enabled) {
return api.setDisplayZoomControlsFromInstance(this, enabled);
}
// TODO(bparrishMines): Update documentation when ZoomButtonsController is added.
/// Sets whether the WebView should use its built-in zoom mechanisms.
///
/// The built-in zoom mechanisms comprise on-screen zoom controls, which are
/// displayed over the WebView's content, and the use of a pinch gesture to
/// control zooming. Whether or not these on-screen controls are displayed can
/// be set with [setDisplayZoomControls]. The default is false.
///
/// The built-in mechanisms are the only currently supported zoom mechanisms,
/// so it is recommended that this setting is always enabled. However,
/// on-screen zoom controls are deprecated in Android so it's recommended to
/// disable [setDisplayZoomControls].
Future<void> setBuiltInZoomControls(bool enabled) {
return api.setBuiltInZoomControlsFromInstance(this, enabled);
}
/// Enables or disables file access within WebView.
///
/// This enables or disables file system access only. Assets and resources are
/// still accessible using file:///android_asset and file:///android_res. The
/// default value is true for apps targeting Build.VERSION_CODES.Q and below,
/// and false when targeting Build.VERSION_CODES.R and above.
Future<void> setAllowFileAccess(bool enabled) {
return api.setAllowFileAccessFromInstance(this, enabled);
}
@override
WebSettings copy() {
return WebSettings.detached();
}
}
/// Exposes a channel to receive calls from javaScript.
///
/// See [WebView.addJavaScriptChannel].
class JavaScriptChannel extends JavaObject {
/// Constructs a [JavaScriptChannel].
JavaScriptChannel(
this.channelName, {
void Function(String message)? postMessage,
}) : super.detached() {
AndroidWebViewFlutterApis.instance.ensureSetUp();
}
/// Constructs a [JavaScriptChannel] without creating the associated Java
/// object.
///
/// This should only be used by subclasses created by this library or to
/// create copies.
JavaScriptChannel.detached(
this.channelName, {
void Function(String message)? postMessage,
}) : super.detached();
/// Pigeon Host Api implementation for [JavaScriptChannel].
@visibleForTesting
static JavaScriptChannelHostApiImpl api = JavaScriptChannelHostApiImpl();
/// Used to identify this object to receive messages from javaScript.
final String channelName;
/// Callback method when javaScript calls `postMessage` on the object instance passed.
void postMessage(String message) {}
@override
JavaScriptChannel copy() {
return JavaScriptChannel.detached(channelName, postMessage: postMessage);
}
}
/// Receive various notifications and requests for [WebView].
class WebViewClient extends JavaObject {
/// Constructs a [WebViewClient].
WebViewClient({
this.shouldOverrideUrlLoading = true,
void Function(WebView webView, String url)? onPageStarted,
void Function(WebView webView, String url)? onPageFinished,
void Function(
WebView webView,
WebResourceRequest request,
WebResourceError error,
)?
onReceivedRequestError,
void Function(
WebView webView,
int errorCode,
String description,
String failingUrl,
)?
onReceivedError,
void Function(WebView webView, WebResourceRequest request)? requestLoading,
void Function(WebView webView, String url)? urlLoading,
}) : super.detached() {
AndroidWebViewFlutterApis.instance.ensureSetUp();
}
/// Constructs a [WebViewClient] without creating the associated Java object.
///
/// This should only be used by subclasses created by this library or to
/// create copies.
WebViewClient.detached({
this.shouldOverrideUrlLoading = true,
void Function(WebView webView, String url)? onPageStarted,
void Function(WebView webView, String url)? onPageFinished,
void Function(
WebView webView,
WebResourceRequest request,
WebResourceError error,
)?
onReceivedRequestError,
void Function(
WebView webView,
int errorCode,
String description,
String failingUrl,
)?
onReceivedError,
void Function(WebView webView, WebResourceRequest request)? requestLoading,
void Function(WebView webView, String url)? urlLoading,
}) : super.detached();
/// User authentication failed on server.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_AUTHENTICATION
static const int errorAuthentication = -4;
/// Malformed URL.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_BAD_URL
static const int errorBadUrl = -12;
/// Failed to connect to the server.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_CONNECT
static const int errorConnect = -6;
/// Failed to perform SSL handshake.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_FAILED_SSL_HANDSHAKE
static const int errorFailedSslHandshake = -11;
/// Generic file error.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_FILE
static const int errorFile = -13;
/// File not found.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_FILE_NOT_FOUND
static const int errorFileNotFound = -14;
/// Server or proxy hostname lookup failed.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_HOST_LOOKUP
static const int errorHostLookup = -2;
/// Failed to read or write to the server.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_IO
static const int errorIO = -7;
/// User authentication failed on proxy.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_PROXY_AUTHENTICATION
static const int errorProxyAuthentication = -5;
/// Too many redirects.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_REDIRECT_LOOP
static const int errorRedirectLoop = -9;
/// Connection timed out.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_TIMEOUT
static const int errorTimeout = -8;
/// Too many requests during this load.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_TOO_MANY_REQUESTS
static const int errorTooManyRequests = -15;
/// Generic error.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNKNOWN
static const int errorUnknown = -1;
/// Resource load was canceled by Safe Browsing.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNSAFE_RESOURCE
static const int errorUnsafeResource = -16;
/// Unsupported authentication scheme (not basic or digest).
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNSUPPORTED_AUTH_SCHEME
static const int errorUnsupportedAuthScheme = -3;
/// Unsupported URI scheme.
///
/// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNSUPPORTED_SCHEME
static const int errorUnsupportedScheme = -10;
/// Pigeon Host Api implementation for [WebViewClient].
@visibleForTesting
static WebViewClientHostApiImpl api = WebViewClientHostApiImpl();
/// Whether loading a url should be overridden.
///
/// In Java, `shouldOverrideUrlLoading()` and `shouldOverrideRequestLoading()`
/// callbacks must synchronously return a boolean. This sets the default
/// return value.
///
/// Setting [shouldOverrideUrlLoading] to true causes the current [WebView] to
/// abort loading the URL, while returning false causes the [WebView] to
/// continue loading the URL as usual. [requestLoading] or [urlLoading] will
/// still be called either way.
///
/// Defaults to true.
final bool shouldOverrideUrlLoading;
/// Notify the host application that a page has started loading.
///
/// This method is called once for each main frame load so a page with iframes
/// or framesets will call onPageStarted one time for the main frame. This
/// also means that [onPageStarted] will not be called when the contents of an
/// embedded frame changes, i.e. clicking a link whose target is an iframe, it
/// will also not be called for fragment navigations (navigations to
/// #fragment_id).
void onPageStarted(WebView webView, String url) {}
// TODO(bparrishMines): Update documentation when WebView.postVisualStateCallback is added.
/// Notify the host application that a page has finished loading.
///
/// This method is called only for main frame. Receiving an [onPageFinished]
/// callback does not guarantee that the next frame drawn by WebView will
/// reflect the state of the DOM at this point.
void onPageFinished(WebView webView, String url) {}
/// Report web resource loading error to the host application.
///
/// These errors usually indicate inability to connect to the server. Note
/// that unlike the deprecated version of the callback, the new version will
/// be called for any resource (iframe, image, etc.), not just for the main
/// page. Thus, it is recommended to perform minimum required work in this
/// callback.
void onReceivedRequestError(
WebView webView,
WebResourceRequest request,
WebResourceError error,
) {}
/// Report an error to the host application.
///
/// These errors are unrecoverable (i.e. the main resource is unavailable).
/// The errorCode parameter corresponds to one of the error* constants.
@Deprecated('Only called on Android version < 23.')
void onReceivedError(
WebView webView,
int errorCode,
String description,
String failingUrl,
) {}
// TODO(bparrishMines): Update documentation once synchronous url handling is supported.
/// When a URL is about to be loaded in the current [WebView].
///
/// If a [WebViewClient] is not provided, by default [WebView] will ask
/// Activity Manager to choose the proper handler for the URL. If a
/// [WebViewClient] is provided, setting [shouldOverrideUrlLoading] to true
/// causes the current [WebView] to abort loading the URL, while returning
/// false causes the [WebView] to continue loading the URL as usual.
void requestLoading(WebView webView, WebResourceRequest request) {}
// TODO(bparrishMines): Update documentation once synchronous url handling is supported.
/// When a URL is about to be loaded in the current [WebView].
///
/// If a [WebViewClient] is not provided, by default [WebView] will ask
/// Activity Manager to choose the proper handler for the URL. If a
/// [WebViewClient] is provided, setting [shouldOverrideUrlLoading] to true
/// causes the current [WebView] to abort loading the URL, while returning
/// false causes the [WebView] to continue loading the URL as usual.
void urlLoading(WebView webView, String url) {}
@override
WebViewClient copy() {
return WebViewClient.detached(
shouldOverrideUrlLoading: shouldOverrideUrlLoading,
onPageStarted: onPageStarted,
onPageFinished: onPageFinished,
onReceivedRequestError: onReceivedRequestError,
onReceivedError: onReceivedError,
requestLoading: requestLoading,
urlLoading: urlLoading,
);
}
}
/// The interface to be used when content can not be handled by the rendering
/// engine for [WebView], and should be downloaded instead.
class DownloadListener extends JavaObject {
/// Constructs a [DownloadListener].
DownloadListener({
void Function(
String url,
String userAgent,
String contentDisposition,
String mimetype,
int contentLength,
)?
onDownloadStart,
}) : super.detached() {
AndroidWebViewFlutterApis.instance.ensureSetUp();
}
/// Constructs a [DownloadListener] without creating the associated Java
/// object.
///
/// This should only be used by subclasses created by this library or to
/// create copies.
DownloadListener.detached({
void Function(
String url,
String userAgent,
String contentDisposition,
String mimetype,
int contentLength,
)?
onDownloadStart,
}) : super.detached();
/// Pigeon Host Api implementation for [DownloadListener].
@visibleForTesting
static DownloadListenerHostApiImpl api = DownloadListenerHostApiImpl();
/// Notify the host application that a file should be downloaded.
void onDownloadStart(
String url,
String userAgent,
String contentDisposition,
String mimetype,
int contentLength,
) {}
@override
DownloadListener copy() {
return DownloadListener(onDownloadStart: onDownloadStart);
}
}
/// Handles JavaScript dialogs, favicons, titles, and the progress for [WebView].
class WebChromeClient extends JavaObject {
/// Constructs a [WebChromeClient].
WebChromeClient({
void Function(WebView webView, int progress)? onProgressChanged,
}) : super.detached() {
AndroidWebViewFlutterApis.instance.ensureSetUp();
}
/// Constructs a [WebChromeClient] without creating the associated Java
/// object.
///
/// This should only be used by subclasses created by this library or to
/// create copies.
WebChromeClient.detached({
void Function(WebView webView, int progress)? onProgressChanged,
}) : super.detached();
/// Pigeon Host Api implementation for [WebChromeClient].
@visibleForTesting
static WebChromeClientHostApiImpl api = WebChromeClientHostApiImpl();
/// Notify the host application that a file should be downloaded.
void onProgressChanged(WebView webView, int progress) {}
@override
WebChromeClient copy() {
return WebChromeClient.detached(onProgressChanged: onProgressChanged);
}
}
/// Encompasses parameters to the [WebViewClient.requestLoading] method.
class WebResourceRequest {
/// Constructs a [WebResourceRequest].
WebResourceRequest({
required this.url,
required this.isForMainFrame,
required this.isRedirect,
required this.hasGesture,
required this.method,
required this.requestHeaders,
});
/// The URL for which the resource request was made.
final String url;
/// Whether the request was made in order to fetch the main frame's document.
final bool isForMainFrame;
/// Whether the request was a result of a server-side redirect.
///
/// Only supported on Android version >= 24.
final bool? isRedirect;
/// Whether a gesture (such as a click) was associated with the request.
final bool hasGesture;
/// The method associated with the request, for example "GET".
final String method;
/// The headers associated with the request.
final Map<String, String> requestHeaders;
}
/// Encapsulates information about errors occurred during loading of web resources.
///
/// See [WebViewClient.onReceivedRequestError].
class WebResourceError {
/// Constructs a [WebResourceError].
WebResourceError({
required this.errorCode,
required this.description,
});
/// The integer code of the error (e.g. [WebViewClient.errorAuthentication].
final int errorCode;
/// Describes the error.
final String description;
}
/// Manages Flutter assets that are part of Android's app bundle.
class FlutterAssetManager {
/// Constructs the [FlutterAssetManager].
const FlutterAssetManager();
/// Pigeon Host Api implementation for [FlutterAssetManager].
@visibleForTesting
static FlutterAssetManagerHostApi api = FlutterAssetManagerHostApi();
/// Lists all assets at the given path.
///
/// The assets are returned as a `List<String>`. The `List<String>` only
/// contains files which are direct childs
Future<List<String?>> list(String path) => api.list(path);
/// Gets the relative file path to the Flutter asset with the given name.
Future<String> getAssetFilePathByName(String name) =>
api.getAssetFilePathByName(name);
}
/// Manages the JavaScript storage APIs provided by the [WebView].
///
/// Wraps [WebStorage](https://developer.android.com/reference/android/webkit/WebStorage).
class WebStorage extends JavaObject {
/// Constructs a [WebStorage].
///
/// This constructor is only used for testing. An instance should be obtained
/// with [WebStorage.instance].
@visibleForTesting
WebStorage() : super.detached() {
AndroidWebViewFlutterApis.instance.ensureSetUp();
api.createFromInstance(this);
}
/// Constructs a [WebStorage] without creating the associated Java object.
///
/// This should only be used by subclasses created by this library or to
/// create copies.
WebStorage.detached() : super.detached();
/// Pigeon Host Api implementation for [WebStorage].
@visibleForTesting
static WebStorageHostApiImpl api = WebStorageHostApiImpl();
/// The singleton instance of this class.
static WebStorage instance = WebStorage();
/// Clears all storage currently being used by the JavaScript storage APIs.
Future<void> deleteAllData() {
return api.deleteAllDataFromInstance(this);
}
@override
WebStorage copy() {
return WebStorage.detached();
}
}