[webview_flutter_web] Copies web implementation of webview_flutter from v4_webview (#6854)
* v4 web impl
* add breaking change
* fix lints
* exclude web plugin
diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md
index 82be36f..3b2c021 100644
--- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md
@@ -1,5 +1,7 @@
-## NEXT
+## 0.2.0
+* **BREAKING CHANGE** Updates platform implementation to `2.0.0` release of
+ `webview_flutter_platform_interface`. See README for updated usage.
* Updates minimum Flutter version to 2.10.
## 0.1.0+4
diff --git a/packages/webview_flutter/webview_flutter_web/README.md b/packages/webview_flutter/webview_flutter_web/README.md
index a7711ee..dcd1410 100644
--- a/packages/webview_flutter/webview_flutter_web/README.md
+++ b/packages/webview_flutter/webview_flutter_web/README.md
@@ -5,10 +5,8 @@
It is currently severely limited and doesn't implement most of the available functionality.
The following functionality is currently available:
-- `loadUrl` (Without headers)
-- `requestUrl`
-- `loadHTMLString` (Without `baseUrl`)
-- Setting the `initialUrl` through `CreationParams`.
+- `loadRequest`
+- `loadHtmlString` (Without `baseUrl`)
Nothing else is currently supported.
@@ -20,7 +18,7 @@
* [Add this package](https://pub.dev/packages/webview_flutter_web/install)
as an explicit dependency of your project, in addition to depending on
`webview_flutter`.
-* Register `WebWebViewPlatform` as the `WebView.platform` before creating a
+* Register `WebWebViewPlatform` as the `WebViewPlatform.instance` before creating a
`WebView`. See below for examples.
Once those steps below are complete, the APIs from `webview_flutter` listed
@@ -39,7 +37,7 @@
import 'package:webview_flutter_web/webview_flutter_web.dart';
main() {
- WebView.platform = WebWebViewPlatform();
+ WebViewPlatform.instance = WebWebViewPlatform();
...
```
@@ -55,7 +53,7 @@
import 'package:webview_flutter_web/webview_flutter_web.dart';
void registerWebViewWebImplementation() {
- WebView.platform = WebWebViewPlatform();
+ WebViewPlatform.instance = WebWebViewPlatform();
}
```
diff --git a/packages/webview_flutter/webview_flutter_web/example/integration_test/legacy/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_web/example/integration_test/legacy/webview_flutter_test.dart
new file mode 100644
index 0000000..db27f7a
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_web/example/integration_test/legacy/webview_flutter_test.dart
@@ -0,0 +1,72 @@
+// 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 'dart:html' as html;
+
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+import 'package:webview_flutter_web_example/legacy/web_view.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ // URLs to navigate to in tests. These need to be URLs that we are confident will
+ // always be accessible, and won't do redirection. (E.g., just
+ // 'https://www.google.com/' will sometimes redirect traffic that looks
+ // like it's coming from a bot, which is true of these tests).
+ const String primaryUrl = 'https://flutter.dev/';
+ const String secondaryUrl = 'https://www.google.com/robots.txt';
+
+ testWidgets('initialUrl', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ );
+ await controllerCompleter.future;
+
+ // Assert an iframe has been rendered to the DOM with the correct src attribute.
+ final html.IFrameElement? element =
+ html.document.querySelector('iframe') as html.IFrameElement?;
+ expect(element, isNotNull);
+ expect(element!.src, primaryUrl);
+ });
+
+ testWidgets('loadUrl', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ await controller.loadUrl(secondaryUrl);
+
+ // Assert an iframe has been rendered to the DOM with the correct src attribute.
+ final html.IFrameElement? element =
+ html.document.querySelector('iframe') as html.IFrameElement?;
+ expect(element, isNotNull);
+ expect(element!.src, secondaryUrl);
+ });
+}
diff --git a/packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart
index 232ecdd..1736d47 100644
--- a/packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart
+++ b/packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart
@@ -2,41 +2,48 @@
// 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:html' as html;
+import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
-import 'package:webview_flutter_web_example/web_view.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_web/webview_flutter_web.dart';
-void main() {
+Future<void> main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
- // URLs to navigate to in tests. These need to be URLs that we are confident will
- // always be accessible, and won't do redirection. (E.g., just
- // 'https://www.google.com/' will sometimes redirect traffic that looks
- // like it's coming from a bot, which is true of these tests).
- const String primaryUrl = 'https://flutter.dev/';
- const String secondaryUrl = 'https://www.google.com/robots.txt';
+ final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0);
+ server.forEach((HttpRequest request) {
+ if (request.uri.path == '/hello.txt') {
+ request.response.writeln('Hello, world.');
+ } else {
+ fail('unexpected request: ${request.method} ${request.uri}');
+ }
+ request.response.close();
+ });
+ final String prefixUrl = 'http://${server.address.address}:${server.port}';
+ final String primaryUrl = '$prefixUrl/hello.txt';
- testWidgets('initialUrl', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
+ testWidgets('loadRequest', (WidgetTester tester) async {
+ final WebWebViewController controller =
+ WebWebViewController(const PlatformWebViewControllerCreationParams())
+ ..loadRequest(
+ LoadRequestParams(uri: Uri.parse(primaryUrl)),
+ );
+
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- ),
+ child: Builder(builder: (BuildContext context) {
+ return WebWebViewWidget(
+ PlatformWebViewWidgetCreationParams(controller: controller),
+ ).build(context);
+ }),
),
);
- await controllerCompleter.future;
// Assert an iframe has been rendered to the DOM with the correct src attribute.
final html.IFrameElement? element =
@@ -45,28 +52,31 @@
expect(element!.src, primaryUrl);
});
- testWidgets('loadUrl', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
+ testWidgets('loadHtmlString', (WidgetTester tester) async {
+ final WebWebViewController controller =
+ WebWebViewController(const PlatformWebViewControllerCreationParams())
+ ..loadHtmlString(
+ 'data:text/html;charset=utf-8,${Uri.encodeFull('test html')}',
+ );
+
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- ),
+ child: Builder(builder: (BuildContext context) {
+ return WebWebViewWidget(
+ PlatformWebViewWidgetCreationParams(controller: controller),
+ ).build(context);
+ }),
),
);
- final WebViewController controller = await controllerCompleter.future;
- await controller.loadUrl(secondaryUrl);
// Assert an iframe has been rendered to the DOM with the correct src attribute.
final html.IFrameElement? element =
html.document.querySelector('iframe') as html.IFrameElement?;
expect(element, isNotNull);
- expect(element!.src, secondaryUrl);
+ expect(
+ element!.src,
+ 'data:text/html;charset=utf-8,data:text/html;charset=utf-8,test%2520html',
+ );
});
}
diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/legacy/web_view.dart
similarity index 97%
rename from packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart
rename to packages/webview_flutter/webview_flutter_web/example/lib/legacy/web_view.dart
index ffd3367..b9b8ce2 100644
--- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart
+++ b/packages/webview_flutter/webview_flutter_web/example/lib/legacy/web_view.dart
@@ -5,8 +5,10 @@
import 'dart:async';
import 'package:flutter/material.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
-import 'package:webview_flutter_web/webview_flutter_web.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_web/src/webview_flutter_web_legacy.dart';
/// Optional callback invoked when a web view is first created. [controller] is
/// the [WebViewController] for the created web view.
diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/main.dart b/packages/webview_flutter/webview_flutter_web/example/lib/main.dart
index c183625..ca268a2 100644
--- a/packages/webview_flutter/webview_flutter_web/example/lib/main.dart
+++ b/packages/webview_flutter/webview_flutter_web/example/lib/main.dart
@@ -8,10 +8,10 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
-
-import 'web_view.dart';
+import 'package:webview_flutter_web/webview_flutter_web.dart';
void main() {
+ WebViewPlatform.instance = WebWebViewPlatform();
runApp(const MaterialApp(home: _WebViewExample()));
}
@@ -23,8 +23,13 @@
}
class _WebViewExampleState extends State<_WebViewExample> {
- final Completer<WebViewController> _controller =
- Completer<WebViewController>();
+ final PlatformWebViewController _controller = PlatformWebViewController(
+ const PlatformWebViewControllerCreationParams(),
+ )..loadRequest(
+ LoadRequestParams(
+ uri: Uri.parse('https://flutter.dev'),
+ ),
+ );
@override
Widget build(BuildContext context) {
@@ -32,15 +37,12 @@
appBar: AppBar(
title: const Text('Flutter WebView example'),
actions: <Widget>[
- _SampleMenu(_controller.future),
+ _SampleMenu(_controller),
],
),
- body: WebView(
- initialUrl: 'https://flutter.dev',
- onWebViewCreated: (WebViewController controller) {
- _controller.complete(controller);
- },
- ),
+ body: PlatformWebViewWidget(
+ PlatformWebViewWidgetCreationParams(controller: _controller),
+ ).build(context),
);
}
}
@@ -52,41 +54,37 @@
class _SampleMenu extends StatelessWidget {
const _SampleMenu(this.controller);
- final Future<WebViewController> controller;
+ final PlatformWebViewController controller;
@override
Widget build(BuildContext context) {
- return FutureBuilder<WebViewController>(
- future: controller,
- builder:
- (BuildContext context, AsyncSnapshot<WebViewController> controller) {
- return PopupMenuButton<_MenuOptions>(
- onSelected: (_MenuOptions value) {
- switch (value) {
- case _MenuOptions.doPostRequest:
- _onDoPostRequest(controller.data!, context);
- break;
- }
- },
- itemBuilder: (BuildContext context) => <PopupMenuItem<_MenuOptions>>[
- const PopupMenuItem<_MenuOptions>(
- value: _MenuOptions.doPostRequest,
- child: Text('Post Request'),
- ),
- ],
- );
+ return PopupMenuButton<_MenuOptions>(
+ onSelected: (_MenuOptions value) {
+ switch (value) {
+ case _MenuOptions.doPostRequest:
+ _onDoPostRequest(controller);
+ break;
+ }
},
+ itemBuilder: (BuildContext context) => <PopupMenuItem<_MenuOptions>>[
+ const PopupMenuItem<_MenuOptions>(
+ value: _MenuOptions.doPostRequest,
+ child: Text('Post Request'),
+ ),
+ ],
);
}
- Future<void> _onDoPostRequest(
- WebViewController controller, BuildContext context) async {
- final WebViewRequest request = WebViewRequest(
+ Future<void> _onDoPostRequest(PlatformWebViewController controller) async {
+ final LoadRequestParams params = LoadRequestParams(
uri: Uri.parse('https://httpbin.org/post'),
- method: WebViewRequestMethod.post,
- headers: <String, String>{'foo': 'bar', 'Content-Type': 'text/plain'},
+ method: LoadRequestMethod.post,
+ headers: const <String, String>{
+ 'foo': 'bar',
+ 'Content-Type': 'text/plain'
+ },
body: Uint8List.fromList('Test Body'.codeUnits),
);
- await controller.loadRequest(request);
+ await controller.loadRequest(params);
}
}
diff --git a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml
index e2e0796..782817e 100644
--- a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml
@@ -10,7 +10,7 @@
sdk: flutter
flutter_web_plugins:
sdk: flutter
- webview_flutter_platform_interface: ^1.8.0
+ webview_flutter_platform_interface: ^2.0.0
webview_flutter_web:
# When depending on this package from a real application you should use:
# webview_flutter_web: ^x.y.z
diff --git a/packages/webview_flutter/webview_flutter_web/lib/src/http_request_factory.dart b/packages/webview_flutter/webview_flutter_web/lib/src/http_request_factory.dart
new file mode 100644
index 0000000..4bd92f0
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_web/lib/src/http_request_factory.dart
@@ -0,0 +1,81 @@
+// 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:html';
+
+/// Factory class for creating [HttpRequest] instances.
+class HttpRequestFactory {
+ /// Creates a [HttpRequestFactory].
+ const HttpRequestFactory();
+
+ /// Creates and sends a URL request for the specified [url].
+ ///
+ /// By default `request` will perform an HTTP GET request, but a different
+ /// method (`POST`, `PUT`, `DELETE`, etc) can be used by specifying the
+ /// [method] parameter. (See also [HttpRequest.postFormData] for `POST`
+ /// requests only.
+ ///
+ /// The Future is completed when the response is available.
+ ///
+ /// If specified, `sendData` will send data in the form of a [ByteBuffer],
+ /// [Blob], [Document], [String], or [FormData] along with the HttpRequest.
+ ///
+ /// If specified, [responseType] sets the desired response format for the
+ /// request. By default it is [String], but can also be 'arraybuffer', 'blob',
+ /// 'document', 'json', or 'text'. See also [HttpRequest.responseType]
+ /// for more information.
+ ///
+ /// The [withCredentials] parameter specified that credentials such as a cookie
+ /// (already) set in the header or
+ /// [authorization headers](http://tools.ietf.org/html/rfc1945#section-10.2)
+ /// should be specified for the request. Details to keep in mind when using
+ /// credentials:
+ ///
+ /// /// Using credentials is only useful for cross-origin requests.
+ /// /// The `Access-Control-Allow-Origin` header of `url` cannot contain a wildcard (///).
+ /// /// The `Access-Control-Allow-Credentials` header of `url` must be set to true.
+ /// /// If `Access-Control-Expose-Headers` has not been set to true, only a subset of all the response headers will be returned when calling [getAllResponseHeaders].
+ ///
+ /// The following is equivalent to the [getString] sample above:
+ ///
+ /// var name = Uri.encodeQueryComponent('John');
+ /// var id = Uri.encodeQueryComponent('42');
+ /// HttpRequest.request('users.json?name=$name&id=$id')
+ /// .then((HttpRequest resp) {
+ /// // Do something with the response.
+ /// });
+ ///
+ /// Here's an example of submitting an entire form with [FormData].
+ ///
+ /// var myForm = querySelector('form#myForm');
+ /// var data = new FormData(myForm);
+ /// HttpRequest.request('/submit', method: 'POST', sendData: data)
+ /// .then((HttpRequest resp) {
+ /// // Do something with the response.
+ /// });
+ ///
+ /// Note that requests for file:// URIs are only supported by Chrome extensions
+ /// with appropriate permissions in their manifest. Requests to file:// URIs
+ /// will also never fail- the Future will always complete successfully, even
+ /// when the file cannot be found.
+ ///
+ /// See also: [authorization headers](http://en.wikipedia.org/wiki/Basic_access_authentication).
+ Future<HttpRequest> request(String url,
+ {String? method,
+ bool? withCredentials,
+ String? responseType,
+ String? mimeType,
+ Map<String, String>? requestHeaders,
+ dynamic sendData,
+ void Function(ProgressEvent e)? onProgress}) {
+ return HttpRequest.request(url,
+ method: method,
+ withCredentials: withCredentials,
+ responseType: responseType,
+ mimeType: mimeType,
+ requestHeaders: requestHeaders,
+ sendData: sendData,
+ onProgress: onProgress);
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui.dart b/packages/webview_flutter/webview_flutter_web/lib/src/shims/dart_ui.dart
similarity index 100%
rename from packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui.dart
rename to packages/webview_flutter/webview_flutter_web/lib/src/shims/dart_ui.dart
diff --git a/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart b/packages/webview_flutter/webview_flutter_web/lib/src/shims/dart_ui_fake.dart
similarity index 100%
rename from packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart
rename to packages/webview_flutter/webview_flutter_web/lib/src/shims/dart_ui_fake.dart
diff --git a/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_real.dart b/packages/webview_flutter/webview_flutter_web/lib/src/shims/dart_ui_real.dart
similarity index 100%
rename from packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_real.dart
rename to packages/webview_flutter/webview_flutter_web/lib/src/shims/dart_ui_real.dart
diff --git a/packages/webview_flutter/webview_flutter_web/lib/src/web_webview_controller.dart b/packages/webview_flutter/webview_flutter_web/lib/src/web_webview_controller.dart
new file mode 100644
index 0000000..7ef7225
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_web/lib/src/web_webview_controller.dart
@@ -0,0 +1,116 @@
+// 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:convert';
+import 'dart:html';
+
+import 'package:flutter/cupertino.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'http_request_factory.dart';
+import 'shims/dart_ui.dart' as ui;
+
+/// An implementation of [PlatformWebViewControllerCreationParams] using Flutter
+/// for Web API.
+@immutable
+class WebWebViewControllerCreationParams
+ extends PlatformWebViewControllerCreationParams {
+ /// Creates a new [AndroidWebViewControllerCreationParams] instance.
+ WebWebViewControllerCreationParams({
+ @visibleForTesting this.httpRequestFactory = const HttpRequestFactory(),
+ }) : super();
+
+ /// Creates a [WebWebViewControllerCreationParams] instance based on [PlatformWebViewControllerCreationParams].
+ WebWebViewControllerCreationParams.fromPlatformWebViewControllerCreationParams(
+ // Recommended placeholder to prevent being broken by platform interface.
+ // ignore: avoid_unused_constructor_parameters
+ PlatformWebViewControllerCreationParams params, {
+ @visibleForTesting
+ HttpRequestFactory httpRequestFactory = const HttpRequestFactory(),
+ }) : this(httpRequestFactory: httpRequestFactory);
+
+ static int _nextIFrameId = 0;
+
+ /// Handles creating and sending URL requests.
+ final HttpRequestFactory httpRequestFactory;
+
+ /// The underlying element used as the WebView.
+ @visibleForTesting
+ final IFrameElement iFrame = IFrameElement()
+ ..id = 'webView${_nextIFrameId++}'
+ ..width = '100%'
+ ..height = '100%'
+ ..style.border = 'none';
+}
+
+/// An implementation of [PlatformWebViewController] using Flutter for Web API.
+class WebWebViewController extends PlatformWebViewController {
+ /// Constructs a [WebWebViewController].
+ WebWebViewController(PlatformWebViewControllerCreationParams params)
+ : super.implementation(params is WebWebViewControllerCreationParams
+ ? params
+ : WebWebViewControllerCreationParams
+ .fromPlatformWebViewControllerCreationParams(params));
+
+ WebWebViewControllerCreationParams get _webWebViewParams =>
+ params as WebWebViewControllerCreationParams;
+
+ @override
+ Future<void> loadHtmlString(String html, {String? baseUrl}) async {
+ // ignore: unsafe_html
+ _webWebViewParams.iFrame.src = Uri.dataFromString(
+ html,
+ mimeType: 'text/html',
+ encoding: utf8,
+ ).toString();
+ }
+
+ @override
+ Future<void> loadRequest(LoadRequestParams params) async {
+ if (!params.uri.hasScheme) {
+ throw ArgumentError(
+ 'LoadRequestParams#uri is required to have a scheme.');
+ }
+ final HttpRequest httpReq =
+ await _webWebViewParams.httpRequestFactory.request(
+ params.uri.toString(),
+ method: params.method.serialize(),
+ requestHeaders: params.headers,
+ sendData: params.body,
+ );
+ final String contentType =
+ httpReq.getResponseHeader('content-type') ?? 'text/html';
+ // ignore: unsafe_html
+ _webWebViewParams.iFrame.src = Uri.dataFromString(
+ httpReq.responseText ?? '',
+ mimeType: contentType,
+ encoding: utf8,
+ ).toString();
+ }
+}
+
+/// An implementation of [PlatformWebViewWidget] using Flutter the for Web API.
+class WebWebViewWidget extends PlatformWebViewWidget {
+ /// Constructs a [WebWebViewWidget].
+ WebWebViewWidget(PlatformWebViewWidgetCreationParams params)
+ : super.implementation(params) {
+ final WebWebViewController controller =
+ params.controller as WebWebViewController;
+ ui.platformViewRegistry.registerViewFactory(
+ controller._webWebViewParams.iFrame.id,
+ (int viewId) => controller._webWebViewParams.iFrame,
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return HtmlElementView(
+ key: params.key,
+ viewType: (params.controller as WebWebViewController)
+ ._webWebViewParams
+ .iFrame
+ .id,
+ );
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_web/lib/src/web_webview_platform.dart b/packages/webview_flutter/webview_flutter_web/lib/src/web_webview_platform.dart
new file mode 100644
index 0000000..2624832
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_web/lib/src/web_webview_platform.dart
@@ -0,0 +1,28 @@
+// 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 'package:flutter_web_plugins/flutter_web_plugins.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'web_webview_controller.dart';
+
+/// An implementation of [WebViewPlatform] using Flutter for Web API.
+class WebWebViewPlatform extends WebViewPlatform {
+ @override
+ PlatformWebViewController createPlatformWebViewController(
+ PlatformWebViewControllerCreationParams params,
+ ) {
+ return WebWebViewController(params);
+ }
+
+ @override
+ PlatformWebViewWidget createPlatformWebViewWidget(
+ PlatformWebViewWidgetCreationParams params,
+ ) {
+ return WebWebViewWidget(params);
+ }
+
+ /// Gets called when the plugin is registered.
+ static void registerWith(Registrar registrar) {}
+}
diff --git a/packages/webview_flutter/webview_flutter_web/lib/src/webview_flutter_web_legacy.dart b/packages/webview_flutter/webview_flutter_web/lib/src/webview_flutter_web_legacy.dart
new file mode 100644
index 0000000..ebf3c79
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_web/lib/src/webview_flutter_web_legacy.dart
@@ -0,0 +1,220 @@
+// 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 'dart:convert';
+import 'dart:html';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_web_plugins/flutter_web_plugins.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
+import 'http_request_factory.dart';
+import 'shims/dart_ui.dart' as ui;
+
+/// Builds an iframe based WebView.
+///
+/// This is used as the default implementation for [WebView.platform] on web.
+class WebWebViewPlatform implements WebViewPlatform {
+ /// Constructs a new instance of [WebWebViewPlatform].
+ WebWebViewPlatform() {
+ ui.platformViewRegistry.registerViewFactory(
+ 'webview-iframe',
+ (int viewId) => IFrameElement()
+ ..id = 'webview-$viewId'
+ ..width = '100%'
+ ..height = '100%'
+ ..style.border = 'none');
+ }
+
+ @override
+ Widget build({
+ required BuildContext context,
+ required CreationParams creationParams,
+ required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
+ required JavascriptChannelRegistry? javascriptChannelRegistry,
+ WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
+ Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
+ }) {
+ return HtmlElementView(
+ viewType: 'webview-iframe',
+ onPlatformViewCreated: (int viewId) {
+ if (onWebViewPlatformCreated == null) {
+ return;
+ }
+ final IFrameElement element =
+ document.getElementById('webview-$viewId')! as IFrameElement;
+ if (creationParams.initialUrl != null) {
+ // ignore: unsafe_html
+ element.src = creationParams.initialUrl;
+ }
+ onWebViewPlatformCreated(WebWebViewPlatformController(
+ element,
+ ));
+ },
+ );
+ }
+
+ @override
+ Future<bool> clearCookies() async => false;
+
+ /// Gets called when the plugin is registered.
+ static void registerWith(Registrar registrar) {}
+}
+
+/// Implementation of [WebViewPlatformController] for web.
+class WebWebViewPlatformController implements WebViewPlatformController {
+ /// Constructs a [WebWebViewPlatformController].
+ WebWebViewPlatformController(this._element);
+
+ final IFrameElement _element;
+ HttpRequestFactory _httpRequestFactory = const HttpRequestFactory();
+
+ /// Setter for setting the HttpRequestFactory, for testing purposes.
+ @visibleForTesting
+ // ignore: avoid_setters_without_getters
+ set httpRequestFactory(HttpRequestFactory factory) {
+ _httpRequestFactory = factory;
+ }
+
+ @override
+ Future<void> addJavascriptChannels(Set<String> javascriptChannelNames) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<bool> canGoBack() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<bool> canGoForward() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> clearCache() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<String?> currentUrl() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<String> evaluateJavascript(String javascript) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<int> getScrollX() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<int> getScrollY() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<String?> getTitle() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> goBack() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> goForward() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> loadUrl(String url, Map<String, String>? headers) async {
+ // ignore: unsafe_html
+ _element.src = url;
+ }
+
+ @override
+ Future<void> reload() {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> removeJavascriptChannels(Set<String> javascriptChannelNames) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> runJavascript(String javascript) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<String> runJavascriptReturningResult(String javascript) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> scrollBy(int x, int y) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> scrollTo(int x, int y) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> updateSettings(WebSettings setting) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> loadFile(String absoluteFilePath) {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future<void> loadHtmlString(
+ String html, {
+ String? baseUrl,
+ }) async {
+ // ignore: unsafe_html
+ _element.src = Uri.dataFromString(
+ html,
+ mimeType: 'text/html',
+ encoding: utf8,
+ ).toString();
+ }
+
+ @override
+ Future<void> loadRequest(WebViewRequest request) async {
+ if (!request.uri.hasScheme) {
+ throw ArgumentError('WebViewRequest#uri is required to have a scheme.');
+ }
+ final HttpRequest httpReq = await _httpRequestFactory.request(
+ request.uri.toString(),
+ method: request.method.serialize(),
+ requestHeaders: request.headers,
+ sendData: request.body);
+ final String contentType =
+ httpReq.getResponseHeader('content-type') ?? 'text/html';
+ // ignore: unsafe_html
+ _element.src = Uri.dataFromString(
+ httpReq.responseText ?? '',
+ mimeType: contentType,
+ encoding: utf8,
+ ).toString();
+ }
+
+ @override
+ Future<void> loadFlutterAsset(String key) {
+ throw UnimplementedError();
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart
index adf6495..f11c85e 100644
--- a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart
+++ b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart
@@ -2,290 +2,8 @@
// 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:convert';
-import 'dart:html';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/widgets.dart';
-import 'package:flutter_web_plugins/flutter_web_plugins.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
-import 'shims/dart_ui.dart' as ui;
+library webview_flutter_web;
-/// Builds an iframe based WebView.
-///
-/// This is used as the default implementation for [WebView.platform] on web.
-class WebWebViewPlatform implements WebViewPlatform {
- /// Constructs a new instance of [WebWebViewPlatform].
- WebWebViewPlatform() {
- ui.platformViewRegistry.registerViewFactory(
- 'webview-iframe',
- (int viewId) => IFrameElement()
- ..id = 'webview-$viewId'
- ..width = '100%'
- ..height = '100%'
- ..style.border = 'none');
- }
-
- @override
- Widget build({
- required BuildContext context,
- required CreationParams creationParams,
- required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
- required JavascriptChannelRegistry? javascriptChannelRegistry,
- WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
- Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
- }) {
- return HtmlElementView(
- viewType: 'webview-iframe',
- onPlatformViewCreated: (int viewId) {
- if (onWebViewPlatformCreated == null) {
- return;
- }
- final IFrameElement element =
- document.getElementById('webview-$viewId')! as IFrameElement;
- if (creationParams.initialUrl != null) {
- // ignore: unsafe_html
- element.src = creationParams.initialUrl;
- }
- onWebViewPlatformCreated(WebWebViewPlatformController(
- element,
- ));
- },
- );
- }
-
- @override
- Future<bool> clearCookies() async => false;
-
- /// Gets called when the plugin is registered.
- static void registerWith(Registrar registrar) {}
-}
-
-/// Implementation of [WebViewPlatformController] for web.
-class WebWebViewPlatformController implements WebViewPlatformController {
- /// Constructs a [WebWebViewPlatformController].
- WebWebViewPlatformController(this._element);
-
- final IFrameElement _element;
- HttpRequestFactory _httpRequestFactory = HttpRequestFactory();
-
- /// Setter for setting the HttpRequestFactory, for testing purposes.
- @visibleForTesting
- // ignore: avoid_setters_without_getters
- set httpRequestFactory(HttpRequestFactory factory) {
- _httpRequestFactory = factory;
- }
-
- @override
- Future<void> addJavascriptChannels(Set<String> javascriptChannelNames) {
- throw UnimplementedError();
- }
-
- @override
- Future<bool> canGoBack() {
- throw UnimplementedError();
- }
-
- @override
- Future<bool> canGoForward() {
- throw UnimplementedError();
- }
-
- @override
- Future<void> clearCache() {
- throw UnimplementedError();
- }
-
- @override
- Future<String?> currentUrl() {
- throw UnimplementedError();
- }
-
- @override
- Future<String> evaluateJavascript(String javascript) {
- throw UnimplementedError();
- }
-
- @override
- Future<int> getScrollX() {
- throw UnimplementedError();
- }
-
- @override
- Future<int> getScrollY() {
- throw UnimplementedError();
- }
-
- @override
- Future<String?> getTitle() {
- throw UnimplementedError();
- }
-
- @override
- Future<void> goBack() {
- throw UnimplementedError();
- }
-
- @override
- Future<void> goForward() {
- throw UnimplementedError();
- }
-
- @override
- Future<void> loadUrl(String url, Map<String, String>? headers) async {
- // ignore: unsafe_html
- _element.src = url;
- }
-
- @override
- Future<void> reload() {
- throw UnimplementedError();
- }
-
- @override
- Future<void> removeJavascriptChannels(Set<String> javascriptChannelNames) {
- throw UnimplementedError();
- }
-
- @override
- Future<void> runJavascript(String javascript) {
- throw UnimplementedError();
- }
-
- @override
- Future<String> runJavascriptReturningResult(String javascript) {
- throw UnimplementedError();
- }
-
- @override
- Future<void> scrollBy(int x, int y) {
- throw UnimplementedError();
- }
-
- @override
- Future<void> scrollTo(int x, int y) {
- throw UnimplementedError();
- }
-
- @override
- Future<void> updateSettings(WebSettings setting) {
- throw UnimplementedError();
- }
-
- @override
- Future<void> loadFile(String absoluteFilePath) {
- throw UnimplementedError();
- }
-
- @override
- Future<void> loadHtmlString(
- String html, {
- String? baseUrl,
- }) async {
- // ignore: unsafe_html
- _element.src = Uri.dataFromString(
- html,
- mimeType: 'text/html',
- encoding: utf8,
- ).toString();
- }
-
- @override
- Future<void> loadRequest(WebViewRequest request) async {
- if (!request.uri.hasScheme) {
- throw ArgumentError('WebViewRequest#uri is required to have a scheme.');
- }
- final HttpRequest httpReq = await _httpRequestFactory.request(
- request.uri.toString(),
- method: request.method.serialize(),
- requestHeaders: request.headers,
- sendData: request.body);
- final String contentType =
- httpReq.getResponseHeader('content-type') ?? 'text/html';
- // ignore: unsafe_html
- _element.src = Uri.dataFromString(
- httpReq.responseText ?? '',
- mimeType: contentType,
- encoding: utf8,
- ).toString();
- }
-
- @override
- Future<void> loadFlutterAsset(String key) {
- throw UnimplementedError();
- }
-}
-
-/// Factory class for creating [HttpRequest] instances.
-class HttpRequestFactory {
- /// Creates and sends a URL request for the specified [url].
- ///
- /// By default `request` will perform an HTTP GET request, but a different
- /// method (`POST`, `PUT`, `DELETE`, etc) can be used by specifying the
- /// [method] parameter. (See also [HttpRequest.postFormData] for `POST`
- /// requests only.
- ///
- /// The Future is completed when the response is available.
- ///
- /// If specified, `sendData` will send data in the form of a [ByteBuffer],
- /// [Blob], [Document], [String], or [FormData] along with the HttpRequest.
- ///
- /// If specified, [responseType] sets the desired response format for the
- /// request. By default it is [String], but can also be 'arraybuffer', 'blob',
- /// 'document', 'json', or 'text'. See also [HttpRequest.responseType]
- /// for more information.
- ///
- /// The [withCredentials] parameter specified that credentials such as a cookie
- /// (already) set in the header or
- /// [authorization headers](http://tools.ietf.org/html/rfc1945#section-10.2)
- /// should be specified for the request. Details to keep in mind when using
- /// credentials:
- ///
- /// /// Using credentials is only useful for cross-origin requests.
- /// /// The `Access-Control-Allow-Origin` header of `url` cannot contain a wildcard (///).
- /// /// The `Access-Control-Allow-Credentials` header of `url` must be set to true.
- /// /// If `Access-Control-Expose-Headers` has not been set to true, only a subset of all the response headers will be returned when calling [getAllResponseHeaders].
- ///
- /// The following is equivalent to the [getString] sample above:
- ///
- /// var name = Uri.encodeQueryComponent('John');
- /// var id = Uri.encodeQueryComponent('42');
- /// HttpRequest.request('users.json?name=$name&id=$id')
- /// .then((HttpRequest resp) {
- /// // Do something with the response.
- /// });
- ///
- /// Here's an example of submitting an entire form with [FormData].
- ///
- /// var myForm = querySelector('form#myForm');
- /// var data = new FormData(myForm);
- /// HttpRequest.request('/submit', method: 'POST', sendData: data)
- /// .then((HttpRequest resp) {
- /// // Do something with the response.
- /// });
- ///
- /// Note that requests for file:// URIs are only supported by Chrome extensions
- /// with appropriate permissions in their manifest. Requests to file:// URIs
- /// will also never fail- the Future will always complete successfully, even
- /// when the file cannot be found.
- ///
- /// See also: [authorization headers](http://en.wikipedia.org/wiki/Basic_access_authentication).
- Future<HttpRequest> request(String url,
- {String? method,
- bool? withCredentials,
- String? responseType,
- String? mimeType,
- Map<String, String>? requestHeaders,
- dynamic sendData,
- void Function(ProgressEvent e)? onProgress}) {
- return HttpRequest.request(url,
- method: method,
- withCredentials: withCredentials,
- responseType: responseType,
- mimeType: mimeType,
- requestHeaders: requestHeaders,
- sendData: sendData,
- onProgress: onProgress);
- }
-}
+export 'src/http_request_factory.dart';
+export 'src/web_webview_controller.dart';
+export 'src/web_webview_platform.dart';
diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml
index f27e640..10f0680 100644
--- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml
@@ -2,7 +2,7 @@
description: A Flutter plugin that provides a WebView widget on web.
repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
-version: 0.1.0+4
+version: 0.2.0
environment:
sdk: ">=2.14.0 <3.0.0"
@@ -21,7 +21,7 @@
sdk: flutter
flutter_web_plugins:
sdk: flutter
- webview_flutter_platform_interface: ^1.8.0
+ webview_flutter_platform_interface: ^2.0.0
dev_dependencies:
build_runner: ^2.1.5
diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart b/packages/webview_flutter/webview_flutter_web/test/legacy/webview_flutter_web_test.dart
similarity index 95%
rename from packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart
rename to packages/webview_flutter/webview_flutter_web/test/legacy/webview_flutter_web_test.dart
index 76dad6f..54e53bb 100644
--- a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart
+++ b/packages/webview_flutter/webview_flutter_web/test/legacy/webview_flutter_web_test.dart
@@ -9,9 +9,11 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
-import 'package:webview_flutter_web/webview_flutter_web.dart';
-import './webview_flutter_web_test.mocks.dart';
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
+import 'package:webview_flutter_web/src/http_request_factory.dart';
+import 'package:webview_flutter_web/src/webview_flutter_web_legacy.dart';
+
+import 'webview_flutter_web_test.mocks.dart';
@GenerateMocks(<Type>[
IFrameElement,
diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart b/packages/webview_flutter/webview_flutter_web/test/legacy/webview_flutter_web_test.mocks.dart
similarity index 99%
rename from packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart
rename to packages/webview_flutter/webview_flutter_web/test/legacy/webview_flutter_web_test.mocks.dart
index db442ee..ac7122e 100644
--- a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_web/test/legacy/webview_flutter_web_test.mocks.dart
@@ -1,5 +1,5 @@
// Mocks generated by Mockito 5.3.2 from annotations
-// in webview_flutter_web/test/webview_flutter_web_test.dart.
+// in webview_flutter_web/test/legacy/webview_flutter_web_test.dart.
// Do not manually edit this file.
// ignore_for_file: no_leading_underscores_for_library_prefixes
@@ -11,10 +11,11 @@
import 'package:flutter/src/widgets/notification_listener.dart' as _i7;
import 'package:flutter/widgets.dart' as _i4;
import 'package:mockito/mockito.dart' as _i1;
-import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i8;
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'
+import 'package:webview_flutter_platform_interface/src/legacy/platform_interface/webview_platform_callbacks_handler.dart'
as _i9;
-import 'package:webview_flutter_web/webview_flutter_web.dart' as _i10;
+import 'package:webview_flutter_platform_interface/src/legacy/types/types.dart'
+ as _i8;
+import 'package:webview_flutter_web/src/http_request_factory.dart' as _i10;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
@@ -2117,11 +2118,6 @@
),
) as _i4.Widget);
@override
- bool get mounted => (super.noSuchMethod(
- Invocation.getter(#mounted),
- returnValue: false,
- ) as bool);
- @override
bool get debugDoingBuild => (super.noSuchMethod(
Invocation.getter(#debugDoingBuild),
returnValue: false,
diff --git a/packages/webview_flutter/webview_flutter_web/test/web_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_web/test/web_webview_controller_test.dart
new file mode 100644
index 0000000..6a8f7379
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_web/test/web_webview_controller_test.dart
@@ -0,0 +1,150 @@
+// 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:html';
+// 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 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_web/webview_flutter_web.dart';
+
+import 'web_webview_controller_test.mocks.dart';
+
+@GenerateMocks(<Type>[
+ HttpRequest,
+ HttpRequestFactory,
+])
+void main() {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ group('WebWebViewController', () {
+ group('WebWebViewControllerCreationParams', () {
+ test('sets iFrame fields', () {
+ final WebWebViewControllerCreationParams params =
+ WebWebViewControllerCreationParams();
+
+ expect(params.iFrame.id, contains('webView'));
+ expect(params.iFrame.width, '100%');
+ expect(params.iFrame.height, '100%');
+ expect(params.iFrame.style.border, 'none');
+ });
+ });
+
+ group('loadHtmlString', () {
+ test('loadHtmlString loads html into iframe', () async {
+ final WebWebViewController controller =
+ WebWebViewController(WebWebViewControllerCreationParams());
+
+ await controller.loadHtmlString('test html');
+ expect(
+ (controller.params as WebWebViewControllerCreationParams).iFrame.src,
+ 'data:text/html;charset=utf-8,${Uri.encodeFull('test html')}',
+ );
+ });
+
+ test('loadHtmlString escapes "#" correctly', () async {
+ final WebWebViewController controller =
+ WebWebViewController(WebWebViewControllerCreationParams());
+
+ await controller.loadHtmlString('#');
+ expect(
+ (controller.params as WebWebViewControllerCreationParams).iFrame.src,
+ contains('%23'),
+ );
+ });
+ });
+
+ group('loadRequest', () {
+ test('loadRequest throws ArgumentError on missing scheme', () async {
+ final WebWebViewController controller =
+ WebWebViewController(WebWebViewControllerCreationParams());
+
+ await expectLater(
+ () async => controller.loadRequest(
+ LoadRequestParams(uri: Uri.parse('flutter.dev')),
+ ),
+ throwsA(const TypeMatcher<ArgumentError>()));
+ });
+
+ test('loadRequest makes request and loads response into iframe',
+ () async {
+ final MockHttpRequestFactory mockHttpRequestFactory =
+ MockHttpRequestFactory();
+ final WebWebViewController controller =
+ WebWebViewController(WebWebViewControllerCreationParams(
+ httpRequestFactory: mockHttpRequestFactory,
+ ));
+
+ final MockHttpRequest mockHttpRequest = MockHttpRequest();
+ when(mockHttpRequest.getResponseHeader('content-type'))
+ .thenReturn('text/plain');
+ when(mockHttpRequest.responseText).thenReturn('test data');
+
+ when(mockHttpRequestFactory.request(
+ any,
+ method: anyNamed('method'),
+ requestHeaders: anyNamed('requestHeaders'),
+ sendData: anyNamed('sendData'),
+ )).thenAnswer((_) => Future<HttpRequest>.value(mockHttpRequest));
+
+ await controller.loadRequest(LoadRequestParams(
+ uri: Uri.parse('https://flutter.dev'),
+ method: LoadRequestMethod.post,
+ body: Uint8List.fromList('test body'.codeUnits),
+ headers: const <String, String>{'Foo': 'Bar'},
+ ));
+
+ verify(mockHttpRequestFactory.request(
+ 'https://flutter.dev',
+ method: 'post',
+ requestHeaders: <String, String>{'Foo': 'Bar'},
+ sendData: Uint8List.fromList('test body'.codeUnits),
+ ));
+
+ expect(
+ (controller.params as WebWebViewControllerCreationParams).iFrame.src,
+ 'data:;charset=utf-8,${Uri.encodeFull('test data')}',
+ );
+ });
+
+ test('loadRequest escapes "#" correctly', () async {
+ final MockHttpRequestFactory mockHttpRequestFactory =
+ MockHttpRequestFactory();
+ final WebWebViewController controller =
+ WebWebViewController(WebWebViewControllerCreationParams(
+ httpRequestFactory: mockHttpRequestFactory,
+ ));
+
+ final MockHttpRequest mockHttpRequest = MockHttpRequest();
+ when(mockHttpRequest.getResponseHeader('content-type'))
+ .thenReturn('text/html');
+ when(mockHttpRequest.responseText).thenReturn('#');
+ when(mockHttpRequestFactory.request(
+ any,
+ method: anyNamed('method'),
+ requestHeaders: anyNamed('requestHeaders'),
+ sendData: anyNamed('sendData'),
+ )).thenAnswer((_) => Future<HttpRequest>.value(mockHttpRequest));
+
+ await controller.loadRequest(LoadRequestParams(
+ uri: Uri.parse('https://flutter.dev'),
+ method: LoadRequestMethod.post,
+ body: Uint8List.fromList('test body'.codeUnits),
+ headers: const <String, String>{'Foo': 'Bar'},
+ ));
+
+ expect(
+ (controller.params as WebWebViewControllerCreationParams).iFrame.src,
+ contains('%23'),
+ );
+ });
+ });
+ });
+}
diff --git a/packages/webview_flutter/webview_flutter_web/test/web_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_web/test/web_webview_controller_test.mocks.dart
new file mode 100644
index 0000000..f74359a
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_web/test/web_webview_controller_test.mocks.dart
@@ -0,0 +1,328 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter_web/test/web_webview_controller_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i3;
+import 'dart:html' as _i2;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_web/src/http_request_factory.dart' as _i4;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakeHttpRequestUpload_0 extends _i1.SmartFake
+ implements _i2.HttpRequestUpload {
+ _FakeHttpRequestUpload_0(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeEvents_1 extends _i1.SmartFake implements _i2.Events {
+ _FakeEvents_1(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeHttpRequest_2 extends _i1.SmartFake implements _i2.HttpRequest {
+ _FakeHttpRequest_2(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+/// A class which mocks [HttpRequest].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockHttpRequest extends _i1.Mock implements _i2.HttpRequest {
+ MockHttpRequest() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ Map<String, String> get responseHeaders => (super.noSuchMethod(
+ Invocation.getter(#responseHeaders),
+ returnValue: <String, String>{},
+ ) as Map<String, String>);
+ @override
+ int get readyState => (super.noSuchMethod(
+ Invocation.getter(#readyState),
+ returnValue: 0,
+ ) as int);
+ @override
+ String get responseType => (super.noSuchMethod(
+ Invocation.getter(#responseType),
+ returnValue: '',
+ ) as String);
+ @override
+ set responseType(String? value) => super.noSuchMethod(
+ Invocation.setter(
+ #responseType,
+ value,
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ set timeout(int? value) => super.noSuchMethod(
+ Invocation.setter(
+ #timeout,
+ value,
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ _i2.HttpRequestUpload get upload => (super.noSuchMethod(
+ Invocation.getter(#upload),
+ returnValue: _FakeHttpRequestUpload_0(
+ this,
+ Invocation.getter(#upload),
+ ),
+ ) as _i2.HttpRequestUpload);
+ @override
+ set withCredentials(bool? value) => super.noSuchMethod(
+ Invocation.setter(
+ #withCredentials,
+ value,
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ _i3.Stream<_i2.Event> get onReadyStateChange => (super.noSuchMethod(
+ Invocation.getter(#onReadyStateChange),
+ returnValue: _i3.Stream<_i2.Event>.empty(),
+ ) as _i3.Stream<_i2.Event>);
+ @override
+ _i3.Stream<_i2.ProgressEvent> get onAbort => (super.noSuchMethod(
+ Invocation.getter(#onAbort),
+ returnValue: _i3.Stream<_i2.ProgressEvent>.empty(),
+ ) as _i3.Stream<_i2.ProgressEvent>);
+ @override
+ _i3.Stream<_i2.ProgressEvent> get onError => (super.noSuchMethod(
+ Invocation.getter(#onError),
+ returnValue: _i3.Stream<_i2.ProgressEvent>.empty(),
+ ) as _i3.Stream<_i2.ProgressEvent>);
+ @override
+ _i3.Stream<_i2.ProgressEvent> get onLoad => (super.noSuchMethod(
+ Invocation.getter(#onLoad),
+ returnValue: _i3.Stream<_i2.ProgressEvent>.empty(),
+ ) as _i3.Stream<_i2.ProgressEvent>);
+ @override
+ _i3.Stream<_i2.ProgressEvent> get onLoadEnd => (super.noSuchMethod(
+ Invocation.getter(#onLoadEnd),
+ returnValue: _i3.Stream<_i2.ProgressEvent>.empty(),
+ ) as _i3.Stream<_i2.ProgressEvent>);
+ @override
+ _i3.Stream<_i2.ProgressEvent> get onLoadStart => (super.noSuchMethod(
+ Invocation.getter(#onLoadStart),
+ returnValue: _i3.Stream<_i2.ProgressEvent>.empty(),
+ ) as _i3.Stream<_i2.ProgressEvent>);
+ @override
+ _i3.Stream<_i2.ProgressEvent> get onProgress => (super.noSuchMethod(
+ Invocation.getter(#onProgress),
+ returnValue: _i3.Stream<_i2.ProgressEvent>.empty(),
+ ) as _i3.Stream<_i2.ProgressEvent>);
+ @override
+ _i3.Stream<_i2.ProgressEvent> get onTimeout => (super.noSuchMethod(
+ Invocation.getter(#onTimeout),
+ returnValue: _i3.Stream<_i2.ProgressEvent>.empty(),
+ ) as _i3.Stream<_i2.ProgressEvent>);
+ @override
+ _i2.Events get on => (super.noSuchMethod(
+ Invocation.getter(#on),
+ returnValue: _FakeEvents_1(
+ this,
+ Invocation.getter(#on),
+ ),
+ ) as _i2.Events);
+ @override
+ void open(
+ String? method,
+ String? url, {
+ bool? async,
+ String? user,
+ String? password,
+ }) =>
+ super.noSuchMethod(
+ Invocation.method(
+ #open,
+ [
+ method,
+ url,
+ ],
+ {
+ #async: async,
+ #user: user,
+ #password: password,
+ },
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ void abort() => super.noSuchMethod(
+ Invocation.method(
+ #abort,
+ [],
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ String getAllResponseHeaders() => (super.noSuchMethod(
+ Invocation.method(
+ #getAllResponseHeaders,
+ [],
+ ),
+ returnValue: '',
+ ) as String);
+ @override
+ String? getResponseHeader(String? name) =>
+ (super.noSuchMethod(Invocation.method(
+ #getResponseHeader,
+ [name],
+ )) as String?);
+ @override
+ void overrideMimeType(String? mime) => super.noSuchMethod(
+ Invocation.method(
+ #overrideMimeType,
+ [mime],
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ void send([dynamic body_OR_data]) => super.noSuchMethod(
+ Invocation.method(
+ #send,
+ [body_OR_data],
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ void setRequestHeader(
+ String? name,
+ String? value,
+ ) =>
+ super.noSuchMethod(
+ Invocation.method(
+ #setRequestHeader,
+ [
+ name,
+ value,
+ ],
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ void addEventListener(
+ String? type,
+ _i2.EventListener? listener, [
+ bool? useCapture,
+ ]) =>
+ super.noSuchMethod(
+ Invocation.method(
+ #addEventListener,
+ [
+ type,
+ listener,
+ useCapture,
+ ],
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ void removeEventListener(
+ String? type,
+ _i2.EventListener? listener, [
+ bool? useCapture,
+ ]) =>
+ super.noSuchMethod(
+ Invocation.method(
+ #removeEventListener,
+ [
+ type,
+ listener,
+ useCapture,
+ ],
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ bool dispatchEvent(_i2.Event? event) => (super.noSuchMethod(
+ Invocation.method(
+ #dispatchEvent,
+ [event],
+ ),
+ returnValue: false,
+ ) as bool);
+}
+
+/// A class which mocks [HttpRequestFactory].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockHttpRequestFactory extends _i1.Mock
+ implements _i4.HttpRequestFactory {
+ MockHttpRequestFactory() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i3.Future<_i2.HttpRequest> request(
+ String? url, {
+ String? method,
+ bool? withCredentials,
+ String? responseType,
+ String? mimeType,
+ Map<String, String>? requestHeaders,
+ dynamic sendData,
+ void Function(_i2.ProgressEvent)? onProgress,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #request,
+ [url],
+ {
+ #method: method,
+ #withCredentials: withCredentials,
+ #responseType: responseType,
+ #mimeType: mimeType,
+ #requestHeaders: requestHeaders,
+ #sendData: sendData,
+ #onProgress: onProgress,
+ },
+ ),
+ returnValue: _i3.Future<_i2.HttpRequest>.value(_FakeHttpRequest_2(
+ this,
+ Invocation.method(
+ #request,
+ [url],
+ {
+ #method: method,
+ #withCredentials: withCredentials,
+ #responseType: responseType,
+ #mimeType: mimeType,
+ #requestHeaders: requestHeaders,
+ #sendData: sendData,
+ #onProgress: onProgress,
+ },
+ ),
+ )),
+ ) as _i3.Future<_i2.HttpRequest>);
+}
diff --git a/packages/webview_flutter/webview_flutter_web/test/web_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_web/test/web_webview_widget_test.dart
new file mode 100644
index 0000000..834d95f
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_web/test/web_webview_widget_test.dart
@@ -0,0 +1,33 @@
+// 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 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_web/webview_flutter_web.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ group('WebWebViewWidget', () {
+ testWidgets('build returns a HtmlElementView', (WidgetTester tester) async {
+ final WebWebViewController controller =
+ WebWebViewController(WebWebViewControllerCreationParams());
+
+ final WebWebViewWidget widget = WebWebViewWidget(
+ PlatformWebViewWidgetCreationParams(
+ key: const Key('keyValue'),
+ controller: controller,
+ ),
+ );
+
+ await tester.pumpWidget(
+ Builder(builder: (BuildContext context) => widget.build(context)),
+ );
+
+ expect(find.byType(HtmlElementView), findsOneWidget);
+ expect(find.byKey(const Key('keyValue')), findsOneWidget);
+ });
+ });
+}
diff --git a/script/configs/exclude_all_plugins_app.yaml b/script/configs/exclude_all_plugins_app.yaml
index c7d589e..a19f5fe 100644
--- a/script/configs/exclude_all_plugins_app.yaml
+++ b/script/configs/exclude_all_plugins_app.yaml
@@ -14,3 +14,4 @@
- webview_flutter_platform_interface
- webview_flutter_wkwebview
- webview_flutter_android
+- webview_flutter_web