diff --git a/BUILD.gn b/BUILD.gn index 72cad64d7..7b06838c2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -385,12 +385,13 @@ source_set("libcef_static_unittested") { sources = [ "libcef/browser/devtools/devtools_util.cc", "libcef/browser/devtools/devtools_util.h", - "libcef/browser/screen_util.h", - "libcef/browser/screen_util.cc", + "libcef/browser/geometry_util.h", + "libcef/browser/geometry_util.cc", ] deps = [ "//base", + "//ui/gfx/geometry", ] configs += [ @@ -406,7 +407,7 @@ test("libcef_static_unittests") { sources = [ "libcef/browser/devtools/devtools_util_unittest.cc", - "libcef/browser/screen_util_unittest.cc", + "libcef/browser/geometry_util_unittest.cc", ] deps = [ diff --git a/libcef/browser/browser_platform_delegate.cc b/libcef/browser/browser_platform_delegate.cc index e503ef7b0..549104c52 100644 --- a/libcef/browser/browser_platform_delegate.cc +++ b/libcef/browser/browser_platform_delegate.cc @@ -241,6 +241,9 @@ std::unique_ptr CefBrowserPlatformDelegate::CreateMenuRunner() { return nullptr; } +void CefBrowserPlatformDelegate::UpdateFindBarBoundingBox( + gfx::Rect* bounds) const {} + bool CefBrowserPlatformDelegate::IsWindowless() const { return false; } diff --git a/libcef/browser/browser_platform_delegate.h b/libcef/browser/browser_platform_delegate.h index af271a4a8..b62e6ffc4 100644 --- a/libcef/browser/browser_platform_delegate.h +++ b/libcef/browser/browser_platform_delegate.h @@ -286,6 +286,9 @@ class CefBrowserPlatformDelegate { // Create the platform-specific menu runner. virtual std::unique_ptr CreateMenuRunner(); + // Optionally modify the bounding box for the Chrome Find bar. + virtual void UpdateFindBarBoundingBox(gfx::Rect* bounds) const; + // Returns true if this delegate implements windowless rendering. May be // called on multiple threads. virtual bool IsWindowless() const; diff --git a/libcef/browser/chrome/browser_delegate.h b/libcef/browser/chrome/browser_delegate.h index b8d58f72d..3ecd82dee 100644 --- a/libcef/browser/chrome/browser_delegate.h +++ b/libcef/browser/chrome/browser_delegate.h @@ -92,6 +92,9 @@ class BrowserDelegate : public content::WebContentsDelegate { return true; } + // Optionally modify the bounding box for the Find bar. + virtual void UpdateFindBarBoundingBox(gfx::Rect* bounds) {} + // Same as RequestMediaAccessPermission but returning |callback| if the // request is unhandled. [[nodiscard]] virtual content::MediaResponseCallback diff --git a/libcef/browser/chrome/chrome_browser_delegate.cc b/libcef/browser/chrome/chrome_browser_delegate.cc index af5e9fa64..4bbdfa5db 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.cc +++ b/libcef/browser/chrome/chrome_browser_delegate.cc @@ -176,6 +176,12 @@ bool ChromeBrowserDelegate::IsToolbarButtonVisible( return true; } +void ChromeBrowserDelegate::UpdateFindBarBoundingBox(gfx::Rect* bounds) { + if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) { + browser->platform_delegate()->UpdateFindBarBoundingBox(bounds); + } +} + content::MediaResponseCallback ChromeBrowserDelegate::RequestMediaAccessPermissionEx( content::WebContents* web_contents, diff --git a/libcef/browser/chrome/chrome_browser_delegate.h b/libcef/browser/chrome/chrome_browser_delegate.h index 290364a99..cb8b19e16 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.h +++ b/libcef/browser/chrome/chrome_browser_delegate.h @@ -60,6 +60,7 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate { bool IsAppMenuItemEnabled(int command_id) override; bool IsPageActionIconVisible(PageActionIconType icon_type) override; bool IsToolbarButtonVisible(ToolbarButtonType button_type) override; + void UpdateFindBarBoundingBox(gfx::Rect* bounds) override; [[nodiscard]] content::MediaResponseCallback RequestMediaAccessPermissionEx( content::WebContents* web_contents, const content::MediaStreamRequest& request, diff --git a/libcef/browser/chrome/views/browser_platform_delegate_chrome_views.cc b/libcef/browser/chrome/views/browser_platform_delegate_chrome_views.cc index f54e6bb85..b6ac634b4 100644 --- a/libcef/browser/chrome/views/browser_platform_delegate_chrome_views.cc +++ b/libcef/browser/chrome/views/browser_platform_delegate_chrome_views.cc @@ -5,6 +5,7 @@ #include "libcef/browser/chrome/views/browser_platform_delegate_chrome_views.h" #include "include/views/cef_window.h" +#include "libcef/browser/views/window_impl.h" #include "chrome/browser/ui/browser.h" #include "ui/views/widget/widget.h" @@ -170,6 +171,23 @@ void CefBrowserPlatformDelegateChromeViews::PopupBrowserCreated( } } +void CefBrowserPlatformDelegateChromeViews::UpdateFindBarBoundingBox( + gfx::Rect* bounds) const { + if (auto* window_impl = GetWindowImpl()) { + if (window_impl->root_view()) { + window_impl->root_view()->UpdateFindBarBoundingBox(bounds); + } + } +} + bool CefBrowserPlatformDelegateChromeViews::IsViewsHosted() const { return true; } + +CefWindowImpl* CefBrowserPlatformDelegateChromeViews::GetWindowImpl() const { + if (auto* widget = GetWindowWidget()) { + CefRefPtr window = view_util::GetWindowFor(widget); + return static_cast(window.get()); + } + return nullptr; +} diff --git a/libcef/browser/chrome/views/browser_platform_delegate_chrome_views.h b/libcef/browser/chrome/views/browser_platform_delegate_chrome_views.h index 4b41b411a..f27ba881d 100644 --- a/libcef/browser/chrome/views/browser_platform_delegate_chrome_views.h +++ b/libcef/browser/chrome/views/browser_platform_delegate_chrome_views.h @@ -8,6 +8,8 @@ #include "libcef/browser/chrome/browser_platform_delegate_chrome.h" #include "libcef/browser/views/browser_view_impl.h" +class CefWindowImpl; + // Implementation of Chrome-based browser functionality. class CefBrowserPlatformDelegateChromeViews : public CefBrowserPlatformDelegateChrome { @@ -35,6 +37,7 @@ class CefBrowserPlatformDelegateChromeViews bool is_devtools) override; void PopupBrowserCreated(CefBrowserHostBase* new_browser, bool is_devtools) override; + void UpdateFindBarBoundingBox(gfx::Rect* bounds) const override; bool IsViewsHosted() const override; CefRefPtr browser_view() const { return browser_view_; } @@ -42,6 +45,8 @@ class CefBrowserPlatformDelegateChromeViews private: void SetBrowserView(CefRefPtr browser_view); + CefWindowImpl* GetWindowImpl() const; + CefRefPtr browser_view_; }; diff --git a/libcef/browser/geometry_util.cc b/libcef/browser/geometry_util.cc new file mode 100644 index 000000000..65e2c6ca7 --- /dev/null +++ b/libcef/browser/geometry_util.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2022 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "libcef/browser/geometry_util.h" + +#include + +#include "ui/gfx/geometry/rect.h" + +namespace { + +constexpr int kMinWidth = 0; +constexpr int kMinHeight = 0; + +// Makes sure that line segment lies entirely between min and max. +int clamp_segment_start(int start, int len, int min, int max) { + start = std::clamp(start, min, max); + const int end = start + len; + const int excess = end - max; + + if (excess > 0) { + start = start - excess; + } + + return start; +} + +} // namespace + +gfx::Rect MakeVisibleOnScreenRect(const gfx::Rect& rect, + const gfx::Rect& screen) { + const int width = std::clamp(rect.width(), kMinWidth, screen.width()); + const int height = std::clamp(rect.height(), kMinHeight, screen.height()); + + const int right_border = screen.x() + screen.width(); + const int x = clamp_segment_start(rect.x(), width, screen.x(), right_border); + + const int bottom_border = screen.y() + screen.height(); + const int y = + clamp_segment_start(rect.y(), height, screen.y(), bottom_border); + + return gfx::Rect(x, y, width, height); +} + +gfx::Rect SubtractOverlayFromBoundingBox(const gfx::Rect& bounds, + const gfx::Rect& overlay, + int max_distance) { + if (overlay.Contains(bounds)) { + // Early exit; |bounds| is completely inside |overlay|. + return bounds; + } + + // Portion of |overlay| that is inside |bounds|. + auto overlap = overlay; + overlap.Intersect(bounds); + if (overlap.IsEmpty()) { + // Early exit; |bounds| and |overlay| don't intersect. + return bounds; + } + + gfx::Insets insets; + + if (overlap.width() >= overlap.height()) { + // Wide overlay; maybe inset |bounds| in the Y direction. + const int delta_top = overlap.y() - bounds.y(); + const int delta_bottom = + bounds.y() + bounds.height() - overlap.y() - overlap.height(); + + // Inset from the closest side that meets |max_distance| requirements. + if (delta_top <= delta_bottom && delta_top <= max_distance) { + // Inset from the top. + insets.set_top(delta_top + overlap.height()); + } else if (delta_bottom <= max_distance) { + // Inset from the bottom. + insets.set_bottom(delta_bottom + overlap.height()); + } + } else { + // Tall overlay; maybe inset |bounds| in the X direction. + const int delta_left = overlap.x() - bounds.x(); + const int delta_right = + bounds.x() + bounds.width() - overlap.x() - overlap.width(); + + // Inset from the closest side that meets |max_distance| requirements. + if (delta_left <= delta_right && delta_left <= max_distance) { + // Inset from the left. + insets.set_left(delta_left + overlap.width()); + } else if (delta_right <= max_distance) { + // Inset from the right. + insets.set_right(delta_right + overlap.width()); + } + } + + if (insets.IsEmpty()) { + // |overlay| is too far inside |bounds| to trigger insets. + return bounds; + } + + auto result = bounds; + result.Inset(insets); + return result; +} \ No newline at end of file diff --git a/libcef/browser/geometry_util.h b/libcef/browser/geometry_util.h new file mode 100644 index 000000000..25ec374f7 --- /dev/null +++ b/libcef/browser/geometry_util.h @@ -0,0 +1,29 @@ +// Copyright (c) 2022 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef CEF_LIBCEF_BROWSER_GEOMETRY_UTIL_H_ +#define CEF_LIBCEF_BROWSER_GEOMETRY_UTIL_H_ +#pragma once + +namespace gfx { +class Rect; +} + +// Create a new rectangle from the input |rect| rectangle that is fully visible +// on provided |screen_rect| screen. The width and height of the resulting +// rectangle are clamped to the screen width and height respectively if they +// would overflow. +gfx::Rect MakeVisibleOnScreenRect(const gfx::Rect& rect, + const gfx::Rect& screen); + +// Possibly subtract |overlay| from |bounds|. We only want to subtract overlays +// that are inside |bounds| and close to the edges, so |max_distance| is the +// maximum allowed distance between |overlay| and |bounds| extents in order to +// trigger the subtraction. Subtraction will occur from the closest edge. If +// distances are otherwise equal then top will be preferred followed by left. +gfx::Rect SubtractOverlayFromBoundingBox(const gfx::Rect& bounds, + const gfx::Rect& overlay, + int max_distance); + +#endif // CEF_LIBCEF_BROWSER_GEOMETRY_UTIL_H_ diff --git a/libcef/browser/geometry_util_unittest.cc b/libcef/browser/geometry_util_unittest.cc new file mode 100644 index 000000000..ef8b9471c --- /dev/null +++ b/libcef/browser/geometry_util_unittest.cc @@ -0,0 +1,352 @@ +// Copyright (c) 2022 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "cef/libcef/browser/geometry_util.h" +#include "tests/gtest/include/gtest/gtest.h" + +#include "ui/gfx/geometry/rect.h" + +namespace { + +constexpr int kScreenWidth = 1024; +constexpr int kScreenHeight = 768; +const gfx::Rect kMainScreen(0, 0, kScreenWidth, kScreenHeight); +const gfx::Rect kLeftScreen(-1024, 0, kScreenWidth, kScreenHeight); + +#define EXPECT_RECT_EQ(rect1, rect2) \ + EXPECT_EQ(rect1.x(), rect2.x()); \ + EXPECT_EQ(rect1.y(), rect2.y()); \ + EXPECT_EQ(rect1.width(), rect2.width()); \ + EXPECT_EQ(rect1.height(), rect2.height()) + +} // namespace + +TEST(GeometryUtil, MakeVisibleOnScreenRect_RectSizeIsBiggerThanScreen) { + const gfx::Rect rect{400, 500, 1500, 800}; + + auto result = MakeVisibleOnScreenRect(rect, kMainScreen); + + EXPECT_EQ(result.x(), 0); + EXPECT_EQ(result.width(), kMainScreen.width()); + EXPECT_EQ(result.y(), 0); + EXPECT_EQ(result.height(), kMainScreen.height()); +} + +TEST(GeometryUtil, MakeVisibleOnScreenRect_RightBorderIsOutsideTheScreen) { + const gfx::Rect rect{600, 400, 500, 300}; + + auto result = MakeVisibleOnScreenRect(rect, kMainScreen); + + EXPECT_EQ(result.x(), 524); + EXPECT_EQ(result.width(), rect.width()); + EXPECT_EQ(result.y(), rect.y()); + EXPECT_EQ(result.height(), rect.height()); +} + +TEST(GeometryUtil, MakeVisibleOnScreenRect_LeftBorderIsOutsideTheScreen) { + const gfx::Rect rect{-400, 400, 500, 300}; + + auto result = MakeVisibleOnScreenRect(rect, kMainScreen); + + EXPECT_EQ(result.x(), 0); + EXPECT_EQ(result.width(), rect.width()); + EXPECT_EQ(result.y(), rect.y()); + EXPECT_EQ(result.height(), rect.height()); +} + +TEST(GeometryUtil, MakeVisibleOnScreenRect_BottomBorderIsOutsideTheScreen) { + const gfx::Rect rect{600, 500, 300, 300}; + + auto result = MakeVisibleOnScreenRect(rect, kMainScreen); + + EXPECT_EQ(result.x(), 600); + EXPECT_EQ(result.width(), rect.width()); + EXPECT_EQ(result.y(), 468); + EXPECT_EQ(result.height(), rect.height()); +} + +TEST(GeometryUtil, MakeVisibleOnScreenRect_RectIsVisibleOnTheLeftScreen) { + const gfx::Rect rect{-500, 300, 300, 300}; + + auto result = MakeVisibleOnScreenRect(rect, kLeftScreen); + + EXPECT_RECT_EQ(result, rect); +} + +TEST(GeometryUtil, MakeVisibleOnScreenRect_RectSizeIsBiggerThanLeftScreen) { + const gfx::Rect rect{-500, 300, 3000, 3000}; + + auto result = MakeVisibleOnScreenRect(rect, kLeftScreen); + + EXPECT_RECT_EQ(result, kLeftScreen); +} + +TEST(GeometryUtil, SubtractOverlayFromBoundingBox_Square_NoIntersect_NoInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{0, 0, 10, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 10); + + EXPECT_RECT_EQ(bounds, result); +} + +TEST(GeometryUtil, SubtractOverlayFromBoundingBox_Square_Contains_NoInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{5, 5, 85, 85}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 10); + + EXPECT_RECT_EQ(bounds, result); +} + +TEST(GeometryUtil, SubtractOverlayFromBoundingBox_Square_AllClose_TopInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{12, 12, 76, 76}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + // When all sides are close, inset from the top. + const gfx::Rect expected_bounds{10, 88, 80, 2}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_Square_TopAndLeftClose_TopInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{12, 12, 30, 30}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + // When top and left sides are close, inset from the top. + const gfx::Rect expected_bounds{10, 42, 80, 48}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_Square_TopAndRightClose_TopInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{58, 12, 30, 30}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + // When top and right sides are close, inset from the top. + const gfx::Rect expected_bounds{10, 42, 80, 48}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_Square_BottomAndLeftClose_BottomInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{12, 58, 30, 30}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + // When bottom and left sides are close, inset from the botom. + const gfx::Rect expected_bounds{10, 10, 80, 48}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_Square_BottomAndRightClose_BottomInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{58, 58, 30, 30}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + // When bottom and right sides are close, inset from the botom. + const gfx::Rect expected_bounds{10, 10, 80, 48}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_TopAndLeftExact_TopInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 10, 10, 5}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 0); + + const gfx::Rect expected_bounds{10, 15, 80, 75}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_TopIntersectLeftExact_TopInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 7, 10, 5}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + const gfx::Rect expected_bounds{10, 12, 80, 78}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_TopInsideLeftExact_TopInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 12, 10, 5}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + const gfx::Rect expected_bounds{10, 17, 80, 73}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_TopTooFarInsideLeftExact_NoInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 16, 10, 5}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + EXPECT_RECT_EQ(bounds, result); +} + +TEST(GeometryUtil, SubtractOverlayFromBoundingBox_WideRect_Oversized_TopInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{5, 5, 85, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 10); + + const gfx::Rect expected_bounds{10, 15, 80, 75}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_BottomAndLeftExact_BottomInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 85, 10, 5}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 0); + + const gfx::Rect expected_bounds{10, 10, 80, 75}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST( + GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_BottomInsideLeftExact_BottomInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 83, 10, 5}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + const gfx::Rect expected_bounds{10, 10, 80, 73}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST( + GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_BottomIntersectLeftExact_BottomInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 87, 10, 5}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + const gfx::Rect expected_bounds{10, 10, 80, 77}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST( + GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_BottomTooFarInsideLeftExact_NoInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 77, 10, 5}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + EXPECT_RECT_EQ(bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_WideRect_Oversized_BottomInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{5, 85, 85, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 10); + + const gfx::Rect expected_bounds{10, 10, 80, 75}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_TopAndLeftExact_LeftInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{10, 10, 5, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 0); + + const gfx::Rect expected_bounds{15, 10, 75, 80}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_TopExactLeftIntersect_LeftInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{7, 10, 5, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + const gfx::Rect expected_bounds{12, 10, 78, 80}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_TopExactLeftInside_LeftInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{12, 10, 5, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + const gfx::Rect expected_bounds{17, 10, 73, 80}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_TopExactLeftTooFarInside_NoInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{16, 10, 5, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + EXPECT_RECT_EQ(bounds, result); +} + +TEST(GeometryUtil, SubtractOverlayFromBoundingBox_TallRect_Oversize_LeftInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{5, 5, 10, 85}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 10); + + const gfx::Rect expected_bounds{15, 10, 75, 80}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_TopAndRightExact_RightInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{85, 10, 5, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 0); + + const gfx::Rect expected_bounds{10, 10, 75, 80}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_TopExactRightInside_RightInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{83, 10, 5, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + const gfx::Rect expected_bounds{10, 10, 73, 80}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST( + GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_TopExactRightIntersect_RightInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{87, 10, 5, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + const gfx::Rect expected_bounds{10, 10, 77, 80}; + EXPECT_RECT_EQ(expected_bounds, result); +} + +TEST( + GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_TopExactRightTooFarInside_NoInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{77, 10, 5, 10}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 5); + + EXPECT_RECT_EQ(bounds, result); +} + +TEST(GeometryUtil, + SubtractOverlayFromBoundingBox_TallRect_Oversize_RightInset) { + const gfx::Rect bounds{10, 10, 80, 80}; + const gfx::Rect overlay{85, 5, 10, 85}; + auto result = SubtractOverlayFromBoundingBox(bounds, overlay, 10); + + const gfx::Rect expected_bounds{10, 10, 75, 80}; + EXPECT_RECT_EQ(expected_bounds, result); +} diff --git a/libcef/browser/native/browser_platform_delegate_native_win.cc b/libcef/browser/native/browser_platform_delegate_native_win.cc index b76cb862a..c2f026b3c 100644 --- a/libcef/browser/native/browser_platform_delegate_native_win.cc +++ b/libcef/browser/native/browser_platform_delegate_native_win.cc @@ -10,8 +10,8 @@ #include "libcef/browser/alloy/alloy_browser_host_impl.h" #include "libcef/browser/context.h" +#include "libcef/browser/geometry_util.h" #include "libcef/browser/native/window_delegate_view.h" -#include "libcef/browser/screen_util.h" #include "libcef/browser/thread_util.h" #include "base/base_paths_win.h" diff --git a/libcef/browser/screen_util.cc b/libcef/browser/screen_util.cc deleted file mode 100644 index ac1023cdf..000000000 --- a/libcef/browser/screen_util.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2022 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#include "libcef/browser/screen_util.h" - -#include - -#include "ui/gfx/geometry/rect.h" - -namespace { - -constexpr int kMinWidth = 0; -constexpr int kMinHeight = 0; - -// Makes sure that line segment lies entirely between min and max. -int clamp_segment_start(int start, int len, int min, int max) { - start = std::clamp(start, min, max); - const int end = start + len; - const int excess = end - max; - - if (excess > 0) { - start = start - excess; - } - - return start; -} - -} // namespace - -gfx::Rect MakeVisibleOnScreenRect(const gfx::Rect& rect, - const gfx::Rect& screen) { - const int width = std::clamp(rect.width(), kMinWidth, screen.width()); - const int height = std::clamp(rect.height(), kMinHeight, screen.height()); - - const int right_border = screen.x() + screen.width(); - const int x = clamp_segment_start(rect.x(), width, screen.x(), right_border); - - const int bottom_border = screen.y() + screen.height(); - const int y = - clamp_segment_start(rect.y(), height, screen.y(), bottom_border); - - return gfx::Rect(x, y, width, height); -} diff --git a/libcef/browser/screen_util.h b/libcef/browser/screen_util.h deleted file mode 100644 index 1cadf9714..000000000 --- a/libcef/browser/screen_util.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2022 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#ifndef CEF_LIBCEF_BROWSER_SCREEN_UTIL_H_ -#define CEF_LIBCEF_BROWSER_SCREEN_UTIL_H_ -#pragma once - -namespace gfx { -class Rect; -} - -// Create a new rectangle from the input |rect| rectangle that is fully visible -// on provided |screen_rect| screen. The width and height of the resulting -// rectangle are clamped to the screen width and height respectively if they -// would overflow. -gfx::Rect MakeVisibleOnScreenRect(const gfx::Rect& rect, - const gfx::Rect& screen); - -#endif // CEF_LIBCEF_BROWSER_SCREEN_UTIL_H_ diff --git a/libcef/browser/screen_util_unittest.cc b/libcef/browser/screen_util_unittest.cc deleted file mode 100644 index 3beefa072..000000000 --- a/libcef/browser/screen_util_unittest.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2022 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#include "cef/libcef/browser/screen_util.h" -#include "tests/gtest/include/gtest/gtest.h" - -#include "ui/gfx/geometry/rect.h" - -namespace { - -constexpr int kScreenWidth = 1024; -constexpr int kScreenHeight = 768; -const gfx::Rect kMainScreen(0, 0, kScreenWidth, kScreenHeight); -const gfx::Rect kLeftScreen(-1024, 0, kScreenWidth, kScreenHeight); - -} // namespace - -TEST(MakeVisibleOnScreenRect, RectSizeIsBiggerThanScreen) { - const gfx::Rect rect{400, 500, 1500, 800}; - - auto result = MakeVisibleOnScreenRect(rect, kMainScreen); - - EXPECT_EQ(result.x(), 0); - EXPECT_EQ(result.width(), kMainScreen.width()); - EXPECT_EQ(result.y(), 0); - EXPECT_EQ(result.height(), kMainScreen.height()); -} - -TEST(MakeVisibleOnScreenRect, RightBorderIsOutsideTheScreen) { - const gfx::Rect rect{600, 400, 500, 300}; - - auto result = MakeVisibleOnScreenRect(rect, kMainScreen); - - EXPECT_EQ(result.x(), 524); - EXPECT_EQ(result.width(), rect.width()); - EXPECT_EQ(result.y(), rect.y()); - EXPECT_EQ(result.height(), rect.height()); -} - -TEST(MakeVisibleOnScreenRect, LeftBorderIsOutsideTheScreen) { - const gfx::Rect rect{-400, 400, 500, 300}; - - auto result = MakeVisibleOnScreenRect(rect, kMainScreen); - - EXPECT_EQ(result.x(), 0); - EXPECT_EQ(result.width(), rect.width()); - EXPECT_EQ(result.y(), rect.y()); - EXPECT_EQ(result.height(), rect.height()); -} - -TEST(MakeVisibleOnScreenRect, BottomBorderIsOutsideTheScreen) { - const gfx::Rect rect{600, 500, 300, 300}; - - auto result = MakeVisibleOnScreenRect(rect, kMainScreen); - - EXPECT_EQ(result.x(), 600); - EXPECT_EQ(result.width(), rect.width()); - EXPECT_EQ(result.y(), 468); - EXPECT_EQ(result.height(), rect.height()); -} - -TEST(MakeVisibleOnScreenRect, RectIsVisibleOnTheLeftScreen) { - const gfx::Rect rect{-500, 300, 300, 300}; - - auto result = MakeVisibleOnScreenRect(rect, kLeftScreen); - - EXPECT_EQ(result.x(), rect.x()); - EXPECT_EQ(result.width(), rect.width()); - EXPECT_EQ(result.y(), rect.y()); - EXPECT_EQ(result.height(), rect.height()); -} - -TEST(MakeVisibleOnScreenRect, RectSizeIsBiggerThanLeftScreen) { - const gfx::Rect rect{-500, 300, 3000, 3000}; - - auto result = MakeVisibleOnScreenRect(rect, kLeftScreen); - - EXPECT_EQ(result.x(), kLeftScreen.x()); - EXPECT_EQ(result.width(), kLeftScreen.width()); - EXPECT_EQ(result.y(), kLeftScreen.y()); - EXPECT_EQ(result.height(), kLeftScreen.height()); -} \ No newline at end of file diff --git a/libcef/browser/views/view_util.h b/libcef/browser/views/view_util.h index 16841f54a..a5fb6e522 100644 --- a/libcef/browser/views/view_util.h +++ b/libcef/browser/views/view_util.h @@ -161,6 +161,10 @@ views::NativeWidget* CreateNativeWidget( void SetHostView(views::Widget* widget, views::View* host_view); views::View* GetHostView(views::Widget* widget); +#if BUILDFLAG(IS_MAC) +float GetNSWindowTitleBarHeight(views::Widget* widget); +#endif + } // namespace view_util #endif // CEF_LIBCEF_BROWSER_VIEWS_VIEW_UTIL_H_ diff --git a/libcef/browser/views/view_util_mac.mm b/libcef/browser/views/view_util_mac.mm index f7e51cbec..903505df1 100644 --- a/libcef/browser/views/view_util_mac.mm +++ b/libcef/browser/views/view_util_mac.mm @@ -17,6 +17,9 @@ namespace { constexpr char kNativeHostViewKey[] = "CefNativeHostViewKey"; +// For Venura 13.3.1. +constexpr float kDefaultTitleBarHeight = 30; + } // namespace gfx::NativeWindow GetNativeWindow(views::Widget* widget) { @@ -67,4 +70,13 @@ views::View* GetHostView(views::Widget* widget) { widget->GetNativeWindowProperty(kNativeHostViewKey)); } +float GetNSWindowTitleBarHeight(views::Widget* widget) { + if (auto window = GetNativeWindow(widget)) { + NSWindow* nswindow = window.GetNativeNSWindow(); + return nswindow.frame.size.height - + [nswindow contentRectForFrameRect:nswindow.frame].size.height; + } + return kDefaultTitleBarHeight; +} + } // namespace view_util diff --git a/libcef/browser/views/window_view.cc b/libcef/browser/views/window_view.cc index 0c5ee9ebc..af8d27fc8 100644 --- a/libcef/browser/views/window_view.cc +++ b/libcef/browser/views/window_view.cc @@ -5,6 +5,7 @@ #include "libcef/browser/views/window_view.h" #include "libcef/browser/chrome/views/chrome_browser_frame.h" +#include "libcef/browser/geometry_util.h" #include "libcef/browser/image_impl.h" #include "libcef/browser/views/window_impl.h" #include "libcef/features/runtime.h" @@ -638,6 +639,7 @@ void CefWindowView::SetDraggableRegions( if (draggable_region_) { draggable_region_.reset(nullptr); } + draggable_rects_.clear(); return; } @@ -648,6 +650,11 @@ void CefWindowView::SetDraggableRegions( region.bounds.x + region.bounds.width, region.bounds.y + region.bounds.height}, region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op); + + if (region.draggable) { + draggable_rects_.push_back({region.bounds.x, region.bounds.y, + region.bounds.width, region.bounds.height}); + } } } @@ -661,3 +668,60 @@ views::NonClientFrameView* CefWindowView::GetNonClientFrameView() const { } return widget->non_client_view()->frame_view(); } + +void CefWindowView::UpdateFindBarBoundingBox(gfx::Rect* bounds) const { + // Max distance from the edges of |bounds| to qualify for subtraction. + const int kMaxDistance = 10; + + for (auto& overlay_host : overlay_hosts_) { + *bounds = SubtractOverlayFromBoundingBox(*bounds, overlay_host->bounds(), + kMaxDistance); + } + + for (auto& rect : draggable_rects_) { + *bounds = SubtractOverlayFromBoundingBox(*bounds, rect, kMaxDistance); + } + + if (auto titlebar_height = GetTitlebarHeight()) { + gfx::Insets inset; + +#if BUILDFLAG(IS_MAC) + // For framed windows on macOS we must add the titlebar height. + const bool add_titlebar_height = !is_frameless_; +#else + const bool add_titlebar_height = false; +#endif + + if (add_titlebar_height) { + inset.set_top(*titlebar_height); + } else if (bounds->y() < *titlebar_height) { + inset.set_top(*titlebar_height - bounds->y()); + } + + if (!inset.IsEmpty()) { + bounds->Inset(inset); + } + } +} + +absl::optional CefWindowView::GetTitlebarHeight() const { + if (cef_delegate()) { + float title_bar_height = 0; + const bool has_title_bar_height = + cef_delegate()->GetTitlebarHeight(GetCefWindow(), &title_bar_height); + if (has_title_bar_height) { + return title_bar_height; + } + } + +#if BUILDFLAG(IS_MAC) + if (!is_frameless_) { + // For framed windows on macOS we must include the titlebar height in the + // UpdateFindBarBoundingBox() calculation. + return view_util::GetNSWindowTitleBarHeight( + const_cast(GetWidget())); + } +#endif + + return absl::nullopt; +} diff --git a/libcef/browser/views/window_view.h b/libcef/browser/views/window_view.h index 6789636b8..46ec20691 100644 --- a/libcef/browser/views/window_view.h +++ b/libcef/browser/views/window_view.h @@ -113,12 +113,17 @@ class CefWindowView // Returns the NonClientFrameView for this Window. May be nullptr. views::NonClientFrameView* GetNonClientFrameView() const; + // Optionally modify the bounding box for the Chrome Find bar. + void UpdateFindBarBoundingBox(gfx::Rect* bounds) const; + private: // Called when removed from the Widget and before |this| is deleted. void DeleteDelegate(); void MoveOverlaysIfNecessary(); + absl::optional GetTitlebarHeight() const; + // Not owned by this object. Delegate* window_delegate_; @@ -130,6 +135,7 @@ class CefWindowView CefRefPtr window_app_icon_; std::unique_ptr draggable_region_; + std::vector draggable_rects_; // Hosts for overlay widgets. std::vector> overlay_hosts_; diff --git a/patch/patches/chrome_runtime_views.patch b/patch/patches/chrome_runtime_views.patch index 9136073d3..9f6fb6e63 100644 --- a/patch/patches/chrome_runtime_views.patch +++ b/patch/patches/chrome_runtime_views.patch @@ -216,6 +216,23 @@ index 26545b0cee2c1..118cf0df456d6 100644 private: friend class ::MockAppMenuModel; +diff --git chrome/browser/ui/views/find_bar_host.cc chrome/browser/ui/views/find_bar_host.cc +index 8ac822b917399..7a7e6fd15bdd9 100644 +--- chrome/browser/ui/views/find_bar_host.cc ++++ chrome/browser/ui/views/find_bar_host.cc +@@ -412,6 +412,12 @@ void FindBarHost::GetWidgetBounds(gfx::Rect* bounds) { + // The BrowserView does Layout for the components that we care about + // positioning relative to, so we ask it to tell us where we should go. + *bounds = browser_view()->GetFindBarBoundingBox(); ++ ++#if BUILDFLAG(ENABLE_CEF) ++ if (browser_view()->browser() && browser_view()->browser()->cef_delegate()) { ++ browser_view()->browser()->cef_delegate()->UpdateFindBarBoundingBox(bounds); ++ } ++#endif + } + + void FindBarHost::RegisterAccelerators() { diff --git chrome/browser/ui/views/frame/browser_frame.cc chrome/browser/ui/views/frame/browser_frame.cc index 8da09b8c01c3f..d4f2a855bb8e8 100644 --- chrome/browser/ui/views/frame/browser_frame.cc