chrome: Improve positioning of the "Find" widget (fixes #3461)

The "Find" widget will be excluded from regions near the edges of the window
that contain overlays, draggable regions or titlebar.
This commit is contained in:
Marshall Greenblatt 2023-04-12 14:34:39 -04:00
parent 17cab6d955
commit a39c2a0068
20 changed files with 630 additions and 151 deletions

View File

@ -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 = [

View File

@ -241,6 +241,9 @@ std::unique_ptr<CefMenuRunner> CefBrowserPlatformDelegate::CreateMenuRunner() {
return nullptr;
}
void CefBrowserPlatformDelegate::UpdateFindBarBoundingBox(
gfx::Rect* bounds) const {}
bool CefBrowserPlatformDelegate::IsWindowless() const {
return false;
}

View File

@ -286,6 +286,9 @@ class CefBrowserPlatformDelegate {
// Create the platform-specific menu runner.
virtual std::unique_ptr<CefMenuRunner> 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;

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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<CefWindow> window = view_util::GetWindowFor(widget);
return static_cast<CefWindowImpl*>(window.get());
}
return nullptr;
}

View File

@ -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<CefBrowserViewImpl> browser_view() const { return browser_view_; }
@ -42,6 +45,8 @@ class CefBrowserPlatformDelegateChromeViews
private:
void SetBrowserView(CefRefPtr<CefBrowserViewImpl> browser_view);
CefWindowImpl* GetWindowImpl() const;
CefRefPtr<CefBrowserViewImpl> browser_view_;
};

View File

@ -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 <algorithm>
#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;
}

View File

@ -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_

View File

@ -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);
}

View File

@ -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"

View File

@ -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 <algorithm>
#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);
}

View File

@ -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_

View File

@ -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());
}

View File

@ -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_

View File

@ -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

View File

@ -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<float> 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<views::Widget*>(GetWidget()));
}
#endif
return absl::nullopt;
}

View File

@ -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<float> GetTitlebarHeight() const;
// Not owned by this object.
Delegate* window_delegate_;
@ -130,6 +135,7 @@ class CefWindowView
CefRefPtr<CefImage> window_app_icon_;
std::unique_ptr<SkRegion> draggable_region_;
std::vector<gfx::Rect> draggable_rects_;
// Hosts for overlay widgets.
std::vector<std::unique_ptr<CefOverlayViewHost>> overlay_hosts_;

View File

@ -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