diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 6bdb9145ff93..e56b98362636 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.3+1 + +* Fixes DOM timing issue with Legacy Widget build method. + ## 0.2.3 * Migrates to `package:web` 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 deleted file mode 100644 index affd7bbda224..000000000000 --- a/packages/webview_flutter/webview_flutter_web/example/integration_test/legacy/webview_flutter_test.dart +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import '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/legacy/web_view.dart'; - -void main() async { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - const String primaryPage = 'first.txt'; - const String secondaryPage = 'second.txt'; - final HttpServer server = - await HttpServer.bind(InternetAddress.loopbackIPv4, 0); - unawaited(server.forEach((HttpRequest request) { - if (request.uri.path == '/$primaryPage') { - request.response.writeln('Hello, world.'); - } - if (request.uri.path == '/$secondaryPage') { - request.response.writeln('Another page.'); - } else { - fail('unexpected request: ${request.method} ${request.uri}'); - } - request.response.close(); - })); - final String prefixUrl = 'http://localhost:${server.port}'; - final String primaryUrl = '$prefixUrl/$primaryPage'; - final String secondaryUrl = '$prefixUrl/$secondaryPage'; - - 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/legacy_webview_flutter_test_manual.dart b/packages/webview_flutter/webview_flutter_web/example/integration_test/legacy_webview_flutter_test_manual.dart new file mode 100644 index 000000000000..173de6bd6dc4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/integration_test/legacy_webview_flutter_test_manual.dart @@ -0,0 +1,63 @@ +// 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. + +// This test flakes badly in headless mode! + +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:web/web.dart' as web; +import 'package:webview_flutter_web_example/legacy/web_view.dart'; + +import 'wrapped_webview.dart'; + +void main() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + const String someUrl = 'about:blank'; + const String fakeUrl = 'https://www.flutter.dev/'; + + testWidgets('initialUrl', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + await tester.pumpWidget( + wrappedLegacyWebView(fakeUrl, (WebViewController controller) { + controllerCompleter.complete(controller); + }), + ); + await controllerCompleter.future; + // Pump 2 frames so the framework injects the platform view into the DOM. + await tester.pump(); + await tester.pump(const Duration(seconds: 5)); + + // Assert an iframe has been rendered to the DOM with the correct src attribute. + final web.HTMLIFrameElement? element = + web.document.querySelector('iframe') as web.HTMLIFrameElement?; + expect(element, isNotNull); + expect(element!.src, fakeUrl); + }); + + testWidgets('loadUrl', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + await tester.pumpWidget( + wrappedLegacyWebView(someUrl, (WebViewController controller) { + controllerCompleter.complete(controller); + }), + ); + + final WebViewController controller = await controllerCompleter.future; + await controller.loadUrl(fakeUrl); + // Pump 2 frames so the framework injects the platform view into the DOM. + await tester.pump(); + await tester.pump(const Duration(seconds: 5)); + + // Assert an iframe has been rendered to the DOM with the correct src attribute. + final web.HTMLIFrameElement? element = + web.document.querySelector('iframe') as web.HTMLIFrameElement?; + expect(element, isNotNull); + expect(element!.src, fakeUrl); + }); +} 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 734ace71fa36..56fd4eee6229 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,82 +2,63 @@ // 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'; - -// FIX (dit): Remove these integration tests, or make them run. They currently never fail. -// (They won't run because they use `dart:io`. If you remove all `dart:io` bits from -// this file, they start failing with `fail()`, for example.) - -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:web/web.dart' as web; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_web/webview_flutter_web.dart'; +import 'wrapped_webview.dart'; + Future main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0); - unawaited(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'; + const String fakeUrl = 'about:blank'; testWidgets('loadRequest', (WidgetTester tester) async { - final WebWebViewController controller = - WebWebViewController(const PlatformWebViewControllerCreationParams()); + final WebWebViewController controller = WebWebViewController( + const PlatformWebViewControllerCreationParams(), + ); await controller.loadRequest( - LoadRequestParams(uri: Uri.parse(primaryUrl)), + LoadRequestParams(uri: Uri.parse(fakeUrl)), ); await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Builder(builder: (BuildContext context) { - return WebWebViewWidget( - PlatformWebViewWidgetCreationParams(controller: controller), - ).build(context); - }), - ), + wrappedWebView(controller), ); + // Pump 2 frames so the framework injects the platform view into the DOM. + // The duration of the second pump is set so the browser has some idle time + // to actually show the contents of the iFrame. + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); - // 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?; + // Assert an iFrame has been rendered to the DOM with the correct src attribute. + final web.HTMLIFrameElement? element = + web.document.querySelector('iframe') as web.HTMLIFrameElement?; expect(element, isNotNull); - expect(element!.src, primaryUrl); + expect(element!.src, fakeUrl); }); testWidgets('loadHtmlString', (WidgetTester tester) async { - final WebWebViewController controller = - WebWebViewController(const PlatformWebViewControllerCreationParams()); + final WebWebViewController controller = WebWebViewController( + const PlatformWebViewControllerCreationParams(), + ); await controller.loadHtmlString( 'data:text/html;charset=utf-8,${Uri.encodeFull('test html')}', ); await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Builder(builder: (BuildContext context) { - return WebWebViewWidget( - PlatformWebViewWidgetCreationParams(controller: controller), - ).build(context); - }), - ), + wrappedWebView(controller), ); + // Pump 2 frames so the framework injects the platform view into the DOM. + // The duration of the second pump is set so the browser has some idle time + // to actually show the contents of the iFrame. + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); - // 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?; + // Assert an iFrame has been rendered to the DOM with the correct src attribute. + final web.HTMLIFrameElement? element = + web.document.querySelector('iframe') as web.HTMLIFrameElement?; expect(element, isNotNull); expect( element!.src, diff --git a/packages/webview_flutter/webview_flutter_web/example/integration_test/wrapped_webview.dart b/packages/webview_flutter/webview_flutter_web/example/integration_test/wrapped_webview.dart new file mode 100644 index 000000000000..0dc10cbf5c2d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/integration_test/wrapped_webview.dart @@ -0,0 +1,51 @@ +// 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:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_web/webview_flutter_web.dart'; +import 'package:webview_flutter_web_example/legacy/web_view.dart'; + +/// Returns the webview widget for a given [controller], wrapped so it works +/// in our integration tests. +Widget wrappedWebView(WebWebViewController controller) { + return _wrapped( + Builder( + builder: (BuildContext ctx) => PlatformWebViewWidget( + PlatformWebViewWidgetCreationParams(controller: controller), + ).build(ctx), + ), + ); +} + +/// Returns a (legacy) webview widget for an [url], that calls [onCreated] when +/// done, wrapped so it works in our integration tests. +Widget wrappedLegacyWebView(String url, WebViewCreatedCallback onCreated) { + return _wrapped( + WebView( + initialUrl: url, + onWebViewCreated: onCreated, + ), + ); +} + +// Wraps a [child] widget in the scaffolding this test needs. +Widget _wrapped(Widget child) { + return MaterialApp( + home: Scaffold( + body: Center( + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + ), + width: 320, + height: 200, + child: child, + ), + ), + ), + ); +} diff --git a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml index 23754739bf48..a71a53777c58 100644 --- a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter + web: ^0.5.0 webview_flutter_platform_interface: ^2.0.0 webview_flutter_web: # When depending on this package from a real application you should use: 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 index 4820743a08d0..460323e4e267 100644 --- 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 @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:js_interop'; -import 'dart:ui_web' as ui_web; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; @@ -21,17 +20,6 @@ import 'http_request_factory.dart'; /// /// This is used as the default implementation for [WebView.platform] on web. class WebWebViewPlatform implements WebViewPlatform { - /// Constructs a new instance of [WebWebViewPlatform]. - WebWebViewPlatform() { - ui_web.platformViewRegistry.registerViewFactory( - 'webview-iframe', - (int viewId) => web.HTMLIFrameElement() - ..id = 'webview-$viewId' - ..width = '100%' - ..height = '100%' - ..style.border = 'none'); - } - @override Widget build({ required BuildContext context, @@ -41,23 +29,21 @@ class WebWebViewPlatform implements WebViewPlatform { WebViewPlatformCreatedCallback? onWebViewPlatformCreated, Set>? gestureRecognizers, }) { - return HtmlElementView( - viewType: 'webview-iframe', - onPlatformViewCreated: (int viewId) { - if (onWebViewPlatformCreated == null) { - return; - } - final web.HTMLIFrameElement element = web.document - .getElementById('webview-$viewId')! as web.HTMLIFrameElement; - + return HtmlElementView.fromTagName( + tagName: 'iframe', + onElementCreated: (Object iFrame) { + iFrame as web.HTMLIFrameElement; + iFrame.style.border = 'none'; final String? initialUrl = creationParams.initialUrl; if (initialUrl != null) { // ignore: unsafe_html - element.src = initialUrl; + iFrame.src = initialUrl; + } + if (onWebViewPlatformCreated != null) { + onWebViewPlatformCreated( + WebWebViewPlatformController(iFrame), + ); } - onWebViewPlatformCreated(WebWebViewPlatformController( - element, - )); }, ); } diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index c10c125030e0..38bd675fcf19 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/packages/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.2.3 +version: 0.2.3+1 environment: sdk: ^3.3.0