diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 82be36f7d094..3b2c0212042a 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 a7711ee171e2..dcd1410f42c7 100644 --- a/packages/webview_flutter/webview_flutter_web/README.md +++ b/packages/webview_flutter/webview_flutter_web/README.md @@ -5,10 +5,8 @@ This is an implementation of the [`webview_flutter`](https://pub.dev/packages/we 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 @@ yet, so it currently requires extra setup to use: * [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/webview_flutter.dart'; import 'package:webview_flutter_web/webview_flutter_web.dart'; main() { - WebView.platform = WebWebViewPlatform(); + WebViewPlatform.instance = WebWebViewPlatform(); ... ``` @@ -55,7 +53,7 @@ import 'package:webview_flutter/webview_flutter.dart'; 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 000000000000..db27f7ab5d8d --- /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 controllerCompleter = + Completer(); + 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 controllerCompleter = + Completer(); + 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 232ecdd302b7..1736d47d39c8 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 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('loadRequest', (WidgetTester tester) async { + final WebWebViewController controller = + WebWebViewController(const PlatformWebViewControllerCreationParams()) + ..loadRequest( + LoadRequestParams(uri: Uri.parse(primaryUrl)), + ); - testWidgets('initialUrl', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); 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 @@ void main() { expect(element!.src, primaryUrl); }); - testWidgets('loadUrl', (WidgetTester tester) async { - final Completer controllerCompleter = - Completer(); + 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 98% 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 ffd3367d33f4..b9b8ce23537b 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 c183625be634..ca268a28e47b 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:async'; 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 _WebViewExample extends StatefulWidget { } class _WebViewExampleState extends State<_WebViewExample> { - final Completer _controller = - Completer(); + final PlatformWebViewController _controller = PlatformWebViewController( + const PlatformWebViewControllerCreationParams(), + )..loadRequest( + LoadRequestParams( + uri: Uri.parse('https://flutter.dev'), + ), + ); @override Widget build(BuildContext context) { @@ -32,15 +37,12 @@ class _WebViewExampleState extends State<_WebViewExample> { appBar: AppBar( title: const Text('Flutter WebView example'), actions: [ - _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 @@ enum _MenuOptions { class _SampleMenu extends StatelessWidget { const _SampleMenu(this.controller); - final Future controller; + final PlatformWebViewController controller; @override Widget build(BuildContext context) { - return FutureBuilder( - future: controller, - builder: - (BuildContext context, AsyncSnapshot controller) { - return PopupMenuButton<_MenuOptions>( - onSelected: (_MenuOptions value) { - switch (value) { - case _MenuOptions.doPostRequest: - _onDoPostRequest(controller.data!, context); - break; - } - }, - itemBuilder: (BuildContext context) => >[ - 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) => >[ + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.doPostRequest, + child: Text('Post Request'), + ), + ], ); } - Future _onDoPostRequest( - WebViewController controller, BuildContext context) async { - final WebViewRequest request = WebViewRequest( + Future _onDoPostRequest(PlatformWebViewController controller) async { + final LoadRequestParams params = LoadRequestParams( uri: Uri.parse('https://httpbin.org/post'), - method: WebViewRequestMethod.post, - headers: {'foo': 'bar', 'Content-Type': 'text/plain'}, + method: LoadRequestMethod.post, + headers: const { + '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 e2e0796e7ea3..782817eb7100 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 @@ dependencies: 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 000000000000..4bd92f0db1db --- /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 request(String url, + {String? method, + bool? withCredentials, + String? responseType, + String? mimeType, + Map? 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 000000000000..7ef72257999f --- /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 loadHtmlString(String html, {String? baseUrl}) async { + // ignore: unsafe_html + _webWebViewParams.iFrame.src = Uri.dataFromString( + html, + mimeType: 'text/html', + encoding: utf8, + ).toString(); + } + + @override + Future 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 000000000000..2624832514cd --- /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 000000000000..ebf3c799947e --- /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>? 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 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 addJavascriptChannels(Set javascriptChannelNames) { + throw UnimplementedError(); + } + + @override + Future canGoBack() { + throw UnimplementedError(); + } + + @override + Future canGoForward() { + throw UnimplementedError(); + } + + @override + Future clearCache() { + throw UnimplementedError(); + } + + @override + Future currentUrl() { + throw UnimplementedError(); + } + + @override + Future evaluateJavascript(String javascript) { + throw UnimplementedError(); + } + + @override + Future getScrollX() { + throw UnimplementedError(); + } + + @override + Future getScrollY() { + throw UnimplementedError(); + } + + @override + Future getTitle() { + throw UnimplementedError(); + } + + @override + Future goBack() { + throw UnimplementedError(); + } + + @override + Future goForward() { + throw UnimplementedError(); + } + + @override + Future loadUrl(String url, Map? headers) async { + // ignore: unsafe_html + _element.src = url; + } + + @override + Future reload() { + throw UnimplementedError(); + } + + @override + Future removeJavascriptChannels(Set javascriptChannelNames) { + throw UnimplementedError(); + } + + @override + Future runJavascript(String javascript) { + throw UnimplementedError(); + } + + @override + Future runJavascriptReturningResult(String javascript) { + throw UnimplementedError(); + } + + @override + Future scrollBy(int x, int y) { + throw UnimplementedError(); + } + + @override + Future scrollTo(int x, int y) { + throw UnimplementedError(); + } + + @override + Future updateSettings(WebSettings setting) { + throw UnimplementedError(); + } + + @override + Future loadFile(String absoluteFilePath) { + throw UnimplementedError(); + } + + @override + Future loadHtmlString( + String html, { + String? baseUrl, + }) async { + // ignore: unsafe_html + _element.src = Uri.dataFromString( + html, + mimeType: 'text/html', + encoding: utf8, + ).toString(); + } + + @override + Future 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 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 adf6495b8f2a..f11c85e4bf29 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>? 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 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 addJavascriptChannels(Set javascriptChannelNames) { - throw UnimplementedError(); - } - - @override - Future canGoBack() { - throw UnimplementedError(); - } - - @override - Future canGoForward() { - throw UnimplementedError(); - } - - @override - Future clearCache() { - throw UnimplementedError(); - } - - @override - Future currentUrl() { - throw UnimplementedError(); - } - - @override - Future evaluateJavascript(String javascript) { - throw UnimplementedError(); - } - - @override - Future getScrollX() { - throw UnimplementedError(); - } - - @override - Future getScrollY() { - throw UnimplementedError(); - } - - @override - Future getTitle() { - throw UnimplementedError(); - } - - @override - Future goBack() { - throw UnimplementedError(); - } - - @override - Future goForward() { - throw UnimplementedError(); - } - - @override - Future loadUrl(String url, Map? headers) async { - // ignore: unsafe_html - _element.src = url; - } - - @override - Future reload() { - throw UnimplementedError(); - } - - @override - Future removeJavascriptChannels(Set javascriptChannelNames) { - throw UnimplementedError(); - } - - @override - Future runJavascript(String javascript) { - throw UnimplementedError(); - } - - @override - Future runJavascriptReturningResult(String javascript) { - throw UnimplementedError(); - } - - @override - Future scrollBy(int x, int y) { - throw UnimplementedError(); - } - - @override - Future scrollTo(int x, int y) { - throw UnimplementedError(); - } - - @override - Future updateSettings(WebSettings setting) { - throw UnimplementedError(); - } - - @override - Future loadFile(String absoluteFilePath) { - throw UnimplementedError(); - } - - @override - Future loadHtmlString( - String html, { - String? baseUrl, - }) async { - // ignore: unsafe_html - _element.src = Uri.dataFromString( - html, - mimeType: 'text/html', - encoding: utf8, - ).toString(); - } - - @override - Future 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 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 request(String url, - {String? method, - bool? withCredentials, - String? responseType, - String? mimeType, - Map? 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 f27e6408335a..10f06801e805 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web 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 @@ dependencies: 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 76dad6f3669d..54e53bb11925 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/material.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 './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([ 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 db442eeea7a3..ac7122eacb63 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/foundation.dart' as _i5; 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 @@ class MockBuildContext extends _i1.Mock implements _i4.BuildContext { ), ) 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 000000000000..6a8f73798107 --- /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([ + 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())); + }); + + 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.value(mockHttpRequest)); + + await controller.loadRequest(LoadRequestParams( + uri: Uri.parse('https://flutter.dev'), + method: LoadRequestMethod.post, + body: Uint8List.fromList('test body'.codeUnits), + headers: const {'Foo': 'Bar'}, + )); + + verify(mockHttpRequestFactory.request( + 'https://flutter.dev', + method: 'post', + requestHeaders: {'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.value(mockHttpRequest)); + + await controller.loadRequest(LoadRequestParams( + uri: Uri.parse('https://flutter.dev'), + method: LoadRequestMethod.post, + body: Uint8List.fromList('test body'.codeUnits), + headers: const {'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 000000000000..f74359aac431 --- /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 get responseHeaders => (super.noSuchMethod( + Invocation.getter(#responseHeaders), + returnValue: {}, + ) as Map); + @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? 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 000000000000..834d95f3ca20 --- /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 c7d589ebef33..a19f5fe0ca37 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