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