[webview_flutter] Enable setAllowFileAccess on Android setting when loading files (#4601)
diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index 1337bab..818a134 100644
--- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.8.2
+
+* Adds the `WebSettings.setAllowFileAccess()` method and ensure that file access is allowed when the `WebViewAndroidWidget.loadFile()` method is executed.
+
## 2.8.1
* Fixes bug where the default user agent string was being set for every rebuild. See
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
index 8ef0b8d..15b78b7 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
@@ -1152,6 +1152,8 @@
void setBuiltInZoomControls(Long instanceId, Boolean enabled);
+ void setAllowFileAccess(Long instanceId, Boolean enabled);
+
/** The codec used by WebSettingsHostApi. */
static MessageCodec<Object> getCodec() {
return WebSettingsHostApiCodec.INSTANCE;
@@ -1556,6 +1558,37 @@
channel.setMessageHandler(null);
}
}
+ {
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess",
+ getCodec());
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ Map<String, Object> wrapped = new HashMap<>();
+ try {
+ ArrayList<Object> args = (ArrayList<Object>) message;
+ Number instanceIdArg = (Number) args.get(0);
+ if (instanceIdArg == null) {
+ throw new NullPointerException("instanceIdArg unexpectedly null.");
+ }
+ Boolean enabledArg = (Boolean) args.get(1);
+ if (enabledArg == null) {
+ throw new NullPointerException("enabledArg unexpectedly null.");
+ }
+ api.setAllowFileAccess(instanceIdArg.longValue(), enabledArg);
+ wrapped.put("result", null);
+ } catch (Error | RuntimeException exception) {
+ wrapped.put("error", wrapError(exception));
+ }
+ reply.reply(wrapped);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
index 239ef47..b168e20 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
@@ -118,4 +118,10 @@
final WebSettings webSettings = (WebSettings) instanceManager.getInstance(instanceId);
webSettings.setBuiltInZoomControls(enabled);
}
+
+ @Override
+ public void setAllowFileAccess(Long instanceId, Boolean enabled) {
+ final WebSettings webSettings = (WebSettings) instanceManager.getInstance(instanceId);
+ webSettings.setAllowFileAccess(enabled);
+ }
}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
index dfa05cd..1098932 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
@@ -546,6 +546,16 @@
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);
+ }
}
/// Exposes a channel to receive calls from javaScript.
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart
index 810a717..20391c4 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart
@@ -1179,6 +1179,31 @@
return;
}
}
+
+ Future<void> setAllowFileAccess(int arg_instanceId, bool arg_enabled) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap = await channel
+ .send(<Object>[arg_instanceId, arg_enabled]) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return;
+ }
+ }
}
class _JavaScriptChannelHostApiCodec extends StandardMessageCodec {
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
index 1db5ed4..ead60f6 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
@@ -437,6 +437,17 @@
enabled,
);
}
+
+ /// Helper method to convert instances ids to objects.
+ Future<void> setAllowFileAccessFromInstance(
+ WebSettings instance,
+ bool enabled,
+ ) {
+ return setAllowFileAccess(
+ instanceManager.getInstanceId(instance)!,
+ enabled,
+ );
+ }
}
/// Host api implementation for [JavaScriptChannel].
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
index de55b52..bf85ac9 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
@@ -179,6 +179,7 @@
? absoluteFilePath
: 'file://$absoluteFilePath';
+ webView.settings.setAllowFileAccess(true);
return webView.loadUrl(url, <String, String>{});
}
diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart
index 36862f7..b298352 100644
--- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart
+++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart
@@ -132,6 +132,8 @@
void setDisplayZoomControls(int instanceId, bool enabled);
void setBuiltInZoomControls(int instanceId, bool enabled);
+
+ void setAllowFileAccess(int instanceId, bool enabled);
}
@HostApi(dartHostTestHandler: 'TestJavaScriptChannelHostApi')
diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
index 0b78c72..8905d7f 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,7 +2,7 @@
description: A Flutter plugin that provides a WebView widget on Android.
repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
-version: 2.8.1
+version: 2.8.2
environment:
sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart
index 1e47c79..720fe40 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart
@@ -649,6 +649,7 @@
void setUseWideViewPort(int instanceId, bool use);
void setDisplayZoomControls(int instanceId, bool enabled);
void setBuiltInZoomControls(int instanceId, bool enabled);
+ void setAllowFileAccess(int instanceId, bool enabled);
static void setup(TestWebSettingsHostApi? api,
{BinaryMessenger? binaryMessenger}) {
{
@@ -940,6 +941,28 @@
});
}
}
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMockMessageHandler(null);
+ } else {
+ channel.setMockMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null.');
+ final List<Object?> args = (message as List<Object?>?)!;
+ final int? arg_instanceId = (args[0] as int?);
+ assert(arg_instanceId != null,
+ 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null, expected non-null int.');
+ final bool? arg_enabled = (args[1] as bool?);
+ assert(arg_enabled != null,
+ 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null, expected non-null bool.');
+ api.setAllowFileAccess(arg_instanceId!, arg_enabled!);
+ return <Object?, Object?>{};
+ });
+ }
+ }
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
index cc29fc7..8688a19 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
@@ -430,6 +430,14 @@
true,
));
});
+
+ test('setAllowFileAccess', () {
+ webSettings.setAllowFileAccess(true);
+ verify(mockPlatformHostApi.setAllowFileAccess(
+ webSettingsInstanceId,
+ true,
+ ));
+ });
});
group('$JavaScriptChannel', () {
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
index d25d233..2134de5 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
@@ -1,7 +1,3 @@
-// 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.
-
// Mocks generated by Mockito 5.0.16 from annotations
// in webview_flutter_android/test/android_webview_test.dart.
// Do not manually edit this file.
@@ -211,6 +207,10 @@
Invocation.method(#setBuiltInZoomControls, [instanceId, enabled]),
returnValueForMissingStub: null);
@override
+ void setAllowFileAccess(int? instanceId, bool? enabled) => super.noSuchMethod(
+ Invocation.method(#setAllowFileAccess, [instanceId, enabled]),
+ returnValueForMissingStub: null);
+ @override
String toString() => super.toString();
}
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
index 2b25022..fed1c11 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
@@ -304,6 +304,15 @@
));
});
+ testWidgets('loadFile should setAllowFileAccess to true',
+ (WidgetTester tester) async {
+ await buildWidget(tester);
+
+ await testController.loadFile('file:///path/to/file.html');
+
+ verify(mockWebSettings.setAllowFileAccess(true));
+ });
+
testWidgets('loadFlutterAsset', (WidgetTester tester) async {
await buildWidget(tester);
const String assetKey = 'test_assets/index.html';
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
index ece17ad..12e993b 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
@@ -1,7 +1,3 @@
-// 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.
-
// Mocks generated by Mockito 5.0.16 from annotations
// in webview_flutter_android/test/webview_android_widget_test.dart.
// Do not manually edit this file.
@@ -120,6 +116,11 @@
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i4.Future<void>);
@override
+ _i4.Future<void> setAllowFileAccess(bool? enabled) =>
+ (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i4.Future<void>);
+ @override
String toString() => super.toString();
}