Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
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
1 change: 1 addition & 0 deletions lib/web_ui/lib/src/engine/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ extension DomElementExtension on DomElement {
createDomListWrapper<DomElement>(_children);

external DomElement? get firstElementChild;
external DomElement? get lastElementChild;

external DomElement? get nextElementSibling;

Expand Down
10 changes: 1 addition & 9 deletions lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,6 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
};
}

PlatformViewMessageHandler? _platformViewMessageHandler;

void _sendPlatformMessage(
String name,
ByteData? data,
Expand Down Expand Up @@ -595,13 +593,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
return;

case 'flutter/platform_views':
_platformViewMessageHandler ??= PlatformViewMessageHandler(
contentManager: PlatformViewManager.instance,
contentHandler: (DomElement content) {
flutterViewEmbedder.glassPaneElement.append(content);
},
);
_platformViewMessageHandler!.handlePlatformViewCall(data, callback!);
implicitView!.platformViewMessageHandler.handlePlatformViewCall(data, callback!);
return;

case 'flutter/accessibility':
Expand Down
19 changes: 10 additions & 9 deletions lib/web_ui/lib/src/engine/platform_views/message_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import 'dart:typed_data';

import '../dom.dart';
import '../platform_dispatcher.dart';
import '../services.dart';
import '../util.dart';
import 'content_manager.dart';
import 'slots.dart';

/// The signature for a callback for a Platform Message. From the `ui` package.
/// Copied here so there's no circular dependencies.
Expand Down Expand Up @@ -40,24 +42,23 @@ typedef PlatformViewContentHandler = void Function(DomElement);
/// [HtmlViewEmbedder.disposeViews]
class PlatformViewMessageHandler {
PlatformViewMessageHandler({
required PlatformViewManager contentManager,
PlatformViewContentHandler? contentHandler,
}) : _contentManager = contentManager,
_contentHandler = contentHandler;
required DomElement platformViewsContainer,
PlatformViewManager? contentManager,
}) : _contentManager = contentManager ?? PlatformViewManager.instance,
_platformViewsContainer = platformViewsContainer;

final MethodCodec _codec = const StandardMethodCodec();
final PlatformViewManager _contentManager;
final PlatformViewContentHandler? _contentHandler;
final DomElement _platformViewsContainer;

/// Handle a `create` Platform View message.
///
/// This will attempt to render the `contents` and of a Platform View, if its
/// `viewType` has been registered previously.
///
/// (See [PlatformViewContentManager.registerFactory] for more details.)
/// (See [PlatformViewManager.registerFactory] for more details.)
///
/// The `contents` are delegated to a [_contentHandler] function, so the
/// active rendering backend can inject them in the right place of the DOM.
/// The `contents` are inserted into the [_platformViewsContainer].
///
/// If all goes well, this function will `callback` with an empty success envelope.
/// In case of error, this will `callback` with an error envelope describing the error.
Expand Down Expand Up @@ -98,7 +99,7 @@ class PlatformViewMessageHandler {

// For now, we don't need anything fancier. If needed, this can be converted
// to a PlatformViewStrategy class for each web-renderer backend?
_contentHandler?.call(content);
_platformViewsContainer.append(content);
Comment on lines -101 to +102
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much simpler, this is fantastic!

callback(_codec.encodeSuccessEnvelope(null));
}

Expand Down
6 changes: 6 additions & 0 deletions lib/web_ui/lib/src/engine/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import 'mouse/context_menu.dart';
import 'mouse/cursor.dart';
import 'navigation/history.dart';
import 'platform_dispatcher.dart';
import 'platform_views/message_handler.dart';
import 'services.dart';
import 'util.dart';

Expand All @@ -39,6 +40,7 @@ const int kImplicitViewId = 0;
abstract interface class EngineFlutterView extends ui.FlutterView {
ContextMenu get contextMenu;
MouseCursor get mouseCursor;
PlatformViewMessageHandler get platformViewMessageHandler;
DomElement get rootElement;
}

Expand Down Expand Up @@ -75,6 +77,10 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow implements EngineFlu
@override
DomElement get rootElement => flutterViewEmbedder.flutterViewElement;

