Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 5b00e5a

Browse files
committed
Update the PR with offline discussion:
* Make the PlatformViewsContentManager a singleton. * Modify the DOM Manager of a view so it can inject platformViews into the right spot. * Use the view.dom.injectPlatformView from renderers so naming is less iffy. * Use a slightly faster way to determine if a platform view is injected in a platformViewHostNode or not. * Break many more tests.
1 parent 0ecf2ff commit 5b00e5a

File tree

7 files changed

+76
-93
lines changed

7 files changed

+76
-93
lines changed

lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,14 @@ class HtmlViewEmbedder {
142142
return recorderToUseForRendering?.recordingCanvas;
143143
}
144144

145-
void _compositeWithParams(int viewId, EmbeddedViewParams params) {
146-
// Ensure platform view with `viewId` is injected into the `rasterizer.view`
145+
void _compositeWithParams(int platformViewId, EmbeddedViewParams params) {
146+
// Ensure platform view with `platformViewId` is injected into the `rasterizer.view`
147147
// before rendering its shadow DOM `slot`.
148-
rasterizer.view.platformViewMessageHandler.injectPlatformView(viewId);
148+
rasterizer.view.dom.injectPlatformView(platformViewId);
149149

150150
// If we haven't seen this viewId yet, cache it for clips/transforms.
151-
final ViewClipChain clipChain = _viewClipChains.putIfAbsent(viewId, () {
152-
return ViewClipChain(view: createPlatformViewSlot(viewId));
151+
final ViewClipChain clipChain = _viewClipChains.putIfAbsent(platformViewId, () {
152+
return ViewClipChain(view: createPlatformViewSlot(platformViewId));
153153
});
154154

155155
final DomElement slot = clipChain.slot;
@@ -179,7 +179,7 @@ class HtmlViewEmbedder {
179179
}
180180

181181
// Apply mutators to the slot
182-
_applyMutators(params, slot, viewId);
182+
_applyMutators(params, slot, platformViewId);
183183
}
184184

185185
int _countClips(MutatorsStack mutators) {

lib/web_ui/lib/src/engine/html/platform_view.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import 'surface.dart';
1010

1111
/// A surface containing a platform view, which is an HTML element.
1212
class PersistedPlatformView extends PersistedLeafSurface {
13-
PersistedPlatformView(this.viewId, this.dx, this.dy, this.width, this.height);
13+
PersistedPlatformView(this.platformViewId, this.dx, this.dy, this.width, this.height);
1414

15-
final int viewId;
15+
final int platformViewId;
1616
final double dx;
1717
final double dy;
1818
final double width;
@@ -23,9 +23,9 @@ class PersistedPlatformView extends PersistedLeafSurface {
2323
// Ensure platform view with `viewId` is injected into the `implicitView`
2424
// before rendering its shadow DOM `slot`.
2525
final EngineFlutterView implicitView = EnginePlatformDispatcher.instance.implicitView!;
26-
implicitView.platformViewMessageHandler.injectPlatformView(viewId);
26+
implicitView.dom.injectPlatformView(platformViewId);
2727

28-
return createPlatformViewSlot(viewId);
28+
return createPlatformViewSlot(platformViewId);
2929
}
3030

3131
@override
@@ -43,21 +43,21 @@ class PersistedPlatformView extends PersistedLeafSurface {
4343
bool canUpdateAsMatch(PersistedSurface oldSurface) {
4444
if (super.canUpdateAsMatch(oldSurface)) {
4545
// super checks the runtimeType of the surface, so we can just cast...
46-
return viewId == ((oldSurface as PersistedPlatformView).viewId);
46+
return platformViewId == ((oldSurface as PersistedPlatformView).platformViewId);
4747
}
4848
return false;
4949
}
5050

5151
@override
5252
double matchForUpdate(PersistedPlatformView existingSurface) {
53-
return existingSurface.viewId == viewId ? 0.0 : 1.0;
53+
return existingSurface.platformViewId == platformViewId ? 0.0 : 1.0;
5454
}
5555

5656
@override
5757
void update(PersistedPlatformView oldSurface) {
5858
assert(
59-
viewId == oldSurface.viewId,
60-
'PersistedPlatformView with different viewId should never be updated. Check the canUpdateAsMatch method.',
59+
platformViewId == oldSurface.platformViewId,
60+
'PersistedPlatformView with different platformViewId should never be updated. Check the canUpdateAsMatch method.',
6161
);
6262
super.update(oldSurface);
6363
// Only update if the view has been resized

lib/web_ui/lib/src/engine/platform_dispatcher.dart

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -614,18 +614,12 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
614614
_handleWebTestEnd2EndMessage(jsonCodec, data)));
615615
return;
616616

617-
case 'flutter/platform_views':
617+
case PlatformViewMessageHandler.channelName:
618+
// `arguments` can be a Map<String, Object> for `create`,
619+
// but an `int` for `dispose`, hence why `dynamic` everywhere.
618620
final MethodCall(:String method, :dynamic arguments) =
619621
standardCodec.decodeMethodCall(data);
620-
final int? flutterViewId = tryViewId(arguments);
621-
if (flutterViewId == null) {
622-
implicitView!.platformViewMessageHandler
623-
.handleLegacyPlatformViewCall(method, arguments, callback!);
624-
return;
625-
}
626-
arguments as Map<dynamic, dynamic>;
627-
viewManager[flutterViewId]!
628-
.platformViewMessageHandler
622+
PlatformViewMessageHandler.instance
629623
.handlePlatformViewCall(method, arguments, callback!);
630624
return;
631625

lib/web_ui/lib/src/engine/platform_views/content_manager.dart

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ class PlatformViewManager {
3838

3939
/// The shared instance of PlatformViewManager shared across the engine to handle
4040
/// rendering of PlatformViews into the web app.
41-
// TODO(dit): How to make this overridable from tests?
42-
static final PlatformViewManager instance = PlatformViewManager();
41+
static PlatformViewManager instance = PlatformViewManager();
4342

4443
// The factory functions, indexed by the viewType
4544
final Map<String, Function> _factories = <String, Function>{};
@@ -65,8 +64,14 @@ class PlatformViewManager {
6564
return _contents.containsKey(viewId);
6665
}
6766

68-
/// Returns the pre-rendered contents of [viewId], to inject them into the DOM.
69-
DomElement getContents(int viewId) {
67+
/// Returns the cached contents of [viewId], to inject them into the DOM.
68+
///
69+
/// This is only used by the active `Renderer` object when a platform view needs
70+
/// to be injected in the DOM, through `FlutterView.DomManager.injectPlatformView`.
71+
///
72+
/// App programmers should not access this directly, and instead use [getViewById].
73+
DomElement getSlottedContent(int viewId) {
74+
assert(knowsViewId(viewId), 'No platform view has been rendered with id: $viewId');
7075
return _contents[viewId]!;
7176
}
7277

@@ -109,9 +114,8 @@ class PlatformViewManager {
109114

110115
/// Creates the HTML markup for the `contents` of a Platform View.
111116
///
112-
/// The result of this call is cached in the `_contents` Map. This is only
113-
/// cached so it can be disposed of later by [clearPlatformView]. _Note that
114-
/// there's no `getContents` function in this class._
117+
/// The result of this call is cached in the `_contents` Map, so the active
118+
/// renderer can inject it as needed.
115119
///
116120
/// The resulting DOM for the `contents` of a Platform View looks like this:
117121
///

lib/web_ui/lib/src/engine/platform_views/message_handler.dart

Lines changed: 24 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,41 @@ typedef PlatformViewContentHandler = void Function(DomElement);
2121

2222
/// This class handles incoming framework messages to create/dispose Platform Views.
2323
///
24-
/// (An instance of this class is connected to the `flutter/platform_views`
24+
/// (The instance of this class is connected to the `flutter/platform_views`
2525
/// Platform Channel in the [EnginePlatformDispatcher] class.)
2626
///
2727
/// It uses a [PlatformViewManager] to handle the CRUD of the DOM of Platform Views.
2828
/// This `contentManager` is shared across the engine, to perform
2929
/// all operations related to platform views (registration, rendering, etc...),
3030
/// regardless of the rendering backend.
3131
///
32-
/// When the `contents` of a Platform View are created, a [PlatformViewContentHandler]
33-
/// function (passed from the outside) will decide where in the DOM to inject
34-
/// said content.
32+
/// Platform views are injected into the DOM when needed by the correct instance
33+
/// of the active renderer.
3534
///
36-
/// The rendering/compositing of Platform Views can create the other "half" of a
35+
/// The rendering and compositing of Platform Views can create the other "half" of a
3736
/// Platform View: the `slot`, through the [createPlatformViewSlot] method.
3837
///
3938
/// When a Platform View is disposed of, it is removed from the cache (and DOM)
4039
/// directly by the `contentManager`. The canvaskit rendering backend needs to do
4140
/// some extra cleanup of its internal state, but it can do it automatically. See
42-
/// [HtmlViewEmbedder.disposeViews]
41+
/// [HtmlViewEmbedder.disposeViews].
4342
class PlatformViewMessageHandler {
4443
PlatformViewMessageHandler({
45-
required DomElement platformViewsContainer,
46-
PlatformViewManager? contentManager,
47-
}) : _contentManager = contentManager ?? PlatformViewManager.instance,
48-
_platformViewsContainer = platformViewsContainer;
44+
required PlatformViewManager contentManager,
45+
}) : _contentManager = contentManager;
46+
47+
static const String channelName = 'flutter/platform_views';
48+
49+
/// The shared instance of PlatformViewMessageHandler.
50+
///
51+
/// Unless configured differently, this connects to the shared instance of the
52+
/// [PlatformViewManager].
53+
static PlatformViewMessageHandler instance = PlatformViewMessageHandler(
54+
contentManager: PlatformViewManager.instance,
55+
);
4956

5057
final MethodCodec _codec = const StandardMethodCodec();
5158
final PlatformViewManager _contentManager;
52-
final DomElement _platformViewsContainer;
5359

5460
/// Handle a `create` Platform View message.
5561
///
@@ -58,10 +64,12 @@ class PlatformViewMessageHandler {
5864
///
5965
/// (See [PlatformViewManager.registerFactory] for more details.)
6066
///
61-
/// The `contents` are inserted into the [_platformViewsContainer].
62-
///
6367
/// If all goes well, this function will `callback` with an empty success envelope.
6468
/// In case of error, this will `callback` with an error envelope describing the error.
69+
///
70+
/// The `callback` signals when the contents of a given [platformViewId] have
71+
/// been rendered. They're now accessible through `platformViewRegistry.getViewById`
72+
/// from `dart:ui_web`. **(Not the DOM!)**
6573
void _createPlatformView(
6674
_PlatformMessageResponseCallback callback, {
6775
required int platformViewId,
@@ -88,8 +96,6 @@ class PlatformViewMessageHandler {
8896
return;
8997
}
9098

91-
// Ensure the DomElement of the view is *created*, so programmers can
92-
// access it through `_contentManager.getViewById` (maybe not the DOM!).
9399
_contentManager.renderContent(
94100
platformViewType,
95101
platformViewId,
@@ -99,18 +105,6 @@ class PlatformViewMessageHandler {
99105
callback(_codec.encodeSuccessEnvelope(null));
100106
}
101107

102-
/// Injects a platform view with [viewId] into this handler's `platformViewsContainer`.
103-
void injectPlatformView(int viewId) {
104-
// For now, we don't need anything fancier. If needed, this can be converted
105-
// to a PlatformViewStrategy class for each web-renderer backend?
106-
final DomElement pv = _contentManager.getContents(viewId);
107-
// If pv is a descendant of _platformViewsContainer -> noop
108-
if (_platformViewsContainer.contains(pv)) {
109-
return;
110-
}
111-
_platformViewsContainer.append(pv);
112-
}
113-
114108
/// Handle a `dispose` Platform View message.
115109
///
116110
/// This will clear the cached information that the framework has about a given
@@ -137,54 +131,26 @@ class PlatformViewMessageHandler {
137131
/// This is transitional code to support the old platform view channel. As
138132
/// soon as the framework code is updated to send the Flutter View ID, this
139133
/// method can be removed.
140-
void handleLegacyPlatformViewCall(
134+
void handlePlatformViewCall(
141135
String method,
142136
dynamic arguments,
143137
_PlatformMessageResponseCallback callback,
144138
) {
145139
switch (method) {
146140
case 'create':
147-
arguments as Map<dynamic, dynamic>;
141+
arguments as Map<String, Object?>;
148142
_createPlatformView(
149143
callback,
150144
platformViewId: arguments.readInt('id'),
151145
platformViewType: arguments.readString('viewType'),
152146
params: arguments['params'],
153147
);
154148
return;
149+
// TODO(web): Send `arguments` as a Map for `dispose` too!
155150
case 'dispose':
156151
_disposePlatformView(callback, platformViewId: arguments as int);
157152
return;
158153
}
159154
callback(null);
160155
}
161-
162-
/// Handles a PlatformViewCall to the `flutter/platform_views` channel.
163-
///
164-
/// This method handles two possible messages:
165-
/// * `create`: See [_createPlatformView]
166-
/// * `dispose`: See [_disposePlatformView]
167-
void handlePlatformViewCall(
168-
String method,
169-
Map<dynamic, dynamic> arguments,
170-
_PlatformMessageResponseCallback callback,
171-
) {
172-
switch (method) {
173-
case 'create':
174-
_createPlatformView(
175-
callback,
176-
platformViewId: arguments.readInt('platformViewId'),
177-
platformViewType: arguments.readString('platformViewType'),
178-
params: arguments['params'],
179-
);
180-
return;
181-
case 'dispose':
182-
_disposePlatformView(
183-
callback,
184-
platformViewId: arguments.readInt('platformViewId'),
185-
);
186-
return;
187-
}
188-
callback(null);
189-
}
190156
}

lib/web_ui/lib/src/engine/view_embedder/dom_manager.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:ui/ui.dart' as ui;
66

77
import '../configuration.dart';
88
import '../dom.dart';
9+
import '../platform_views/content_manager.dart';
910
import '../safe_browser_api.dart';
1011
import '../semantics/semantics.dart';
1112
import 'style_manager.dart';
@@ -204,6 +205,28 @@ class DomManager {
204205
sceneHost.append(sceneElement);
205206
}
206207
}
208+
209+
/// Injects a platform view with [platformViewId] into [platformViewsHost].
210+
///
211+
/// If the platform view is already injected, this method does *nothing*.
212+
///
213+
/// The `platformViewsHost` can only be different if `platformViewId` is moving
214+
/// from one [FlutterView] to another. In that case, the browser will move the
215+
/// slot contents from the old `platformViewsHost` to the new one, but that
216+
/// will cause the platformView to reset its state (an iframe will re-render,
217+
/// text selections will be lost, video playback interrupted, etc...)
218+
///
219+
/// Try not to move platform views across views!
220+
void injectPlatformView(int platformViewId) {
221+
// For now, we don't need anything fancier. If needed, this can be converted
222+
// to a PlatformViewStrategy class for each web-renderer backend?
223+
final DomElement pv = PlatformViewManager.instance.getSlottedContent(platformViewId);
224+
// If pv is a descendant of _platformViewsContainer -> noop
225+
if (pv.parent == platformViewsHost) {
226+
return;
227+
}
228+
platformViewsHost.append(pv);
229+
}
207230
}
208231

209232
DomShadowRoot _attachShadowRoot(DomElement element) {

lib/web_ui/lib/src/engine/window.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import 'mouse/context_menu.dart';
1717
import 'mouse/cursor.dart';
1818
import 'navigation/history.dart';
1919
import 'platform_dispatcher.dart';
20-
import 'platform_views/message_handler.dart';
2120
import 'pointer_binding.dart';
2221
import 'semantics.dart';
2322
import 'services.dart';
@@ -130,9 +129,6 @@ base class EngineFlutterView implements ui.FlutterView {
130129

131130
late final DomManager dom = DomManager(viewId: viewId, devicePixelRatio: devicePixelRatio);
132131

133-
late final PlatformViewMessageHandler platformViewMessageHandler =
134-
PlatformViewMessageHandler(platformViewsContainer: dom.platformViewsHost);
135-
136132
late final PointerBinding pointerBinding;
137133

138134
// TODO(goderbauer): Provide API to configure constraints. See also TODO in "render".

0 commit comments

Comments
 (0)