Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web] Support flutterViewId in platform view messages #46891

Merged
merged 7 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
61 changes: 33 additions & 28 deletions lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ ui.VoidCallback? scheduleFrameCallback;
typedef HighContrastListener = void Function(bool enabled);
typedef _KeyDataResponseCallback = void Function(bool handled);

const StandardMethodCodec standardCodec = StandardMethodCodec();
const JSONMethodCodec jsonCodec = JSONMethodCodec();

/// Determines if high contrast is enabled using media query 'forced-colors: active' for Windows
class HighContrastSupport {
static HighContrastSupport instance = HighContrastSupport();
Expand Down Expand Up @@ -129,13 +132,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {

/// The current list of windows.
@override
Iterable<ui.FlutterView> get views => viewData.values;
final Map<int, ui.FlutterView> viewData = <int, ui.FlutterView>{};
Iterable<EngineFlutterView> get views => viewData.values;
final Map<int, EngineFlutterView> viewData = <int, EngineFlutterView>{};

/// Returns the [FlutterView] with the provided ID if one exists, or null
/// otherwise.
@override
ui.FlutterView? view({required int id}) => viewData[id];
EngineFlutterView? view({required int id}) => viewData[id];

/// A map of opaque platform window identifiers to window configurations.
///
Expand Down Expand Up @@ -470,8 +473,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {

/// This should be in sync with shell/common/shell.cc
case 'flutter/skia':
const MethodCodec codec = JSONMethodCodec();
final MethodCall decoded = codec.decodeMethodCall(data);
final MethodCall decoded = jsonCodec.decodeMethodCall(data);
switch (decoded.method) {
case 'Skia.setResourceCacheMaxBytes':
if (renderer is CanvasKitRenderer) {
Expand All @@ -486,7 +488,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
// Also respond in HTML mode. Otherwise, apps would have to detect
// CanvasKit vs HTML before invoking this method.
replyToPlatformMessage(
callback, codec.encodeSuccessEnvelope(<bool>[true]));
callback, jsonCodec.encodeSuccessEnvelope(<bool>[true]));
}
return;

Expand All @@ -496,22 +498,21 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
return;

case 'flutter/platform':
const MethodCodec codec = JSONMethodCodec();
final MethodCall decoded = codec.decodeMethodCall(data);
final MethodCall decoded = jsonCodec.decodeMethodCall(data);
switch (decoded.method) {
case 'SystemNavigator.pop':
// TODO(a-wallen): As multi-window support expands, the pop call
// will need to include the view ID. Right now only one view is
// supported.
implicitView!.browserHistory.exit().then((_) {
replyToPlatformMessage(
callback, codec.encodeSuccessEnvelope(true));
callback, jsonCodec.encodeSuccessEnvelope(true));
});
return;
case 'HapticFeedback.vibrate':
final String? type = decoded.arguments as String?;
vibrate(_getHapticFeedbackDuration(type));
replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true));
return;
case 'SystemChrome.setApplicationSwitcherDescription':
final Map<String, Object?> arguments = decoded.arguments as Map<String, Object?>;
Expand All @@ -520,24 +521,24 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
final int primaryColor = arguments['primaryColor'] as int? ?? 0xFF000000;
domDocument.title = label;
setThemeColor(ui.Color(primaryColor));
replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true));
return;
case 'SystemChrome.setSystemUIOverlayStyle':
final Map<String, Object?> arguments = decoded.arguments as Map<String, Object?>;
final int? statusBarColor = arguments['statusBarColor'] as int?;
setThemeColor(statusBarColor == null ? null : ui.Color(statusBarColor));
replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true));
return;
case 'SystemChrome.setPreferredOrientations':
final List<dynamic> arguments = decoded.arguments as List<dynamic>;
ScreenOrientation.instance.setPreferredOrientation(arguments).then((bool success) {
replyToPlatformMessage(
callback, codec.encodeSuccessEnvelope(success));
callback, jsonCodec.encodeSuccessEnvelope(success));
});
return;
case 'SystemSound.play':
// There are no default system sounds on web.
replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true));
return;
case 'Clipboard.setData':
ClipboardMessageHandler().setDataMethodCall(decoded, callback);
Expand All @@ -560,23 +561,21 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
return;

case 'flutter/contextmenu':
const MethodCodec codec = JSONMethodCodec();
final MethodCall decoded = codec.decodeMethodCall(data);
final MethodCall decoded = jsonCodec.decodeMethodCall(data);
switch (decoded.method) {
case 'enableContextMenu':
implicitView!.contextMenu.enable();
replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true));
return;
case 'disableContextMenu':
implicitView!.contextMenu.disable();
replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true));
return;
}
return;

case 'flutter/mousecursor':
const MethodCodec codec = StandardMethodCodec();
final MethodCall decoded = codec.decodeMethodCall(data);
final MethodCall decoded = standardCodec.decodeMethodCall(data);
final Map<dynamic, dynamic> arguments = decoded.arguments as Map<dynamic, dynamic>;
switch (decoded.method) {
case 'activateSystemCursor':
Expand All @@ -585,15 +584,22 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
return;

case 'flutter/web_test_e2e':
const MethodCodec codec = JSONMethodCodec();
replyToPlatformMessage(
callback,
codec.encodeSuccessEnvelope(
_handleWebTestEnd2EndMessage(codec, data)));
jsonCodec.encodeSuccessEnvelope(
_handleWebTestEnd2EndMessage(jsonCodec, data)));
return;

