1 // Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "libcef/browser/native/browser_platform_delegate_native_linux.h"
6
7 #include "libcef/browser/browser_host_base.h"
8 #include "libcef/browser/context.h"
9 #include "libcef/browser/native/menu_runner_linux.h"
10 #include "libcef/browser/native/window_delegate_view.h"
11 #include "libcef/browser/thread_util.h"
12
13 #include "base/no_destructor.h"
14 #include "content/browser/renderer_host/render_widget_host_impl.h"
15 #include "content/public/browser/native_web_keyboard_event.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
18 #include "ui/events/keycodes/dom/dom_key.h"
19 #include "ui/events/keycodes/dom/keycode_converter.h"
20 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
21 #include "ui/events/keycodes/keyboard_code_conversion_xkb.h"
22 #include "ui/events/keycodes/keysym_to_unicode.h"
23 #include "ui/gfx/font_render_params.h"
24 #include "ui/views/widget/widget.h"
25
26 #if BUILDFLAG(OZONE_PLATFORM_X11)
27 #include "libcef/browser/native/window_x11.h"
28 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
29 #endif
30
CefBrowserPlatformDelegateNativeLinux(const CefWindowInfo & window_info,SkColor background_color)31 CefBrowserPlatformDelegateNativeLinux::CefBrowserPlatformDelegateNativeLinux(
32 const CefWindowInfo& window_info,
33 SkColor background_color)
34 : CefBrowserPlatformDelegateNativeAura(window_info, background_color),
35 host_window_created_(false),
36 window_widget_(nullptr) {}
37
BrowserDestroyed(CefBrowserHostBase * browser)38 void CefBrowserPlatformDelegateNativeLinux::BrowserDestroyed(
39 CefBrowserHostBase* browser) {
40 CefBrowserPlatformDelegateNative::BrowserDestroyed(browser);
41
42 if (host_window_created_) {
43 // Release the reference added in CreateHostWindow().
44 browser->Release();
45 }
46 }
47
CreateHostWindow()48 bool CefBrowserPlatformDelegateNativeLinux::CreateHostWindow() {
49 DCHECK(!window_widget_);
50
51 if (window_info_.bounds.width == 0)
52 window_info_.bounds.width = 800;
53 if (window_info_.bounds.height == 0)
54 window_info_.bounds.height = 600;
55
56 gfx::Rect rect(window_info_.bounds.x, window_info_.bounds.y,
57 window_info_.bounds.width, window_info_.bounds.height);
58
59 #if BUILDFLAG(OZONE_PLATFORM_X11)
60 DCHECK(!window_x11_);
61
62 x11::Window parent_window = x11::Window::None;
63 if (window_info_.parent_window != kNullWindowHandle) {
64 parent_window = static_cast<x11::Window>(window_info_.parent_window);
65 }
66
67 // Create a new window object. It will delete itself when the associated X11
68 // window is destroyed.
69 window_x11_ =
70 new CefWindowX11(browser_, parent_window, rect,
71 CefString(&window_info_.window_name).ToString());
72 DCHECK_NE(window_x11_->xwindow(), x11::Window::None);
73 window_info_.window =
74 static_cast<cef_window_handle_t>(window_x11_->xwindow());
75
76 host_window_created_ = true;
77
78 // Add a reference that will be released in BrowserDestroyed().
79 browser_->AddRef();
80
81 CefWindowDelegateView* delegate_view = new CefWindowDelegateView(
82 GetBackgroundColor(), window_x11_->TopLevelAlwaysOnTop(),
83 GetBoundsChangedCallback());
84 delegate_view->Init(static_cast<gfx::AcceleratedWidget>(window_info_.window),
85 web_contents_, gfx::Rect(gfx::Point(), rect.size()));
86
87 window_widget_ = delegate_view->GetWidget();
88 window_widget_->Show();
89
90 window_x11_->Show();
91 #endif // BUILDFLAG(OZONE_PLATFORM_X11)
92
93 // As an additional requirement on Linux, we must set the colors for the
94 // render widgets in webkit.
95 auto prefs = web_contents_->GetMutableRendererPrefs();
96 prefs->focus_ring_color = SkColorSetARGB(255, 229, 151, 0);
97
98 prefs->active_selection_bg_color = SkColorSetRGB(30, 144, 255);
99 prefs->active_selection_fg_color = SK_ColorWHITE;
100 prefs->inactive_selection_bg_color = SkColorSetRGB(200, 200, 200);
101 prefs->inactive_selection_fg_color = SkColorSetRGB(50, 50, 50);
102
103 // Set font-related attributes.
104 static const gfx::FontRenderParams params(
105 gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr));
106 prefs->should_antialias_text = params.antialiasing;
107 prefs->use_subpixel_positioning = params.subpixel_positioning;
108 prefs->hinting = params.hinting;
109 prefs->use_autohinter = params.autohinter;
110 prefs->use_bitmaps = params.use_bitmaps;
111 prefs->subpixel_rendering = params.subpixel_rendering;
112
113 web_contents_->SyncRendererPrefs();
114
115 return true;
116 }
117
CloseHostWindow()118 void CefBrowserPlatformDelegateNativeLinux::CloseHostWindow() {
119 #if BUILDFLAG(OZONE_PLATFORM_X11)
120 if (window_x11_)
121 window_x11_->Close();
122 #endif
123 }
124
GetHostWindowHandle() const125 CefWindowHandle CefBrowserPlatformDelegateNativeLinux::GetHostWindowHandle()
126 const {
127 if (windowless_handler_)
128 return windowless_handler_->GetParentWindowHandle();
129 return window_info_.window;
130 }
131
GetWindowWidget() const132 views::Widget* CefBrowserPlatformDelegateNativeLinux::GetWindowWidget() const {
133 return window_widget_;
134 }
135
SetFocus(bool setFocus)136 void CefBrowserPlatformDelegateNativeLinux::SetFocus(bool setFocus) {
137 if (!setFocus)
138 return;
139
140 if (web_contents_) {
141 // Give logical focus to the RenderWidgetHostViewAura in the views
142 // hierarchy. This does not change the native keyboard focus.
143 web_contents_->Focus();
144 }
145
146 #if BUILDFLAG(OZONE_PLATFORM_X11)
147 if (window_x11_) {
148 // Give native focus to the DesktopNativeWidgetAura for the root window.
149 // Needs to be done via the ::Window so that keyboard focus is assigned
150 // correctly.
151 window_x11_->Focus();
152 }
153 #endif // BUILDFLAG(OZONE_PLATFORM_X11)
154 }
155
NotifyMoveOrResizeStarted()156 void CefBrowserPlatformDelegateNativeLinux::NotifyMoveOrResizeStarted() {
157 // Call the parent method to dismiss any existing popups.
158 CefBrowserPlatformDelegateNative::NotifyMoveOrResizeStarted();
159
160 if (!web_contents_)
161 return;
162
163 #if BUILDFLAG(OZONE_PLATFORM_X11)
164 if (!window_x11_)
165 return;
166
167 views::DesktopWindowTreeHostLinux* tree_host = window_x11_->GetHost();
168 if (!tree_host)
169 return;
170
171 // Explicitly set the screen bounds so that WindowTreeHost::*Screen()
172 // methods return the correct results.
173 const gfx::Rect& bounds = window_x11_->GetBoundsInScreen();
174 tree_host->set_screen_bounds(bounds);
175
176 // Send updated screen rectangle information to the renderer process so that
177 // popups are displayed in the correct location.
178 content::RenderWidgetHostImpl::From(
179 web_contents_->GetRenderViewHost()->GetWidget())
180 ->SendScreenRects();
181 #endif // BUILDFLAG(OZONE_PLATFORM_X11)
182 }
183
SizeTo(int width,int height)184 void CefBrowserPlatformDelegateNativeLinux::SizeTo(int width, int height) {
185 #if BUILDFLAG(OZONE_PLATFORM_X11)
186 if (window_x11_) {
187 window_x11_->SetBounds(
188 gfx::Rect(window_x11_->bounds().origin(), gfx::Size(width, height)));
189 }
190 #endif // BUILDFLAG(OZONE_PLATFORM_X11)
191 }
192
GetScreenPoint(const gfx::Point & view) const193 gfx::Point CefBrowserPlatformDelegateNativeLinux::GetScreenPoint(
194 const gfx::Point& view) const {
195 if (windowless_handler_)
196 return windowless_handler_->GetParentScreenPoint(view);
197
198 #if BUILDFLAG(OZONE_PLATFORM_X11)
199 if (!window_x11_)
200 return view;
201
202 // We can't use aura::Window::GetBoundsInScreen on Linux because it will
203 // return bounds from DesktopWindowTreeHostLinux which in our case is relative
204 // to the parent window instead of the root window (screen).
205 const gfx::Rect& bounds_in_screen = window_x11_->GetBoundsInScreen();
206 return gfx::Point(bounds_in_screen.x() + view.x(),
207 bounds_in_screen.y() + view.y());
208 #else // !BUILDFLAG(OZONE_PLATFORM_X11)
209 return gfx::Point();
210 #endif
211 }
212
ViewText(const std::string & text)213 void CefBrowserPlatformDelegateNativeLinux::ViewText(const std::string& text) {
214 char buff[] = "/tmp/CEFSourceXXXXXX";
215 int fd = mkstemp(buff);
216
217 if (fd == -1)
218 return;
219
220 FILE* srcOutput = fdopen(fd, "w+");
221 if (!srcOutput)
222 return;
223
224 if (fputs(text.c_str(), srcOutput) < 0) {
225 fclose(srcOutput);
226 return;
227 }
228
229 fclose(srcOutput);
230
231 std::string newName(buff);
232 newName.append(".txt");
233 if (rename(buff, newName.c_str()) != 0)
234 return;
235
236 std::string openCommand("xdg-open ");
237 openCommand += newName;
238
239 int result = system(openCommand.c_str());
240 ALLOW_UNUSED_LOCAL(result);
241 }
242
HandleKeyboardEvent(const content::NativeWebKeyboardEvent & event)243 bool CefBrowserPlatformDelegateNativeLinux::HandleKeyboardEvent(
244 const content::NativeWebKeyboardEvent& event) {
245 // TODO(cef): Is something required here to handle shortcut keys?
246 return false;
247 }
248
249 // static
HandleExternalProtocol(const GURL & url)250 void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) {}
251
GetEventHandle(const content::NativeWebKeyboardEvent & event) const252 CefEventHandle CefBrowserPlatformDelegateNativeLinux::GetEventHandle(
253 const content::NativeWebKeyboardEvent& event) const {
254 // TODO(cef): We need to return an XEvent* from this method, but
255 // |event.os_event->native_event()| now returns a ui::Event* instead.
256 // See https://crbug.com/965991.
257 return nullptr;
258 }
259
260 std::unique_ptr<CefMenuRunner>
CreateMenuRunner()261 CefBrowserPlatformDelegateNativeLinux::CreateMenuRunner() {
262 return base::WrapUnique(new CefMenuRunnerLinux);
263 }
264
GetDialogPosition(const gfx::Size & size)265 gfx::Point CefBrowserPlatformDelegateNativeLinux::GetDialogPosition(
266 const gfx::Size& size) {
267 const gfx::Size& max_size = GetMaximumDialogSize();
268 return gfx::Point((max_size.width() - size.width()) / 2,
269 (max_size.height() - size.height()) / 2);
270 }
271
GetMaximumDialogSize()272 gfx::Size CefBrowserPlatformDelegateNativeLinux::GetMaximumDialogSize() {
273 return GetWindowWidget()->GetWindowBoundsInScreen().size();
274 }
275
TranslateUiKeyEvent(const CefKeyEvent & key_event) const276 ui::KeyEvent CefBrowserPlatformDelegateNativeLinux::TranslateUiKeyEvent(
277 const CefKeyEvent& key_event) const {
278 int flags = TranslateUiEventModifiers(key_event.modifiers);
279 ui::KeyboardCode key_code =
280 static_cast<ui::KeyboardCode>(key_event.windows_key_code);
281 ui::DomCode dom_code =
282 ui::KeycodeConverter::NativeKeycodeToDomCode(key_event.native_key_code);
283 int keysym = ui::XKeysymForWindowsKeyCode(
284 key_code, !!(key_event.modifiers & EVENTFLAG_SHIFT_DOWN));
285 char16_t character = ui::GetUnicodeCharacterFromXKeySym(keysym);
286 base::TimeTicks time_stamp = GetEventTimeStamp();
287
288 if (key_event.type == KEYEVENT_CHAR) {
289 return ui::KeyEvent(character, key_code, dom_code, flags, time_stamp);
290 }
291
292 ui::EventType type = ui::ET_UNKNOWN;
293 switch (key_event.type) {
294 case KEYEVENT_RAWKEYDOWN:
295 case KEYEVENT_KEYDOWN:
296 type = ui::ET_KEY_PRESSED;
297 break;
298 case KEYEVENT_KEYUP:
299 type = ui::ET_KEY_RELEASED;
300 break;
301 default:
302 NOTREACHED();
303 }
304
305 ui::DomKey dom_key = ui::XKeySymToDomKey(keysym, character);
306 return ui::KeyEvent(type, key_code, dom_code, flags, dom_key, time_stamp);
307 }
308
309 content::NativeWebKeyboardEvent
TranslateWebKeyEvent(const CefKeyEvent & key_event) const310 CefBrowserPlatformDelegateNativeLinux::TranslateWebKeyEvent(
311 const CefKeyEvent& key_event) const {
312 ui::KeyEvent ui_event = TranslateUiKeyEvent(key_event);
313 if (key_event.type == KEYEVENT_CHAR) {
314 return content::NativeWebKeyboardEvent(ui_event, key_event.character);
315 }
316 return content::NativeWebKeyboardEvent(ui_event);
317 }
318