diff --git a/DEPS b/DEPS index 471822b61502d..c15d9cddc6cd7 100644 --- a/DEPS +++ b/DEPS @@ -35,7 +35,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '654db247514fd82fe4037af9c7ef419cab8484b5', + 'dart_revision': '395f6163303984a9012b5dffd66e02f8f3cc5f77', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 93a2faecc7e70..ddf0a9ffb82ce 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: af6717216660c358a8a89f8c8a9cf21b +Signature: dcfd74d659e97884eace387ac5dfbd83 UNUSED LICENSES: diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 79ff437355df9..1b752ab120662 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include +#include #include "flutter/fml/backtrace.h" #include "flutter/fml/command_line.h" @@ -20,6 +21,21 @@ namespace impeller { namespace compiler { +// Sets the file access mode of the file at path 'p' to 0644. +static bool SetPermissiveAccess(const std::filesystem::path& p) { + auto permissions = + std::filesystem::perms::owner_read | std::filesystem::perms::owner_write | + std::filesystem::perms::group_read | std::filesystem::perms::others_read; + std::error_code error; + std::filesystem::permissions(p, permissions, error); + if (error) { + std::cerr << "Failed to set access on file '" << p + << "': " << error.message() << std::endl; + return false; + } + return true; +} + bool Main(const fml::CommandLine& command_line) { fml::InstallCrashHandler(); if (command_line.HasOption("help")) { @@ -110,6 +126,11 @@ bool Main(const fml::CommandLine& command_line) { << std::endl; return false; } + // Tools that consume the runtime stage data expect the access mode to + // be 0644. + if (!SetPermissiveAccess(sl_file_name)) { + return false; + } } else { if (!fml::WriteAtomically(*switches.working_directory, sl_file_name.string().c_str(), diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index d98275d34f9c7..b3b51035edcb8 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -21,23 +21,6 @@ class Scene extends NativeFieldWrapperClass1 { @pragma('vm:entry-point') Scene._(); - /// Synchronously creates a handle to an image from this scene. - /// - /// {@macro dart.ui.painting.Picture.toImageSync} - Image toImageSync(int width, int height) { - if (width <= 0 || height <= 0) { - throw Exception('Invalid image dimensions.'); - } - - final _Image image = _Image._(); - final String? result = _toImageSync(width, height, image); - if (result != null) { - throw PictureRasterizationException._(result); - } - return Image._(image, image.width, image.height); - } - String? _toImageSync(int width, int height, _Image outImage) native 'Scene_toImageSync'; - /// Creates a raster image representation of the current state of the scene. /// This is a slow operation that is performed on a background thread. /// diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 48def9ddc2941..a6448970f662d 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -22,7 +22,6 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(ui, Scene); #define FOR_EACH_BINDING(V) \ - V(Scene, toImageSync) \ V(Scene, toImage) \ V(Scene, dispose) @@ -67,24 +66,6 @@ void Scene::dispose() { ClearDartWrapper(); } -Dart_Handle Scene::toImageSync(uint32_t width, - uint32_t height, - Dart_Handle raw_image_handle) { - TRACE_EVENT0("flutter", "Scene::toImageSync"); - - if (!layer_tree_) { - return tonic::ToDart("Scene did not contain a layer tree."); - } - - auto picture = layer_tree_->Flatten(SkRect::MakeWH(width, height)); - if (!picture) { - return tonic::ToDart("Could not flatten scene into a layer tree."); - } - - Picture::RasterizeToImageSync(picture, width, height, raw_image_handle); - return Dart_Null(); -} - Dart_Handle Scene::toImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index d550eac484ab6..f7a8d93711814 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -32,10 +32,6 @@ class Scene : public RefCountedDartWrappable { std::unique_ptr takeLayerTree(); - Dart_Handle toImageSync(uint32_t width, - uint32_t height, - Dart_Handle raw_image_handle); - Dart_Handle toImage(uint32_t width, uint32_t height, Dart_Handle image_callback); diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 92fa4967a15b5..c0baaec260b79 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -5375,35 +5375,6 @@ class Picture extends NativeFieldWrapperClass1 { } String? _toImage(int width, int height, _Callback<_Image?> callback) native 'Picture_toImage'; - /// Synchronously creates a handle to an image of this picture. - /// - /// {@template dart.ui.painting.Picture.toImageSync} - /// The returned image will be `width` pixels wide and `height` pixels high. - /// The picture is rasterized within the 0 (left), 0 (top), `width` (right), - /// `height` (bottom) bounds. Content outside these bounds is clipped. - /// - /// The image object is created and returned synchronously, but is rasterized - /// asynchronously. If the rasterization fails, an exception will be thrown - /// when the image is drawn to a [Canvas]. - /// - /// If a GPU context is available, this image will be created as GPU resident - /// and not copied back to the host. This means the image will be more - /// efficient to draw. - /// - /// If no GPU context is availalbe, the image will be rasterized on the CPU. - /// {@endtemplate} - Image toImageSync(int width, int height) { - assert(!_disposed); - if (width <= 0 || height <= 0) { - throw Exception('Invalid image dimensions.'); - } - - final _Image image = _Image._(); - _toImageSync(width, height, image); - return Image._(image, image.width, image.height); - } - void _toImageSync(int width, int height, _Image outImage) native 'Picture_toImageSync'; - /// Release the resources used by this object. The object is no longer usable /// after this method is called. void dispose() { @@ -5912,7 +5883,7 @@ Future _futurize(_Callbacker callbacker) { } /// An exception thrown by [Canvas.drawImage] and related methods when drawing -/// an [Image] created via [Picture.toImageSync] that is in an invalid state. +/// an [Image] that is in an invalid state. /// /// This exception may be thrown if the requested image dimensions exceeded the /// maximum 2D texture size allowed by the GPU, or if no GPU surface or context diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 4ec907e093cdd..95564670a381d 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -23,7 +23,6 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, Picture); #define FOR_EACH_BINDING(V) \ V(Picture, toImage) \ - V(Picture, toImageSync) \ V(Picture, dispose) \ V(Picture, GetAllocationSize) @@ -53,46 +52,6 @@ Dart_Handle Picture::toImage(uint32_t width, raw_image_callback); } -void Picture::toImageSync(uint32_t width, - uint32_t height, - Dart_Handle raw_image_handle) { - FML_DCHECK(display_list_.skia_object()); - RasterizeToImageSync(display_list_.skia_object(), width, height, - raw_image_handle); -} - -// static -void Picture::RasterizeToImageSync(sk_sp display_list, - uint32_t width, - uint32_t height, - Dart_Handle raw_image_handle) { - auto* dart_state = UIDartState::Current(); - auto unref_queue = dart_state->GetSkiaUnrefQueue(); - auto snapshot_delegate = dart_state->GetSnapshotDelegate(); - auto raster_task_runner = dart_state->GetTaskRunners().GetRasterTaskRunner(); - - auto image = CanvasImage::Create(); - auto dl_image = DlDeferredImageGPU::Make(SkISize::Make(width, height)); - image->set_image(dl_image); - - fml::TaskRunner::RunNowOrPostTask( - raster_task_runner, - [snapshot_delegate, unref_queue, dl_image = std::move(dl_image), - display_list = std::move(display_list)]() { - sk_sp sk_image; - std::string error; - std::tie(sk_image, error) = snapshot_delegate->MakeGpuImage( - display_list, dl_image->dimensions()); - if (sk_image) { - dl_image->set_image(std::move(sk_image)); - } else { - dl_image->set_error(std::move(error)); - } - }); - - image->AssociateWithDartWrapper(raw_image_handle); -} - void Picture::dispose() { display_list_.reset(); ClearDartWrapper(); diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index 92653e9123cd6..33e5cb5772cf4 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -37,21 +37,12 @@ class Picture : public RefCountedDartWrappable { uint32_t height, Dart_Handle raw_image_callback); - void toImageSync(uint32_t width, - uint32_t height, - Dart_Handle raw_image_handle); - void dispose(); size_t GetAllocationSize() const override; static void RegisterNatives(tonic::DartLibraryNatives* natives); - static void RasterizeToImageSync(sk_sp display_list, - uint32_t width, - uint32_t height, - Dart_Handle raw_image_handle); - static Dart_Handle RasterizeToImage(sk_sp display_list, uint32_t width, uint32_t height, diff --git a/lib/web_ui/lib/canvas.dart b/lib/web_ui/lib/canvas.dart index c9359b6930781..995bc66299617 100644 --- a/lib/web_ui/lib/canvas.dart +++ b/lib/web_ui/lib/canvas.dart @@ -155,7 +155,6 @@ abstract class Canvas { abstract class Picture { Future toImage(int width, int height); - Image toImageSync(int width, int height); void dispose(); bool get debugDisposed; int get approximateBytesUsed; diff --git a/lib/web_ui/lib/compositing.dart b/lib/web_ui/lib/compositing.dart index 96c51cbeeecf4..de1e6cdbee255 100644 --- a/lib/web_ui/lib/compositing.dart +++ b/lib/web_ui/lib/compositing.dart @@ -6,7 +6,6 @@ part of ui; abstract class Scene { Future toImage(int width, int height); - Image toImageSync(int width, int height); void dispose(); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart index eb6d860a0e072..f0cbd4a12a545 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart @@ -25,12 +25,6 @@ class LayerScene implements ui.Scene { final ui.Picture picture = layerTree.flatten(); return picture.toImage(width, height); } - - @override - ui.Image toImageSync(int width, int height) { - final ui.Picture picture = layerTree.flatten(); - return picture.toImageSync(width, height); - } } class LayerSceneBuilder implements ui.SceneBuilder { diff --git a/lib/web_ui/lib/src/engine/canvaskit/picture.dart b/lib/web_ui/lib/src/engine/canvaskit/picture.dart index c2af7eb844e09..0227246be5157 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/picture.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/picture.dart @@ -92,11 +92,6 @@ class CkPicture extends ManagedSkiaObject implements ui.Picture { @override Future toImage(int width, int height) async { - return toImageSync(width, height); - } - - @override - ui.Image toImageSync(int width, int height) { assert(debugCheckNotDisposed('Cannot convert picture to image.')); final SkSurface skSurface = canvasKit.MakeSurface(width, height); final SkCanvas skCanvas = skSurface.getCanvas(); diff --git a/lib/web_ui/lib/src/engine/html/scene.dart b/lib/web_ui/lib/src/engine/html/scene.dart index 645390e28eaa0..7ae9a36d69096 100644 --- a/lib/web_ui/lib/src/engine/html/scene.dart +++ b/lib/web_ui/lib/src/engine/html/scene.dart @@ -24,11 +24,6 @@ class SurfaceScene implements ui.Scene { throw UnsupportedError('toImage is not supported on the Web'); } - @override - ui.Image toImageSync(int width, int height) { - throw UnsupportedError('toImageSync is not supported on the Web'); - } - /// Releases the resources used by this scene. /// /// After calling this function, the scene is cannot be used further. diff --git a/lib/web_ui/lib/src/engine/picture.dart b/lib/web_ui/lib/src/engine/picture.dart index dc432b2b9f34c..6cc68063c91bf 100644 --- a/lib/web_ui/lib/src/engine/picture.dart +++ b/lib/web_ui/lib/src/engine/picture.dart @@ -88,11 +88,6 @@ class EnginePicture implements ui.Picture { return onImageLoaded.future; } - @override - ui.Image toImageSync(int width, int height) { - throw UnsupportedError('toImageSync is not supported on the HTML backend. Use drawPicture instead, or toImage.'); - } - bool _disposed = false; @override diff --git a/lib/web_ui/test/canvaskit/picture_test.dart b/lib/web_ui/test/canvaskit/picture_test.dart index b082d13d6e260..59851bfc7c3bc 100644 --- a/lib/web_ui/test/canvaskit/picture_test.dart +++ b/lib/web_ui/test/canvaskit/picture_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:typed_data'; - import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -85,23 +83,6 @@ void testMain() { expect(picture.resurrect(), isNotNull); }); }); - - test('toImageSync', () async { - const ui.Color color = ui.Color(0xFFAAAAAA); - final ui.PictureRecorder recorder = ui.PictureRecorder(); - final ui.Canvas canvas = ui.Canvas(recorder); - canvas.drawPaint(ui.Paint()..color = color); - final ui.Picture picture = recorder.endRecording(); - final ui.Image image = picture.toImageSync(10, 15); - - expect(image.width, 10); - expect(image.height, 15); - - final ByteData? data = await image.toByteData(); - expect(data, isNotNull); - expect(data!.lengthInBytes, 10 * 15 * 4); - expect(data.buffer.asUint32List().first, color.value); - }); // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); } diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index b8908bdd24563..f9d526ae6db45 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -321,26 +321,3 @@ void scene_with_red_box() { }; PlatformDispatcher.instance.scheduleFrame(); } - - -@pragma('vm:entry-point') -Future toImageSync() async { - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawPaint(Paint()..color = const Color(0xFFAAAAAA)); - final Picture picture = recorder.endRecording(); - - final Image image = picture.toImageSync(20, 25); - void expect(Object? a, Object? b) { - if (a != b) { - throw 'Expected $a to == $b'; - } - } - expect(image.width, 20); - expect(image.height, 25); - - final ByteData data = (await image.toByteData())!; - expect(data.lengthInBytes, 20 * 25 * 4); - expect(data.buffer.asUint32List().every((int byte) => byte == 0xFFAAAAAA), true); - notifyNative(); -} diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 23f91cdbda900..5b97ac0c27b46 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -3754,40 +3754,6 @@ TEST_F(ShellTest, SpawnWorksWithOnError) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); } -TEST_F(ShellTest, PictureToImageSync) { -#if !SHELL_ENABLE_GL - // GL emulation does not exist on Fuchsia. - GTEST_SKIP(); -#endif // !SHELL_ENABLE_GL - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = - CreateShell(settings, // - GetTaskRunnersForFixture(), // - false, // - nullptr, // - false, // - ShellTestPlatformView::BackendType::kGLBackend // - ); - - fml::AutoResetWaitableEvent latch; - AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { - latch.Signal(); - })); - - ASSERT_NE(shell, nullptr); - ASSERT_TRUE(shell->IsSetup()); - auto configuration = RunConfiguration::InferFromSettings(settings); - PlatformViewNotifyCreated(shell.get()); - configuration.SetEntrypoint("toImageSync"); - RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get()); - - latch.Wait(); - - PlatformViewNotifyDestroyed(shell.get()); - DestroyShell(std::move(shell)); -} - } // namespace testing } // namespace flutter diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java index c57d90f0162c1..d55b0a6b7ce7f 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java @@ -9,10 +9,10 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; -import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; +import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.os.Build; @@ -169,11 +169,7 @@ public void setTexture(@Nullable SurfaceTexture newTx) { // to the user until the platform view draws its first frame. final Canvas canvas = surface.lockHardwareCanvas(); try { - if (Build.VERSION.SDK_INT >= 29) { - canvas.drawColor(Color.TRANSPARENT, BlendMode.CLEAR); - } else { - canvas.drawColor(Color.TRANSPARENT); - } + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); onFrameProduced(); } finally { surface.unlockCanvasAndPost(canvas); @@ -305,11 +301,7 @@ public void draw(Canvas canvas) { try { // Clear the current pixels in the canvas. // This helps when a WebView renders an HTML document with transparent background. - if (Build.VERSION.SDK_INT >= 29) { - surfaceCanvas.drawColor(Color.TRANSPARENT, BlendMode.CLEAR); - } else { - surfaceCanvas.drawColor(Color.TRANSPARENT); - } + surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); super.draw(surfaceCanvas); onFrameProduced(); } finally { diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewWrapperTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewWrapperTest.java index 2893df22834f5..630504f9aaeef 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewWrapperTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewWrapperTest.java @@ -7,9 +7,9 @@ import android.annotation.TargetApi; import android.content.Context; -import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.PorterDuff; import android.graphics.SurfaceTexture; import android.view.Surface; import android.view.View; @@ -65,7 +65,7 @@ protected Surface createSurface(@NonNull SurfaceTexture tx) { // Verify. verify(surface, times(1)).lockHardwareCanvas(); verify(surface, times(1)).unlockCanvasAndPost(canvas); - verify(canvas, times(1)).drawColor(Color.TRANSPARENT, BlendMode.CLEAR); + verify(canvas, times(1)).drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); verifyNoMoreInteractions(surface); verifyNoMoreInteractions(canvas); } @@ -112,7 +112,7 @@ public void draw(Canvas canvas) { wrapper.draw(new Canvas()); // Verify. - verify(canvas, times(1)).drawColor(Color.TRANSPARENT, BlendMode.CLEAR); + verify(canvas, times(1)).drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); verify(surface, times(1)).isValid(); verify(surface, times(1)).lockHardwareCanvas(); verify(surface, times(1)).unlockCanvasAndPost(canvas); diff --git a/shell/platform/windows/keyboard_key_embedder_handler.cc b/shell/platform/windows/keyboard_key_embedder_handler.cc index 762051e723884..8f8eca7b1fc9c 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -186,6 +186,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl( const bool is_event_down = action == WM_KEYDOWN || action == WM_SYSKEYDOWN; + bool event_key_can_be_repeat = true; UpdateLastSeenCritialKey(key, physical_key, sequence_logical_key); // Synchronize the toggled states of critical keys (such as whether CapsLocks // is enabled). Toggled states can only be changed upon a down event, so if @@ -197,14 +198,14 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl( // updated to the true state, while the critical keys whose toggled state have // been changed will be pressed regardless of their true pressed state. // Updating the pressed state will be done by SynchronizeCritialPressedStates. - SynchronizeCritialToggledStates(key, is_event_down); + SynchronizeCritialToggledStates(key, is_event_down, &event_key_can_be_repeat); // Synchronize the pressed states of critical keys (such as whether CapsLocks // is pressed). // // After this function, all critical keys except for the target key will have // their toggled state and pressed state matched with their true states. The // target key's pressed state will be updated immediately after this. - SynchronizeCritialPressedStates(key, physical_key, is_event_down); + SynchronizeCritialPressedStates(key, physical_key, is_event_down, event_key_can_be_repeat); // The resulting event's `type`. FlutterKeyEventType type; @@ -340,7 +341,8 @@ void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey( void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates( int event_virtual_key, - bool is_event_down) { + bool is_event_down, + bool* event_key_can_be_repeat) { // NowState ----------------> PreEventState --------------> TrueState // Synchronization Event for (auto& kv : critical_keys_) { @@ -385,6 +387,7 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates( key_info.physical_key, key_info.logical_key, empty_character), nullptr, nullptr); + *event_key_can_be_repeat = false; } key_info.toggled_on = true_toggled; } @@ -394,7 +397,8 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates( void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates( int event_virtual_key, int event_physical_key, - bool is_event_down) { + bool is_event_down, + bool event_key_can_be_repeat) { // During an incoming event, there might be a synthesized Flutter event for // each key of each pressing goal, followed by an eventual main Flutter // event. @@ -424,8 +428,18 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates( if (is_event_down) { // For down events, this key is the event key if they have the same // virtual key, because virtual key represents "functionality." + // + // In that case, normally Flutter should synthesize nothing since the + // resulting event can adapt to the current state by flexing between a + // down and a repeat event. However, in certain cases (when Flutter has + // just synchronized the key's toggling state) the event must not be a + // repeat event. if (virtual_key == event_virtual_key) { - pre_event_pressed = false; + if (event_key_can_be_repeat) { + continue; + } else { + pre_event_pressed = false; + } } } else { // For up events, this key is the event key if they have the same diff --git a/shell/platform/windows/keyboard_key_embedder_handler.h b/shell/platform/windows/keyboard_key_embedder_handler.h index 426a38d42deab..67ff6490e5fc4 100644 --- a/shell/platform/windows/keyboard_key_embedder_handler.h +++ b/shell/platform/windows/keyboard_key_embedder_handler.h @@ -115,12 +115,14 @@ class KeyboardKeyEmbedderHandler // Check each key's state from |get_key_state_| and synthesize events // if their toggling states have been desynchronized. void SynchronizeCritialToggledStates(int event_virtual_key, - bool is_event_down); + bool is_event_down, + bool* event_key_can_be_repeat); // Check each key's state from |get_key_state_| and synthesize events // if their pressing states have been desynchronized. void SynchronizeCritialPressedStates(int event_virtual_key, int event_physical_key, - bool is_event_down); + bool is_event_down, + bool event_key_can_be_repeat); // Wraps perform_send_event_ with state tracking. Use this instead of // |perform_send_event_| to send events to the framework. diff --git a/shell/platform/windows/keyboard_win32_unittests.cc b/shell/platform/windows/keyboard_win32_unittests.cc index 7b21788e795e8..5ed31f7822bd1 100644 --- a/shell/platform/windows/keyboard_win32_unittests.cc +++ b/shell/platform/windows/keyboard_win32_unittests.cc @@ -697,6 +697,18 @@ TEST(KeyboardTest, ShiftLeftUnhandled) { clear_key_calls(); EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1); + // Hold ShiftLeft + tester.InjectKeyboardChanges(std::vector{ + WmKeyDownInfo{VK_SHIFT, kScanCodeShiftLeft, kNotExtended, kWasDown}.Build( + kWmResultZero)}); + + EXPECT_EQ(key_calls.size(), 1); + EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeRepeat, + kPhysicalShiftLeft, kLogicalShiftLeft, "", + kNotSynthesized); + clear_key_calls(); + EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1); + // Release ShiftLeft tester.InjectKeyboardChanges(std::vector{ KeyStateChange{VK_LSHIFT, false, true}, diff --git a/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 4de620f9750cd..c0bae43fb5f0a 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -410,103 +410,6 @@ void main() { expect(areEqual, true); }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 - test('toImageSync - too big', () async { - PictureRecorder recorder = PictureRecorder(); - Canvas canvas = Canvas(recorder); - canvas.drawPaint(Paint()..color = const Color(0xFF123456)); - final Picture picture = recorder.endRecording(); - final Image image = picture.toImageSync(300000, 4000000); - picture.dispose(); - - expect(image.width, 300000); - expect(image.height, 4000000); - - recorder = PictureRecorder(); - canvas = Canvas(recorder); - - // On a slower CI machine, the raster thread may get behind the UI thread - // here. However, once the image is in an error state it will immediately - // throw on subsequent attempts. - bool caughtException = false; - for (int iterations = 0; iterations < 1000; iterations += 1) { - try { - canvas.drawImage(image, Offset.zero, Paint()); - } on PictureRasterizationException catch (e) { - caughtException = true; - expect(e.message, contains('unable to create render target at specified size')); - break; - } - // Let the event loop turn. - await Future.delayed(const Duration(milliseconds: 1)); - } - expect(caughtException, true); - expect( - () => canvas.drawImageRect(image, Rect.zero, Rect.zero, Paint()), - throwsException, - ); - expect( - () => canvas.drawImageNine(image, Rect.zero, Rect.zero, Paint()), - throwsException, - ); - expect( - () => canvas.drawAtlas(image, [], [], null, null, null, Paint()), - throwsException, - ); - }); - - test('toImageSync - succeeds', () async { - PictureRecorder recorder = PictureRecorder(); - Canvas canvas = Canvas(recorder); - canvas.drawPaint(Paint()..color = const Color(0xFF123456)); - final Picture picture = recorder.endRecording(); - final Image image = picture.toImageSync(30, 40); - picture.dispose(); - - expect(image.width, 30); - expect(image.height, 40); - - recorder = PictureRecorder(); - canvas = Canvas(recorder); - expect( - () => canvas.drawImage(image, Offset.zero, Paint()), - returnsNormally, - ); - expect( - () => canvas.drawImageRect(image, Rect.zero, Rect.zero, Paint()), - returnsNormally, - ); - expect( - () => canvas.drawImageNine(image, Rect.zero, Rect.zero, Paint()), - returnsNormally, - ); - expect( - () => canvas.drawAtlas(image, [], [], null, null, null, Paint()), - returnsNormally, - ); - }); - - test('toImageSync - toByteData', () async { - const Color color = Color(0xFF123456); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawPaint(Paint()..color = color); - final Picture picture = recorder.endRecording(); - final Image image = picture.toImageSync(6, 8); - picture.dispose(); - - expect(image.width, 6); - expect(image.height, 8); - - final ByteData? data = await image.toByteData(format: ImageByteFormat.rawRgba); - - expect(data, isNotNull); - expect(data!.lengthInBytes, 6 * 8 * 4); - expect(data.buffer.asUint8List()[0], 0x12); - expect(data.buffer.asUint8List()[1], 0x34); - expect(data.buffer.asUint8List()[2], 0x56); - expect(data.buffer.asUint8List()[3], 0xFF); - }); - test('Canvas.drawParagraph throws when Paragraph.layout was not called', () async { // Regression test for https://github.com/flutter/flutter/issues/97172 bool assertsEnabled = false; diff --git a/testing/dart/compositing_test.dart b/testing/dart/compositing_test.dart index 7815fa2aaaf57..69b147c2514ac 100644 --- a/testing/dart/compositing_test.dart +++ b/testing/dart/compositing_test.dart @@ -8,34 +8,6 @@ import 'dart:ui'; import 'package:litetest/litetest.dart'; void main() { - test('Scene.toImageSync succeeds', () async { - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - const Color color = Color(0xFF123456); - canvas.drawPaint(Paint()..color = color); - final Picture picture = recorder.endRecording(); - final SceneBuilder builder = SceneBuilder(); - builder.pushOffset(10, 10); - builder.addPicture(const Offset(5, 5), picture); - final Scene scene = builder.build(); - - final Image image = scene.toImageSync(6, 8); - picture.dispose(); - scene.dispose(); - - expect(image.width, 6); - expect(image.height, 8); - - final ByteData? data = await image.toByteData(); - - expect(data, isNotNull); - expect(data!.lengthInBytes, 6 * 8 * 4); - expect(data.buffer.asUint8List()[0], 0x12); - expect(data.buffer.asUint8List()[1], 0x34); - expect(data.buffer.asUint8List()[2], 0x56); - expect(data.buffer.asUint8List()[3], 0xFF); - }); - test('addPicture with disposed picture does not crash', () { bool assertsEnabled = false; assert(() { diff --git a/testing/dart/image_shader_test.dart b/testing/dart/image_shader_test.dart index 56c502b132596..f34b91ae85315 100644 --- a/testing/dart/image_shader_test.dart +++ b/testing/dart/image_shader_test.dart @@ -16,20 +16,4 @@ void main() { const Rect rect = Rect.fromLTRB(0, 0, 100, 100); testCanvas((Canvas canvas) => canvas.drawRect(rect, paint)); }); - - test('Construct an ImageShader - GPU image', () async { - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawPaint(Paint()..color = const Color(0xFFABCDEF)); - final Picture picture = recorder.endRecording(); - final Image image = picture.toImageSync(50, 50); - picture.dispose(); - - // TODO(dnfield): this should not throw once - // https://github.com/flutter/flutter/issues/105085 is fixed. - expect( - () => ImageShader(image, TileMode.clamp, TileMode.clamp, Float64List(16)), - throwsException, - ); - }); }