From b683af0cecc0f51dac1968c19f691aa3111b5852 Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Thu, 18 Sep 2025 09:16:24 -0700 Subject: [PATCH] Engine Support for Dynamic View Resizing (#173610) Part of https://github.com/flutter/flutter/issues/169147 (iOS) and https://github.com/flutter/flutter/issues/149033 (Android). Implementation PRs coming up! This enables support for dynamic view resizing in non-web embedders. While this PR enables support, it does not _implement_ support. Specifically, this creates the pipes (in viewport metrics) for min/max width/height constraints that tell the engine and framework that when rendering the frame it should adjust the width/height as needed for the content. The engine should also discard layer trees that do not fit the constraints. Since the engine now relies on BoxConstraints, geometry.h has now been moved out of embedder specific codepath so that both embedder and engine can depend on it. Related PRs: `dart:ui`: https://github.com/flutter/engine/pull/48090 `framework`: https://github.com/flutter/flutter/pull/138648 `framework reland`: https://github.com/flutter/flutter/pull/140918 `Web`: https://github.com/flutter/engine/pull/50271 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: Matt Boetger Co-authored-by: Matt Boetger --- engine/src/flutter/BUILD.gn | 1 + .../src/flutter/lib/ui/fixtures/ui_test.dart | 40 +++++++-- engine/src/flutter/lib/ui/hooks.dart | 26 ++++++ .../flutter/lib/ui/platform_dispatcher.dart | 4 + engine/src/flutter/lib/ui/window.dart | 7 +- .../lib/ui/window/platform_configuration.cc | 8 ++ .../flutter/lib/ui/window/viewport_metrics.cc | 21 ++++- .../flutter/lib/ui/window/viewport_metrics.h | 9 +- engine/src/flutter/shell/common/BUILD.gn | 1 + engine/src/flutter/shell/common/shell.cc | 83 ++++++++++++++++--- engine/src/flutter/shell/common/shell.h | 13 +-- engine/src/flutter/shell/common/shell_test.cc | 10 +++ engine/src/flutter/shell/common/shell_test.h | 4 + .../flutter/shell/common/shell_unittests.cc | 67 +++++++++++++++ engine/src/flutter/shell/geometry/BUILD.gn | 31 +++++++ .../{platform/common => geometry}/geometry.h | 13 ++- .../common => geometry}/geometry_unittests.cc | 2 +- .../android/platform_view_android_jni_impl.cc | 51 +++++++----- .../flutter/shell/platform/common/BUILD.gn | 7 +- .../framework/Source/FlutterViewController.mm | 5 ++ .../shell/platform/embedder/embedder.cc | 5 ++ .../platform/fuchsia/flutter/platform_view.cc | 8 ++ .../flutter/shell/platform/windows/BUILD.gn | 1 + .../shell/platform/windows/flutter_window.h | 2 +- .../platform/windows/flutter_windows_view.h | 2 +- .../shell/platform/windows/host_window.h | 2 +- .../platform/windows/text_input_manager.h | 2 +- .../platform/windows/text_input_plugin.h | 2 +- .../platform/windows/window_binding_handler.h | 2 +- .../windows/window_binding_handler_delegate.h | 2 +- .../src/flutter/shell/testing/tester_main.cc | 4 + engine/src/flutter/testing/run_tests.py | 1 + 32 files changed, 370 insertions(+), 66 deletions(-) create mode 100644 engine/src/flutter/shell/geometry/BUILD.gn rename engine/src/flutter/shell/{platform/common => geometry}/geometry.h (88%) rename engine/src/flutter/shell/{platform/common => geometry}/geometry_unittests.cc (96%) diff --git a/engine/src/flutter/BUILD.gn b/engine/src/flutter/BUILD.gn index f5ace7c3531..ec7b55d24f1 100644 --- a/engine/src/flutter/BUILD.gn +++ b/engine/src/flutter/BUILD.gn @@ -213,6 +213,7 @@ group("unittests") { "//flutter/runtime:no_dart_plugin_registrant_unittests", "//flutter/runtime:runtime_unittests", "//flutter/shell/common:shell_unittests", + "//flutter/shell/geometry:geometry_unittests", "//flutter/shell/platform/embedder:embedder_a11y_unittests", "//flutter/shell/platform/embedder:embedder_proctable_unittests", "//flutter/shell/platform/embedder:embedder_unittests", diff --git a/engine/src/flutter/lib/ui/fixtures/ui_test.dart b/engine/src/flutter/lib/ui/fixtures/ui_test.dart index 4f0b4a3bfe0..44e17a36322 100644 --- a/engine/src/flutter/lib/ui/fixtures/ui_test.dart +++ b/engine/src/flutter/lib/ui/fixtures/ui_test.dart @@ -730,7 +730,7 @@ void hooksTests() async { window.onMetricsChanged!(); _callHook( '_updateWindowMetrics', - 21, + 25, 0, // window Id 0.1234, // device pixel ratio 0.0, // width @@ -752,6 +752,10 @@ void hooksTests() async { [], // display features types [], // display features states 0, // Display ID + 0.0, // minWidth + 0.0, // maxWidth + 0.0, // minHeight + 0.0, // maxHeight ); expectIdentical(originalZone, callbackZone); @@ -844,7 +848,7 @@ void hooksTests() async { await test('View padding/insets/viewPadding/systemGestureInsets', () { _callHook( '_updateWindowMetrics', - 21, + 25, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -866,6 +870,10 @@ void hooksTests() async { [], // display features types [], // display features states 0, // Display ID + 0.0, // minWidth + 1000.0, // maxWidth + 0.0, // minHeight + 1000.0, // maxHeight ); expectEquals(window.viewInsets.bottom, 0.0); @@ -875,7 +883,7 @@ void hooksTests() async { _callHook( '_updateWindowMetrics', - 21, + 25, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -897,6 +905,10 @@ void hooksTests() async { [], // display features types [], // display features states 0, // Display ID + 0.0, // minWidth + 0.0, // maxWidth + 0.0, // minHeight + 0.0, // maxHeight ); expectEquals(window.viewInsets.bottom, 400.0); @@ -908,7 +920,7 @@ void hooksTests() async { await test('Window physical touch slop', () { _callHook( '_updateWindowMetrics', - 21, + 25, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -930,13 +942,17 @@ void hooksTests() async { [], // display features types [], // display features states 0, // Display ID + 0.0, // minWidth + 0.0, // maxWidth + 0.0, // minHeight + 0.0, // maxHeight ); expectEquals(window.gestureSettings, GestureSettings(physicalTouchSlop: 11.0)); _callHook( '_updateWindowMetrics', - 21, + 25, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -958,13 +974,17 @@ void hooksTests() async { [], // display features types [], // display features states 0, // Display ID + 0.0, // minWidth + 0.0, // maxWidth + 0.0, // minHeight + 0.0, // maxHeight ); expectEquals(window.gestureSettings, GestureSettings(physicalTouchSlop: null)); _callHook( '_updateWindowMetrics', - 21, + 25, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -986,6 +1006,10 @@ void hooksTests() async { [], // display features types [], // display features states 0, // Display ID + 0.0, // minWidth + 0.0, // maxWidth + 0.0, // minHeight + 0.0, // maxHeight ); expectEquals(window.gestureSettings, GestureSettings(physicalTouchSlop: 22.0)); @@ -1382,4 +1406,8 @@ external void _callHook( Object? arg19, Object? arg20, Object? arg21, + Object? arg22, + Object? arg23, + Object? arg24, + Object? arg25, ]); diff --git a/engine/src/flutter/lib/ui/hooks.dart b/engine/src/flutter/lib/ui/hooks.dart index d9965953940..602f520b1e2 100644 --- a/engine/src/flutter/lib/ui/hooks.dart +++ b/engine/src/flutter/lib/ui/hooks.dart @@ -27,6 +27,10 @@ void _addView( List displayFeaturesType, List displayFeaturesState, int displayId, + double minWidth, + double maxWidth, + double minHeight, + double maxHeight, ) { final _ViewConfiguration viewConfiguration = _buildViewConfiguration( devicePixelRatio, @@ -49,6 +53,10 @@ void _addView( displayFeaturesType, displayFeaturesState, displayId, + minWidth, + maxWidth, + minHeight, + maxHeight, ); PlatformDispatcher.instance._addView(viewId, viewConfiguration); } @@ -151,6 +159,10 @@ _ViewConfiguration _buildViewConfiguration( List displayFeaturesType, List displayFeaturesState, int displayId, + double minWidth, + double maxWidth, + double minHeight, + double maxHeight, ) { return _ViewConfiguration( devicePixelRatio: devicePixelRatio, @@ -189,6 +201,12 @@ _ViewConfiguration _buildViewConfiguration( devicePixelRatio: devicePixelRatio, ), displayId: displayId, + viewConstraints: ViewConstraints( + minWidth: minWidth, + maxWidth: maxWidth, + minHeight: minHeight, + maxHeight: maxHeight, + ), ); } @@ -215,6 +233,10 @@ void _updateWindowMetrics( List displayFeaturesType, List displayFeaturesState, int displayId, + double minWidth, + double maxWidth, + double minHeight, + double maxHeight, ) { final _ViewConfiguration viewConfiguration = _buildViewConfiguration( devicePixelRatio, @@ -237,6 +259,10 @@ void _updateWindowMetrics( displayFeaturesType, displayFeaturesState, displayId, + minWidth, + maxWidth, + minHeight, + maxHeight, ); PlatformDispatcher.instance._updateWindowMetrics(viewId, viewConfiguration); } diff --git a/engine/src/flutter/lib/ui/platform_dispatcher.dart b/engine/src/flutter/lib/ui/platform_dispatcher.dart index c2aa5419a05..ff5a56e0b4e 100644 --- a/engine/src/flutter/lib/ui/platform_dispatcher.dart +++ b/engine/src/flutter/lib/ui/platform_dispatcher.dart @@ -1886,12 +1886,16 @@ class _ViewConfiguration { this.gestureSettings = const GestureSettings(), this.displayFeatures = const [], this.displayId = 0, + this.viewConstraints = const ViewConstraints(maxWidth: 0, maxHeight: 0), }); /// The identifier for a display for this view, in /// [PlatformDispatcher._displays]. final int displayId; + /// The sizing constraints for this view in physical pixels. + final ViewConstraints viewConstraints; + /// The pixel density of the output surface. final double devicePixelRatio; diff --git a/engine/src/flutter/lib/ui/window.dart b/engine/src/flutter/lib/ui/window.dart index ef8551c892e..3af4bb9af81 100644 --- a/engine/src/flutter/lib/ui/window.dart +++ b/engine/src/flutter/lib/ui/window.dart @@ -145,7 +145,7 @@ class FlutterView { /// /// The view can take on any [Size] that fulfills these constraints. These /// constraints are typically used by an UI framework as the input for its - /// layout algorithm to determine an approrpiate size for the view. To size + /// layout algorithm to determine an appropriate size for the view. To size /// the view, the selected size must be provided to the [render] method and it /// must satisfy the constraints. /// @@ -164,10 +164,7 @@ class FlutterView { /// See also: /// /// * [physicalSize], which returns the current size of the view. - // TODO(goderbauer): Wire this up so embedders can configure it. This will - // also require to message the size provided to the render call back to the - // embedder. - ViewConstraints get physicalConstraints => ViewConstraints.tight(physicalSize); + ViewConstraints get physicalConstraints => _viewConfiguration.viewConstraints; /// The current dimensions of the rectangle as last reported by the platform /// into which scenes rendered in this view are drawn. diff --git a/engine/src/flutter/lib/ui/window/platform_configuration.cc b/engine/src/flutter/lib/ui/window/platform_configuration.cc index 46b0d745a59..2edeb2cf598 100644 --- a/engine/src/flutter/lib/ui/window/platform_configuration.cc +++ b/engine/src/flutter/lib/ui/window/platform_configuration.cc @@ -126,6 +126,10 @@ bool PlatformConfiguration::AddView(int64_t view_id, tonic::ToDart(view_metrics.physical_display_features_type), tonic::ToDart(view_metrics.physical_display_features_state), tonic::ToDart(view_metrics.display_id), + tonic::ToDart(view_metrics.physical_min_width_constraint), + tonic::ToDart(view_metrics.physical_max_width_constraint), + tonic::ToDart(view_metrics.physical_min_height_constraint), + tonic::ToDart(view_metrics.physical_max_height_constraint), })); return true; } @@ -224,6 +228,10 @@ bool PlatformConfiguration::UpdateViewMetrics( tonic::ToDart(view_metrics.physical_display_features_type), tonic::ToDart(view_metrics.physical_display_features_state), tonic::ToDart(view_metrics.display_id), + tonic::ToDart(view_metrics.physical_min_width_constraint), + tonic::ToDart(view_metrics.physical_max_width_constraint), + tonic::ToDart(view_metrics.physical_min_height_constraint), + tonic::ToDart(view_metrics.physical_max_height_constraint), })); return true; } diff --git a/engine/src/flutter/lib/ui/window/viewport_metrics.cc b/engine/src/flutter/lib/ui/window/viewport_metrics.cc index 05d01c47c8e..14919d94367 100644 --- a/engine/src/flutter/lib/ui/window/viewport_metrics.cc +++ b/engine/src/flutter/lib/ui/window/viewport_metrics.cc @@ -18,13 +18,20 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, : device_pixel_ratio(p_device_pixel_ratio), physical_width(p_physical_width), physical_height(p_physical_height), + physical_min_width_constraint(p_physical_width), + physical_max_width_constraint(p_physical_width), + physical_min_height_constraint(p_physical_height), + physical_max_height_constraint(p_physical_height), physical_touch_slop(p_physical_touch_slop), display_id(p_display_id) {} - ViewportMetrics::ViewportMetrics( double p_device_pixel_ratio, double p_physical_width, double p_physical_height, + double p_physical_min_width_constraint, + double p_physical_max_width_constraint, + double p_physical_min_height_constraint, + double p_physical_max_height_constraint, double p_physical_padding_top, double p_physical_padding_right, double p_physical_padding_bottom, @@ -45,6 +52,10 @@ ViewportMetrics::ViewportMetrics( : device_pixel_ratio(p_device_pixel_ratio), physical_width(p_physical_width), physical_height(p_physical_height), + physical_min_width_constraint(p_physical_min_width_constraint), + physical_max_width_constraint(p_physical_max_width_constraint), + physical_min_height_constraint(p_physical_min_height_constraint), + physical_max_height_constraint(p_physical_max_height_constraint), physical_padding_top(p_physical_padding_top), physical_padding_right(p_physical_padding_right), physical_padding_bottom(p_physical_padding_bottom), @@ -69,6 +80,10 @@ bool operator==(const ViewportMetrics& a, const ViewportMetrics& b) { return a.device_pixel_ratio == b.device_pixel_ratio && a.physical_width == b.physical_width && a.physical_height == b.physical_height && + a.physical_min_width_constraint == b.physical_min_width_constraint && + a.physical_max_width_constraint == b.physical_max_width_constraint && + a.physical_min_height_constraint == b.physical_min_height_constraint && + a.physical_max_height_constraint == b.physical_max_height_constraint && a.physical_padding_top == b.physical_padding_top && a.physical_padding_right == b.physical_padding_right && a.physical_padding_bottom == b.physical_padding_bottom && @@ -99,6 +114,10 @@ std::ostream& operator<<(std::ostream& os, const ViewportMetrics& a) { << "W " << a.physical_height << "H] " << "Padding: [" << a.physical_padding_top << "T " << a.physical_padding_right << "R " << a.physical_padding_bottom << "B " << a.physical_padding_left << "L] " + << "View Constraints: [" << a.physical_min_width_constraint << "-" + << a.physical_max_width_constraint << "W " + << a.physical_min_height_constraint << "-" + << a.physical_max_height_constraint << "H] " << "Insets: [" << a.physical_view_inset_top << "T " << a.physical_view_inset_right << "R " << a.physical_view_inset_bottom << "B " << a.physical_view_inset_left << "L] " << "Gesture Insets: [" diff --git a/engine/src/flutter/lib/ui/window/viewport_metrics.h b/engine/src/flutter/lib/ui/window/viewport_metrics.h index 13a0dee9439..2ec2aaeff61 100644 --- a/engine/src/flutter/lib/ui/window/viewport_metrics.h +++ b/engine/src/flutter/lib/ui/window/viewport_metrics.h @@ -20,6 +20,10 @@ struct ViewportMetrics { ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height, + double p_physical_min_width_constraint, + double p_physical_max_width_constraint, + double p_physical_min_height_constraint, + double p_physical_max_height_constraint, double p_physical_padding_top, double p_physical_padding_right, double p_physical_padding_bottom, @@ -37,10 +41,13 @@ struct ViewportMetrics { const std::vector& p_physical_display_features_type, const std::vector& p_physical_display_features_state, size_t p_display_id); - double device_pixel_ratio = 1.0; double physical_width = 0; double physical_height = 0; + double physical_min_width_constraint = 0; + double physical_max_width_constraint = 0; + double physical_min_height_constraint = 0; + double physical_max_height_constraint = 0; double physical_padding_top = 0; double physical_padding_right = 0; double physical_padding_bottom = 0; diff --git a/engine/src/flutter/shell/common/BUILD.gn b/engine/src/flutter/shell/common/BUILD.gn index c3ffd57d5c4..d577e322877 100644 --- a/engine/src/flutter/shell/common/BUILD.gn +++ b/engine/src/flutter/shell/common/BUILD.gn @@ -148,6 +148,7 @@ source_set("common") { "//flutter/lib/ui", "//flutter/runtime", "//flutter/shell/common:base64", + "//flutter/shell/geometry", "//flutter/shell/profiling", "//flutter/skia", ] diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 226e35932f0..c8e9154a7a5 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -149,6 +149,56 @@ void PerformInitializationTasks(Settings& settings) { #endif // !SLIMPELLER } +bool ValidateViewportMetrics(const ViewportMetrics& metrics) { + // Pixel ratio cannot be zero. + if (metrics.device_pixel_ratio <= 0) { + return false; + } + + // If negative values are passed in. + if (metrics.physical_width < 0 || metrics.physical_height < 0 || + metrics.physical_min_width_constraint < 0 || + metrics.physical_max_width_constraint < 0 || + metrics.physical_min_height_constraint < 0 || + metrics.physical_max_height_constraint < 0) { + return false; + } + + // If width is zero and the constraints are tight. + if (metrics.physical_width == 0 && + metrics.physical_min_width_constraint == + metrics.physical_max_width_constraint) { + return false; + } + + // If not tight constraints, check the width fits in the constraints. + if (metrics.physical_min_width_constraint != + metrics.physical_max_width_constraint) { + if (metrics.physical_min_width_constraint > metrics.physical_width || + metrics.physical_width > metrics.physical_max_width_constraint) { + return false; + } + } + + // If height is zero and the constraints are tight. + if (metrics.physical_height == 0 && + metrics.physical_min_height_constraint == + metrics.physical_max_height_constraint) { + return false; + } + + // If not tight constraints, check the height fits in the constraints. + if (metrics.physical_min_height_constraint != + metrics.physical_max_height_constraint) { + if (metrics.physical_min_height_constraint > metrics.physical_height || + metrics.physical_height > metrics.physical_max_height_constraint) { + return false; + } + } + + return true; +} + } // namespace std::pair> @@ -1078,8 +1128,7 @@ void Shell::OnPlatformViewSetViewportMetrics(int64_t view_id, FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - if (metrics.device_pixel_ratio <= 0 || metrics.physical_width <= 0 || - metrics.physical_height <= 0) { + if (!ValidateViewportMetrics(metrics)) { // Ignore invalid view-port metrics. return; } @@ -1107,8 +1156,12 @@ void Shell::OnPlatformViewSetViewportMetrics(int64_t view_id, { std::scoped_lock lock(resize_mutex_); - expected_frame_sizes_[view_id] = - DlISize(metrics.physical_width, metrics.physical_height); + + expected_frame_constraints_[view_id] = + BoxConstraints(Size(metrics.physical_min_width_constraint, + metrics.physical_min_height_constraint), + Size(metrics.physical_max_width_constraint, + metrics.physical_max_height_constraint)); device_pixel_ratio_ = metrics.device_pixel_ratio; } } @@ -1756,9 +1809,9 @@ fml::TimePoint Shell::GetLatestFrameTargetTime() const { bool Shell::ShouldDiscardLayerTree(int64_t view_id, const flutter::LayerTree& tree) { std::scoped_lock lock(resize_mutex_); - auto expected_frame_size = ExpectedFrameSize(view_id); - return !expected_frame_size.IsEmpty() && - tree.frame_size() != expected_frame_size; + auto expected_frame_constraints = ExpectedFrameConstraints(view_id); + return !expected_frame_constraints.IsSatisfiedBy( + Size(tree.frame_size().width, tree.frame_size().height)); } // |ServiceProtocol::Handler| @@ -2169,8 +2222,10 @@ void Shell::OnPlatformViewRemoveView(int64_t view_id, FML_DCHECK(view_id != kFlutterImplicitViewId) << "Unexpected request to remove the implicit view #" << kFlutterImplicitViewId << ". This view should never be removed."; - - expected_frame_sizes_.erase(view_id); + { + std::scoped_lock lock(resize_mutex_); + expected_frame_constraints_.erase(view_id); + } task_runners_.GetUITaskRunner()->RunNowOrPostTask( task_runners_.GetUITaskRunner(), [&task_runners = task_runners_, // @@ -2363,11 +2418,13 @@ Shell::GetConcurrentWorkerTaskRunner() const { return vm_->GetConcurrentWorkerTaskRunner(); } -DlISize Shell::ExpectedFrameSize(int64_t view_id) { - auto found = expected_frame_sizes_.find(view_id); - if (found == expected_frame_sizes_.end()) { - return DlISize(); +BoxConstraints Shell::ExpectedFrameConstraints(int64_t view_id) { + auto found = expected_frame_constraints_.find(view_id); + + if (found == expected_frame_constraints_.end()) { + return {}; } + return found->second; } diff --git a/engine/src/flutter/shell/common/shell.h b/engine/src/flutter/shell/common/shell.h index 41ae6e263fb..a98c3a2aaed 100644 --- a/engine/src/flutter/shell/common/shell.h +++ b/engine/src/flutter/shell/common/shell.h @@ -39,6 +39,7 @@ #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/resource_cache_limit_calculator.h" #include "flutter/shell/common/shell_io_manager.h" +#include "flutter/shell/geometry/geometry.h" #include "impeller/core/runtime_types.h" #include "impeller/renderer/context.h" #include "impeller/runtime_stage/runtime_stage.h" @@ -511,13 +512,13 @@ class Shell final : public PlatformView::Delegate, /// any of the threads. std::unique_ptr display_manager_; - // protects expected_frame_size_ which is set on platform thread and read on - // raster thread + // Protects expected_frame_constraints_ which is set on platform thread and + // read on raster thread. std::mutex resize_mutex_; - // used to discard wrong size layer tree produced during interactive - // resizing - std::unordered_map expected_frame_sizes_; + // Used to discard wrong size layer tree produced during interactive + // resizing. + std::unordered_map expected_frame_constraints_; // Used to communicate the right frame bounds via service protocol. double device_pixel_ratio_ = 0.0; @@ -804,7 +805,7 @@ class Shell final : public PlatformView::Delegate, // directory. std::unique_ptr RestoreOriginalAssetResolver(); - DlISize ExpectedFrameSize(int64_t view_id); + BoxConstraints ExpectedFrameConstraints(int64_t view_id); // For accessing the Shell via the raster thread, necessary for various // rasterizer callbacks. diff --git a/engine/src/flutter/shell/common/shell_test.cc b/engine/src/flutter/shell/common/shell_test.cc index 24ce62e7a7b..71086a6b201 100644 --- a/engine/src/flutter/shell/common/shell_test.cc +++ b/engine/src/flutter/shell/common/shell_test.cc @@ -165,6 +165,10 @@ void ShellTest::SetViewportMetrics(Shell* shell, double width, double height) { 1, // device pixel ratio width, // physical width height, // physical height + 0, // min width constraint + 0, // max width constraint + 0, // min height constraint + 0, // max height constraint 0, // padding top 0, // padding right 0, // padding bottom @@ -425,5 +429,11 @@ void ShellTest::TurnOffGPU(Shell* shell, bool value) { shell->is_gpu_disabled_sync_switch_->SetSwitch(value); } +bool ShellTest::ShouldDiscardLayerTree(Shell* shell, + int64_t view_id, + const flutter::LayerTree& tree) { + return shell->ShouldDiscardLayerTree(view_id, tree); +} + } // namespace testing } // namespace flutter diff --git a/engine/src/flutter/shell/common/shell_test.h b/engine/src/flutter/shell/common/shell_test.h index 0fd729f5a67..0034ecf816a 100644 --- a/engine/src/flutter/shell/common/shell_test.h +++ b/engine/src/flutter/shell/common/shell_test.h @@ -163,6 +163,10 @@ class ShellTest : public FixtureTest { static void TurnOffGPU(Shell* shell, bool value); + static bool ShouldDiscardLayerTree(Shell* shell, + int64_t view_id, + const flutter::LayerTree& tree); + private: ThreadHost thread_host_; diff --git a/engine/src/flutter/shell/common/shell_unittests.cc b/engine/src/flutter/shell/common/shell_unittests.cc index b0b5bfb5ea2..bb57adc49e4 100644 --- a/engine/src/flutter/shell/common/shell_unittests.cc +++ b/engine/src/flutter/shell/common/shell_unittests.cc @@ -24,6 +24,7 @@ #include "flutter/flow/layers/clip_rect_layer.h" #include "flutter/flow/layers/display_list_layer.h" #include "flutter/flow/layers/layer_raster_cache_item.h" +#include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/platform_view_layer.h" #include "flutter/flow/layers/transform_layer.h" #include "flutter/fml/backtrace.h" @@ -5040,6 +5041,72 @@ TEST_F(ShellTest, ReleaseResourceContextWhenIOManagerIsDeleted) { ASSERT_TRUE(called_release_resource_context); } +TEST_F(ShellTest, ShoulDiscardLayerTreeIfFrameIsSizedIncorrectly) { + Settings settings = CreateSettingsForFixture(); + auto task_runner = CreateNewThread(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + std::unique_ptr shell = CreateShell(settings, task_runners); + + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { + shell->GetPlatformView()->SetViewportMetrics( + kImplicitViewId, + { + 1.0, // p_device_pixel_ratio + 500, // p_physical_width + 800, // p_physical_height + 1, // p_min_width_constraint, + 1000, // p_max_width_constraint, + 1, // p_min_height_constraint, + 1000, // p_max_height_constraint, + 0, // p_physical_padding_top + 0, // p_physical_padding_right + 0, // p_physical_padding_bottom + 0, // p_physical_padding_left + 0, // p_physical_view_inset_top, + 0, // p_physical_view_inset_right, + 0, // p_physical_view_inset_bottom, + 0, // p_physical_view_inset_left, + 0, // p_physical_system_gesture_inset_top, + 0, // p_physical_system_gesture_inset_right, + 0, // p_physical_system_gesture_inset_bottom, + 0, // p_physical_system_gesture_inset_left, + 22, // p_physical_touch_slop, + {}, // p_physical_display_features_bounds, + {}, // p_physical_display_features_type, + {}, // p_physical_display_features_state, + 0 // p_display_id + }); + }); + PumpOneFrame(shell.get()); + + auto layer_tree = + std::make_unique(/*root_layer=*/nullptr, + /*frame_size=*/DlISize(100, 100)); + ASSERT_FALSE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId, + *layer_tree)); + auto over_width = + std::make_unique(/*root_layer=*/nullptr, + /*frame_size=*/DlISize(1010, 100)); + ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId, + *over_width)); + auto over_height = + std::make_unique(/*root_layer=*/nullptr, + /*frame_size=*/DlISize(100, 1010)); + ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId, + *over_height)); + auto min_width = std::make_unique(/*root_layer=*/nullptr, + /*frame_size=*/DlISize(0, 100)); + ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId, + *min_width)); + auto min_height = std::make_unique(/*root_layer=*/nullptr, + /*frame_size=*/DlISize(100, 0)); + ASSERT_TRUE(ShellTest::ShouldDiscardLayerTree(shell.get(), kImplicitViewId, + *min_height)); + DestroyShell(std::move(shell), task_runners); +} + } // namespace testing } // namespace flutter diff --git a/engine/src/flutter/shell/geometry/BUILD.gn b/engine/src/flutter/shell/geometry/BUILD.gn new file mode 100644 index 00000000000..0d65bb739d5 --- /dev/null +++ b/engine/src/flutter/shell/geometry/BUILD.gn @@ -0,0 +1,31 @@ +import("//flutter/common/config.gni") +import("//flutter/testing/testing.gni") + +source_set("geometry") { + visibility = [ + ":geometry_unittests", + "//flutter/shell/common:*", + "//flutter/shell/platform/common:*", + "//flutter/shell/platform/windows:*", + ] + + sources = [ "geometry.h" ] +} + +if (enable_unittests) { + test_fixtures("geometry_fixtures") { + fixtures = [] + } + + executable("geometry_unittests") { + testonly = true + + sources = [ "geometry_unittests.cc" ] + + deps = [ + ":geometry", + ":geometry_fixtures", + "//flutter/testing", + ] + } +} diff --git a/engine/src/flutter/shell/platform/common/geometry.h b/engine/src/flutter/shell/geometry/geometry.h similarity index 88% rename from engine/src/flutter/shell/platform/common/geometry.h rename to engine/src/flutter/shell/geometry/geometry.h index eabe4d685ce..e5572e924c9 100644 --- a/engine/src/flutter/shell/platform/common/geometry.h +++ b/engine/src/flutter/shell/geometry/geometry.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_PLATFORM_COMMON_GEOMETRY_H_ -#define FLUTTER_SHELL_PLATFORM_COMMON_GEOMETRY_H_ +#ifndef FLUTTER_SHELL_GEOMETRY_GEOMETRY_H_ +#define FLUTTER_SHELL_GEOMETRY_GEOMETRY_H_ #include #include @@ -93,6 +93,13 @@ class BoxConstraints { Size biggest() const { return biggest_; } Size smallest() const { return smallest_; } + bool IsSatisfiedBy(Size size) { + return smallest().width() <= size.width() && + size.width() <= biggest().width() && + smallest().height() <= size.height() && + size.height() <= biggest().height(); + } + private: Size smallest_ = Size(0, 0); Size biggest_ = Size(std::numeric_limits::infinity(), @@ -101,4 +108,4 @@ class BoxConstraints { } // namespace flutter -#endif // FLUTTER_SHELL_PLATFORM_COMMON_GEOMETRY_H_ +#endif // FLUTTER_SHELL_GEOMETRY_GEOMETRY_H_ diff --git a/engine/src/flutter/shell/platform/common/geometry_unittests.cc b/engine/src/flutter/shell/geometry/geometry_unittests.cc similarity index 96% rename from engine/src/flutter/shell/platform/common/geometry_unittests.cc rename to engine/src/flutter/shell/geometry/geometry_unittests.cc index c8645028be9..4be46ae696d 100644 --- a/engine/src/flutter/shell/platform/common/geometry_unittests.cc +++ b/engine/src/flutter/shell/geometry/geometry_unittests.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/platform/common/geometry.h" +#include "flutter/shell/geometry/geometry.h" #include "gtest/gtest.h" diff --git a/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc b/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc index b2ed47f64f2..80dcf7458f3 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc @@ -367,27 +367,38 @@ static void SetViewportMetrics(JNIEnv* env, env->GetIntArrayRegion(javaDisplayFeaturesState, 0, stateSize, &displayFeaturesState[0]); + // TODO(boetger): update for https://github.com/flutter/flutter/issues/149033 const flutter::ViewportMetrics metrics{ - static_cast(devicePixelRatio), - static_cast(physicalWidth), - static_cast(physicalHeight), - static_cast(physicalPaddingTop), - static_cast(physicalPaddingRight), - static_cast(physicalPaddingBottom), - static_cast(physicalPaddingLeft), - static_cast(physicalViewInsetTop), - static_cast(physicalViewInsetRight), - static_cast(physicalViewInsetBottom), - static_cast(physicalViewInsetLeft), - static_cast(systemGestureInsetTop), - static_cast(systemGestureInsetRight), - static_cast(systemGestureInsetBottom), - static_cast(systemGestureInsetLeft), - static_cast(physicalTouchSlop), - displayFeaturesBounds, - displayFeaturesType, - displayFeaturesState, - 0, // Display ID + static_cast(devicePixelRatio), // p_device_pixel_ratio + static_cast(physicalWidth), // p_physical_width + static_cast(physicalHeight), // p_physical_height + static_cast(physicalWidth), // p_physical_min_width_constraint + static_cast(physicalWidth), // p_physical_max_width_constraint + static_cast(physicalHeight), // p_physical_min_height_constraint + static_cast(physicalHeight), // p_physical_max_height_constraint + static_cast(physicalPaddingTop), // p_physical_padding_top + static_cast(physicalPaddingRight), // p_physical_padding_right + static_cast(physicalPaddingBottom), // p_physical_padding_bottom + static_cast(physicalPaddingLeft), // p_physical_padding_left + static_cast(physicalViewInsetTop), // p_physical_view_inset_top + static_cast( + physicalViewInsetRight), // p_physical_view_inset_right + static_cast( + physicalViewInsetBottom), // p_physical_view_inset_bottom + static_cast(physicalViewInsetLeft), // p_physical_view_inset_left + static_cast( + systemGestureInsetTop), // p_physical_system_gesture_inset_top + static_cast( + systemGestureInsetRight), // p_physical_system_gesture_inset_right + static_cast( + systemGestureInsetBottom), // p_physical_system_gesture_inset_bottom + static_cast( + systemGestureInsetLeft), // p_physical_system_gesture_inset_left + static_cast(physicalTouchSlop), // p_physical_touch_slop + displayFeaturesBounds, // p_physical_display_features_bounds + displayFeaturesType, // p_physical_display_features_type + displayFeaturesState, // p_physical_display_features_state + 0, // p_display_id }; ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics( diff --git a/engine/src/flutter/shell/platform/common/BUILD.gn b/engine/src/flutter/shell/platform/common/BUILD.gn index b27be6bfa03..e7cc8609551 100644 --- a/engine/src/flutter/shell/platform/common/BUILD.gn +++ b/engine/src/flutter/shell/platform/common/BUILD.gn @@ -150,7 +150,6 @@ source_set("common_cpp") { # API surface there will be. source_set("common_cpp_core") { public = [ - "geometry.h", "path_utils.h", "windowing.h", ] @@ -159,7 +158,10 @@ source_set("common_cpp_core") { public_configs = [ "//flutter:config" ] - deps = [ "//flutter/fml:fml" ] + deps = [ + "//flutter/fml:fml", + "//flutter/shell/geometry:geometry", + ] } if (enable_unittests) { @@ -191,7 +193,6 @@ if (enable_unittests) { sources = [ "engine_switches_unittests.cc", - "geometry_unittests.cc", "incoming_message_dispatcher_unittests.cc", "json_message_codec_unittests.cc", "json_method_codec_unittests.cc", diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 1e5aa0a4489..f7fd6cd518d 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1478,6 +1478,11 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch) CGFloat scale = screen.scale; _viewportMetrics.physical_width = self.view.bounds.size.width * scale; _viewportMetrics.physical_height = self.view.bounds.size.height * scale; + // TODO(louisehsu): update for https://github.com/flutter/flutter/issues/169147 + _viewportMetrics.physical_min_width_constraint = _viewportMetrics.physical_width; + _viewportMetrics.physical_max_width_constraint = _viewportMetrics.physical_width; + _viewportMetrics.physical_min_height_constraint = _viewportMetrics.physical_height; + _viewportMetrics.physical_max_height_constraint = _viewportMetrics.physical_height; } // Set _viewportMetrics physical paddings. diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index d3434052663..a2e5b51086d 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -1649,6 +1649,11 @@ MakeViewportMetricsFromWindowMetrics( "physical height or width."; } + metrics.physical_min_width_constraint = metrics.physical_width; + metrics.physical_max_width_constraint = metrics.physical_width; + metrics.physical_min_height_constraint = metrics.physical_height; + metrics.physical_max_height_constraint = metrics.physical_height; + return metrics; } diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/platform_view.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/platform_view.cc index 35147128eef..b2c15e3021b 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/platform_view.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/platform_view.cc @@ -342,6 +342,14 @@ void PlatformView::OnGetLayout(fuchsia::ui::composition::LayoutInfo info) { pixel_ratio), // physical_width std::round(view_logical_size_.value()[1] * pixel_ratio), // physical_height + std::round(view_logical_size_.value()[0] * + pixel_ratio), // physical_min_width_constraint + std::round(view_logical_size_.value()[0] * + pixel_ratio), // physical_max_width_constraint + std::round(view_logical_size_.value()[1] * + pixel_ratio), // physical_min_height_constraint + std::round(view_logical_size_.value()[1] * + pixel_ratio), // physical_max_height_constraint 0.0f, // physical_padding_top 0.0f, // physical_padding_right 0.0f, // physical_padding_bottom diff --git a/engine/src/flutter/shell/platform/windows/BUILD.gn b/engine/src/flutter/shell/platform/windows/BUILD.gn index 4f49f5dc600..600fa110d68 100644 --- a/engine/src/flutter/shell/platform/windows/BUILD.gn +++ b/engine/src/flutter/shell/platform/windows/BUILD.gn @@ -165,6 +165,7 @@ source_set("flutter_windows_source") { ":flutter_windows_headers", "//flutter/fml:fml", "//flutter/impeller/renderer/backend/gles", + "//flutter/shell/geometry:geometry", "//flutter/shell/platform/common:common_cpp", "//flutter/shell/platform/common:common_cpp_input", "//flutter/shell/platform/common:common_cpp_isolate_scope", diff --git a/engine/src/flutter/shell/platform/windows/flutter_window.h b/engine/src/flutter/shell/platform/windows/flutter_window.h index d1b20f17181..d0c206aa963 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_window.h +++ b/engine/src/flutter/shell/platform/windows/flutter_window.h @@ -9,8 +9,8 @@ #include #include "flutter/fml/macros.h" +#include "flutter/shell/geometry/geometry.h" #include "flutter/shell/platform/common/alert_platform_node_delegate.h" -#include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/direct_manipulation.h" #include "flutter/shell/platform/windows/flutter_windows_view.h" diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_view.h b/engine/src/flutter/shell/platform/windows/flutter_windows_view.h index 66ab0f0f0a4..738b37dbeff 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_view.h +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_view.h @@ -13,8 +13,8 @@ #include #include "flutter/fml/macros.h" +#include "flutter/shell/geometry/geometry.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/plugin_registrar.h" -#include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/accessibility_bridge_windows.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" diff --git a/engine/src/flutter/shell/platform/windows/host_window.h b/engine/src/flutter/shell/platform/windows/host_window.h index e3773d65ec5..77e28aa00dc 100644 --- a/engine/src/flutter/shell/platform/windows/host_window.h +++ b/engine/src/flutter/shell/platform/windows/host_window.h @@ -12,7 +12,7 @@ #include #include "flutter/fml/macros.h" -#include "flutter/shell/platform/common/geometry.h" +#include "flutter/shell/geometry/geometry.h" #include "flutter/shell/platform/common/windowing.h" #include "flutter/shell/platform/windows/window_manager.h" diff --git a/engine/src/flutter/shell/platform/windows/text_input_manager.h b/engine/src/flutter/shell/platform/windows/text_input_manager.h index 4e1c262fa31..d6e7d6866c4 100644 --- a/engine/src/flutter/shell/platform/windows/text_input_manager.h +++ b/engine/src/flutter/shell/platform/windows/text_input_manager.h @@ -12,7 +12,7 @@ #include #include "flutter/fml/macros.h" -#include "flutter/shell/platform/common/geometry.h" +#include "flutter/shell/geometry/geometry.h" namespace flutter { diff --git a/engine/src/flutter/shell/platform/windows/text_input_plugin.h b/engine/src/flutter/shell/platform/windows/text_input_plugin.h index 1a836f9db83..1ef228a3144 100644 --- a/engine/src/flutter/shell/platform/windows/text_input_plugin.h +++ b/engine/src/flutter/shell/platform/windows/text_input_plugin.h @@ -10,9 +10,9 @@ #include #include "flutter/fml/macros.h" +#include "flutter/shell/geometry/geometry.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/method_channel.h" -#include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/common/json_method_codec.h" #include "flutter/shell/platform/common/text_editing_delta.h" #include "flutter/shell/platform/common/text_input_model.h" diff --git a/engine/src/flutter/shell/platform/windows/window_binding_handler.h b/engine/src/flutter/shell/platform/windows/window_binding_handler.h index 6585567b287..a9f9c320082 100644 --- a/engine/src/flutter/shell/platform/windows/window_binding_handler.h +++ b/engine/src/flutter/shell/platform/windows/window_binding_handler.h @@ -10,8 +10,8 @@ #include #include +#include "flutter/shell/geometry/geometry.h" #include "flutter/shell/platform/common/alert_platform_node_delegate.h" -#include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/public/flutter_windows.h" #include "flutter/shell/platform/windows/window_binding_handler_delegate.h" diff --git a/engine/src/flutter/shell/platform/windows/window_binding_handler_delegate.h b/engine/src/flutter/shell/platform/windows/window_binding_handler_delegate.h index 0245a742286..7a755b1018b 100644 --- a/engine/src/flutter/shell/platform/windows/window_binding_handler_delegate.h +++ b/engine/src/flutter/shell/platform/windows/window_binding_handler_delegate.h @@ -7,7 +7,7 @@ #include -#include "flutter/shell/platform/common/geometry.h" +#include "flutter/shell/geometry/geometry.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/windows_lifecycle_manager.h" #include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h" diff --git a/engine/src/flutter/shell/testing/tester_main.cc b/engine/src/flutter/shell/testing/tester_main.cc index d3e65d720be..4640b0d40d1 100644 --- a/engine/src/flutter/shell/testing/tester_main.cc +++ b/engine/src/flutter/shell/testing/tester_main.cc @@ -140,6 +140,10 @@ static void ConfigureShell(Shell* shell) { metrics.physical_width = physical_width; metrics.physical_height = physical_height; metrics.display_id = 0; + metrics.physical_min_width_constraint = physical_width; + metrics.physical_max_width_constraint = physical_width; + metrics.physical_min_height_constraint = physical_height; + metrics.physical_max_height_constraint = physical_height; shell->GetPlatformView()->SetViewportMetrics(kImplicitViewId, metrics); } diff --git a/engine/src/flutter/testing/run_tests.py b/engine/src/flutter/testing/run_tests.py index 97676a9c0ff..bfd58723746 100755 --- a/engine/src/flutter/testing/run_tests.py +++ b/engine/src/flutter/testing/run_tests.py @@ -431,6 +431,7 @@ def run_cc_tests(build_dir, executable_filter, coverage, capture_core_dump): make_test('embedder_proctable_unittests'), make_test('embedder_unittests'), make_test('fml_unittests'), + make_test('geometry_unittests'), make_test('no_dart_plugin_registrant_unittests'), make_test('runtime_unittests'), make_test('testing_unittests'),