• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Embedded Framework Authors. Portions copyright
2 // 2011 The Chromium Authors. All rights reserved. Use of this source code is
3 // governed by a BSD-style license that can be found in the LICENSE file.
4 
5 #include "libcef/browser/views/overlay_view_host.h"
6 
7 #include "libcef/browser/views/view_util.h"
8 #include "libcef/browser/views/window_view.h"
9 
10 #include "base/i18n/rtl.h"
11 #include "chrome/browser/ui/views/frame/browser_view.h"
12 #include "chrome/browser/ui/views/theme_copying_widget.h"
13 #include "third_party/skia/include/core/SkColor.h"
14 #include "ui/compositor/compositor.h"
15 #include "ui/compositor/layer.h"
16 
17 #if defined(USE_AURA)
18 #include "ui/aura/window.h"
19 #include "ui/views/view_constants_aura.h"
20 #endif
21 
22 namespace {
23 
24 class CefOverlayControllerImpl : public CefOverlayController {
25  public:
CefOverlayControllerImpl(CefOverlayViewHost * host,CefRefPtr<CefView> view)26   CefOverlayControllerImpl(CefOverlayViewHost* host, CefRefPtr<CefView> view)
27       : host_(host), view_(view) {}
28 
29   CefOverlayControllerImpl(const CefOverlayControllerImpl&) = delete;
30   CefOverlayControllerImpl& operator=(const CefOverlayControllerImpl&) = delete;
31 
IsValid()32   bool IsValid() override {
33     // View validity implies that CefOverlayViewHost is still valid, because the
34     // Widget that it owns (and that owns the View) is still valid.
35     return view_ && view_->IsValid();
36   }
37 
IsSame(CefRefPtr<CefOverlayController> that)38   bool IsSame(CefRefPtr<CefOverlayController> that) override {
39     return that && that->GetContentsView()->IsSame(view_);
40   }
41 
GetContentsView()42   CefRefPtr<CefView> GetContentsView() override { return view_; }
43 
GetWindow()44   CefRefPtr<CefWindow> GetWindow() override {
45     if (IsValid()) {
46       return view_util::GetWindowFor(host_->window_view()->GetWidget());
47     }
48     return nullptr;
49   }
50 
GetDockingMode()51   cef_docking_mode_t GetDockingMode() override {
52     if (IsValid()) {
53       return host_->docking_mode();
54     }
55     return CEF_DOCKING_MODE_TOP_LEFT;
56   }
57 
Destroy()58   void Destroy() override {
59     if (IsValid()) {
60       host_->Destroy();
61       view_ = nullptr;
62     }
63   }
64 
SetBounds(const CefRect & bounds)65   void SetBounds(const CefRect& bounds) override {
66     if (IsValid() && host_->docking_mode() == CEF_DOCKING_MODE_CUSTOM) {
67       host_->SetOverlayBounds(
68           gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height));
69     }
70   }
71 
GetBounds()72   CefRect GetBounds() override {
73     if (IsValid()) {
74       const auto& bounds = host_->bounds();
75       return CefRect(bounds.x(), bounds.y(), bounds.width(), bounds.height());
76     }
77     return CefRect();
78   }
79 
GetBoundsInScreen()80   CefRect GetBoundsInScreen() override {
81     if (IsValid()) {
82       const auto& bounds = host_->widget()->GetWindowBoundsInScreen();
83       return CefRect(bounds.x(), bounds.y(), bounds.width(), bounds.height());
84     }
85     return CefRect();
86   }
87 
SetSize(const CefSize & size)88   void SetSize(const CefSize& size) override {
89     if (IsValid() && host_->docking_mode() == CEF_DOCKING_MODE_CUSTOM) {
90       // Update the size without changing the origin.
91       const auto& origin = host_->bounds().origin();
92       host_->SetOverlayBounds(
93           gfx::Rect(origin, gfx::Size(size.width, size.height)));
94     }
95   }
96 
GetSize()97   CefSize GetSize() override {
98     const auto& bounds = GetBounds();
99     return CefSize(bounds.width, bounds.height);
100   }
101 
SetPosition(const CefPoint & position)102   void SetPosition(const CefPoint& position) override {
103     if (IsValid() && host_->docking_mode() == CEF_DOCKING_MODE_CUSTOM) {
104       // Update the origin without changing the size.
105       const auto& size = host_->bounds().size();
106       host_->SetOverlayBounds(
107           gfx::Rect(gfx::Point(position.x, position.y), size));
108     }
109   }
110 
GetPosition()111   CefPoint GetPosition() override {
112     const auto& bounds = GetBounds();
113     return CefPoint(bounds.x, bounds.y);
114   }
115 
SetInsets(const CefInsets & insets)116   void SetInsets(const CefInsets& insets) override {
117     if (IsValid() && host_->docking_mode() != CEF_DOCKING_MODE_CUSTOM) {
118       host_->SetOverlayInsets(insets);
119     }
120   }
121 
GetInsets()122   CefInsets GetInsets() override {
123     if (IsValid()) {
124       return host_->insets();
125     }
126     return CefInsets();
127   }
128 
SizeToPreferredSize()129   void SizeToPreferredSize() override {
130     if (IsValid()) {
131       if (host_->docking_mode() == CEF_DOCKING_MODE_CUSTOM) {
132         // Update the size without changing the origin.
133         const auto& origin = host_->bounds().origin();
134         const auto& preferred_size = host_->view()->GetPreferredSize();
135         host_->SetOverlayBounds(gfx::Rect(origin, preferred_size));
136       } else {
137         host_->MoveIfNecessary();
138       }
139     }
140   }
141 
SetVisible(bool visible)142   void SetVisible(bool visible) override {
143     if (IsValid()) {
144       if (visible) {
145         host_->MoveIfNecessary();
146         host_->widget()->Show();
147       } else {
148         host_->widget()->Hide();
149       }
150     }
151   }
152 
IsVisible()153   bool IsVisible() override {
154     if (IsValid()) {
155       return host_->widget()->IsVisible();
156     }
157     return false;
158   }
159 
IsDrawn()160   bool IsDrawn() override { return IsVisible(); }
161 
162  private:
163   CefOverlayViewHost* const host_;
164   CefRefPtr<CefView> view_;
165 
166   IMPLEMENT_REFCOUNTING(CefOverlayControllerImpl);
167 };
168 
169 }  // namespace
170 
CefOverlayViewHost(CefWindowView * window_view,cef_docking_mode_t docking_mode)171 CefOverlayViewHost::CefOverlayViewHost(CefWindowView* window_view,
172                                        cef_docking_mode_t docking_mode)
173     : window_view_(window_view), docking_mode_(docking_mode) {}
174 
Init(views::View * widget_view,CefRefPtr<CefView> view)175 void CefOverlayViewHost::Init(views::View* widget_view,
176                               CefRefPtr<CefView> view) {
177   DCHECK(view);
178 
179   // Match the logic in CEF_PANEL_IMPL_D::AddChildView().
180   auto controls_view = view->IsAttached()
181                            ? base::WrapUnique(view_util::GetFor(view))
182                            : view_util::PassOwnership(view);
183   DCHECK(controls_view.get());
184 
185   cef_controller_ = new CefOverlayControllerImpl(this, view);
186 
187   // Initialize the Widget.
188   widget_ = std::make_unique<ThemeCopyingWidget>(window_view_->GetWidget());
189   views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
190   params.delegate = this;
191   params.name = "CefOverlayViewHost";
192   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
193   params.parent = window_view_->GetWidget()->GetNativeView();
194   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
195   params.activatable = views::Widget::InitParams::Activatable::kNo;
196   widget_->Init(std::move(params));
197 
198   view_ = widget_->GetContentsView()->AddChildView(std::move(controls_view));
199 
200   // Make the Widget background transparent. The View might still be opaque.
201   if (widget_->GetCompositor()) {
202     widget_->GetCompositor()->SetBackgroundColor(SK_ColorTRANSPARENT);
203   }
204 
205 #if defined(USE_AURA)
206   // See matching logic in view_util::GetWindowFor.
207   widget_->GetNativeView()->SetProperty(views::kHostViewKey, widget_view);
208 #endif
209 
210   if (cef::IsChromeRuntimeEnabled()) {
211     // Some attributes associated with a Chrome toolbar are located via the
212     // Widget. See matching logic in BrowserView::AddedToWidget.
213     auto browser_view = BrowserView::GetBrowserViewForNativeWindow(
214         view_util::GetNativeWindow(window_view_->GetWidget()));
215     if (browser_view) {
216       widget_->SetNativeWindowProperty(BrowserView::kBrowserViewKey,
217                                        browser_view);
218     }
219   }
220 
221   // Set the initial bounds after the View has been added to the Widget.
222   // Otherwise, preferred size won't calculate correctly.
223   gfx::Rect bounds;
224   if (docking_mode_ == CEF_DOCKING_MODE_CUSTOM) {
225     if (view_->size().IsEmpty()) {
226       // Size to the preferred size to start.
227       view_->SizeToPreferredSize();
228     }
229 
230     // Top-left origin with existing size.
231     bounds = gfx::Rect(gfx::Point(), view_->size());
232   } else {
233     bounds = ComputeBounds();
234   }
235   SetOverlayBounds(bounds);
236 
237   // Register for future bounds change notifications.
238   view_->AddObserver(this);
239 
240   // Initially hidden.
241   widget_->Hide();
242 }
243 
Destroy()244 void CefOverlayViewHost::Destroy() {
245   if (widget_ && !widget_->IsClosed()) {
246     // Remove the child View immediately. It may be reused by the client.
247     auto view = view_util::GetFor(view_, /*find_known_parent=*/false);
248     widget_->GetContentsView()->RemoveChildView(view_);
249     if (view) {
250       view_util::ResumeOwnership(view);
251     }
252 
253     widget_->Close();
254   }
255 }
256 
MoveIfNecessary()257 void CefOverlayViewHost::MoveIfNecessary() {
258   if (bounds_changing_ || docking_mode_ == CEF_DOCKING_MODE_CUSTOM) {
259     return;
260   }
261   SetOverlayBounds(ComputeBounds());
262 }
263 
SetOverlayBounds(const gfx::Rect & bounds)264 void CefOverlayViewHost::SetOverlayBounds(const gfx::Rect& bounds) {
265   // Avoid re-entrancy of this method.
266   if (bounds_changing_)
267     return;
268 
269   gfx::Rect new_bounds = bounds;
270 
271   // Keep the result inside the widget.
272   new_bounds.Intersect(window_view_->bounds());
273 
274   if (new_bounds == bounds_)
275     return;
276 
277   bounds_changing_ = true;
278 
279   bounds_ = new_bounds;
280   if (view_->size() != bounds_.size()) {
281     view_->SetSize(bounds_.size());
282   }
283   widget_->SetBounds(bounds_);
284 
285   bounds_changing_ = false;
286 }
287 
SetOverlayInsets(const CefInsets & insets)288 void CefOverlayViewHost::SetOverlayInsets(const CefInsets& insets) {
289   if (insets == insets_)
290     return;
291   insets_ = insets;
292   MoveIfNecessary();
293 }
294 
OnViewBoundsChanged(views::View * observed_view)295 void CefOverlayViewHost::OnViewBoundsChanged(views::View* observed_view) {
296   MoveIfNecessary();
297 }
298 
ComputeBounds() const299 gfx::Rect CefOverlayViewHost::ComputeBounds() const {
300   // This method is only used with corner docking.
301   DCHECK_NE(docking_mode_, CEF_DOCKING_MODE_CUSTOM);
302 
303   // Find the area we have to work with.
304   const auto& widget_bounds = window_view_->bounds();
305 
306   // Ask the view how large an area it needs to draw on.
307   const auto& prefsize = view_->GetPreferredSize();
308 
309   // Swap left/right docking with RTL.
310   const bool is_rtl = base::i18n::IsRTL();
311 
312   // Dock to the correct corner, considering insets in the docking corner only.
313   int x = widget_bounds.x();
314   int y = widget_bounds.y();
315   if (((docking_mode_ == CEF_DOCKING_MODE_TOP_RIGHT ||
316         docking_mode_ == CEF_DOCKING_MODE_BOTTOM_RIGHT) &&
317        !is_rtl) ||
318       ((docking_mode_ == CEF_DOCKING_MODE_TOP_LEFT ||
319         docking_mode_ == CEF_DOCKING_MODE_BOTTOM_LEFT) &&
320        is_rtl)) {
321     x += widget_bounds.width() - prefsize.width() - insets_.right;
322   } else {
323     x += insets_.left;
324   }
325   if (docking_mode_ == CEF_DOCKING_MODE_BOTTOM_LEFT ||
326       docking_mode_ == CEF_DOCKING_MODE_BOTTOM_RIGHT) {
327     y += widget_bounds.height() - prefsize.height() - insets_.bottom;
328   } else {
329     y += insets_.top;
330   }
331 
332   return gfx::Rect(x, y, prefsize.width(), prefsize.height());
333 }
334