diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 4f50276dc7e6b..c83eb27629473 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -1632,6 +1632,10 @@ removed in future Electron releases. * `rot90` - The widget content is rotated to portrait at 90 degrees (clockwise). * `rot180` - The widget content is rotated to portrait at 180 degrees (clockwise). * `rot270` - The widget content is rotated to portrait at 270 degrees (clockwise). + * `mirror` - The widget content is mirrored horizontally. + * `mirror_rot90` - The widget content is mirrored horizontally and rotated 90 degrees clockwise. + * `mirror_rot180` - The widget content is mirrored horizontally and rotated 180 degrees clockwise. + * `mirror_rot270` - The widget content is mirrored horizontally and rotated 270 degrees clockwise. This method sets the browser window's transform. diff --git a/docs/api/structures/web-preferences.md b/docs/api/structures/web-preferences.md index 17789255a99d7..945d122b5f6b5 100644 --- a/docs/api/structures/web-preferences.md +++ b/docs/api/structures/web-preferences.md @@ -151,6 +151,10 @@ * `rot90` - The widget content is rotated to portrait at 90 degrees (clockwise). * `rot180` - The widget content is rotated to portrait at 180 degrees (clockwise). * `rot270` - The widget content is rotated to portrait at 270 degrees (clockwise). + * `mirror` - The widget content is mirrored horizontally. + * `mirror_rot90` - The widget content is mirrored horizontally and rotated 90 degrees clockwise. + * `mirror_rot180` - The widget content is mirrored horizontally and rotated 180 degrees clockwise. + * `mirror_rot270` - The widget content is mirrored horizontally and rotated 270 degrees clockwise. [chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment [runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5 diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 30bb54b2d28a6..654178e566dc9 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -228,3 +228,4 @@ feat_brightsign_add_setattribute_and_setsyncparams_os-21421.patch add_removetrap_api_os-21158.patch fix_mojo_trap_leak_os-21158.patch fix_brightsign_fire_timechanged_for_equal-time-seeks_on-seek_os-21551.patch +os-20201_add_mirrored_window_transforms.patch diff --git a/patches/chromium/os-20201_add_mirrored_window_transforms.patch b/patches/chromium/os-20201_add_mirrored_window_transforms.patch new file mode 100644 index 0000000000000..a3c642f478d69 --- /dev/null +++ b/patches/chromium/os-20201_add_mirrored_window_transforms.patch @@ -0,0 +1,374 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> +Date: Tue, 21 Jul 2026 12:00:00 +0000 +Subject: Add hardware-accelerated mirrored window transforms + +Extend window and overlay transforms with mirrored quarter turns. Propagate +these transforms through Viz and the Linux Wayland, Vulkan, and DRM hardware +presentation paths, and update Aura bounds, popup, and input transforms. + +diff --git a/components/viz/service/display/overlay_candidate_factory.cc b/components/viz/service/display/overlay_candidate_factory.cc +index ffd035def63968742be4d45242d41d95996cd9b2..a7b18031ffc57f96233052b50374c924afb40b1b 100644 +--- a/components/viz/service/display/overlay_candidate_factory.cc ++++ b/components/viz/service/display/overlay_candidate_factory.cc +@@ -81,6 +81,10 @@ gfx::OverlayTransform GetOverlayTransform(const gfx::Transform& quad_transform, + return gfx::OVERLAY_TRANSFORM_ROTATE_180; + else if (x_to == AXIS_POS_Y && y_to == AXIS_NEG_X) + return gfx::OVERLAY_TRANSFORM_ROTATE_90; ++ else if (x_to == AXIS_NEG_Y && y_to == AXIS_NEG_X) ++ return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90; ++ else if (x_to == AXIS_POS_Y && y_to == AXIS_POS_X) ++ return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270; + else + return gfx::OVERLAY_TRANSFORM_INVALID; + } +diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc +index a169716f9996ad111a5535c380c47e73b288036a..c45dcb5fdbf1069d82e68396f3f48a52bb4b5cbb 100644 +--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc ++++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc +@@ -608,6 +608,8 @@ gfx::Size SkiaOutputDeviceBufferQueue::GetSwapBuffersSize() { + switch (overlay_transform_) { + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + case gfx::OVERLAY_TRANSFORM_ROTATE_270: ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: + return gfx::Size(image_size_.height(), image_size_.width()); + case gfx::OVERLAY_TRANSFORM_INVALID: + case gfx::OVERLAY_TRANSFORM_NONE: +diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc +index 395be305b02e20a9112d5eabb2e6fabab6788596..d8197f26395af73a3f10eaf7a0712f709a8ecfc8 100644 +--- a/content/browser/renderer_host/render_widget_host_view_aura.cc ++++ b/content/browser/renderer_host/render_widget_host_view_aura.cc +@@ -315,6 +315,23 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura( + case blink::mojom::WindowTransformType::kWindowTransformTypeRotate270: + window_transform_ = gfx::OVERLAY_TRANSFORM_ROTATE_270; + break; ++ case blink::mojom::WindowTransformType::kWindowTransformTypeMirror: ++ window_transform_ = gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL; ++ break; ++ case blink::mojom::WindowTransformType:: ++ kWindowTransformTypeMirrorRotate90: ++ window_transform_ = ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90; ++ break; ++ case blink::mojom::WindowTransformType:: ++ kWindowTransformTypeMirrorRotate180: ++ window_transform_ = gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL; ++ break; ++ case blink::mojom::WindowTransformType:: ++ kWindowTransformTypeMirrorRotate270: ++ window_transform_ = ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270; ++ break; + default: + window_transform_ = gfx::OVERLAY_TRANSFORM_NONE; + break; +@@ -3151,6 +3168,14 @@ gfx::Rect RenderWidgetHostViewAura::GetTransformedPopupBounds(const gfx::Rect& n + transformed_bounds.Offset(parent_bounds.OffsetFromOrigin()); + + switch (window_transform_) { ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: ++ transformed_bounds.Offset(-original_popup_bounds_.width(), 0); ++ break; ++ ++ case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: ++ transformed_bounds.Offset(0, -original_popup_bounds_.height()); ++ break; ++ + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + transformed_bounds.set_size(gfx::Size(original_popup_bounds_.height(), original_popup_bounds_.width())); + transformed_bounds.Offset(-original_popup_bounds_.height(), 0); +@@ -3165,6 +3190,16 @@ gfx::Rect RenderWidgetHostViewAura::GetTransformedPopupBounds(const gfx::Rect& n + transformed_bounds.Offset(0, -original_popup_bounds_.width()); + break; + ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ transformed_bounds.set_size(gfx::Size(original_popup_bounds_.height(), original_popup_bounds_.width())); ++ transformed_bounds.Offset(-original_popup_bounds_.height(), ++ -original_popup_bounds_.width()); ++ break; ++ ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ transformed_bounds.set_size(gfx::Size(original_popup_bounds_.height(), original_popup_bounds_.width())); ++ break; ++ + default: + break; + } +diff --git a/gpu/vulkan/vulkan_surface.cc b/gpu/vulkan/vulkan_surface.cc +index 35d1d433945ed3b9a1f8ccc5b0d404341015f871..fca8c23fac1147380d97d835b51480cba9b757a2 100644 +--- a/gpu/vulkan/vulkan_surface.cc ++++ b/gpu/vulkan/vulkan_surface.cc +@@ -43,6 +43,10 @@ VkSurfaceTransformFlagBitsKHR ToVkSurfaceTransformFlag( + return VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + return VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR; ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR; ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR; + default: + NOTREACHED() << "transform:" << transform; + return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; +@@ -64,6 +68,10 @@ gfx::OverlayTransform FromVkSurfaceTransformFlag( + return gfx::OVERLAY_TRANSFORM_ROTATE_180; + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: + return gfx::OVERLAY_TRANSFORM_ROTATE_270; ++ case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR: ++ return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90; ++ case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR: ++ return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270; + default: + NOTREACHED() << "transform:" << transform; + return gfx::OVERLAY_TRANSFORM_INVALID; +diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom +index 13fe8c8640bc896a76f9e5e0eba0d95fa025275a..3d3713ef591d96ff09524ff5e4307c99312a8ffb 100644 +--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom ++++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom +@@ -112,6 +112,10 @@ enum WindowTransformType { + kWindowTransformTypeRotate90, + kWindowTransformTypeRotate180, + kWindowTransformTypeRotate270, ++ kWindowTransformTypeMirror, ++ kWindowTransformTypeMirrorRotate90, ++ kWindowTransformTypeMirrorRotate180, ++ kWindowTransformTypeMirrorRotate270, + }; + + struct WebPreferences { +diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc +index 6527d15683c7055baa358fb5e2b90e198d5fc9bc..1c6e790e2dd91ff39d145fc58a776d95cdc46370 100644 +--- a/ui/aura/window_tree_host.cc ++++ b/ui/aura/window_tree_host.cc +@@ -235,6 +235,7 @@ void WindowTreeHost::SetDisplayTransformHint(gfx::OverlayTransform transform) { + return; + + compositor()->SetDisplayTransformHint(transform); ++ compositor()->ScheduleFullRedraw(); + OnHostResizedInPixels(GetBoundsInPixels().size()); + CalculateInputTransform(transform); + } +@@ -257,7 +258,11 @@ gfx::Rect WindowTreeHost::GetTransformedWindowBounds(const gfx::Rect& new_bounds + + if (IsPopup()) { + if (transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_90 || +- transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_270) { ++ transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_270 || ++ transform_hint_ == ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90 || ++ transform_hint_ == ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270) { + // Make the window bounds equal the original popup bounds with the width and height swapped. + transformed_bounds.set_size(gfx::Size(original_popup_bounds_.height(), original_popup_bounds_.width())); + } +@@ -265,7 +270,11 @@ gfx::Rect WindowTreeHost::GetTransformedWindowBounds(const gfx::Rect& new_bounds + else { + gfx::Rect window_bounds = GetBoundsInPixels(); + if ((transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_90 || +- transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_270) && ++ transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_270 || ++ transform_hint_ == ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90 || ++ transform_hint_ == ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270) && + window_bounds.height() && window_bounds.width()) { + float window_aspect_ratio = (float)window_bounds.width() / (float)window_bounds.height(); + float new_aspect_ratio = (float)new_bounds.width() / (float)new_bounds.height(); +@@ -286,7 +295,11 @@ gfx::Rect WindowTreeHost::GetNonTransformedWindowBounds(const gfx::Rect& new_bou + gfx::Rect window_bounds = GetBoundsInPixels(); + + if ((transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_90 || +- transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_270) && ++ transform_hint_ == gfx::OVERLAY_TRANSFORM_ROTATE_270 || ++ transform_hint_ == ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90 || ++ transform_hint_ == ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270) && + window_bounds.height() && window_bounds.width()) { + float window_aspect_ratio = (float)window_bounds.width() / (float)window_bounds.height(); + float new_aspect_ratio = (float)new_bounds.width() / (float)new_bounds.height(); +@@ -306,6 +319,18 @@ void WindowTreeHost::CalculateInputTransform(gfx::OverlayTransform transform) { + popup_transform_.MakeIdentity(); + + switch (transform) { ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: ++ input_transform_ = gfx::Transform::Affine( ++ -1, 0, 0, 1, GetBoundsInPixels().width(), 0); ++ popup_transform_ = input_transform_; ++ break; ++ ++ case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: ++ input_transform_ = gfx::Transform::Affine( ++ 1, 0, 0, -1, 0, GetBoundsInPixels().height()); ++ popup_transform_ = input_transform_; ++ break; ++ + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + input_transform_.Translate(0, GetBoundsInPixels().width()); + input_transform_.Rotate(-90); +@@ -330,6 +355,20 @@ void WindowTreeHost::CalculateInputTransform(gfx::OverlayTransform transform) { + popup_transform_.Rotate(-90); + break; + ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ input_transform_ = gfx::Transform::Affine( ++ 0, -1, -1, 0, GetBoundsInPixels().height(), ++ GetBoundsInPixels().width()); ++ popup_transform_ = gfx::Transform::Affine( ++ 0, -1, -1, 0, GetBoundsInPixels().width(), ++ GetBoundsInPixels().height()); ++ break; ++ ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ input_transform_ = gfx::Transform::Affine(0, 1, 1, 0, 0, 0); ++ popup_transform_ = input_transform_; ++ break; ++ + default: + break; + } +diff --git a/ui/gfx/mojom/overlay_transform.mojom b/ui/gfx/mojom/overlay_transform.mojom +index 6021528f40c74e09d8b209dff81dd1a7fb0eac06..21b29b5ca3ff4b52431c6e55807bb84f589ef370 100644 +--- a/ui/gfx/mojom/overlay_transform.mojom ++++ b/ui/gfx/mojom/overlay_transform.mojom +@@ -13,5 +13,7 @@ enum OverlayTransform { + OVERLAY_TRANSFORM_ROTATE_90, + OVERLAY_TRANSFORM_ROTATE_180, + OVERLAY_TRANSFORM_ROTATE_270, +- OVERLAY_TRANSFORM_LAST = OVERLAY_TRANSFORM_ROTATE_270 ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90, ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270, ++ OVERLAY_TRANSFORM_LAST = OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270 + }; +diff --git a/ui/gfx/mojom/overlay_transform_mojom_traits.h b/ui/gfx/mojom/overlay_transform_mojom_traits.h +index 62c028ed3faf9860167526cf0a8ac333fb261bd5..b46de1abe852c5460fa4773b3e5ad94691640a90 100644 +--- a/ui/gfx/mojom/overlay_transform_mojom_traits.h ++++ b/ui/gfx/mojom/overlay_transform_mojom_traits.h +@@ -29,6 +29,12 @@ struct EnumTraits { + return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_180; + case gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270: + return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270; ++ case gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ return gfx::mojom::OverlayTransform:: ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90; ++ case gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ return gfx::mojom::OverlayTransform:: ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270; + } + NOTREACHED(); + return gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_INVALID; +@@ -58,6 +64,16 @@ struct EnumTraits { + case gfx::mojom::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270: + *out = gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270; + return true; ++ case gfx::mojom::OverlayTransform:: ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ *out = gfx::OverlayTransform:: ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90; ++ return true; ++ case gfx::mojom::OverlayTransform:: ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ *out = gfx::OverlayTransform:: ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270; ++ return true; + } + NOTREACHED(); + return false; +diff --git a/ui/gfx/overlay_transform.h b/ui/gfx/overlay_transform.h +index 3fbe5e3fee8f147c50bea210c30d3dcf5860d4f1..318f04bf2836bfd30df5372be787947a3eb85352 100644 +--- a/ui/gfx/overlay_transform.h ++++ b/ui/gfx/overlay_transform.h +@@ -20,7 +20,9 @@ enum OverlayTransform : uint8_t { + OVERLAY_TRANSFORM_ROTATE_90, + OVERLAY_TRANSFORM_ROTATE_180, + OVERLAY_TRANSFORM_ROTATE_270, +- OVERLAY_TRANSFORM_LAST = OVERLAY_TRANSFORM_ROTATE_270 ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90, ++ OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270, ++ OVERLAY_TRANSFORM_LAST = OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270 + }; + + } // namespace gfx +diff --git a/ui/gfx/overlay_transform_utils.cc b/ui/gfx/overlay_transform_utils.cc +index 5d23bb53da955d45ca1543d0fb36ad33d709b395..1b33b48059ee2e449cd3645c2ec10f83fc8f172b 100644 +--- a/ui/gfx/overlay_transform_utils.cc ++++ b/ui/gfx/overlay_transform_utils.cc +@@ -28,6 +28,11 @@ Transform OverlayTransformToTransform(OverlayTransform overlay_transform, + viewport_bounds.height()); + case OVERLAY_TRANSFORM_ROTATE_270: + return Transform::Affine(0, -1, 1, 0, 0, viewport_bounds.width()); ++ case OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ return Transform::Affine(0, -1, -1, 0, viewport_bounds.height(), ++ viewport_bounds.width()); ++ case OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ return Transform::Affine(0, 1, 1, 0, 0, 0); + } + + NOTREACHED(); +@@ -51,6 +56,10 @@ OverlayTransform InvertOverlayTransform(OverlayTransform transform) { + return OVERLAY_TRANSFORM_ROTATE_180; + case OVERLAY_TRANSFORM_ROTATE_270: + return OVERLAY_TRANSFORM_ROTATE_90; ++ case OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ return OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90; ++ case OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ return OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270; + } + NOTREACHED(); + return OVERLAY_TRANSFORM_NONE; +diff --git a/ui/gfx/overlay_transform_utils_unittest.cc b/ui/gfx/overlay_transform_utils_unittest.cc +index d4bea640d3c2f280c6da29210bfdc06932d93685..5bd049cb10e4e9d572bad711c9166459d80bea5e 100644 +--- a/ui/gfx/overlay_transform_utils_unittest.cc ++++ b/ui/gfx/overlay_transform_utils_unittest.cc +@@ -26,6 +26,10 @@ TEST(OverlayTransformUtilTest, All) { + {OVERLAY_TRANSFORM_ROTATE_90, Rect(90, 10, 100, 50)}, + {OVERLAY_TRANSFORM_ROTATE_180, Rect(40, 90, 50, 100)}, + {OVERLAY_TRANSFORM_ROTATE_270, Rect(10, 40, 100, 50)}, ++ {OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90, ++ Rect(90, 40, 100, 50)}, ++ {OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270, ++ Rect(10, 10, 100, 50)}, + }; + + for (const auto& test_case : test_cases) { +diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc +index 4c3055758f7a0f75225d4cc646c6483e2a05a940..4d401e3a46351365d34c3ff4a7705114cf9acf73 100644 +--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc ++++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc +@@ -36,6 +36,10 @@ uint32_t OverlayTransformToDrmRotationPropertyValue( + return DRM_MODE_ROTATE_180; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + return DRM_MODE_ROTATE_90; ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ return DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270; ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ return DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90; + default: + NOTREACHED(); + } +@@ -55,6 +59,10 @@ bool IsRotationTransformSupported(gfx::OverlayTransform transform, + bool is_original_buffer) { + if ((transform == gfx::OVERLAY_TRANSFORM_ROTATE_90) || + (transform == gfx::OVERLAY_TRANSFORM_ROTATE_270) || ++ (transform == ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90) || ++ (transform == ++ gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270) || + (transform == gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL)) { + if (is_original_buffer && (format_fourcc == DRM_FORMAT_NV12 || + format_fourcc == DRM_FORMAT_P010)) { +diff --git a/ui/ozone/platform/wayland/common/wayland_util.cc b/ui/ozone/platform/wayland/common/wayland_util.cc +index dc02f56e1d72f822b5ee8b41b45af7d767f50812..27b81614e4419fee30a2ce7c2bc8b336d4f516bd 100644 +--- a/ui/ozone/platform/wayland/common/wayland_util.cc ++++ b/ui/ozone/platform/wayland/common/wayland_util.cc +@@ -136,6 +136,10 @@ wl_output_transform ToWaylandTransform(gfx::OverlayTransform transform) { + return WL_OUTPUT_TRANSFORM_180; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + return WL_OUTPUT_TRANSFORM_90; ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90: ++ return WL_OUTPUT_TRANSFORM_FLIPPED_270; ++ case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270: ++ return WL_OUTPUT_TRANSFORM_FLIPPED_90; + default: + break; + } diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 167dd22cfbc63..b6053bc4f2ba8 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -1145,7 +1145,19 @@ void BaseWindow::SetWindowTransform(const std::string& transform) { blink::mojom::WindowTransformType transform_type = blink::mojom::WindowTransformType::kWindowTransformTypeNone; - if (transform == "rot180") { + if (transform == "mirror") { + transform_type = + blink::mojom::WindowTransformType::kWindowTransformTypeMirror; + } else if (transform == "mirror_rot90") { + transform_type = + blink::mojom::WindowTransformType::kWindowTransformTypeMirrorRotate90; + } else if (transform == "mirror_rot180") { + transform_type = + blink::mojom::WindowTransformType::kWindowTransformTypeMirrorRotate180; + } else if (transform == "mirror_rot270") { + transform_type = + blink::mojom::WindowTransformType::kWindowTransformTypeMirrorRotate270; + } else if (transform == "rot180") { transform_type = blink::mojom::WindowTransformType::kWindowTransformTypeRotate180; } else if (transform == "rot270") { @@ -1155,6 +1167,7 @@ void BaseWindow::SetWindowTransform(const std::string& transform) { transform_type = blink::mojom::WindowTransformType::kWindowTransformTypeRotate90; } + window_->SetWindowTransform(transform_type); } #endif diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 960038dd6486a..564079e06bb08 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -512,6 +512,18 @@ void NativeWindowViews::SetWindowTransform( case blink::mojom::WindowTransformType::kWindowTransformTypeRotate270: window_transform = gfx::OVERLAY_TRANSFORM_ROTATE_270; break; + case blink::mojom::WindowTransformType::kWindowTransformTypeMirror: + window_transform = gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL; + break; + case blink::mojom::WindowTransformType::kWindowTransformTypeMirrorRotate90: + window_transform = gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_90; + break; + case blink::mojom::WindowTransformType::kWindowTransformTypeMirrorRotate180: + window_transform = gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL; + break; + case blink::mojom::WindowTransformType::kWindowTransformTypeMirrorRotate270: + window_transform = gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL_ROTATE_270; + break; default: window_transform = gfx::OVERLAY_TRANSFORM_NONE; break; diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc index be24bdcd48a22..4586b8e31cd2b 100644 --- a/shell/browser/web_contents_preferences.cc +++ b/shell/browser/web_contents_preferences.cc @@ -81,6 +81,10 @@ struct Converter { using Val = blink::mojom::WindowTransformType; static constexpr auto Lookup = base::MakeFixedFlatMapSorted({ + {"mirror", Val::kWindowTransformTypeMirror}, + {"mirror_rot180", Val::kWindowTransformTypeMirrorRotate180}, + {"mirror_rot270", Val::kWindowTransformTypeMirrorRotate270}, + {"mirror_rot90", Val::kWindowTransformTypeMirrorRotate90}, {"none", Val::kWindowTransformTypeNone}, {"rot180", Val::kWindowTransformTypeRotate180}, {"rot270", Val::kWindowTransformTypeRotate270}, diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index f90f01829c0cb..bb2a6a6381907 100644 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -6718,6 +6718,20 @@ describe('BrowserWindow module', () => { const offsetBounds = { x: 100, y: 200, width: 600, height: 400 }; const negativeOffsetBounds = { x: -5, y: -5, width: 600, height: 400 }; const fullScreenBounds = { x: 0, y: 0, width: screenSize.width - 1, height: screenSize.height - 1 }; + const windowBounds = [ + { description: 'landscape', bounds: landscapeBounds }, + { description: 'portrait', bounds: portraitBounds }, + { description: 'square', bounds: squareBounds }, + { description: 'x,y offset', bounds: offsetBounds }, + { description: 'x,y negative offset', bounds: negativeOffsetBounds }, + { description: 'full screen', bounds: fullScreenBounds } + ]; + const rotatedResizeBounds = [ + { description: 'portraitBounds', bounds: portraitBounds }, + { description: 'squareBounds', bounds: squareBounds }, + { description: 'bounds with x,y offsets', bounds: offsetBounds }, + { description: 'bounds with negative x,y offsets', bounds: negativeOffsetBounds } + ]; const SELECT_HEIGHT = 10; const SELECT_WIDTH = 20; const SELECT_OPTION_HEIGHT = 18; @@ -6733,11 +6747,93 @@ describe('BrowserWindow module', () => { const MOUSE_X_OFFSET = 10; const MOUSE_Y_OFFSET = 5; + type WindowTransform = Parameters[0]; + type WindowTransformTest = { + name: WindowTransform; + description: string; + swapsDimensions: boolean; + transformPosition: (bounds: Electron.Rectangle, x: number, y: number) => {x: number, y: number}; + }; + const windowTransforms: readonly WindowTransformTest[] = [ + { + name: 'none', + description: 'identity', + swapsDimensions: false, + transformPosition: (_bounds, x, y) => ({ x, y }) + }, + { + name: 'rot90', + description: 'rot90', + swapsDimensions: true, + transformPosition: (bounds, x, y) => ({ x: bounds.width - y, y: x }) + }, + { + name: 'rot180', + description: 'rot180', + swapsDimensions: false, + transformPosition: (bounds, x, y) => ({ x: bounds.width - x, y: bounds.height - y }) + }, + { + name: 'mirror', + description: 'mirror', + swapsDimensions: false, + transformPosition: (bounds, x, y) => ({ x: bounds.width - x, y }) + }, + { + name: 'mirror_rot90', + description: 'mirror_rot90', + swapsDimensions: true, + transformPosition: (bounds, x, y) => ({ x: bounds.width - y, y: bounds.height - x }) + }, + { + name: 'mirror_rot180', + description: 'mirror_rot180', + swapsDimensions: false, + transformPosition: (bounds, x, y) => ({ x, y: bounds.height - y }) + }, + { + name: 'mirror_rot270', + description: 'mirror_rot270', + swapsDimensions: true, + transformPosition: (_bounds, x, y) => ({ x: y, y: x }) + }, + { + name: 'rot270', + description: 'rot270', + swapsDimensions: true, + transformPosition: (bounds, x, y) => ({ x: y, y: bounds.height - x }) + } + ]; + const identityTransform = windowTransforms.find(({ name }) => name === 'none')!; + const rot270Transform = windowTransforms.find(({ name }) => name === 'rot270')!; let w: BrowserWindow; const clickMouse = (w: BrowserWindow, pos: {x: number, y: number}) => { w.webContents.sendInputEvent({ type: 'mouseDown', clickCount: 1, x: pos.x, y: pos.y }); w.webContents.sendInputEvent({ type: 'mouseUp', clickCount: 1, x: pos.x, y: pos.y }); }; + const verifyWindowTransform = async (bounds: Electron.Rectangle, transform: WindowTransformTest) => { + await setTimeout(500); + const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); + expect(windowWidth).to.be.equal(transform.swapsDimensions ? bounds.height : bounds.width); + expect(windowHeight).to.be.equal(transform.swapsDimensions ? bounds.width : bounds.height); + expectBoundsEqual(w.getBounds(), bounds); + + const selectPosition = transform.transformPosition(bounds, SELECT_WIDTH_OFFSET, SELECT_HEIGHT_OFFSET); + const selectColour = getPixelColor(await captureScreen(), { + x: bounds.x + selectPosition.x, + y: bounds.y + selectPosition.y + }); + expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); + + clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); + await setTimeout(500); + const optionPosition = transform.transformPosition(bounds, SELECT_OPTION_WIDTH_OFFSET, SELECT_OPTION_HEIGHT_OFFSET); + const selectOptionColour = getPixelColor(await captureScreen(), { + x: bounds.x + optionPosition.x, + y: bounds.y + optionPosition.y + }); + expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); + }; // Enable to help debug problems. // const dumpImageForDebug = async (filename: string) => { // const screen = await captureScreen(); @@ -6749,103 +6845,13 @@ describe('BrowserWindow module', () => { afterEach(closeAllWindows); after(() => { w = null as unknown as BrowserWindow; }); - [{ name: 'landscape', bounds: landscapeBounds }, { name: 'portrait', bounds: portraitBounds }, { name: 'square', bounds: squareBounds }, - { name: 'x,y offset', bounds: offsetBounds }, { name: 'x,y negative offset', bounds: negativeOffsetBounds }, - { name: 'full screen', bounds: fullScreenBounds }].forEach((item) => { - it(`identity: ${item.name}`, async () => { - w = new BrowserWindow({ ...item.bounds, frame: false, webPreferences: { windowTransform: 'none' } }); - await w.loadURL(url); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(item.bounds.width); - expect(windowHeight).to.be.equal(item.bounds.height); - expectBoundsEqual(w.getBounds(), item.bounds); - - await setTimeout(500); - const selectColour = getPixelColor(await captureScreen(), { - x: item.bounds.x + SELECT_WIDTH_OFFSET, - y: item.bounds.y + SELECT_HEIGHT_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: item.bounds.x + SELECT_OPTION_WIDTH_OFFSET, - y: item.bounds.y + SELECT_OPTION_HEIGHT_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); - }); - - it(`rot90: ${item.name}`, async () => { - w = new BrowserWindow({ ...item.bounds, frame: false, webPreferences: { windowTransform: 'rot90' } }); - await w.loadURL(url); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(item.bounds.height); - expect(windowHeight).to.be.equal(item.bounds.width); - expectBoundsEqual(w.getBounds(), item.bounds); - - await setTimeout(500); - const selectColour = getPixelColor(await captureScreen(), { - x: item.bounds.x + item.bounds.width - SELECT_HEIGHT_OFFSET, - y: item.bounds.y + SELECT_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: item.bounds.x + item.bounds.width - SELECT_OPTION_HEIGHT_OFFSET, - y: item.bounds.y + SELECT_OPTION_WIDTH_OFFSET + windowBounds.forEach(({ description, bounds }) => { + windowTransforms.forEach((transform) => { + it(`${transform.description}: ${description}`, async () => { + w = new BrowserWindow({ ...bounds, frame: false, webPreferences: { windowTransform: transform.name } }); + await w.loadURL(url); + await verifyWindowTransform(bounds, transform); }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); - }); - - it(`rot180: ${item.name}`, async () => { - w = new BrowserWindow({ ...item.bounds, frame: false, webPreferences: { windowTransform: 'rot180' } }); - await w.loadURL(url); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(item.bounds.width); - expect(windowHeight).to.be.equal(item.bounds.height); - expectBoundsEqual(w.getBounds(), item.bounds); - - await setTimeout(500); - const selectColour = getPixelColor(await captureScreen(), { - x: item.bounds.x + item.bounds.width - SELECT_WIDTH_OFFSET, - y: item.bounds.y + item.bounds.height - SELECT_HEIGHT_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: item.bounds.x + item.bounds.width - SELECT_OPTION_WIDTH_OFFSET, - y: item.bounds.y + item.bounds.height - SELECT_OPTION_HEIGHT_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); - }); - - it(`rot270: ${item.name}`, async () => { - w = new BrowserWindow({ ...item.bounds, frame: false, webPreferences: { windowTransform: 'rot270' } }); - await w.loadURL(url); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(item.bounds.height); - expect(windowHeight).to.be.equal(item.bounds.width); - expectBoundsEqual(w.getBounds(), item.bounds); - - await setTimeout(500); - const selectColour = getPixelColor(await captureScreen(), { - x: item.bounds.x + SELECT_HEIGHT_OFFSET, - y: item.bounds.y + item.bounds.height - SELECT_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: item.bounds.x + SELECT_OPTION_HEIGHT_OFFSET, - y: item.bounds.y + item.bounds.height - SELECT_OPTION_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); }); }); }); @@ -6859,190 +6865,28 @@ describe('BrowserWindow module', () => { await closeWindow(w); w = null as unknown as BrowserWindow; }); - afterEach(() => { clickMouse(w, { x: 500, y: 500 }); }); - - it('rot90', async () => { - w.setWindowTransform('rot90'); - await setTimeout(500); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(landscapeBounds.height); - expect(windowHeight).to.be.equal(landscapeBounds.width); - expectBoundsEqual(w.getBounds(), landscapeBounds); - - const selectColour = getPixelColor(await captureScreen(), { - x: landscapeBounds.x + landscapeBounds.width - SELECT_HEIGHT_OFFSET, - y: landscapeBounds.y + SELECT_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: landscapeBounds.x + landscapeBounds.width - SELECT_OPTION_HEIGHT_OFFSET, - y: landscapeBounds.y + SELECT_OPTION_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); - }); - - it('rot180', async () => { - w.setWindowTransform('rot180'); - await setTimeout(500); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(landscapeBounds.width); - expect(windowHeight).to.be.equal(landscapeBounds.height); - expectBoundsEqual(w.getBounds(), landscapeBounds); - - const selectColour = getPixelColor(await captureScreen(), { - x: landscapeBounds.x + landscapeBounds.width - SELECT_WIDTH_OFFSET, - y: landscapeBounds.y + landscapeBounds.height - SELECT_HEIGHT_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: landscapeBounds.x + landscapeBounds.width - SELECT_OPTION_WIDTH_OFFSET, - y: landscapeBounds.y + landscapeBounds.height - SELECT_OPTION_HEIGHT_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); - }); - - it('rot270', async () => { - w.setWindowTransform('rot270'); - await setTimeout(500); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(landscapeBounds.height); - expect(windowHeight).to.be.equal(landscapeBounds.width); - expectBoundsEqual(w.getBounds(), landscapeBounds); - - const selectColour = getPixelColor(await captureScreen(), { - x: landscapeBounds.x + SELECT_HEIGHT_OFFSET, - y: landscapeBounds.y + landscapeBounds.height - SELECT_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: landscapeBounds.x + SELECT_OPTION_HEIGHT_OFFSET, - y: landscapeBounds.y + landscapeBounds.height - SELECT_OPTION_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); - }); - - it('Resize while rotated to portraitBounds', async () => { - w.setBounds(portraitBounds); - await setTimeout(500); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(portraitBounds.height); - expect(windowHeight).to.be.equal(portraitBounds.width); - expectBoundsEqual(w.getBounds(), portraitBounds); - - const selectColour = getPixelColor(await captureScreen(), { - x: portraitBounds.x + SELECT_HEIGHT_OFFSET, - y: portraitBounds.y + portraitBounds.height - SELECT_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: portraitBounds.x + SELECT_OPTION_HEIGHT_OFFSET, - y: portraitBounds.y + portraitBounds.height - SELECT_OPTION_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); - }); - - it('Resize while rotated to squareBounds', async () => { - w.setBounds(squareBounds); - await setTimeout(500); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(squareBounds.height); - expect(windowHeight).to.be.equal(squareBounds.width); - expectBoundsEqual(w.getBounds(), squareBounds); - - const selectColour = getPixelColor(await captureScreen(), { - x: squareBounds.x + SELECT_HEIGHT_OFFSET, - y: squareBounds.y + squareBounds.height - SELECT_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: squareBounds.x + SELECT_OPTION_HEIGHT_OFFSET, - y: squareBounds.y + squareBounds.height - SELECT_OPTION_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); + afterEach(async () => { + clickMouse(w, { x: 200, y: 200 }); + await setTimeout(100); }); - it('Resize while rotated to bounds with x,y offsets', async () => { - w.setBounds(offsetBounds); - await setTimeout(500); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(offsetBounds.height); - expect(windowHeight).to.be.equal(offsetBounds.width); - expectBoundsEqual(w.getBounds(), offsetBounds); - - const selectColour = getPixelColor(await captureScreen(), { - x: offsetBounds.x + SELECT_HEIGHT_OFFSET, - y: offsetBounds.y + offsetBounds.height - SELECT_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: offsetBounds.x + SELECT_OPTION_HEIGHT_OFFSET, - y: offsetBounds.y + offsetBounds.height - SELECT_OPTION_WIDTH_OFFSET + windowTransforms.filter(({ name }) => name !== 'none').forEach((transform) => { + it(transform.description, async () => { + w.setWindowTransform(transform.name); + await verifyWindowTransform(landscapeBounds, transform); }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); }); - it('Resize while rotated to bounds with negative x,y offsets', async () => { - w.setBounds(negativeOffsetBounds); - await setTimeout(500); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(negativeOffsetBounds.height); - expect(windowHeight).to.be.equal(negativeOffsetBounds.width); - expectBoundsEqual(w.getBounds(), negativeOffsetBounds); - - const selectColour = getPixelColor(await captureScreen(), { - x: negativeOffsetBounds.x + SELECT_HEIGHT_OFFSET, - y: negativeOffsetBounds.y + negativeOffsetBounds.height - SELECT_WIDTH_OFFSET + rotatedResizeBounds.forEach(({ description, bounds }) => { + it(`Resize while rotated to ${description}`, async () => { + w.setBounds(bounds); + await verifyWindowTransform(bounds, rot270Transform); }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: negativeOffsetBounds.x + SELECT_OPTION_HEIGHT_OFFSET, - y: negativeOffsetBounds.y + negativeOffsetBounds.height - SELECT_OPTION_WIDTH_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); }); it('identity', async () => { w.setWindowTransform('none'); - await setTimeout(500); - const { windowWidth, windowHeight } = await w.webContents.executeJavaScript('({windowWidth: window.innerWidth, windowHeight: window.innerHeight})'); - expect(windowWidth).to.be.equal(negativeOffsetBounds.width); - expect(windowHeight).to.be.equal(negativeOffsetBounds.height); - expectBoundsEqual(w.getBounds(), negativeOffsetBounds); - - const selectColour = getPixelColor(await captureScreen(), { - x: negativeOffsetBounds.x + SELECT_WIDTH_OFFSET, - y: negativeOffsetBounds.y + SELECT_HEIGHT_OFFSET - }); - expect(areColorsSimilar(selectColour, HexColors.RED)).to.be.true(); - - clickMouse(w, { x: MOUSE_X_OFFSET, y: MOUSE_Y_OFFSET }); - await setTimeout(500); - const selectOptionColour = getPixelColor(await captureScreen(), { - x: negativeOffsetBounds.x + SELECT_OPTION_WIDTH_OFFSET, - y: negativeOffsetBounds.y + SELECT_OPTION_HEIGHT_OFFSET - }); - expect(areColorsSimilar(selectOptionColour, HexColors.GREEN)).to.be.true(); + await verifyWindowTransform(negativeOffsetBounds, identityTransform); }); it('Check fullscreen bottom right option in correct location', async () => {