Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/webview_flutter/webview_flutter_web/AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

Google Inc.
Bodhi Mulders <info@bemacized.net>

Sophie Bremer
4 changes: 3 additions & 1 deletion packages/webview_flutter/webview_flutter_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 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.

## 0.2.3+4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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();
Expand All @@ -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.
Expand All @@ -62,14 +66,72 @@ class WebWebViewController extends PlatformWebViewController {
WebWebViewControllerCreationParams get _webWebViewParams =>
params as WebWebViewControllerCreationParams;

// Retrieves the iFrame's content body after attachment to DOM.
Future<web.HTMLBodyElement> _getIFrameBody() async {
final web.Document document = await _getIFrameDocument();

while (document.body == null) {
await Future<void>.delayed(_webWebViewParams._iFrameWaitDelay);
}

return document.body! as HTMLBodyElement;
}

// Retrieves the iFrame's content document after attachment to DOM.
Future<web.Document> _getIFrameDocument() async {
try {
// If the document is not yet available, wait for the 'load' event.
if (_webWebViewParams.iFrame.contentDocument == null) {
final Completer<void> completer = Completer<void>();
_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;
}
// Test origin permission
_webWebViewParams.iFrame.contentDocument!.body;
// Return on success
return _webWebViewParams.iFrame.contentDocument!;
} catch (_) {
throw StateError('Web view origin mismatch');
}
}

@override
Future<void> loadHtmlString(String html, {String? baseUrl}) async {
_webWebViewParams.iFrame.src =
Uri.dataFromString(
html,
mimeType: 'text/html',
encoding: utf8,
).toString();
Future<void> loadHtmlString(String html, {String? baseUrl}) {
final Completer<void> loading = Completer<void>();

// Load listener for load completion
_webWebViewParams.iFrame.addEventListener(
'load',
() {
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<void>.delayed(
const Duration(minutes: 3),
).then<void>((_) => loading.complete());

// Return future completion
return loading.future;
}

@override
Expand All @@ -89,6 +151,49 @@ class WebWebViewController extends PlatformWebViewController {
}
}

@override
Future<void> runJavaScript(String javaScript) async {
final Completer<void> run = Completer<void>();
final web.Document document = await _getIFrameDocument();
final web.HTMLBodyElement body = await _getIFrameBody();
final web.HTMLScriptElement script =
document.createElement('script') as web.HTMLScriptElement;

// Load listener for script completion
script.addEventListener(
'load',
() {
try {
body.removeChild(script);
} finally {
run.complete();
}
}.toJS,
AddEventListenerOptions(once: true),
);

// 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<void>.delayed(
const Duration(seconds: 3),
).then<void>((_) => run.complete()),
);

// Return future completion
await run.future;
}

/// Performs an AJAX request defined by [params].
Future<void> _updateIFrameFromXhr(LoadRequestParams params) async {
final web.Response response =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
);
});
});
Expand Down