@override
late final PlatformViewMessageHandler platformViewMessageHandler =
PlatformViewMessageHandler(platformViewsContainer: flutterViewEmbedder.glassPaneElement);

/// Handles the browser history integration to allow users to use the back
/// button, etc.
BrowserHistory get browserHistory {
Expand Down
42 changes: 29 additions & 13 deletions lib/web_ui/test/engine/platform_views/message_handler_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,17 @@ void testMain() {
const int viewId = 6;
late PlatformViewManager contentManager;
late Completer<ByteData?> completer;
late Completer<DomElement> contentCompleter;

setUp(() {
contentManager = PlatformViewManager();
completer = Completer<ByteData?>();
contentCompleter = Completer<DomElement>();
});

group('"create" message', () {
test('unregistered viewType, fails with descriptive exception',
() async {
final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler(
platformViewsContainer: createDomElement('div'),
contentManager: contentManager,
);
final ByteData? message = _getCreateMessage(viewType, viewId);
Expand All @@ -57,6 +56,7 @@ void testMain() {
viewType, (int id) => createDomHTMLDivElement());
contentManager.renderContent(viewType, viewId, null);
final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler(
platformViewsContainer: createDomElement('div'),
contentManager: contentManager,
);
final ByteData? message = _getCreateMessage(viewType, viewId);
Expand All @@ -77,6 +77,7 @@ void testMain() {
contentManager.registerFactory(
viewType, (int id) => createDomHTMLDivElement()..id = 'success');
final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler(
platformViewsContainer: createDomElement('div'),
contentManager: contentManager,
);
final ByteData? message = _getCreateMessage(viewType, viewId);
Expand All @@ -89,27 +90,37 @@ void testMain() {
'The response should be a success envelope, with null in it.');
});

test('calls a contentHandler with the result of creating a view',
test('inserts the created view into the platformViewsContainer',
() async {
final DomElement platformViewsContainer = createDomElement('pv-container');
contentManager.registerFactory(
viewType, (int id) => createDomHTMLDivElement()..id = 'success');
final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler(
platformViewsContainer: platformViewsContainer,
contentManager: contentManager,
contentHandler: contentCompleter.complete,
);
final ByteData? message = _getCreateMessage(viewType, viewId);

messageHandler.handlePlatformViewCall(message, completer.complete);

final DomElement contents = await contentCompleter.future;
final ByteData? response = await completer.future;

expect(contents.querySelector('div#success'), isNotNull,
reason:
'The element created by the factory should be present in the created view.');
expect(codec.decodeEnvelope(response!), isNull,
reason:
'The response should be a success envelope, with null in it.');
expect(
platformViewsContainer.children.single,
isNotNull,
reason: 'The container has a single child, the created view.',
);
final DomElement platformView = platformViewsContainer.children.single;
expect(
platformView.querySelector('div#success'),
isNotNull,
reason: 'The element created by the factory should be present in the created view.',
);
expect(
codec.decodeEnvelope(response!),
isNull,
reason: 'The response should be a success envelope, with null in it.',
);
});

test('passes creation params to the factory', () async {
Expand All @@ -119,6 +130,7 @@ void testMain() {
return createDomHTMLDivElement();
});
final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler(
platformViewsContainer: createDomElement('div'),
contentManager: contentManager,
);

Expand Down Expand Up @@ -177,8 +189,10 @@ void testMain() {
return Object();
});

final PlatformViewMessageHandler messageHandler =
PlatformViewMessageHandler(contentManager: contentManager);
final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler(
platformViewsContainer: createDomElement('div'),
contentManager: contentManager,
);
final ByteData? message = _getCreateMessage(viewType, viewId);

expect(() {
Expand All @@ -196,6 +210,7 @@ void testMain() {

test('never fails, even for unknown viewIds', () async {
final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler(
platformViewsContainer: createDomElement('div'),
contentManager: contentManager,
);
final ByteData? message = _getDisposeMessage(viewId);
Expand All @@ -210,6 +225,7 @@ void testMain() {

test('never fails, even for unknown viewIds', () async {
final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler(
platformViewsContainer: createDomElement('div'),
contentManager: _FakePlatformViewManager(viewIdCompleter.complete),
);
final ByteData? message = _getDisposeMessage(viewId);
Expand Down