From 3fa15ebb7e8aef2029cd23370fdc96f382c9d161 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 6 Jul 2023 00:02:05 +0900 Subject: [PATCH] fix: use Chromium's way to compute min/max sizes (#38974) --- patches/chromium/.patches | 1 - ..._simplify_dwm_transitions_on_windows.patch | 368 ------------------ shell/browser/native_window.cc | 68 ++-- shell/browser/native_window.h | 10 +- shell/browser/native_window_views.cc | 43 ++ shell/browser/native_window_views.h | 3 + 6 files changed, 96 insertions(+), 397 deletions(-) delete mode 100644 patches/chromium/revert_simplify_dwm_transitions_on_windows.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index f07d408448..90cc77fa80 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -129,5 +129,4 @@ chore_patch_out_profile_methods_in_titlebar_config.patch fix_crash_on_nativetheme_change_during_context_menu_close.patch fix_select_the_first_menu_item_when_opened_via_keyboard.patch fix_return_v8_value_from_localframe_requestexecutescript.patch -revert_simplify_dwm_transitions_on_windows.patch fix_harden_blink_scriptstate_maybefrom.patch diff --git a/patches/chromium/revert_simplify_dwm_transitions_on_windows.patch b/patches/chromium/revert_simplify_dwm_transitions_on_windows.patch deleted file mode 100644 index 63cd6ffa47..0000000000 --- a/patches/chromium/revert_simplify_dwm_transitions_on_windows.patch +++ /dev/null @@ -1,368 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: deepak1556 -Date: Tue, 27 Jun 2023 22:05:17 +0900 -Subject: Revert "Simplify DWM transitions on Windows" - -This reverts commit 392e5f43aae8d225a118145cbc5f5bb104cbe541. - -Can be removed once https://github.com/electron/electron/issues/38937 is resolved. - -diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h -index 15f4aac24744228c0e74ec521c18eb6ab5f59c5b..b9a71c9063d8ee573424a9161cc127af169944a9 100644 ---- a/chrome/app/chrome_command_ids.h -+++ b/chrome/app/chrome_command_ids.h -@@ -59,6 +59,7 @@ - #define IDC_MOVE_TAB_NEXT 34032 - #define IDC_MOVE_TAB_PREVIOUS 34033 - #define IDC_SEARCH 34035 -+#define IDC_DEBUG_FRAME_TOGGLE 34038 - #define IDC_WINDOW_MENU 34045 - #define IDC_MINIMIZE_WINDOW 34046 - #define IDC_MAXIMIZE_WINDOW 34047 -diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc -index b198e41661d459302ddccfab70ac3de8cd2c48b5..405c0ac7c41ef4cb6a8804c8a34a7328d796d7bd 100644 ---- a/chrome/browser/ui/browser_command_controller.cc -+++ b/chrome/browser/ui/browser_command_controller.cc -@@ -1184,6 +1184,7 @@ void BrowserCommandController::InitCommandState() { - IDC_DUPLICATE_TAB, !browser_->is_type_picture_in_picture()); - UpdateTabRestoreCommandState(); - command_updater_.UpdateCommandEnabled(IDC_EXIT, true); -+ command_updater_.UpdateCommandEnabled(IDC_DEBUG_FRAME_TOGGLE, true); - command_updater_.UpdateCommandEnabled(IDC_NAME_WINDOW, true); - #if BUILDFLAG(IS_CHROMEOS) - command_updater_.UpdateCommandEnabled( -diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc -index fa718692b769c3bbcf83f700718cf88dc631d058..c8ed066b698ab08d5cfbc644ca1f66f23c0fbeec 100644 ---- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc -+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc -@@ -397,6 +397,13 @@ void BrowserDesktopWindowTreeHostWin::HandleDestroying() { - DesktopWindowTreeHostWin::HandleDestroying(); - } - -+void BrowserDesktopWindowTreeHostWin::HandleFrameChanged() { -+ // Reinitialize the status bubble, since it needs to be initialized -+ // differently depending on whether or not DWM composition is enabled -+ browser_view_->InitStatusBubble(); -+ DesktopWindowTreeHostWin::HandleFrameChanged(); -+} -+ - void BrowserDesktopWindowTreeHostWin::HandleWindowScaleFactorChanged( - float window_scale_factor) { - DesktopWindowTreeHostWin::HandleWindowScaleFactorChanged(window_scale_factor); -diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h -index 28412d00adf463a1453aecc82ca1179f0521822d..a3bd2e0cae1d341adfe9dd498886ae5914e1cba7 100644 ---- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h -+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h -@@ -66,6 +66,7 @@ class BrowserDesktopWindowTreeHostWin - bool GetDwmFrameInsetsInPixels(gfx::Insets* insets) const override; - void HandleCreate() override; - void HandleDestroying() override; -+ void HandleFrameChanged() override; - void HandleWindowScaleFactorChanged(float window_scale_factor) override; - bool PreHandleMSG(UINT message, - WPARAM w_param, -diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc -index 6920106ba91e0d1c0c1706a28b4ce5a14b5f3aed..306affc1d9573acd475d79f30a3583f0e716f33e 100644 ---- a/chrome/browser/ui/views/frame/browser_view.cc -+++ b/chrome/browser/ui/views/frame/browser_view.cc -@@ -927,8 +927,7 @@ BrowserView::BrowserView(std::unique_ptr browser) - infobar_container_ = - AddChildView(std::make_unique(this)); - -- status_bubble_ = std::make_unique(contents_web_view_); -- contents_web_view_->SetStatusBubble(status_bubble_.get()); -+ InitStatusBubble(); - - // Create do-nothing view for the sake of controlling the z-order of the find - // bar widget. -@@ -1049,6 +1048,11 @@ void BrowserView::SetDisableRevealerDelayForTesting(bool disable) { - g_disable_revealer_delay_for_testing = disable; - } - -+void BrowserView::InitStatusBubble() { -+ status_bubble_ = std::make_unique(contents_web_view_); -+ contents_web_view_->SetStatusBubble(status_bubble_.get()); -+} -+ - gfx::Rect BrowserView::GetFindBarBoundingBox() const { - gfx::Rect contents_bounds = contents_container_->ConvertRectToWidget( - contents_container_->GetLocalBounds()); -@@ -3397,6 +3401,11 @@ ui::ImageModel BrowserView::GetWindowIcon() { - } - - bool BrowserView::ExecuteWindowsCommand(int command_id) { -+ // This function handles WM_SYSCOMMAND, WM_APPCOMMAND, and WM_COMMAND. -+#if BUILDFLAG(IS_WIN) -+ if (command_id == IDC_DEBUG_FRAME_TOGGLE) -+ GetWidget()->DebugToggleFrameType(); -+#endif - // Translate WM_APPCOMMAND command ids into a command id that the browser - // knows how to handle. - int command_id_from_app_command = GetCommandIDForAppCommandID(command_id); -diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h -index d80a6df7380e5aec6ecba092315c905a09d7e2cc..bde91001c6f75f9918b9063eb0eaff5d56ea06c1 100644 ---- a/chrome/browser/ui/views/frame/browser_view.h -+++ b/chrome/browser/ui/views/frame/browser_view.h -@@ -164,6 +164,12 @@ class BrowserView : public BrowserWindow, - - void SetDownloadShelfForTest(DownloadShelf* download_shelf); - -+ // Initializes (or re-initializes) the status bubble. We try to only create -+ // the bubble once and re-use it for the life of the browser, but certain -+ // events (such as changing enabling/disabling Aero on Win) can force a need -+ // to change some of the bubble's creation parameters. -+ void InitStatusBubble(); -+ - // Returns the constraining bounding box that should be used to lay out the - // FindBar within. This is _not_ the size of the find bar, just the bounding - // box it should be laid out within. The coordinate system of the returned -diff --git a/chrome/browser/ui/views/frame/system_menu_model_builder.cc b/chrome/browser/ui/views/frame/system_menu_model_builder.cc -index 984929bb899dbd791443bf3ccd841f5a975a7dbd..75719ef6280ce46c176cb3277e602a11d99a45e0 100644 ---- a/chrome/browser/ui/views/frame/system_menu_model_builder.cc -+++ b/chrome/browser/ui/views/frame/system_menu_model_builder.cc -@@ -69,6 +69,7 @@ void SystemMenuModelBuilder::BuildMenu(ui::SimpleMenuModel* model) { - BuildSystemMenuForBrowserWindow(model); - else - BuildSystemMenuForAppOrPopupWindow(model); -+ AddFrameToggleItems(model); - } - - void SystemMenuModelBuilder::BuildSystemMenuForBrowserWindow( -@@ -157,6 +158,14 @@ void SystemMenuModelBuilder::BuildSystemMenuForAppOrPopupWindow( - AppendTeleportMenu(model); - } - -+void SystemMenuModelBuilder::AddFrameToggleItems(ui::SimpleMenuModel* model) { -+ if (base::CommandLine::ForCurrentProcess()->HasSwitch( -+ switches::kDebugEnableFrameToggle)) { -+ model->AddSeparator(ui::NORMAL_SEPARATOR); -+ model->AddItem(IDC_DEBUG_FRAME_TOGGLE, u"Toggle Frame Type"); -+ } -+} -+ - #if BUILDFLAG(IS_CHROMEOS) - void SystemMenuModelBuilder::AppendMoveToDesksMenu(ui::SimpleMenuModel* model) { - gfx::NativeWindow window = -diff --git a/chrome/browser/ui/views/frame/system_menu_model_builder.h b/chrome/browser/ui/views/frame/system_menu_model_builder.h -index 8f69eab1fc2b9c81d14f7b547b4f434c722b98aa..8acaa2816a03f41b19ec364eea2658682737798c 100644 ---- a/chrome/browser/ui/views/frame/system_menu_model_builder.h -+++ b/chrome/browser/ui/views/frame/system_menu_model_builder.h -@@ -47,6 +47,9 @@ class SystemMenuModelBuilder { - void BuildSystemMenuForBrowserWindow(ui::SimpleMenuModel* model); - void BuildSystemMenuForAppOrPopupWindow(ui::SimpleMenuModel* model); - -+ // Adds items for toggling the frame type (if necessary). -+ void AddFrameToggleItems(ui::SimpleMenuModel* model); -+ - #if BUILDFLAG(IS_CHROMEOS) - // Add the submenu for move to desks. - void AppendMoveToDesksMenu(ui::SimpleMenuModel* model); -diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc -index b23fbffea35f1f9936b998bbe501c9e5acdecf6a..4d635764d6c5db0b493aba9d3b2add095fc9ebe4 100644 ---- a/chrome/common/chrome_switches.cc -+++ b/chrome/common/chrome_switches.cc -@@ -143,6 +143,10 @@ const char kCredits[] = "credits"; - // devtools://devtools/bundled/ - const char kCustomDevtoolsFrontend[] = "custom-devtools-frontend"; - -+// Enables a frame context menu item that toggles the frame in and out of glass -+// mode (Windows Vista and up only). -+const char kDebugEnableFrameToggle[] = "debug-enable-frame-toggle"; -+ - // Adds debugging entries such as Inspect Element to context menus of packed - // apps. - const char kDebugPackedApps[] = "debug-packed-apps"; -diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h -index be1f7824d85a6705aa8c220578ac62f0d1a4114e..7ce3dd85d1cab1fbd5aff6104ea0d1b864ebcb0c 100644 ---- a/chrome/common/chrome_switches.h -+++ b/chrome/common/chrome_switches.h -@@ -61,6 +61,7 @@ extern const char kCrashOnHangThreads[]; - extern const char kCreateBrowserOnStartupForTests[]; - extern const char kCredits[]; - extern const char kCustomDevtoolsFrontend[]; -+extern const char kDebugEnableFrameToggle[]; - extern const char kDebugPackedApps[]; - extern const char kDevToolsFlags[]; - extern const char kDiagnostics[]; -diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc -index d0dc2b4993891837c6ac76e095833126db461032..ba1bcbc63475b7151e2335c9237124ca8ca31dc7 100644 ---- a/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc -+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc -@@ -136,6 +136,30 @@ TEST_F(DesktopNativeWidgetAuraTest, WidgetNotVisibleOnlyWindowTreeHostShown) { - } - #endif - -+TEST_F(DesktopNativeWidgetAuraTest, DesktopAuraWindowShowFrameless) { -+ Widget widget; -+ Widget::InitParams init_params = -+ CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); -+ init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; -+ widget.Init(std::move(init_params)); -+ -+ // Make sure that changing frame type doesn't crash when there's no non-client -+ // view. -+ ASSERT_EQ(nullptr, widget.non_client_view()); -+ widget.DebugToggleFrameType(); -+ widget.Show(); -+ -+#if BUILDFLAG(IS_WIN) -+ // On Windows also make sure that handling WM_SYSCOMMAND doesn't crash with -+ // custom frame. Frame type needs to be toggled again if Aero Glass is -+ // disabled. -+ if (widget.ShouldUseNativeFrame()) -+ widget.DebugToggleFrameType(); -+ SendMessage(widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(), -+ WM_SYSCOMMAND, SC_RESTORE, 0); -+#endif // BUILDFLAG(IS_WIN) -+} -+ - #if BUILDFLAG(IS_CHROMEOS_ASH) - // TODO(crbug.com/916272): investigate fixing and enabling on Chrome OS. - #define MAYBE_GlobalCursorState DISABLED_GlobalCursorState -diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc -index 73e76c0d940f09336bbec6db47f1afee5153ced0..61673ac08ca19816dc01c89b6687f5b2a7c289e2 100644 ---- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc -+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc -@@ -1028,6 +1028,8 @@ void DesktopWindowTreeHostWin::HandleClientSizeChanged( - } - - void DesktopWindowTreeHostWin::HandleFrameChanged() { -+ CheckForMonitorChange(); -+ desktop_native_widget_aura_->UpdateWindowTransparency(); - // Replace the frame and layout the contents. - if (GetWidget()->non_client_view()) - GetWidget()->non_client_view()->UpdateFrame(); -diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc -index 2b52ad2f63bd6924fcd3d000bbb7ed6fd9f4b0dd..3d7a2f986c38de6b2e69722e63ee3b139d257756 100644 ---- a/ui/views/widget/widget.cc -+++ b/ui/views/widget/widget.cc -@@ -1209,6 +1209,21 @@ bool Widget::ShouldWindowContentsBeTransparent() const { - : false; - } - -+void Widget::DebugToggleFrameType() { -+ if (!native_widget_) -+ return; -+ -+ if (frame_type_ == FrameType::kDefault) { -+ frame_type_ = ShouldUseNativeFrame() ? FrameType::kForceCustom -+ : FrameType::kForceNative; -+ } else { -+ frame_type_ = frame_type_ == FrameType::kForceCustom -+ ? FrameType::kForceNative -+ : FrameType::kForceCustom; -+ } -+ FrameTypeChanged(); -+} -+ - void Widget::FrameTypeChanged() { - if (native_widget_) - native_widget_->FrameTypeChanged(); -diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h -index 51017b6f5dd9a9d5f26723c7ec7a6e0404c93b65..54b9e676c9423b78184fc63b80299dd7529cbd28 100644 ---- a/ui/views/widget/widget.h -+++ b/ui/views/widget/widget.h -@@ -922,6 +922,10 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, - // (for example, so that they can overhang onto the window title bar). - bool ShouldWindowContentsBeTransparent() const; - -+ // Forces the frame into the alternate frame type (custom or native) depending -+ // on its current state. -+ void DebugToggleFrameType(); -+ - // Tell the window that something caused the frame type to change. - void FrameTypeChanged(); - -diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc -index 9aced70287c3ac877310457c1aad3b2544a85800..35f435d3d4d5eda44b69e5e66ec9480534d3ef44 100644 ---- a/ui/views/widget/widget_unittest.cc -+++ b/ui/views/widget/widget_unittest.cc -@@ -1317,6 +1317,10 @@ TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Deactivate) { - widget()->Deactivate(); - } - -+TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, DebugToggleFrameType) { -+ widget()->DebugToggleFrameType(); -+} -+ - TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, DraggedView) { - widget()->dragged_view(); - } -diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 575877b4fb0929d0cdd22af399f7078fcd713584..90dd6487fdf438a61672a81f08b545742045944a 100644 ---- a/ui/views/win/hwnd_message_handler.cc -+++ b/ui/views/win/hwnd_message_handler.cc -@@ -1741,6 +1741,20 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { - } - } - -+void HWNDMessageHandler::UpdateDwmNcRenderingPolicy() { -+ if (IsFullscreen()) -+ return; -+ -+ DWMNCRENDERINGPOLICY policy = -+ custom_window_region_.is_valid() || -+ delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN -+ ? DWMNCRP_DISABLED -+ : DWMNCRP_ENABLED; -+ -+ DwmSetWindowAttribute(hwnd(), DWMWA_NCRENDERING_POLICY, &policy, -+ sizeof(DWMNCRENDERINGPOLICY)); -+} -+ - LRESULT HWNDMessageHandler::DefWindowProcWithRedrawLock(UINT message, - WPARAM w_param, - LPARAM l_param) { -@@ -3607,10 +3621,34 @@ bool HWNDMessageHandler::IsSynthesizedMouseMessage(unsigned int message, - } - - void HWNDMessageHandler::PerformDwmTransition() { -- CHECK(IsFrameSystemDrawn()); -- - dwm_transition_desired_ = false; -+ -+ UpdateDwmNcRenderingPolicy(); -+ // Don't redraw the window here, because we need to hide and show the window -+ // which will also trigger a redraw. -+ ResetWindowRegion(true, false); -+ // The non-client view needs to update too. - delegate_->HandleFrameChanged(); -+ // This calls DwmExtendFrameIntoClientArea which must be called when DWM -+ // composition state changes. -+ UpdateDwmFrame(); -+ -+ if (IsVisible() && IsFrameSystemDrawn()) { -+ // For some reason, we need to hide the window after we change from a custom -+ // frame to a native frame. If we don't, the client area will be filled -+ // with black. This seems to be related to an interaction between DWM and -+ // SetWindowRgn, but the details aren't clear. Additionally, we need to -+ // specify SWP_NOZORDER here, otherwise if you have multiple chrome windows -+ // open they will re-appear with a non-deterministic Z-order. -+ // Note: caused http://crbug.com/895855, where a laptop lid close+reopen -+ // puts window in the background but acts like a foreground window. Fixed by -+ // not calling this unless DWM composition actually changes. Finally, since -+ // we don't want windows stealing focus if they're not already active, we -+ // set SWP_NOACTIVATE. -+ UINT flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE; -+ SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0, flags | SWP_HIDEWINDOW); -+ SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0, flags | SWP_SHOWWINDOW); -+ } - } - - void HWNDMessageHandler::UpdateDwmFrame() { -diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h -index 7238af3f51179145a1c1eb9639ca756c2a697554..694945c4f3a62ed13a3b80cc5fba5673e29eef4e 100644 ---- a/ui/views/win/hwnd_message_handler.h -+++ b/ui/views/win/hwnd_message_handler.h -@@ -324,6 +324,11 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, - // frame windows. - void ResetWindowRegion(bool force, bool redraw); - -+ // Enables or disables rendering of the non-client (glass) area by DWM, -+ // under Vista and above, depending on whether the caller has requested a -+ // custom frame. -+ void UpdateDwmNcRenderingPolicy(); -+ - // Calls DefWindowProc, safely wrapping the call in a ScopedRedrawLock to - // prevent frame flicker. DefWindowProc handling can otherwise render the - // classic-look window title bar directly. diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index d33973ae9c..1c6a6b0bc7 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -316,47 +316,65 @@ bool NativeWindow::IsNormal() { void NativeWindow::SetSizeConstraints( const extensions::SizeConstraints& window_constraints) { - extensions::SizeConstraints content_constraints(GetContentSizeConstraints()); - if (window_constraints.HasMaximumSize()) { - gfx::Rect max_bounds = WindowBoundsToContentBounds( - gfx::Rect(window_constraints.GetMaximumSize())); - content_constraints.set_maximum_size(max_bounds.size()); - } - if (window_constraints.HasMinimumSize()) { - gfx::Rect min_bounds = WindowBoundsToContentBounds( - gfx::Rect(window_constraints.GetMinimumSize())); - content_constraints.set_minimum_size(min_bounds.size()); - } - SetContentSizeConstraints(content_constraints); + size_constraints_ = window_constraints; + content_size_constraints_.reset(); } extensions::SizeConstraints NativeWindow::GetSizeConstraints() const { - extensions::SizeConstraints content_constraints = GetContentSizeConstraints(); - extensions::SizeConstraints window_constraints; - if (content_constraints.HasMaximumSize()) { + if (size_constraints_) + return *size_constraints_; + if (!content_size_constraints_) + return extensions::SizeConstraints(); + // Convert content size constraints to window size constraints. + extensions::SizeConstraints constraints; + if (content_size_constraints_->HasMaximumSize()) { gfx::Rect max_bounds = ContentBoundsToWindowBounds( - gfx::Rect(content_constraints.GetMaximumSize())); - window_constraints.set_maximum_size(max_bounds.size()); + gfx::Rect(content_size_constraints_->GetMaximumSize())); + constraints.set_maximum_size(max_bounds.size()); } - if (content_constraints.HasMinimumSize()) { + if (content_size_constraints_->HasMinimumSize()) { gfx::Rect min_bounds = ContentBoundsToWindowBounds( - gfx::Rect(content_constraints.GetMinimumSize())); - window_constraints.set_minimum_size(min_bounds.size()); + gfx::Rect(content_size_constraints_->GetMinimumSize())); + constraints.set_minimum_size(min_bounds.size()); } - return window_constraints; + return constraints; } void NativeWindow::SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints) { - size_constraints_ = size_constraints; + content_size_constraints_ = size_constraints; + size_constraints_.reset(); } +// The return value of GetContentSizeConstraints will be passed to Chromium +// to set min/max sizes of window. Note that we are returning content size +// instead of window size because that is what Chromium expects, see the +// comment of |WidgetSizeIsClientSize| in Chromium's codebase to learn more. extensions::SizeConstraints NativeWindow::GetContentSizeConstraints() const { - return size_constraints_; + if (content_size_constraints_) + return *content_size_constraints_; + if (!size_constraints_) + return extensions::SizeConstraints(); + // Convert window size constraints to content size constraints. + // Note that we are not caching the results, because Chromium reccalculates + // window frame size everytime when min/max sizes are passed, and we must + // do the same otherwise the resulting size with frame included will be wrong. + extensions::SizeConstraints constraints; + if (size_constraints_->HasMaximumSize()) { + gfx::Rect max_bounds = WindowBoundsToContentBounds( + gfx::Rect(size_constraints_->GetMaximumSize())); + constraints.set_maximum_size(max_bounds.size()); + } + if (size_constraints_->HasMinimumSize()) { + gfx::Rect min_bounds = WindowBoundsToContentBounds( + gfx::Rect(size_constraints_->GetMinimumSize())); + constraints.set_minimum_size(min_bounds.size()); + } + return constraints; } void NativeWindow::SetMinimumSize(const gfx::Size& size) { - extensions::SizeConstraints size_constraints; + extensions::SizeConstraints size_constraints = GetSizeConstraints(); size_constraints.set_minimum_size(size); SetSizeConstraints(size_constraints); } @@ -366,7 +384,7 @@ gfx::Size NativeWindow::GetMinimumSize() const { } void NativeWindow::SetMaximumSize(const gfx::Size& size) { - extensions::SizeConstraints size_constraints; + extensions::SizeConstraints size_constraints = GetSizeConstraints(); size_constraints.set_maximum_size(size); SetSizeConstraints(size_constraints); } diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 2394be0016..fcdf419d7e 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -423,6 +423,13 @@ class NativeWindow : public base::SupportsUserData, // The "titleBarStyle" option. TitleBarStyle title_bar_style_ = TitleBarStyle::kNormal; + // Minimum and maximum size. + absl::optional size_constraints_; + // Same as above but stored as content size, we are storing 2 types of size + // constraints beacause converting between them will cause rounding errors + // on HiDPI displays on some environments. + absl::optional content_size_constraints_; + std::queue pending_transitions_; FullScreenTransitionState fullscreen_transition_state_ = FullScreenTransitionState::kNone; @@ -450,9 +457,6 @@ class NativeWindow : public base::SupportsUserData, // Whether window is transparent. bool transparent_ = false; - // Minimum and maximum size, stored as content size. - extensions::SizeConstraints size_constraints_; - // Whether window can be resized larger than screen. bool enable_larger_than_screen_ = false; diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index a1581d98d0..028b35c1ce 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -78,6 +78,7 @@ #include "ui/display/screen.h" #include "ui/display/win/screen_win.h" #include "ui/gfx/color_utils.h" +#include "ui/gfx/win/msg_util.h" #endif namespace electron { @@ -138,6 +139,25 @@ gfx::Rect DIPToScreenRect(HWND hwnd, const gfx::Rect& pixel_bounds) { return screen_rect; } +// Chromium uses a buggy implementation that converts content rect to window +// rect when calculating min/max size, we should use the same implementation +// when passing min/max size so we can get correct results. +gfx::Size WindowSizeToContentSizeBuggy(HWND hwnd, const gfx::Size& size) { + // Calculate the size of window frame, using same code with the + // HWNDMessageHandler::OnGetMinMaxInfo method. + // The pitfall is, when window is minimized the calculated window frame size + // will be different from other states. + RECT client_rect, rect; + GetClientRect(hwnd, &client_rect); + GetWindowRect(hwnd, &rect); + CR_DEFLATE_RECT(&rect, &client_rect); + // Convert DIP size to pixel size, do calculation and then return DIP size. + gfx::Rect screen_rect = DIPToScreenRect(hwnd, gfx::Rect(size)); + gfx::Size screen_client_size(screen_rect.width() - (rect.right - rect.left), + screen_rect.height() - (rect.bottom - rect.top)); + return ScreenToDIPRect(hwnd, gfx::Rect(screen_client_size)).size(); +} + #endif #if defined(USE_OZONE) @@ -812,6 +832,29 @@ void NativeWindowViews::SetContentSizeConstraints( old_size_constraints_ = size_constraints; } +#if BUILDFLAG(IS_WIN) +// This override does almost the same with its parent, except that it uses +// the WindowSizeToContentSizeBuggy method to convert window size to content +// size. See the comment of the method for the reason behind this. +extensions::SizeConstraints NativeWindowViews::GetContentSizeConstraints() + const { + if (content_size_constraints_) + return *content_size_constraints_; + if (!size_constraints_) + return extensions::SizeConstraints(); + extensions::SizeConstraints constraints; + if (size_constraints_->HasMaximumSize()) { + constraints.set_maximum_size(WindowSizeToContentSizeBuggy( + GetAcceleratedWidget(), size_constraints_->GetMaximumSize())); + } + if (size_constraints_->HasMinimumSize()) { + constraints.set_minimum_size(WindowSizeToContentSizeBuggy( + GetAcceleratedWidget(), size_constraints_->GetMinimumSize())); + } + return constraints; +} +#endif + void NativeWindowViews::SetResizable(bool resizable) { if (resizable != resizable_) { // On Linux there is no "resizable" property of a window, we have to set diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 8a2bf583bd..e97d22ace0 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -78,6 +78,9 @@ class NativeWindowViews : public NativeWindow, SkColor GetBackgroundColor() override; void SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints) override; +#if BUILDFLAG(IS_WIN) + extensions::SizeConstraints GetContentSizeConstraints() const override; +#endif void SetResizable(bool resizable) override; bool MoveAbove(const std::string& sourceId) override; void MoveTop() override;