From 20e1f9dfdbed7cadc251ae4ba5e67da109ffa1fd Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 16 Aug 2025 12:19:23 +0200 Subject: [PATCH 01/13] Add WebWebViewController.runJavaScript --- .../webview_flutter_web/AUTHORS | 2 +- .../webview_flutter_web/CHANGELOG.md | 1 + .../lib/src/web_webview_controller.dart | 70 ++++++++++++++++--- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_web/AUTHORS b/packages/webview_flutter/webview_flutter_web/AUTHORS index 05432a7fbf9..d7fd51bac63 100644 --- a/packages/webview_flutter/webview_flutter_web/AUTHORS +++ b/packages/webview_flutter/webview_flutter_web/AUTHORS @@ -5,4 +5,4 @@ Google Inc. Bodhi Mulders - +Sophie Bremer diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 7296d418b68..850f706bf19 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Adds `runJavaScript`. * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. ## 0.2.3+4 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 index 4be9dc2a375..74e97164a24 100644 --- 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 @@ -2,11 +2,13 @@ // 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:js_interop'; import 'dart:ui_web' as ui_web; import 'package:flutter/widgets.dart'; +import 'package:web/helpers.dart'; import 'package:web/web.dart' as web; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; @@ -18,7 +20,7 @@ import 'http_request_factory.dart'; @immutable class WebWebViewControllerCreationParams extends PlatformWebViewControllerCreationParams { - /// Creates a new [AndroidWebViewControllerCreationParams] instance. + /// Creates a new [WebWebViewControllerCreationParams] instance. WebWebViewControllerCreationParams({ @visibleForTesting this.httpRequestFactory = const HttpRequestFactory(), }) : super(); @@ -45,6 +47,8 @@ class WebWebViewControllerCreationParams ..style.width = '100%' ..style.height = '100%' ..style.border = 'none'; + + final Duration _iFrameWaitDelay = const Duration(milliseconds: 100); } /// An implementation of [PlatformWebViewController] using Flutter for Web API. @@ -62,14 +66,39 @@ class WebWebViewController extends PlatformWebViewController { WebWebViewControllerCreationParams get _webWebViewParams => params as WebWebViewControllerCreationParams; + // Retrieves the iFrame's content body after attachment to DOM. + Future _getIFrameBody() async { + final web.Document document = await _getIFrameDocument(); + + while (document.body == null) { + await Future.delayed(_webWebViewParams._iFrameWaitDelay); + } + + return document.body! as HTMLBodyElement; + } + + // Retrieves the iFrame's content document after attachment to DOM. + Future _getIFrameDocument() async { + while (_webWebViewParams.iFrame.contentDocument == null) { + await Future.delayed(_webWebViewParams._iFrameWaitDelay); + } + + return _webWebViewParams.iFrame.contentDocument!; + } + @override - Future loadHtmlString(String html, {String? baseUrl}) async { - _webWebViewParams.iFrame.src = - Uri.dataFromString( - html, - mimeType: 'text/html', - encoding: utf8, - ).toString(); + Future loadHtmlString(String html, {String? baseUrl}) { + final Completer loading = Completer(); + + _webWebViewParams.iFrame.addEventListener( + 'load', + () { + _webWebViewParams.iFrame.contentDocument?.write(html.toJS); + loading.complete(); + }.toJS); + _webWebViewParams.iFrame.src = baseUrl ?? 'about:blank'; + + return loading.future; } @override @@ -89,6 +118,31 @@ class WebWebViewController extends PlatformWebViewController { } } + @override + Future runJavaScript(String javaScript) async { + final Completer run = Completer(); + final web.Document document = await _getIFrameDocument(); + final web.HTMLBodyElement body = await _getIFrameBody(); + final web.HTMLScriptElement script = + document.createElement('script') as web.HTMLScriptElement; + + script.addEventListener( + 'load', + () { + body.removeChild(script); + run.complete(); + }.toJS); + script.src = Uri.dataFromString( + javaScript, + mimeType: 'text/javascript', + encoding: utf8, + ).toString(); + + body.appendChild(script); + + await run.future; + } + /// Performs an AJAX request defined by [params]. Future _updateIFrameFromXhr(LoadRequestParams params) async { final web.Response response = From 9c81052cfd2b7514103bfe481314848c9cb5e615 Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 16 Aug 2025 12:38:54 +0200 Subject: [PATCH 02/13] Replace polling with event listener in WebWebViewController._getIFrameDocument. --- .../lib/src/web_webview_controller.dart | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) 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 index 74e97164a24..e37cfa6e3b4 100644 --- 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 @@ -79,10 +79,22 @@ class WebWebViewController extends PlatformWebViewController { // Retrieves the iFrame's content document after attachment to DOM. Future _getIFrameDocument() async { - while (_webWebViewParams.iFrame.contentDocument == null) { - await Future.delayed(_webWebViewParams._iFrameWaitDelay); + // If the document is not yet available, wait for the 'load' event. + if (_webWebViewParams.iFrame.contentDocument == null) { + final Completer completer = Completer(); + _webWebViewParams.iFrame.addEventListener( + 'load', + (web.Event _) { + completer.complete(); + }.toJS, + AddEventListenerOptions(once: true), + ); + // If src is not set, the iframe will never load. + if (_webWebViewParams.iFrame.src.isEmpty) { + _webWebViewParams.iFrame.src = 'about:blank'; + } + await completer.future; } - return _webWebViewParams.iFrame.contentDocument!; } From d9e4fd555900407251eafa5dd0841c497f9e59ae Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 16 Aug 2025 13:57:30 +0200 Subject: [PATCH 03/13] Limit event listener attachments in WebWebViewController. --- .../lib/src/web_webview_controller.dart | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) 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 index e37cfa6e3b4..b96ec16b183 100644 --- 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 @@ -103,11 +103,13 @@ class WebWebViewController extends PlatformWebViewController { final Completer loading = Completer(); _webWebViewParams.iFrame.addEventListener( - 'load', - () { - _webWebViewParams.iFrame.contentDocument?.write(html.toJS); - loading.complete(); - }.toJS); + 'load', + () { + _webWebViewParams.iFrame.contentDocument?.write(html.toJS); + loading.complete(); + }.toJS, + AddEventListenerOptions(once: true), + ); _webWebViewParams.iFrame.src = baseUrl ?? 'about:blank'; return loading.future; @@ -139,11 +141,14 @@ class WebWebViewController extends PlatformWebViewController { document.createElement('script') as web.HTMLScriptElement; script.addEventListener( - 'load', - () { - body.removeChild(script); - run.complete(); - }.toJS); + 'load', + () { + body.removeChild(script); + run.complete(); + }.toJS, + AddEventListenerOptions(once: true), + ); + script.src = Uri.dataFromString( javaScript, mimeType: 'text/javascript', From c410fbdb6a94a669a2c59fa0715d52967fdaad57 Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 16 Aug 2025 17:54:40 +0200 Subject: [PATCH 04/13] Update changelog and version of webview_flutter_web --- packages/webview_flutter/webview_flutter_web/CHANGELOG.md | 3 ++- packages/webview_flutter/webview_flutter_web/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 850f706bf19..9501b67d01a 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.3.0 +* Adds support for `baseUrl` parameter in `loadHtmlString`. * Adds `runJavaScript`. * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index c77b853f16f..259ab990cf2 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+4 +version: 0.3.0 environment: sdk: ^3.7.0 From c4b4afff81d047217d0cdb6bc60f2b9e4ddbe2ce Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:50:23 +0200 Subject: [PATCH 05/13] Add StateError to WebWebViewController.runJavaScript. --- .../lib/src/web_webview_controller.dart | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) 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 index b96ec16b183..38c8cb18887 100644 --- 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 @@ -79,23 +79,30 @@ class WebWebViewController extends PlatformWebViewController { // Retrieves the iFrame's content document after attachment to DOM. Future _getIFrameDocument() async { - // If the document is not yet available, wait for the 'load' event. - if (_webWebViewParams.iFrame.contentDocument == null) { - final Completer completer = Completer(); - _webWebViewParams.iFrame.addEventListener( - 'load', - (web.Event _) { - completer.complete(); - }.toJS, - AddEventListenerOptions(once: true), - ); - // If src is not set, the iframe will never load. - if (_webWebViewParams.iFrame.src.isEmpty) { - _webWebViewParams.iFrame.src = 'about:blank'; + try { + // If the document is not yet available, wait for the 'load' event. + if (_webWebViewParams.iFrame.contentDocument == null) { + final Completer completer = Completer(); + _webWebViewParams.iFrame.addEventListener( + 'load', + (web.Event _) { + completer.complete(); + }.toJS, + AddEventListenerOptions(once: true), + ); + // If src is not set, the iframe will never load. + if (_webWebViewParams.iFrame.src.isEmpty) { + _webWebViewParams.iFrame.src = 'about:blank'; + } + await completer.future; } - await completer.future; + // Test origin permission + _webWebViewParams.iFrame.contentDocument!.body; + // Return on success + return _webWebViewParams.iFrame.contentDocument!; + } catch (_) { + throw StateError('Web view origin mismatch'); } - return _webWebViewParams.iFrame.contentDocument!; } @override From e95f8709e86464a17dc350cad08344e9ab6efe33 Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:50:14 +0200 Subject: [PATCH 06/13] Add time out to WebWebViewController.loadHtmlString and runJavaScript --- .../lib/src/web_webview_controller.dart | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) 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 index 38c8cb18887..613e2c2192a 100644 --- 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 @@ -109,16 +109,28 @@ class WebWebViewController extends PlatformWebViewController { Future loadHtmlString(String html, {String? baseUrl}) { final Completer loading = Completer(); + // Load listener for load completion _webWebViewParams.iFrame.addEventListener( 'load', () { - _webWebViewParams.iFrame.contentDocument?.write(html.toJS); - loading.complete(); + try { + _webWebViewParams.iFrame.contentDocument?.write(html.toJS); + } finally { + loading.complete(); + } }.toJS, AddEventListenerOptions(once: true), ); + + // Initiate load _webWebViewParams.iFrame.src = baseUrl ?? 'about:blank'; + // Time out in case load listener is not triggered + Future.delayed( + const Duration(minutes: 3), + ).then((_) => loading.complete()); + + // Return future completion return loading.future; } @@ -147,23 +159,38 @@ class WebWebViewController extends PlatformWebViewController { final web.HTMLScriptElement script = document.createElement('script') as web.HTMLScriptElement; + // Load listener for script completion script.addEventListener( 'load', () { - body.removeChild(script); - run.complete(); + try { + body.removeChild(script); + } finally { + run.complete(); + } }.toJS, AddEventListenerOptions(once: true), ); - script.src = Uri.dataFromString( - javaScript, - mimeType: 'text/javascript', - encoding: utf8, - ).toString(); + // Prepare script + script.src = + Uri.dataFromString( + javaScript, + mimeType: 'text/javascript', + encoding: utf8, + ).toString(); + // Initiate script execution body.appendChild(script); + // Time out in case load listener is not triggered + unawaited( + Future.delayed( + const Duration(seconds: 3), + ).then((_) => run.complete()), + ); + + // Return future completion await run.future; } From 43ffc5dbab681b8f3974fc607403c1b7db992a66 Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 16 Aug 2025 12:19:23 +0200 Subject: [PATCH 07/13] Add WebWebViewController.runJavaScript --- .../webview_flutter_web/AUTHORS | 2 +- .../webview_flutter_web/CHANGELOG.md | 1 + .../lib/src/web_webview_controller.dart | 70 ++++++++++++++++--- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_web/AUTHORS b/packages/webview_flutter/webview_flutter_web/AUTHORS index 05432a7fbf9..d7fd51bac63 100644 --- a/packages/webview_flutter/webview_flutter_web/AUTHORS +++ b/packages/webview_flutter/webview_flutter_web/AUTHORS @@ -5,4 +5,4 @@ Google Inc. Bodhi Mulders - +Sophie Bremer diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 7296d418b68..850f706bf19 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Adds `runJavaScript`. * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. ## 0.2.3+4 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 index 4be9dc2a375..74e97164a24 100644 --- 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 @@ -2,11 +2,13 @@ // 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:js_interop'; import 'dart:ui_web' as ui_web; import 'package:flutter/widgets.dart'; +import 'package:web/helpers.dart'; import 'package:web/web.dart' as web; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; @@ -18,7 +20,7 @@ import 'http_request_factory.dart'; @immutable class WebWebViewControllerCreationParams extends PlatformWebViewControllerCreationParams { - /// Creates a new [AndroidWebViewControllerCreationParams] instance. + /// Creates a new [WebWebViewControllerCreationParams] instance. WebWebViewControllerCreationParams({ @visibleForTesting this.httpRequestFactory = const HttpRequestFactory(), }) : super(); @@ -45,6 +47,8 @@ class WebWebViewControllerCreationParams ..style.width = '100%' ..style.height = '100%' ..style.border = 'none'; + + final Duration _iFrameWaitDelay = const Duration(milliseconds: 100); } /// An implementation of [PlatformWebViewController] using Flutter for Web API. @@ -62,14 +66,39 @@ class WebWebViewController extends PlatformWebViewController { WebWebViewControllerCreationParams get _webWebViewParams => params as WebWebViewControllerCreationParams; + // Retrieves the iFrame's content body after attachment to DOM. + Future _getIFrameBody() async { + final web.Document document = await _getIFrameDocument(); + + while (document.body == null) { + await Future.delayed(_webWebViewParams._iFrameWaitDelay); + } + + return document.body! as HTMLBodyElement; + } + + // Retrieves the iFrame's content document after attachment to DOM. + Future _getIFrameDocument() async { + while (_webWebViewParams.iFrame.contentDocument == null) { + await Future.delayed(_webWebViewParams._iFrameWaitDelay); + } + + return _webWebViewParams.iFrame.contentDocument!; + } + @override - Future loadHtmlString(String html, {String? baseUrl}) async { - _webWebViewParams.iFrame.src = - Uri.dataFromString( - html, - mimeType: 'text/html', - encoding: utf8, - ).toString(); + Future loadHtmlString(String html, {String? baseUrl}) { + final Completer loading = Completer(); + + _webWebViewParams.iFrame.addEventListener( + 'load', + () { + _webWebViewParams.iFrame.contentDocument?.write(html.toJS); + loading.complete(); + }.toJS); + _webWebViewParams.iFrame.src = baseUrl ?? 'about:blank'; + + return loading.future; } @override @@ -89,6 +118,31 @@ class WebWebViewController extends PlatformWebViewController { } } + @override + Future runJavaScript(String javaScript) async { + final Completer run = Completer(); + final web.Document document = await _getIFrameDocument(); + final web.HTMLBodyElement body = await _getIFrameBody(); + final web.HTMLScriptElement script = + document.createElement('script') as web.HTMLScriptElement; + + script.addEventListener( + 'load', + () { + body.removeChild(script); + run.complete(); + }.toJS); + script.src = Uri.dataFromString( + javaScript, + mimeType: 'text/javascript', + encoding: utf8, + ).toString(); + + body.appendChild(script); + + await run.future; + } + /// Performs an AJAX request defined by [params]. Future _updateIFrameFromXhr(LoadRequestParams params) async { final web.Response response = From 393c69cb54a33f1af9e434463d413dcfd91e7ff0 Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 16 Aug 2025 12:38:54 +0200 Subject: [PATCH 08/13] Replace polling with event listener in WebWebViewController._getIFrameDocument. --- .../lib/src/web_webview_controller.dart | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) 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 index 74e97164a24..e37cfa6e3b4 100644 --- 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 @@ -79,10 +79,22 @@ class WebWebViewController extends PlatformWebViewController { // Retrieves the iFrame's content document after attachment to DOM. Future _getIFrameDocument() async { - while (_webWebViewParams.iFrame.contentDocument == null) { - await Future.delayed(_webWebViewParams._iFrameWaitDelay); + // If the document is not yet available, wait for the 'load' event. + if (_webWebViewParams.iFrame.contentDocument == null) { + final Completer completer = Completer(); + _webWebViewParams.iFrame.addEventListener( + 'load', + (web.Event _) { + completer.complete(); + }.toJS, + AddEventListenerOptions(once: true), + ); + // If src is not set, the iframe will never load. + if (_webWebViewParams.iFrame.src.isEmpty) { + _webWebViewParams.iFrame.src = 'about:blank'; + } + await completer.future; } - return _webWebViewParams.iFrame.contentDocument!; } From 4d19701f7658fa6ba3d1cd434a4acd5729026052 Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 16 Aug 2025 13:57:30 +0200 Subject: [PATCH 09/13] Limit event listener attachments in WebWebViewController. --- .../lib/src/web_webview_controller.dart | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) 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 index e37cfa6e3b4..b96ec16b183 100644 --- 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 @@ -103,11 +103,13 @@ class WebWebViewController extends PlatformWebViewController { final Completer loading = Completer(); _webWebViewParams.iFrame.addEventListener( - 'load', - () { - _webWebViewParams.iFrame.contentDocument?.write(html.toJS); - loading.complete(); - }.toJS); + 'load', + () { + _webWebViewParams.iFrame.contentDocument?.write(html.toJS); + loading.complete(); + }.toJS, + AddEventListenerOptions(once: true), + ); _webWebViewParams.iFrame.src = baseUrl ?? 'about:blank'; return loading.future; @@ -139,11 +141,14 @@ class WebWebViewController extends PlatformWebViewController { document.createElement('script') as web.HTMLScriptElement; script.addEventListener( - 'load', - () { - body.removeChild(script); - run.complete(); - }.toJS); + 'load', + () { + body.removeChild(script); + run.complete(); + }.toJS, + AddEventListenerOptions(once: true), + ); + script.src = Uri.dataFromString( javaScript, mimeType: 'text/javascript', From ebe821fa9c50db38d133e5d35778db74621cd041 Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 16 Aug 2025 17:54:40 +0200 Subject: [PATCH 10/13] Update changelog and version of webview_flutter_web --- packages/webview_flutter/webview_flutter_web/CHANGELOG.md | 3 ++- packages/webview_flutter/webview_flutter_web/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 850f706bf19..9501b67d01a 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.3.0 +* Adds support for `baseUrl` parameter in `loadHtmlString`. * Adds `runJavaScript`. * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index c77b853f16f..259ab990cf2 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+4 +version: 0.3.0 environment: sdk: ^3.7.0 From dcc59da1a977dd9e1f4574beb5129fd0c55ed71a Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:50:23 +0200 Subject: [PATCH 11/13] Add StateError to WebWebViewController.runJavaScript. --- .../lib/src/web_webview_controller.dart | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) 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 index b96ec16b183..38c8cb18887 100644 --- 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 @@ -79,23 +79,30 @@ class WebWebViewController extends PlatformWebViewController { // Retrieves the iFrame's content document after attachment to DOM. Future _getIFrameDocument() async { - // If the document is not yet available, wait for the 'load' event. - if (_webWebViewParams.iFrame.contentDocument == null) { - final Completer completer = Completer(); - _webWebViewParams.iFrame.addEventListener( - 'load', - (web.Event _) { - completer.complete(); - }.toJS, - AddEventListenerOptions(once: true), - ); - // If src is not set, the iframe will never load. - if (_webWebViewParams.iFrame.src.isEmpty) { - _webWebViewParams.iFrame.src = 'about:blank'; + try { + // If the document is not yet available, wait for the 'load' event. + if (_webWebViewParams.iFrame.contentDocument == null) { + final Completer completer = Completer(); + _webWebViewParams.iFrame.addEventListener( + 'load', + (web.Event _) { + completer.complete(); + }.toJS, + AddEventListenerOptions(once: true), + ); + // If src is not set, the iframe will never load. + if (_webWebViewParams.iFrame.src.isEmpty) { + _webWebViewParams.iFrame.src = 'about:blank'; + } + await completer.future; } - await completer.future; + // Test origin permission + _webWebViewParams.iFrame.contentDocument!.body; + // Return on success + return _webWebViewParams.iFrame.contentDocument!; + } catch (_) { + throw StateError('Web view origin mismatch'); } - return _webWebViewParams.iFrame.contentDocument!; } @override From 379ee76567d523cea6d768614ea3103bfae5be5d Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:50:14 +0200 Subject: [PATCH 12/13] Add time out to WebWebViewController.loadHtmlString and runJavaScript --- .../lib/src/web_webview_controller.dart | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) 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 index 38c8cb18887..613e2c2192a 100644 --- 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 @@ -109,16 +109,28 @@ class WebWebViewController extends PlatformWebViewController { Future loadHtmlString(String html, {String? baseUrl}) { final Completer loading = Completer(); + // Load listener for load completion _webWebViewParams.iFrame.addEventListener( 'load', () { - _webWebViewParams.iFrame.contentDocument?.write(html.toJS); - loading.complete(); + try { + _webWebViewParams.iFrame.contentDocument?.write(html.toJS); + } finally { + loading.complete(); + } }.toJS, AddEventListenerOptions(once: true), ); + + // Initiate load _webWebViewParams.iFrame.src = baseUrl ?? 'about:blank'; + // Time out in case load listener is not triggered + Future.delayed( + const Duration(minutes: 3), + ).then((_) => loading.complete()); + + // Return future completion return loading.future; } @@ -147,23 +159,38 @@ class WebWebViewController extends PlatformWebViewController { final web.HTMLScriptElement script = document.createElement('script') as web.HTMLScriptElement; + // Load listener for script completion script.addEventListener( 'load', () { - body.removeChild(script); - run.complete(); + try { + body.removeChild(script); + } finally { + run.complete(); + } }.toJS, AddEventListenerOptions(once: true), ); - script.src = Uri.dataFromString( - javaScript, - mimeType: 'text/javascript', - encoding: utf8, - ).toString(); + // Prepare script + script.src = + Uri.dataFromString( + javaScript, + mimeType: 'text/javascript', + encoding: utf8, + ).toString(); + // Initiate script execution body.appendChild(script); + // Time out in case load listener is not triggered + unawaited( + Future.delayed( + const Duration(seconds: 3), + ).then((_) => run.complete()), + ); + + // Return future completion await run.future; } From b04c2d012950b022d2d7a0190cb2fa90f01845c9 Mon Sep 17 00:00:00 2001 From: sophiebremer <44302338+sophiebremer@users.noreply.github.com> Date: Sat, 11 Oct 2025 20:13:35 +0200 Subject: [PATCH 13/13] Update WebWebViewController tests --- .../test/web_webview_controller_test.dart | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) 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 index 23d996ed3ca..bdd4b93062e 100644 --- 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 @@ -44,22 +44,18 @@ void main() { WebWebViewControllerCreationParams(), ); - await controller.loadHtmlString('test html'); + await controller.loadHtmlString('test #html'); expect( (controller.params as WebWebViewControllerCreationParams).iFrame.src, - 'data:text/html;charset=utf-8,${Uri.encodeFull('test html')}', + 'about:blank', ); - }); - - test('loadHtmlString escapes "#" correctly', () async { - final WebWebViewController controller = WebWebViewController( - WebWebViewControllerCreationParams(), - ); - - await controller.loadHtmlString('#'); expect( - (controller.params as WebWebViewControllerCreationParams).iFrame.src, - contains('%23'), + (controller.params as WebWebViewControllerCreationParams) + .iFrame + .contentDocument! + .body! + .innerHTML, + 'test #html', ); }); });