1 // Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be found
3 // in the LICENSE file.
4
5 #include "libcef/browser/views/view_util.h"
6
7 #include <utility>
8
9 #include "libcef/browser/views/view_adapter.h"
10
11 #include "ui/display/display.h"
12 #include "ui/display/screen.h"
13 #include "ui/gfx/geometry/point.h"
14 #include "ui/gfx/geometry/point_conversions.h"
15 #include "ui/views/widget/widget.h"
16 #include "ui/views/widget/widget_delegate.h"
17 #include "ui/views/window/non_client_view.h"
18
19 #if BUILDFLAG(IS_WIN)
20 #include "ui/display/win/screen_win.h"
21 #endif
22
23 #if defined(USE_AURA)
24 #include "ui/aura/window.h"
25 #include "ui/views/view_constants_aura.h"
26 #endif
27
28 namespace view_util {
29
30 namespace {
31
32 // Manages the association between views::View and CefView instances.
33 class UserData : public base::SupportsUserData::Data {
34 public:
35 // Create the initial association between the views::View and the CefView. The
36 // CefView owns the views::View at this stage.
Register(CefRefPtr<CefView> cef_view)37 static void Register(CefRefPtr<CefView> cef_view) {
38 DCHECK(cef_view->IsValid());
39 DCHECK(!cef_view->IsAttached());
40
41 views::View* view = CefViewAdapter::GetFor(cef_view)->Get();
42 DCHECK(view);
43
44 // The CefView should not already be registered.
45 DCHECK(!view->GetUserData(UserDataKey()));
46
47 view->SetUserData(UserDataKey(), base::WrapUnique(new UserData(cef_view)));
48 }
49
GetFor(const views::View * view)50 static CefRefPtr<CefView> GetFor(const views::View* view) {
51 DCHECK(view);
52 UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
53 if (data)
54 return data->view_ref_;
55 return nullptr;
56 }
57
58 // Transfer ownership of the views::View to the caller. The views::View will
59 // gain a ref-counted reference to the CefView and the CefView will keep an
60 // unowned reference to the views::View. Destruction of the views::View will
61 // release the ref-counted reference to the CefView.
PassOwnership(CefRefPtr<CefView> cef_view)62 static std::unique_ptr<views::View> PassOwnership(CefRefPtr<CefView> cef_view)
63 WARN_UNUSED_RESULT {
64 DCHECK(cef_view->IsValid());
65 DCHECK(!cef_view->IsAttached());
66
67 std::unique_ptr<views::View> view =
68 CefViewAdapter::GetFor(cef_view)->PassOwnership();
69 DCHECK(view);
70
71 UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
72 DCHECK(data);
73 data->TakeReference();
74
75 return view;
76 }
77
78 // The CefView resumes ownership of the views::View. The views::View no longer
79 // keeps a ref-counted reference to the CefView.
ResumeOwnership(CefRefPtr<CefView> cef_view)80 static void ResumeOwnership(CefRefPtr<CefView> cef_view) {
81 DCHECK(cef_view->IsValid());
82 DCHECK(cef_view->IsAttached());
83
84 CefViewAdapter* adapter = CefViewAdapter::GetFor(cef_view);
85 adapter->ResumeOwnership();
86
87 views::View* view = adapter->Get();
88 DCHECK(view);
89
90 UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
91 DCHECK(data);
92 data->ReleaseReference();
93 }
94
95 private:
96 friend std::default_delete<UserData>;
97
UserData(CefRefPtr<CefView> cef_view)98 explicit UserData(CefRefPtr<CefView> cef_view) : view_ref_(cef_view.get()) {
99 DCHECK(view_ref_);
100 }
101
~UserData()102 ~UserData() override {
103 if (view_) {
104 // The CefView does not own the views::View. Remove the CefView's
105 // reference to the views::View.
106 CefViewAdapter::GetFor(view_)->Detach();
107 }
108 }
109
TakeReference()110 void TakeReference() { view_ = view_ref_; }
111
ReleaseReference()112 void ReleaseReference() { view_ = nullptr; }
113
UserDataKey()114 static void* UserDataKey() {
115 // We just need a unique constant. Use the address of a static that
116 // COMDAT folding won't touch in an optimizing linker.
117 static int data_key = 0;
118 return reinterpret_cast<void*>(&data_key);
119 }
120
121 CefRefPtr<CefView> view_;
122 CefView* view_ref_;
123 };
124
125 } // namespace
126
127 const SkColor kDefaultBackgroundColor = SkColorSetARGB(255, 255, 255, 255);
128 const char kDefaultFontList[] = "Arial, Helvetica, 14px";
129
Register(CefRefPtr<CefView> view)130 void Register(CefRefPtr<CefView> view) {
131 UserData::Register(view);
132 }
133
GetFor(const views::View * view,bool find_known_parent)134 CefRefPtr<CefView> GetFor(const views::View* view, bool find_known_parent) {
135 if (!view)
136 return nullptr;
137
138 if (!find_known_parent)
139 return UserData::GetFor(view);
140
141 CefRefPtr<CefView> cef_view;
142 const views::View* current_view = view;
143 do {
144 cef_view = UserData::GetFor(current_view);
145 if (cef_view)
146 break;
147 current_view = current_view->parent();
148 } while (current_view);
149
150 return cef_view;
151 }
152
GetFor(CefRefPtr<CefView> view)153 views::View* GetFor(CefRefPtr<CefView> view) {
154 return CefViewAdapter::GetFor(view)->Get();
155 }
156
PassOwnership(CefRefPtr<CefView> view)157 std::unique_ptr<views::View> PassOwnership(CefRefPtr<CefView> view) {
158 return UserData::PassOwnership(view);
159 }
160
ResumeOwnership(CefRefPtr<CefView> view)161 void ResumeOwnership(CefRefPtr<CefView> view) {
162 UserData::ResumeOwnership(view);
163 }
164
GetWindowFor(views::Widget * widget)165 CefRefPtr<CefWindow> GetWindowFor(views::Widget* widget) {
166 CefRefPtr<CefWindow> window;
167
168 #if defined(USE_AURA)
169 // Retrieve the parent Widget for an overlay.
170 if (widget) {
171 // See matching logic in CefOverlayViewHost::Init.
172 auto widget_view =
173 widget->GetNativeView()->GetProperty(views::kHostViewKey);
174 if (widget_view) {
175 widget = widget_view->GetWidget();
176 }
177 }
178 #endif // defined(USE_AURA)
179
180 if (widget) {
181 // The views::WidgetDelegate should be a CefWindowView and |content_view|
182 // should be the same CefWindowView. However, just in case the views::Widget
183 // was created by something else let's go about this the safer way.
184 views::View* content_view = widget->widget_delegate()->GetContentsView();
185 CefRefPtr<CefView> cef_view = GetFor(content_view, false);
186 if (cef_view && cef_view->AsPanel())
187 window = cef_view->AsPanel()->AsWindow();
188
189 // The Window should always exist if we created the views::Widget.
190 DCHECK(window);
191 }
192
193 return window;
194 }
195
GetDisplayNearestPoint(const gfx::Point & point,bool input_pixel_coords)196 display::Display GetDisplayNearestPoint(const gfx::Point& point,
197 bool input_pixel_coords) {
198 gfx::Point find_point = point;
199 #if BUILDFLAG(IS_WIN)
200 if (input_pixel_coords) {
201 find_point = gfx::ToFlooredPoint(
202 display::win::ScreenWin::ScreenToDIPPoint(gfx::PointF(point)));
203 }
204 #endif
205 return display::Screen::GetScreen()->GetDisplayNearestPoint(find_point);
206 }
207
GetDisplayMatchingBounds(const gfx::Rect & bounds,bool input_pixel_coords)208 display::Display GetDisplayMatchingBounds(const gfx::Rect& bounds,
209 bool input_pixel_coords) {
210 gfx::Rect find_bounds = bounds;
211 #if BUILDFLAG(IS_WIN)
212 if (input_pixel_coords) {
213 find_bounds =
214 display::win::ScreenWin::ScreenToDIPRect(nullptr, find_bounds);
215 }
216 #endif
217 return display::Screen::GetScreen()->GetDisplayMatching(find_bounds);
218 }
219
ConvertPointFromPixels(gfx::Point * point,int device_scale_factor)220 void ConvertPointFromPixels(gfx::Point* point, int device_scale_factor) {
221 *point = gfx::ToFlooredPoint(
222 gfx::ScalePoint(gfx::PointF(*point), 1.0f / device_scale_factor));
223 }
224
ConvertPointToPixels(gfx::Point * point,int device_scale_factor)225 void ConvertPointToPixels(gfx::Point* point, int device_scale_factor) {
226 *point = gfx::ToFlooredPoint(
227 gfx::ScalePoint(gfx::PointF(*point), device_scale_factor));
228 }
229
ConvertPointToScreen(views::View * view,gfx::Point * point,bool output_pixel_coords)230 bool ConvertPointToScreen(views::View* view,
231 gfx::Point* point,
232 bool output_pixel_coords) {
233 if (!view->GetWidget())
234 return false;
235
236 views::View::ConvertPointToScreen(view, point);
237
238 if (output_pixel_coords) {
239 const display::Display& display = GetDisplayNearestPoint(*point, false);
240 ConvertPointToPixels(point, display.device_scale_factor());
241 }
242
243 return true;
244 }
245
ConvertPointFromScreen(views::View * view,gfx::Point * point,bool input_pixel_coords)246 bool ConvertPointFromScreen(views::View* view,
247 gfx::Point* point,
248 bool input_pixel_coords) {
249 if (!view->GetWidget())
250 return false;
251
252 if (input_pixel_coords) {
253 const display::Display& display = GetDisplayNearestPoint(*point, true);
254 ConvertPointFromPixels(point, display.device_scale_factor());
255 }
256
257 views::View::ConvertPointFromScreen(view, point);
258
259 return true;
260 }
261
ConvertPointToWindow(views::View * view,gfx::Point * point)262 bool ConvertPointToWindow(views::View* view, gfx::Point* point) {
263 views::Widget* widget = view->GetWidget();
264 if (!widget)
265 return false;
266
267 views::View::ConvertPointToWidget(view, point);
268
269 if (widget->non_client_view()) {
270 views::NonClientFrameView* non_client_frame_view =
271 widget->non_client_view()->frame_view();
272 if (non_client_frame_view) {
273 // When using a custom drawn NonClientFrameView the native Window will not
274 // know the actual client bounds. Adjust the native Window bounds for the
275 // reported client bounds.
276 const gfx::Rect& client_bounds =
277 non_client_frame_view->GetBoundsForClientView();
278 *point -= client_bounds.OffsetFromOrigin();
279 }
280 }
281
282 return true;
283 }
284
ConvertPointFromWindow(views::View * view,gfx::Point * point)285 bool ConvertPointFromWindow(views::View* view, gfx::Point* point) {
286 views::Widget* widget = view->GetWidget();
287 if (!widget)
288 return false;
289
290 if (widget->non_client_view()) {
291 views::NonClientFrameView* non_client_frame_view =
292 widget->non_client_view()->frame_view();
293 if (non_client_frame_view) {
294 // When using a custom drawn NonClientFrameView the native Window will not
295 // know the actual client bounds. Adjust the native Window bounds for the
296 // reported client bounds.
297 const gfx::Rect& client_bounds =
298 non_client_frame_view->GetBoundsForClientView();
299 *point += client_bounds.OffsetFromOrigin();
300 }
301 }
302
303 views::View::ConvertPointFromWidget(view, point);
304
305 return true;
306 }
307
308 } // namespace view_util
309