case 'flutter/platform_views':
implicitView!.platformViewMessageHandler.handlePlatformViewCall(data, callback!);
final MethodCall(:String method, :dynamic arguments) = standardCodec.decodeMethodCall(data);
mdebbar marked this conversation as resolved.
Show resolved Hide resolved
if (arguments is Map<dynamic, dynamic>) {
final int? flutterViewId = arguments.tryInt('flutterViewId');
if (flutterViewId != null) {
viewData[flutterViewId]!.platformViewMessageHandler.handlePlatformViewCall(method, arguments, callback!);
return;
}
}
mdebbar marked this conversation as resolved.
Show resolved Hide resolved
implicitView!.platformViewMessageHandler.handleLegacyPlatformViewCall(method, arguments, callback!);
return;

case 'flutter/accessibility':
Expand All @@ -609,8 +615,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
// supported.
implicitView!.handleNavigationMessage(data).then((bool handled) {
if (handled) {
const MethodCodec codec = JSONMethodCodec();
replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true));
} else {
callback?.call(null);
}
Expand Down Expand Up @@ -1350,7 +1355,7 @@ class ViewConfiguration {
});

ViewConfiguration copyWith({
ui.FlutterView? view,
EngineFlutterView? view,
double? devicePixelRatio,
ui.Rect? geometry,
bool? visible,
Expand All @@ -1375,7 +1380,7 @@ class ViewConfiguration {
);
}

final ui.FlutterView? view;
final EngineFlutterView? view;
final double devicePixelRatio;
final ui.Rect geometry;
final bool visible;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,9 @@ DomElement _defaultFactory(
}) {
params!;
params as Map<Object?, Object?>;
return domDocument.createElement(params.readString('tagName'));
final DomElement element = domDocument.createElement(params.readString('tagName'));
element.style
..width = '100%'
..height = '100%';
mdebbar marked this conversation as resolved.
Show resolved Hide resolved
return element;
}
44 changes: 31 additions & 13 deletions lib/web_ui/lib/src/engine/platform_views/message_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,12 @@ class PlatformViewMessageHandler {
/// 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.
void _createPlatformView(
MethodCall methodCall,
Map<dynamic, dynamic> arguments,
_PlatformMessageResponseCallback callback,
) {
final Map<dynamic, dynamic> args = methodCall.arguments as Map<dynamic, dynamic>;
final int viewId = args.readInt('id');
final String viewType = args.readString('viewType');
final Object? params = args['params'];
final int viewId = arguments.readInt('id');
final String viewType = arguments.readString('viewType');
final Object? params = arguments['params'];

if (!_contentManager.knowsViewType(viewType)) {
callback(_codec.encodeErrorEnvelope(
Expand Down Expand Up @@ -114,34 +113,53 @@ class PlatformViewMessageHandler {
///
/// This function should always `callback` with an empty success envelope.
void _disposePlatformView(
MethodCall methodCall,
int viewId,
_PlatformMessageResponseCallback callback,
) {
final int viewId = methodCall.arguments as int;

// The contentManager removes the slot and the contents from its internal
// cache, and the DOM.
_contentManager.clearPlatformView(viewId);

callback(_codec.encodeSuccessEnvelope(null));
}

/// Handles legacy PlatformViewCalls that don't contain a `flutterViewId`.
///
/// This is transitional code to support the old platform view channel. As
/// soon as the framework code is updated to send the `flutterViewId`, this
/// method can be removed.
void handleLegacyPlatformViewCall(
Copy link
Member

Choose a reason for hiding this comment

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

I totally forgot about this method. Good repurposing though!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You must've been looking at an individual commit from the PR 🙂 This method is new.

String method,
dynamic arguments,
_PlatformMessageResponseCallback callback,
) {
switch (method) {
case 'create':
_createPlatformView(arguments as Map<dynamic, dynamic>, callback);
return;
case 'dispose':
_disposePlatformView(arguments as int, callback);
return;
}
callback(null);
}

/// Handles a PlatformViewCall to the `flutter/platform_views` channel.
///
/// This method handles two possible messages:
/// * `create`: See [_createPlatformView]
/// * `dispose`: See [_disposePlatformView]
void handlePlatformViewCall(
ByteData? data,
String method,
Map<dynamic, dynamic> arguments,
_PlatformMessageResponseCallback callback,
) {
final MethodCall decoded = _codec.decodeMethodCall(data);
switch (decoded.method) {
switch (method) {
case 'create':
_createPlatformView(decoded, callback);
_createPlatformView(arguments, callback);
return;
case 'dispose':
_disposePlatformView(decoded, callback);
_disposePlatformView(arguments.readInt('id'), callback);
return;
}
callback(null);
Expand Down
Loading