1 // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "tests/cefclient/browser/browser_window_osr_gtk.h"
6
7 #include <GL/gl.h>
8 #include <gdk/gdk.h>
9 #include <gdk/gdkkeysyms-compat.h>
10 #include <gdk/gdkx.h>
11 #include <glib-object.h>
12 #include <gtk/gtk.h>
13
14 #define XK_3270 // for XK_3270_BackTab
15 #include <X11/XF86keysym.h>
16 #include <X11/Xcursor/Xcursor.h>
17 #include <X11/keysym.h>
18
19 #include <algorithm>
20
21 #include "include/base/cef_cxx17_backports.h"
22 #include "include/base/cef_logging.h"
23 #include "include/base/cef_macros.h"
24 #include "include/wrapper/cef_closure_task.h"
25 #include "tests/cefclient/browser/util_gtk.h"
26 #include "tests/shared/browser/geometry_util.h"
27 #include "tests/shared/browser/main_message_loop.h"
28
29 namespace client {
30
31 namespace {
32
33 // Static BrowserWindowOsrGtk::EventFilter needs to forward touch events
34 // to correct browser, so we maintain a vector of all windows.
35 std::vector<BrowserWindowOsrGtk*> g_browser_windows;
36
GetCefStateModifiers(guint state)37 int GetCefStateModifiers(guint state) {
38 int modifiers = 0;
39 if (state & GDK_SHIFT_MASK)
40 modifiers |= EVENTFLAG_SHIFT_DOWN;
41 if (state & GDK_LOCK_MASK)
42 modifiers |= EVENTFLAG_CAPS_LOCK_ON;
43 if (state & GDK_CONTROL_MASK)
44 modifiers |= EVENTFLAG_CONTROL_DOWN;
45 if (state & GDK_MOD1_MASK)
46 modifiers |= EVENTFLAG_ALT_DOWN;
47 if (state & GDK_BUTTON1_MASK)
48 modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
49 if (state & GDK_BUTTON2_MASK)
50 modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
51 if (state & GDK_BUTTON3_MASK)
52 modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
53 return modifiers;
54 }
55
56 // From ui/events/keycodes/keyboard_codes_posix.h.
57 enum KeyboardCode {
58 VKEY_BACK = 0x08,
59 VKEY_TAB = 0x09,
60 VKEY_BACKTAB = 0x0A,
61 VKEY_CLEAR = 0x0C,
62 VKEY_RETURN = 0x0D,
63 VKEY_SHIFT = 0x10,
64 VKEY_CONTROL = 0x11,
65 VKEY_MENU = 0x12,
66 VKEY_PAUSE = 0x13,
67 VKEY_CAPITAL = 0x14,
68 VKEY_KANA = 0x15,
69 VKEY_HANGUL = 0x15,
70 VKEY_JUNJA = 0x17,
71 VKEY_FINAL = 0x18,
72 VKEY_HANJA = 0x19,
73 VKEY_KANJI = 0x19,
74 VKEY_ESCAPE = 0x1B,
75 VKEY_CONVERT = 0x1C,
76 VKEY_NONCONVERT = 0x1D,
77 VKEY_ACCEPT = 0x1E,
78 VKEY_MODECHANGE = 0x1F,
79 VKEY_SPACE = 0x20,
80 VKEY_PRIOR = 0x21,
81 VKEY_NEXT = 0x22,
82 VKEY_END = 0x23,
83 VKEY_HOME = 0x24,
84 VKEY_LEFT = 0x25,
85 VKEY_UP = 0x26,
86 VKEY_RIGHT = 0x27,
87 VKEY_DOWN = 0x28,
88 VKEY_SELECT = 0x29,
89 VKEY_PRINT = 0x2A,
90 VKEY_EXECUTE = 0x2B,
91 VKEY_SNAPSHOT = 0x2C,
92 VKEY_INSERT = 0x2D,
93 VKEY_DELETE = 0x2E,
94 VKEY_HELP = 0x2F,
95 VKEY_0 = 0x30,
96 VKEY_1 = 0x31,
97 VKEY_2 = 0x32,
98 VKEY_3 = 0x33,
99 VKEY_4 = 0x34,
100 VKEY_5 = 0x35,
101 VKEY_6 = 0x36,
102 VKEY_7 = 0x37,
103 VKEY_8 = 0x38,
104 VKEY_9 = 0x39,
105 VKEY_A = 0x41,
106 VKEY_B = 0x42,
107 VKEY_C = 0x43,
108 VKEY_D = 0x44,
109 VKEY_E = 0x45,
110 VKEY_F = 0x46,
111 VKEY_G = 0x47,
112 VKEY_H = 0x48,
113 VKEY_I = 0x49,
114 VKEY_J = 0x4A,
115 VKEY_K = 0x4B,
116 VKEY_L = 0x4C,
117 VKEY_M = 0x4D,
118 VKEY_N = 0x4E,
119 VKEY_O = 0x4F,
120 VKEY_P = 0x50,
121 VKEY_Q = 0x51,
122 VKEY_R = 0x52,
123 VKEY_S = 0x53,
124 VKEY_T = 0x54,
125 VKEY_U = 0x55,
126 VKEY_V = 0x56,
127 VKEY_W = 0x57,
128 VKEY_X = 0x58,
129 VKEY_Y = 0x59,
130 VKEY_Z = 0x5A,
131 VKEY_LWIN = 0x5B,
132 VKEY_COMMAND = VKEY_LWIN, // Provide the Mac name for convenience.
133 VKEY_RWIN = 0x5C,
134 VKEY_APPS = 0x5D,
135 VKEY_SLEEP = 0x5F,
136 VKEY_NUMPAD0 = 0x60,
137 VKEY_NUMPAD1 = 0x61,
138 VKEY_NUMPAD2 = 0x62,
139 VKEY_NUMPAD3 = 0x63,
140 VKEY_NUMPAD4 = 0x64,
141 VKEY_NUMPAD5 = 0x65,
142 VKEY_NUMPAD6 = 0x66,
143 VKEY_NUMPAD7 = 0x67,
144 VKEY_NUMPAD8 = 0x68,
145 VKEY_NUMPAD9 = 0x69,
146 VKEY_MULTIPLY = 0x6A,
147 VKEY_ADD = 0x6B,
148 VKEY_SEPARATOR = 0x6C,
149 VKEY_SUBTRACT = 0x6D,
150 VKEY_DECIMAL = 0x6E,
151 VKEY_DIVIDE = 0x6F,
152 VKEY_F1 = 0x70,
153 VKEY_F2 = 0x71,
154 VKEY_F3 = 0x72,
155 VKEY_F4 = 0x73,
156 VKEY_F5 = 0x74,
157 VKEY_F6 = 0x75,
158 VKEY_F7 = 0x76,
159 VKEY_F8 = 0x77,
160 VKEY_F9 = 0x78,
161 VKEY_F10 = 0x79,
162 VKEY_F11 = 0x7A,
163 VKEY_F12 = 0x7B,
164 VKEY_F13 = 0x7C,
165 VKEY_F14 = 0x7D,
166 VKEY_F15 = 0x7E,
167 VKEY_F16 = 0x7F,
168 VKEY_F17 = 0x80,
169 VKEY_F18 = 0x81,
170 VKEY_F19 = 0x82,
171 VKEY_F20 = 0x83,
172 VKEY_F21 = 0x84,
173 VKEY_F22 = 0x85,
174 VKEY_F23 = 0x86,
175 VKEY_F24 = 0x87,
176 VKEY_NUMLOCK = 0x90,
177 VKEY_SCROLL = 0x91,
178 VKEY_LSHIFT = 0xA0,
179 VKEY_RSHIFT = 0xA1,
180 VKEY_LCONTROL = 0xA2,
181 VKEY_RCONTROL = 0xA3,
182 VKEY_LMENU = 0xA4,
183 VKEY_RMENU = 0xA5,
184 VKEY_BROWSER_BACK = 0xA6,
185 VKEY_BROWSER_FORWARD = 0xA7,
186 VKEY_BROWSER_REFRESH = 0xA8,
187 VKEY_BROWSER_STOP = 0xA9,
188 VKEY_BROWSER_SEARCH = 0xAA,
189 VKEY_BROWSER_FAVORITES = 0xAB,
190 VKEY_BROWSER_HOME = 0xAC,
191 VKEY_VOLUME_MUTE = 0xAD,
192 VKEY_VOLUME_DOWN = 0xAE,
193 VKEY_VOLUME_UP = 0xAF,
194 VKEY_MEDIA_NEXT_TRACK = 0xB0,
195 VKEY_MEDIA_PREV_TRACK = 0xB1,
196 VKEY_MEDIA_STOP = 0xB2,
197 VKEY_MEDIA_PLAY_PAUSE = 0xB3,
198 VKEY_MEDIA_LAUNCH_MAIL = 0xB4,
199 VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5,
200 VKEY_MEDIA_LAUNCH_APP1 = 0xB6,
201 VKEY_MEDIA_LAUNCH_APP2 = 0xB7,
202 VKEY_OEM_1 = 0xBA,
203 VKEY_OEM_PLUS = 0xBB,
204 VKEY_OEM_COMMA = 0xBC,
205 VKEY_OEM_MINUS = 0xBD,
206 VKEY_OEM_PERIOD = 0xBE,
207 VKEY_OEM_2 = 0xBF,
208 VKEY_OEM_3 = 0xC0,
209 VKEY_OEM_4 = 0xDB,
210 VKEY_OEM_5 = 0xDC,
211 VKEY_OEM_6 = 0xDD,
212 VKEY_OEM_7 = 0xDE,
213 VKEY_OEM_8 = 0xDF,
214 VKEY_OEM_102 = 0xE2,
215 VKEY_OEM_103 = 0xE3, // GTV KEYCODE_MEDIA_REWIND
216 VKEY_OEM_104 = 0xE4, // GTV KEYCODE_MEDIA_FAST_FORWARD
217 VKEY_PROCESSKEY = 0xE5,
218 VKEY_PACKET = 0xE7,
219 VKEY_DBE_SBCSCHAR = 0xF3,
220 VKEY_DBE_DBCSCHAR = 0xF4,
221 VKEY_ATTN = 0xF6,
222 VKEY_CRSEL = 0xF7,
223 VKEY_EXSEL = 0xF8,
224 VKEY_EREOF = 0xF9,
225 VKEY_PLAY = 0xFA,
226 VKEY_ZOOM = 0xFB,
227 VKEY_NONAME = 0xFC,
228 VKEY_PA1 = 0xFD,
229 VKEY_OEM_CLEAR = 0xFE,
230 VKEY_UNKNOWN = 0,
231
232 // POSIX specific VKEYs. Note that as of Windows SDK 7.1, 0x97-9F, 0xD8-DA,
233 // and 0xE8 are unassigned.
234 VKEY_WLAN = 0x97,
235 VKEY_POWER = 0x98,
236 VKEY_BRIGHTNESS_DOWN = 0xD8,
237 VKEY_BRIGHTNESS_UP = 0xD9,
238 VKEY_KBD_BRIGHTNESS_DOWN = 0xDA,
239 VKEY_KBD_BRIGHTNESS_UP = 0xE8,
240
241 // Windows does not have a specific key code for AltGr. We use the unused 0xE1
242 // (VK_OEM_AX) code to represent AltGr, matching the behaviour of Firefox on
243 // Linux.
244 VKEY_ALTGR = 0xE1,
245 // Windows does not have a specific key code for Compose. We use the unused
246 // 0xE6 (VK_ICO_CLEAR) code to represent Compose.
247 VKEY_COMPOSE = 0xE6,
248 };
249
250 // From ui/events/keycodes/keyboard_code_conversion_x.cc.
251 // Gdk key codes (e.g. GDK_BackSpace) and X keysyms (e.g. XK_BackSpace) share
252 // the same values.
KeyboardCodeFromXKeysym(unsigned int keysym)253 KeyboardCode KeyboardCodeFromXKeysym(unsigned int keysym) {
254 switch (keysym) {
255 case XK_BackSpace:
256 return VKEY_BACK;
257 case XK_Delete:
258 case XK_KP_Delete:
259 return VKEY_DELETE;
260 case XK_Tab:
261 case XK_KP_Tab:
262 case XK_ISO_Left_Tab:
263 case XK_3270_BackTab:
264 return VKEY_TAB;
265 case XK_Linefeed:
266 case XK_Return:
267 case XK_KP_Enter:
268 case XK_ISO_Enter:
269 return VKEY_RETURN;
270 case XK_Clear:
271 case XK_KP_Begin: // NumPad 5 without Num Lock, for crosbug.com/29169.
272 return VKEY_CLEAR;
273 case XK_KP_Space:
274 case XK_space:
275 return VKEY_SPACE;
276 case XK_Home:
277 case XK_KP_Home:
278 return VKEY_HOME;
279 case XK_End:
280 case XK_KP_End:
281 return VKEY_END;
282 case XK_Page_Up:
283 case XK_KP_Page_Up: // aka XK_KP_Prior
284 return VKEY_PRIOR;
285 case XK_Page_Down:
286 case XK_KP_Page_Down: // aka XK_KP_Next
287 return VKEY_NEXT;
288 case XK_Left:
289 case XK_KP_Left:
290 return VKEY_LEFT;
291 case XK_Right:
292 case XK_KP_Right:
293 return VKEY_RIGHT;
294 case XK_Down:
295 case XK_KP_Down:
296 return VKEY_DOWN;
297 case XK_Up:
298 case XK_KP_Up:
299 return VKEY_UP;
300 case XK_Escape:
301 return VKEY_ESCAPE;
302 case XK_Kana_Lock:
303 case XK_Kana_Shift:
304 return VKEY_KANA;
305 case XK_Hangul:
306 return VKEY_HANGUL;
307 case XK_Hangul_Hanja:
308 return VKEY_HANJA;
309 case XK_Kanji:
310 return VKEY_KANJI;
311 case XK_Henkan:
312 return VKEY_CONVERT;
313 case XK_Muhenkan:
314 return VKEY_NONCONVERT;
315 case XK_Zenkaku_Hankaku:
316 return VKEY_DBE_DBCSCHAR;
317 case XK_A:
318 case XK_a:
319 return VKEY_A;
320 case XK_B:
321 case XK_b:
322 return VKEY_B;
323 case XK_C:
324 case XK_c:
325 return VKEY_C;
326 case XK_D:
327 case XK_d:
328 return VKEY_D;
329 case XK_E:
330 case XK_e:
331 return VKEY_E;
332 case XK_F:
333 case XK_f:
334 return VKEY_F;
335 case XK_G:
336 case XK_g:
337 return VKEY_G;
338 case XK_H:
339 case XK_h:
340 return VKEY_H;
341 case XK_I:
342 case XK_i:
343 return VKEY_I;
344 case XK_J:
345 case XK_j:
346 return VKEY_J;
347 case XK_K:
348 case XK_k:
349 return VKEY_K;
350 case XK_L:
351 case XK_l:
352 return VKEY_L;
353 case XK_M:
354 case XK_m:
355 return VKEY_M;
356 case XK_N:
357 case XK_n:
358 return VKEY_N;
359 case XK_O:
360 case XK_o:
361 return VKEY_O;
362 case XK_P:
363 case XK_p:
364 return VKEY_P;
365 case XK_Q:
366 case XK_q:
367 return VKEY_Q;
368 case XK_R:
369 case XK_r:
370 return VKEY_R;
371 case XK_S:
372 case XK_s:
373 return VKEY_S;
374 case XK_T:
375 case XK_t:
376 return VKEY_T;
377 case XK_U:
378 case XK_u:
379 return VKEY_U;
380 case XK_V:
381 case XK_v:
382 return VKEY_V;
383 case XK_W:
384 case XK_w:
385 return VKEY_W;
386 case XK_X:
387 case XK_x:
388 return VKEY_X;
389 case XK_Y:
390 case XK_y:
391 return VKEY_Y;
392 case XK_Z:
393 case XK_z:
394 return VKEY_Z;
395
396 case XK_0:
397 case XK_1:
398 case XK_2:
399 case XK_3:
400 case XK_4:
401 case XK_5:
402 case XK_6:
403 case XK_7:
404 case XK_8:
405 case XK_9:
406 return static_cast<KeyboardCode>(VKEY_0 + (keysym - XK_0));
407
408 case XK_parenright:
409 return VKEY_0;
410 case XK_exclam:
411 return VKEY_1;
412 case XK_at:
413 return VKEY_2;
414 case XK_numbersign:
415 return VKEY_3;
416 case XK_dollar:
417 return VKEY_4;
418 case XK_percent:
419 return VKEY_5;
420 case XK_asciicircum:
421 return VKEY_6;
422 case XK_ampersand:
423 return VKEY_7;
424 case XK_asterisk:
425 return VKEY_8;
426 case XK_parenleft:
427 return VKEY_9;
428
429 case XK_KP_0:
430 case XK_KP_1:
431 case XK_KP_2:
432 case XK_KP_3:
433 case XK_KP_4:
434 case XK_KP_5:
435 case XK_KP_6:
436 case XK_KP_7:
437 case XK_KP_8:
438 case XK_KP_9:
439 return static_cast<KeyboardCode>(VKEY_NUMPAD0 + (keysym - XK_KP_0));
440
441 case XK_multiply:
442 case XK_KP_Multiply:
443 return VKEY_MULTIPLY;
444 case XK_KP_Add:
445 return VKEY_ADD;
446 case XK_KP_Separator:
447 return VKEY_SEPARATOR;
448 case XK_KP_Subtract:
449 return VKEY_SUBTRACT;
450 case XK_KP_Decimal:
451 return VKEY_DECIMAL;
452 case XK_KP_Divide:
453 return VKEY_DIVIDE;
454 case XK_KP_Equal:
455 case XK_equal:
456 case XK_plus:
457 return VKEY_OEM_PLUS;
458 case XK_comma:
459 case XK_less:
460 return VKEY_OEM_COMMA;
461 case XK_minus:
462 case XK_underscore:
463 return VKEY_OEM_MINUS;
464 case XK_greater:
465 case XK_period:
466 return VKEY_OEM_PERIOD;
467 case XK_colon:
468 case XK_semicolon:
469 return VKEY_OEM_1;
470 case XK_question:
471 case XK_slash:
472 return VKEY_OEM_2;
473 case XK_asciitilde:
474 case XK_quoteleft:
475 return VKEY_OEM_3;
476 case XK_bracketleft:
477 case XK_braceleft:
478 return VKEY_OEM_4;
479 case XK_backslash:
480 case XK_bar:
481 return VKEY_OEM_5;
482 case XK_bracketright:
483 case XK_braceright:
484 return VKEY_OEM_6;
485 case XK_quoteright:
486 case XK_quotedbl:
487 return VKEY_OEM_7;
488 case XK_ISO_Level5_Shift:
489 return VKEY_OEM_8;
490 case XK_Shift_L:
491 case XK_Shift_R:
492 return VKEY_SHIFT;
493 case XK_Control_L:
494 case XK_Control_R:
495 return VKEY_CONTROL;
496 case XK_Meta_L:
497 case XK_Meta_R:
498 case XK_Alt_L:
499 case XK_Alt_R:
500 return VKEY_MENU;
501 case XK_ISO_Level3_Shift:
502 return VKEY_ALTGR;
503 case XK_Multi_key:
504 return VKEY_COMPOSE;
505 case XK_Pause:
506 return VKEY_PAUSE;
507 case XK_Caps_Lock:
508 return VKEY_CAPITAL;
509 case XK_Num_Lock:
510 return VKEY_NUMLOCK;
511 case XK_Scroll_Lock:
512 return VKEY_SCROLL;
513 case XK_Select:
514 return VKEY_SELECT;
515 case XK_Print:
516 return VKEY_PRINT;
517 case XK_Execute:
518 return VKEY_EXECUTE;
519 case XK_Insert:
520 case XK_KP_Insert:
521 return VKEY_INSERT;
522 case XK_Help:
523 return VKEY_HELP;
524 case XK_Super_L:
525 return VKEY_LWIN;
526 case XK_Super_R:
527 return VKEY_RWIN;
528 case XK_Menu:
529 return VKEY_APPS;
530 case XK_F1:
531 case XK_F2:
532 case XK_F3:
533 case XK_F4:
534 case XK_F5:
535 case XK_F6:
536 case XK_F7:
537 case XK_F8:
538 case XK_F9:
539 case XK_F10:
540 case XK_F11:
541 case XK_F12:
542 case XK_F13:
543 case XK_F14:
544 case XK_F15:
545 case XK_F16:
546 case XK_F17:
547 case XK_F18:
548 case XK_F19:
549 case XK_F20:
550 case XK_F21:
551 case XK_F22:
552 case XK_F23:
553 case XK_F24:
554 return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_F1));
555 case XK_KP_F1:
556 case XK_KP_F2:
557 case XK_KP_F3:
558 case XK_KP_F4:
559 return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_KP_F1));
560
561 case XK_guillemotleft:
562 case XK_guillemotright:
563 case XK_degree:
564 // In the case of canadian multilingual keyboard layout, VKEY_OEM_102 is
565 // assigned to ugrave key.
566 case XK_ugrave:
567 case XK_Ugrave:
568 case XK_brokenbar:
569 return VKEY_OEM_102; // international backslash key in 102 keyboard.
570
571 // When evdev is in use, /usr/share/X11/xkb/symbols/inet maps F13-18 keys
572 // to the special XF86XK symbols to support Microsoft Ergonomic keyboards:
573 // https://bugs.freedesktop.org/show_bug.cgi?id=5783
574 // In Chrome, we map these X key symbols back to F13-18 since we don't have
575 // VKEYs for these XF86XK symbols.
576 case XF86XK_Tools:
577 return VKEY_F13;
578 case XF86XK_Launch5:
579 return VKEY_F14;
580 case XF86XK_Launch6:
581 return VKEY_F15;
582 case XF86XK_Launch7:
583 return VKEY_F16;
584 case XF86XK_Launch8:
585 return VKEY_F17;
586 case XF86XK_Launch9:
587 return VKEY_F18;
588 case XF86XK_Refresh:
589 case XF86XK_History:
590 case XF86XK_OpenURL:
591 case XF86XK_AddFavorite:
592 case XF86XK_Go:
593 case XF86XK_ZoomIn:
594 case XF86XK_ZoomOut:
595 // ui::AcceleratorGtk tries to convert the XF86XK_ keysyms on Chrome
596 // startup. It's safe to return VKEY_UNKNOWN here since ui::AcceleratorGtk
597 // also checks a Gdk keysym. http://crbug.com/109843
598 return VKEY_UNKNOWN;
599 // For supporting multimedia buttons on a USB keyboard.
600 case XF86XK_Back:
601 return VKEY_BROWSER_BACK;
602 case XF86XK_Forward:
603 return VKEY_BROWSER_FORWARD;
604 case XF86XK_Reload:
605 return VKEY_BROWSER_REFRESH;
606 case XF86XK_Stop:
607 return VKEY_BROWSER_STOP;
608 case XF86XK_Search:
609 return VKEY_BROWSER_SEARCH;
610 case XF86XK_Favorites:
611 return VKEY_BROWSER_FAVORITES;
612 case XF86XK_HomePage:
613 return VKEY_BROWSER_HOME;
614 case XF86XK_AudioMute:
615 return VKEY_VOLUME_MUTE;
616 case XF86XK_AudioLowerVolume:
617 return VKEY_VOLUME_DOWN;
618 case XF86XK_AudioRaiseVolume:
619 return VKEY_VOLUME_UP;
620 case XF86XK_AudioNext:
621 return VKEY_MEDIA_NEXT_TRACK;
622 case XF86XK_AudioPrev:
623 return VKEY_MEDIA_PREV_TRACK;
624 case XF86XK_AudioStop:
625 return VKEY_MEDIA_STOP;
626 case XF86XK_AudioPlay:
627 return VKEY_MEDIA_PLAY_PAUSE;
628 case XF86XK_Mail:
629 return VKEY_MEDIA_LAUNCH_MAIL;
630 case XF86XK_LaunchA: // F3 on an Apple keyboard.
631 return VKEY_MEDIA_LAUNCH_APP1;
632 case XF86XK_LaunchB: // F4 on an Apple keyboard.
633 case XF86XK_Calculator:
634 return VKEY_MEDIA_LAUNCH_APP2;
635 case XF86XK_WLAN:
636 return VKEY_WLAN;
637 case XF86XK_PowerOff:
638 return VKEY_POWER;
639 case XF86XK_MonBrightnessDown:
640 return VKEY_BRIGHTNESS_DOWN;
641 case XF86XK_MonBrightnessUp:
642 return VKEY_BRIGHTNESS_UP;
643 case XF86XK_KbdBrightnessDown:
644 return VKEY_KBD_BRIGHTNESS_DOWN;
645 case XF86XK_KbdBrightnessUp:
646 return VKEY_KBD_BRIGHTNESS_UP;
647
648 // TODO(sad): some keycodes are still missing.
649 }
650 return VKEY_UNKNOWN;
651 }
652
653 // From content/browser/renderer_host/input/web_input_event_util_posix.cc.
GdkEventToWindowsKeyCode(const GdkEventKey * event)654 KeyboardCode GdkEventToWindowsKeyCode(const GdkEventKey* event) {
655 static const unsigned int kHardwareCodeToGDKKeyval[] = {
656 0, // 0x00:
657 0, // 0x01:
658 0, // 0x02:
659 0, // 0x03:
660 0, // 0x04:
661 0, // 0x05:
662 0, // 0x06:
663 0, // 0x07:
664 0, // 0x08:
665 0, // 0x09: GDK_Escape
666 GDK_1, // 0x0A: GDK_1
667 GDK_2, // 0x0B: GDK_2
668 GDK_3, // 0x0C: GDK_3
669 GDK_4, // 0x0D: GDK_4
670 GDK_5, // 0x0E: GDK_5
671 GDK_6, // 0x0F: GDK_6
672 GDK_7, // 0x10: GDK_7
673 GDK_8, // 0x11: GDK_8
674 GDK_9, // 0x12: GDK_9
675 GDK_0, // 0x13: GDK_0
676 GDK_minus, // 0x14: GDK_minus
677 GDK_equal, // 0x15: GDK_equal
678 0, // 0x16: GDK_BackSpace
679 0, // 0x17: GDK_Tab
680 GDK_q, // 0x18: GDK_q
681 GDK_w, // 0x19: GDK_w
682 GDK_e, // 0x1A: GDK_e
683 GDK_r, // 0x1B: GDK_r
684 GDK_t, // 0x1C: GDK_t
685 GDK_y, // 0x1D: GDK_y
686 GDK_u, // 0x1E: GDK_u
687 GDK_i, // 0x1F: GDK_i
688 GDK_o, // 0x20: GDK_o
689 GDK_p, // 0x21: GDK_p
690 GDK_bracketleft, // 0x22: GDK_bracketleft
691 GDK_bracketright, // 0x23: GDK_bracketright
692 0, // 0x24: GDK_Return
693 0, // 0x25: GDK_Control_L
694 GDK_a, // 0x26: GDK_a
695 GDK_s, // 0x27: GDK_s
696 GDK_d, // 0x28: GDK_d
697 GDK_f, // 0x29: GDK_f
698 GDK_g, // 0x2A: GDK_g
699 GDK_h, // 0x2B: GDK_h
700 GDK_j, // 0x2C: GDK_j
701 GDK_k, // 0x2D: GDK_k
702 GDK_l, // 0x2E: GDK_l
703 GDK_semicolon, // 0x2F: GDK_semicolon
704 GDK_apostrophe, // 0x30: GDK_apostrophe
705 GDK_grave, // 0x31: GDK_grave
706 0, // 0x32: GDK_Shift_L
707 GDK_backslash, // 0x33: GDK_backslash
708 GDK_z, // 0x34: GDK_z
709 GDK_x, // 0x35: GDK_x
710 GDK_c, // 0x36: GDK_c
711 GDK_v, // 0x37: GDK_v
712 GDK_b, // 0x38: GDK_b
713 GDK_n, // 0x39: GDK_n
714 GDK_m, // 0x3A: GDK_m
715 GDK_comma, // 0x3B: GDK_comma
716 GDK_period, // 0x3C: GDK_period
717 GDK_slash, // 0x3D: GDK_slash
718 0, // 0x3E: GDK_Shift_R
719 0, // 0x3F:
720 0, // 0x40:
721 0, // 0x41:
722 0, // 0x42:
723 0, // 0x43:
724 0, // 0x44:
725 0, // 0x45:
726 0, // 0x46:
727 0, // 0x47:
728 0, // 0x48:
729 0, // 0x49:
730 0, // 0x4A:
731 0, // 0x4B:
732 0, // 0x4C:
733 0, // 0x4D:
734 0, // 0x4E:
735 0, // 0x4F:
736 0, // 0x50:
737 0, // 0x51:
738 0, // 0x52:
739 0, // 0x53:
740 0, // 0x54:
741 0, // 0x55:
742 0, // 0x56:
743 0, // 0x57:
744 0, // 0x58:
745 0, // 0x59:
746 0, // 0x5A:
747 0, // 0x5B:
748 0, // 0x5C:
749 0, // 0x5D:
750 0, // 0x5E:
751 0, // 0x5F:
752 0, // 0x60:
753 0, // 0x61:
754 0, // 0x62:
755 0, // 0x63:
756 0, // 0x64:
757 0, // 0x65:
758 0, // 0x66:
759 0, // 0x67:
760 0, // 0x68:
761 0, // 0x69:
762 0, // 0x6A:
763 0, // 0x6B:
764 0, // 0x6C:
765 0, // 0x6D:
766 0, // 0x6E:
767 0, // 0x6F:
768 0, // 0x70:
769 0, // 0x71:
770 0, // 0x72:
771 GDK_Super_L, // 0x73: GDK_Super_L
772 GDK_Super_R, // 0x74: GDK_Super_R
773 };
774
775 // |windows_key_code| has to include a valid virtual-key code even when we
776 // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
777 // on the Hebrew layout, |windows_key_code| should be VK_A.
778 // On the other hand, |event->keyval| value depends on the current
779 // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
780 // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
781 // KeyboardCodeFromXKeysym() call returns 0.
782 // To improve compatibilty with Windows, we use |event->hardware_keycode|
783 // for retrieving its Windows key-code for the keys when the
784 // WebCore::windows_key_codeForEvent() call returns 0.
785 // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
786 // objects cannot change because |event->hardware_keycode| doesn't change
787 // even when we change the layout options, e.g. when we swap a control
788 // key and a caps-lock key, GTK doesn't swap their
789 // |event->hardware_keycode| values but swap their |event->keyval| values.
790 KeyboardCode windows_key_code = KeyboardCodeFromXKeysym(event->keyval);
791 if (windows_key_code)
792 return windows_key_code;
793
794 if (event->hardware_keycode < base::size(kHardwareCodeToGDKKeyval)) {
795 int keyval = kHardwareCodeToGDKKeyval[event->hardware_keycode];
796 if (keyval)
797 return KeyboardCodeFromXKeysym(keyval);
798 }
799
800 // This key is one that keyboard-layout drivers cannot change.
801 // Use |event->keyval| to retrieve its |windows_key_code| value.
802 return KeyboardCodeFromXKeysym(event->keyval);
803 }
804
805 // From content/browser/renderer_host/input/web_input_event_util_posix.cc.
GetWindowsKeyCodeWithoutLocation(KeyboardCode key_code)806 KeyboardCode GetWindowsKeyCodeWithoutLocation(KeyboardCode key_code) {
807 switch (key_code) {
808 case VKEY_LCONTROL:
809 case VKEY_RCONTROL:
810 return VKEY_CONTROL;
811 case VKEY_LSHIFT:
812 case VKEY_RSHIFT:
813 return VKEY_SHIFT;
814 case VKEY_LMENU:
815 case VKEY_RMENU:
816 return VKEY_MENU;
817 default:
818 return key_code;
819 }
820 }
821
822 // From content/browser/renderer_host/input/web_input_event_builders_gtk.cc.
823 // Gets the corresponding control character of a specified key code. See:
824 // http://en.wikipedia.org/wiki/Control_characters
825 // We emulate Windows behavior here.
GetControlCharacter(KeyboardCode windows_key_code,bool shift)826 int GetControlCharacter(KeyboardCode windows_key_code, bool shift) {
827 if (windows_key_code >= VKEY_A && windows_key_code <= VKEY_Z) {
828 // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
829 return windows_key_code - VKEY_A + 1;
830 }
831 if (shift) {
832 // following graphics chars require shift key to input.
833 switch (windows_key_code) {
834 // ctrl-@ maps to \x00 (Null byte)
835 case VKEY_2:
836 return 0;
837 // ctrl-^ maps to \x1E (Record separator, Information separator two)
838 case VKEY_6:
839 return 0x1E;
840 // ctrl-_ maps to \x1F (Unit separator, Information separator one)
841 case VKEY_OEM_MINUS:
842 return 0x1F;
843 // Returns 0 for all other keys to avoid inputting unexpected chars.
844 default:
845 return 0;
846 }
847 } else {
848 switch (windows_key_code) {
849 // ctrl-[ maps to \x1B (Escape)
850 case VKEY_OEM_4:
851 return 0x1B;
852 // ctrl-\ maps to \x1C (File separator, Information separator four)
853 case VKEY_OEM_5:
854 return 0x1C;
855 // ctrl-] maps to \x1D (Group separator, Information separator three)
856 case VKEY_OEM_6:
857 return 0x1D;
858 // ctrl-Enter maps to \x0A (Line feed)
859 case VKEY_RETURN:
860 return 0x0A;
861 // Returns 0 for all other keys to avoid inputting unexpected chars.
862 default:
863 return 0;
864 }
865 }
866 }
867
GetWidgetRectInScreen(GtkWidget * widget,GdkRectangle * r)868 void GetWidgetRectInScreen(GtkWidget* widget, GdkRectangle* r) {
869 gint x, y, w, h;
870 GdkRectangle extents;
871
872 GdkWindow* window = gtk_widget_get_parent_window(widget);
873
874 // Get parent's left-top screen coordinates.
875 gdk_window_get_root_origin(window, &x, &y);
876 // Get parent's width and height.
877 w = gdk_window_get_width(window);
878 h = gdk_window_get_height(window);
879 // Get parent's extents including decorations.
880 gdk_window_get_frame_extents(window, &extents);
881
882 // X and Y calculations assume that left, right and bottom border sizes are
883 // all the same.
884 const gint border = (extents.width - w) / 2;
885 GtkAllocation allocation;
886 gtk_widget_get_allocation(widget, &allocation);
887 r->x = x + border + allocation.x;
888 r->y = y + (extents.height - h) - border + allocation.y;
889 r->width = allocation.width;
890 r->height = allocation.height;
891 }
892
GetDragOperationsMask(GdkDragContext * drag_context)893 CefBrowserHost::DragOperationsMask GetDragOperationsMask(
894 GdkDragContext* drag_context) {
895 int allowed_ops = DRAG_OPERATION_NONE;
896 GdkDragAction drag_action = gdk_drag_context_get_actions(drag_context);
897 if (drag_action & GDK_ACTION_COPY)
898 allowed_ops |= DRAG_OPERATION_COPY;
899 if (drag_action & GDK_ACTION_MOVE)
900 allowed_ops |= DRAG_OPERATION_MOVE;
901 if (drag_action & GDK_ACTION_LINK)
902 allowed_ops |= DRAG_OPERATION_LINK;
903 if (drag_action & GDK_ACTION_PRIVATE)
904 allowed_ops |= DRAG_OPERATION_PRIVATE;
905 return static_cast<CefBrowserHost::DragOperationsMask>(allowed_ops);
906 }
907
908 class ScopedGLContext {
909 public:
ScopedGLContext(GtkWidget * widget,bool swap_buffers)910 ScopedGLContext(GtkWidget* widget, bool swap_buffers)
911 : swap_buffers_(swap_buffers), widget_(widget) {
912 gtk_gl_area_make_current(GTK_GL_AREA(widget));
913 is_valid_ = gtk_gl_area_get_error(GTK_GL_AREA(widget)) == nullptr;
914 if (swap_buffers_ && is_valid_) {
915 gtk_gl_area_queue_render(GTK_GL_AREA(widget_));
916 gtk_gl_area_attach_buffers(GTK_GL_AREA(widget));
917 }
918 }
919
~ScopedGLContext()920 virtual ~ScopedGLContext() {
921 if (swap_buffers_ && is_valid_)
922 glFlush();
923 }
924
IsValid() const925 bool IsValid() const { return is_valid_; }
926
927 private:
928 bool swap_buffers_;
929 GtkWidget* const widget_;
930 bool is_valid_;
931 ScopedGdkThreadsEnter scoped_gdk_threads_;
932 };
933
934 } // namespace
935
BrowserWindowOsrGtk(BrowserWindow::Delegate * delegate,const std::string & startup_url,const OsrRendererSettings & settings)936 BrowserWindowOsrGtk::BrowserWindowOsrGtk(BrowserWindow::Delegate* delegate,
937 const std::string& startup_url,
938 const OsrRendererSettings& settings)
939 : BrowserWindow(delegate),
940 xdisplay_(nullptr),
941 renderer_(settings),
942 gl_enabled_(false),
943 painting_popup_(false),
944 hidden_(false),
945 glarea_(nullptr),
946 drag_trigger_event_(nullptr),
947 drag_data_(nullptr),
948 drag_operation_(DRAG_OPERATION_NONE),
949 drag_context_(nullptr),
950 drag_targets_(gtk_target_list_new(nullptr, 0)),
951 drag_leave_(false),
952 drag_drop_(false),
953 device_scale_factor_(1.0f) {
954 client_handler_ = new ClientHandlerOsr(this, this, startup_url);
955 g_browser_windows.push_back(this);
956 }
957
~BrowserWindowOsrGtk()958 BrowserWindowOsrGtk::~BrowserWindowOsrGtk() {
959 g_browser_windows.erase(
960 std::find(g_browser_windows.begin(), g_browser_windows.end(), this));
961 ScopedGdkThreadsEnter scoped_gdk_threads;
962
963 if (drag_trigger_event_) {
964 gdk_event_free(drag_trigger_event_);
965 }
966 if (drag_context_) {
967 g_object_unref(drag_context_);
968 }
969 gtk_target_list_unref(drag_targets_);
970 }
971
set_xdisplay(XDisplay * xdisplay)972 void BrowserWindowOsrGtk::set_xdisplay(XDisplay* xdisplay) {
973 REQUIRE_MAIN_THREAD();
974 DCHECK(!xdisplay_);
975 xdisplay_ = xdisplay;
976 }
977
CreateBrowser(ClientWindowHandle parent_handle,const CefRect & rect,const CefBrowserSettings & settings,CefRefPtr<CefDictionaryValue> extra_info,CefRefPtr<CefRequestContext> request_context)978 void BrowserWindowOsrGtk::CreateBrowser(
979 ClientWindowHandle parent_handle,
980 const CefRect& rect,
981 const CefBrowserSettings& settings,
982 CefRefPtr<CefDictionaryValue> extra_info,
983 CefRefPtr<CefRequestContext> request_context) {
984 REQUIRE_MAIN_THREAD();
985
986 // Create the native window.
987 Create(parent_handle);
988
989 ScopedGdkThreadsEnter scoped_gdk_threads;
990
991 // Retrieve the X11 Window ID for the GTK parent window.
992 GtkWidget* window =
993 gtk_widget_get_ancestor(GTK_WIDGET(parent_handle), GTK_TYPE_WINDOW);
994 CefWindowHandle handle = GDK_WINDOW_XID(gtk_widget_get_window(window));
995 DCHECK(handle);
996
997 CefWindowInfo window_info;
998 window_info.SetAsWindowless(handle);
999
1000 // Create the browser asynchronously.
1001 CefBrowserHost::CreateBrowser(window_info, client_handler_,
1002 client_handler_->startup_url(), settings,
1003 extra_info, request_context);
1004 }
1005
GetPopupConfig(CefWindowHandle temp_handle,CefWindowInfo & windowInfo,CefRefPtr<CefClient> & client,CefBrowserSettings & settings)1006 void BrowserWindowOsrGtk::GetPopupConfig(CefWindowHandle temp_handle,
1007 CefWindowInfo& windowInfo,
1008 CefRefPtr<CefClient>& client,
1009 CefBrowserSettings& settings) {
1010 CEF_REQUIRE_UI_THREAD();
1011
1012 windowInfo.SetAsWindowless(temp_handle);
1013 client = client_handler_;
1014 }
1015
ShowPopup(ClientWindowHandle parent_handle,int x,int y,size_t width,size_t height)1016 void BrowserWindowOsrGtk::ShowPopup(ClientWindowHandle parent_handle,
1017 int x,
1018 int y,
1019 size_t width,
1020 size_t height) {
1021 REQUIRE_MAIN_THREAD();
1022 DCHECK(browser_.get());
1023
1024 // Create the native window.
1025 Create(parent_handle);
1026
1027 // Send resize notification so the compositor is assigned the correct
1028 // viewport size and begins rendering.
1029 browser_->GetHost()->WasResized();
1030
1031 Show();
1032 }
1033
Show()1034 void BrowserWindowOsrGtk::Show() {
1035 REQUIRE_MAIN_THREAD();
1036
1037 if (hidden_) {
1038 // Set the browser as visible.
1039 browser_->GetHost()->WasHidden(false);
1040 hidden_ = false;
1041 }
1042
1043 // Give focus to the browser.
1044 browser_->GetHost()->SetFocus(true);
1045 }
1046
Hide()1047 void BrowserWindowOsrGtk::Hide() {
1048 REQUIRE_MAIN_THREAD();
1049
1050 if (!browser_)
1051 return;
1052
1053 // Remove focus from the browser.
1054 browser_->GetHost()->SetFocus(false);
1055
1056 if (!hidden_) {
1057 // Set the browser as hidden.
1058 browser_->GetHost()->WasHidden(true);
1059 hidden_ = true;
1060 }
1061 }
1062
SetBounds(int x,int y,size_t width,size_t height)1063 void BrowserWindowOsrGtk::SetBounds(int x, int y, size_t width, size_t height) {
1064 REQUIRE_MAIN_THREAD();
1065 // Nothing to do here. GTK will take care of positioning in the container.
1066 }
1067
SetFocus(bool focus)1068 void BrowserWindowOsrGtk::SetFocus(bool focus) {
1069 REQUIRE_MAIN_THREAD();
1070 if (glarea_ && focus) {
1071 gtk_widget_grab_focus(glarea_);
1072 }
1073 }
1074
SetDeviceScaleFactor(float device_scale_factor)1075 void BrowserWindowOsrGtk::SetDeviceScaleFactor(float device_scale_factor) {
1076 REQUIRE_MAIN_THREAD();
1077 {
1078 base::AutoLock lock_scope(lock_);
1079 if (device_scale_factor == device_scale_factor_)
1080 return;
1081
1082 // Apply some sanity checks.
1083 if (device_scale_factor < 1.0f || device_scale_factor > 4.0f)
1084 return;
1085
1086 device_scale_factor_ = device_scale_factor;
1087 }
1088
1089 if (browser_) {
1090 browser_->GetHost()->NotifyScreenInfoChanged();
1091 browser_->GetHost()->WasResized();
1092 }
1093 }
1094
GetDeviceScaleFactor() const1095 float BrowserWindowOsrGtk::GetDeviceScaleFactor() const {
1096 REQUIRE_MAIN_THREAD();
1097 base::AutoLock lock_scope(lock_);
1098 return device_scale_factor_;
1099 }
1100
GetWindowHandle() const1101 ClientWindowHandle BrowserWindowOsrGtk::GetWindowHandle() const {
1102 REQUIRE_MAIN_THREAD();
1103 return glarea_;
1104 }
1105
OnAfterCreated(CefRefPtr<CefBrowser> browser)1106 void BrowserWindowOsrGtk::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
1107 CEF_REQUIRE_UI_THREAD();
1108 }
1109
OnBeforeClose(CefRefPtr<CefBrowser> browser)1110 void BrowserWindowOsrGtk::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
1111 CEF_REQUIRE_UI_THREAD();
1112
1113 // Detach |this| from the ClientHandlerOsr.
1114 static_cast<ClientHandlerOsr*>(client_handler_.get())->DetachOsrDelegate();
1115
1116 ScopedGdkThreadsEnter scoped_gdk_threads;
1117
1118 UnregisterDragDrop();
1119
1120 // Disconnect all signal handlers that reference |this|.
1121 g_signal_handlers_disconnect_matched(glarea_, G_SIGNAL_MATCH_DATA, 0, 0,
1122 nullptr, nullptr, this);
1123
1124 DisableGL();
1125 }
1126
GetRootScreenRect(CefRefPtr<CefBrowser> browser,CefRect & rect)1127 bool BrowserWindowOsrGtk::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
1128 CefRect& rect) {
1129 CEF_REQUIRE_UI_THREAD();
1130 return false;
1131 }
1132
GetViewRect(CefRefPtr<CefBrowser> browser,CefRect & rect)1133 void BrowserWindowOsrGtk::GetViewRect(CefRefPtr<CefBrowser> browser,
1134 CefRect& rect) {
1135 CEF_REQUIRE_UI_THREAD();
1136
1137 rect.x = rect.y = 0;
1138
1139 if (!glarea_) {
1140 // Never return an empty rectangle.
1141 rect.width = rect.height = 1;
1142 return;
1143 }
1144
1145 float device_scale_factor;
1146 {
1147 base::AutoLock lock_scope(lock_);
1148 device_scale_factor = device_scale_factor_;
1149 }
1150
1151 // The simulated screen and view rectangle are the same. This is necessary
1152 // for popup menus to be located and sized inside the view.
1153 GtkAllocation allocation;
1154 gtk_widget_get_allocation(glarea_, &allocation);
1155 rect.width = DeviceToLogical(allocation.width, device_scale_factor);
1156 if (rect.width == 0)
1157 rect.width = 1;
1158 rect.height = DeviceToLogical(allocation.height, device_scale_factor);
1159 if (rect.height == 0)
1160 rect.height = 1;
1161 }
1162
GetScreenPoint(CefRefPtr<CefBrowser> browser,int viewX,int viewY,int & screenX,int & screenY)1163 bool BrowserWindowOsrGtk::GetScreenPoint(CefRefPtr<CefBrowser> browser,
1164 int viewX,
1165 int viewY,
1166 int& screenX,
1167 int& screenY) {
1168 CEF_REQUIRE_UI_THREAD();
1169
1170 float device_scale_factor;
1171 {
1172 base::AutoLock lock_scope(lock_);
1173 device_scale_factor = device_scale_factor_;
1174 }
1175
1176 GdkRectangle screen_rect;
1177 GetWidgetRectInScreen(glarea_, &screen_rect);
1178 screenX = screen_rect.x + LogicalToDevice(viewX, device_scale_factor);
1179 screenY = screen_rect.y + LogicalToDevice(viewY, device_scale_factor);
1180 return true;
1181 }
1182
GetScreenInfo(CefRefPtr<CefBrowser> browser,CefScreenInfo & screen_info)1183 bool BrowserWindowOsrGtk::GetScreenInfo(CefRefPtr<CefBrowser> browser,
1184 CefScreenInfo& screen_info) {
1185 CEF_REQUIRE_UI_THREAD();
1186
1187 CefRect view_rect;
1188 GetViewRect(browser, view_rect);
1189
1190 float device_scale_factor;
1191 {
1192 base::AutoLock lock_scope(lock_);
1193 device_scale_factor = device_scale_factor_;
1194 }
1195
1196 screen_info.device_scale_factor = device_scale_factor;
1197
1198 // The screen info rectangles are used by the renderer to create and position
1199 // popups. Keep popups inside the view rectangle.
1200 screen_info.rect = view_rect;
1201 screen_info.available_rect = view_rect;
1202 return true;
1203 }
1204
OnPopupShow(CefRefPtr<CefBrowser> browser,bool show)1205 void BrowserWindowOsrGtk::OnPopupShow(CefRefPtr<CefBrowser> browser,
1206 bool show) {
1207 CEF_REQUIRE_UI_THREAD();
1208
1209 if (!show) {
1210 renderer_.ClearPopupRects();
1211 browser->GetHost()->Invalidate(PET_VIEW);
1212 }
1213 renderer_.OnPopupShow(browser, show);
1214 }
1215
OnPopupSize(CefRefPtr<CefBrowser> browser,const CefRect & rect)1216 void BrowserWindowOsrGtk::OnPopupSize(CefRefPtr<CefBrowser> browser,
1217 const CefRect& rect) {
1218 CEF_REQUIRE_UI_THREAD();
1219
1220 float device_scale_factor;
1221 {
1222 base::AutoLock lock_scope(lock_);
1223 device_scale_factor = device_scale_factor_;
1224 }
1225
1226 renderer_.OnPopupSize(browser, LogicalToDevice(rect, device_scale_factor));
1227 }
1228
OnPaint(CefRefPtr<CefBrowser> browser,CefRenderHandler::PaintElementType type,const CefRenderHandler::RectList & dirtyRects,const void * buffer,int width,int height)1229 void BrowserWindowOsrGtk::OnPaint(CefRefPtr<CefBrowser> browser,
1230 CefRenderHandler::PaintElementType type,
1231 const CefRenderHandler::RectList& dirtyRects,
1232 const void* buffer,
1233 int width,
1234 int height) {
1235 CEF_REQUIRE_UI_THREAD();
1236
1237 if (width <= 2 && height <= 2) {
1238 // Ignore really small buffer sizes while the widget is starting up.
1239 return;
1240 }
1241
1242 if (painting_popup_) {
1243 renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
1244 return;
1245 }
1246
1247 if (!gl_enabled_)
1248 EnableGL();
1249
1250 ScopedGLContext scoped_gl_context(glarea_, true);
1251 if (!scoped_gl_context.IsValid())
1252 return;
1253
1254 renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
1255 if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
1256 painting_popup_ = true;
1257 browser->GetHost()->Invalidate(PET_POPUP);
1258 painting_popup_ = false;
1259 }
1260 renderer_.Render();
1261 }
1262
OnCursorChange(CefRefPtr<CefBrowser> browser,CefCursorHandle cursor,cef_cursor_type_t type,const CefCursorInfo & custom_cursor_info)1263 void BrowserWindowOsrGtk::OnCursorChange(
1264 CefRefPtr<CefBrowser> browser,
1265 CefCursorHandle cursor,
1266 cef_cursor_type_t type,
1267 const CefCursorInfo& custom_cursor_info) {
1268 CEF_REQUIRE_UI_THREAD();
1269
1270 // Retrieve the X11 display shared with Chromium.
1271 CHECK(xdisplay_ != 0);
1272
1273 ScopedGdkThreadsEnter scoped_gdk_threads;
1274
1275 // Retrieve the X11 window handle for the GTK widget.
1276 ::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(glarea_));
1277
1278 // Set the cursor.
1279 XDefineCursor(xdisplay_, xwindow, cursor);
1280 }
1281
StartDragging(CefRefPtr<CefBrowser> browser,CefRefPtr<CefDragData> drag_data,CefRenderHandler::DragOperationsMask allowed_ops,int x,int y)1282 bool BrowserWindowOsrGtk::StartDragging(
1283 CefRefPtr<CefBrowser> browser,
1284 CefRefPtr<CefDragData> drag_data,
1285 CefRenderHandler::DragOperationsMask allowed_ops,
1286 int x,
1287 int y) {
1288 CEF_REQUIRE_UI_THREAD();
1289
1290 if (!drag_data->HasImage()) {
1291 LOG(ERROR) << "Drag image representation not available";
1292 return false;
1293 }
1294
1295 ScopedGdkThreadsEnter scoped_gdk_threads;
1296
1297 DragReset();
1298 drag_data_ = drag_data;
1299
1300 // Begin drag.
1301 if (drag_trigger_event_) {
1302 LOG(ERROR) << "Dragging started, but last mouse event is missing";
1303 DragReset();
1304 return false;
1305 }
1306 drag_context_ = gtk_drag_begin(glarea_, drag_targets_, GDK_ACTION_COPY,
1307 1, // left mouse button
1308 drag_trigger_event_);
1309 if (!drag_context_) {
1310 LOG(ERROR) << "GTK drag begin failed";
1311 DragReset();
1312 return false;
1313 }
1314 g_object_ref(drag_context_);
1315
1316 // Send drag enter event.
1317 CefMouseEvent ev;
1318 ev.x = x;
1319 ev.y = y;
1320 ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
1321 browser->GetHost()->DragTargetDragEnter(drag_data, ev, allowed_ops);
1322
1323 return true;
1324 }
1325
UpdateDragCursor(CefRefPtr<CefBrowser> browser,CefRenderHandler::DragOperation operation)1326 void BrowserWindowOsrGtk::UpdateDragCursor(
1327 CefRefPtr<CefBrowser> browser,
1328 CefRenderHandler::DragOperation operation) {
1329 CEF_REQUIRE_UI_THREAD();
1330 drag_operation_ = operation;
1331 }
1332
OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,const CefRange & selection_range,const CefRenderHandler::RectList & character_bounds)1333 void BrowserWindowOsrGtk::OnImeCompositionRangeChanged(
1334 CefRefPtr<CefBrowser> browser,
1335 const CefRange& selection_range,
1336 const CefRenderHandler::RectList& character_bounds) {
1337 CEF_REQUIRE_UI_THREAD();
1338 }
1339
UpdateAccessibilityTree(CefRefPtr<CefValue> value)1340 void BrowserWindowOsrGtk::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
1341 CEF_REQUIRE_UI_THREAD();
1342 }
1343
UpdateAccessibilityLocation(CefRefPtr<CefValue> value)1344 void BrowserWindowOsrGtk::UpdateAccessibilityLocation(
1345 CefRefPtr<CefValue> value) {
1346 CEF_REQUIRE_UI_THREAD();
1347 }
1348
Create(ClientWindowHandle parent_handle)1349 void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
1350 REQUIRE_MAIN_THREAD();
1351 DCHECK(!glarea_);
1352
1353 ScopedGdkThreadsEnter scoped_gdk_threads;
1354
1355 glarea_ = gtk_gl_area_new();
1356 DCHECK(glarea_);
1357
1358 gtk_widget_set_can_focus(glarea_, TRUE);
1359
1360 gtk_gl_area_set_auto_render(GTK_GL_AREA(glarea_), FALSE);
1361
1362 g_signal_connect(G_OBJECT(glarea_), "size_allocate",
1363 G_CALLBACK(&BrowserWindowOsrGtk::SizeAllocation), this);
1364
1365 gtk_widget_set_events(
1366 glarea_, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1367 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
1368 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
1369 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1370 GDK_SCROLL_MASK | GDK_FOCUS_CHANGE_MASK);
1371 g_signal_connect(G_OBJECT(glarea_), "button_press_event",
1372 G_CALLBACK(&BrowserWindowOsrGtk::ClickEvent), this);
1373 g_signal_connect(G_OBJECT(glarea_), "button_release_event",
1374 G_CALLBACK(&BrowserWindowOsrGtk::ClickEvent), this);
1375 g_signal_connect(G_OBJECT(glarea_), "key_press_event",
1376 G_CALLBACK(&BrowserWindowOsrGtk::KeyEvent), this);
1377 g_signal_connect(G_OBJECT(glarea_), "key_release_event",
1378 G_CALLBACK(&BrowserWindowOsrGtk::KeyEvent), this);
1379 g_signal_connect(G_OBJECT(glarea_), "enter_notify_event",
1380 G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
1381 g_signal_connect(G_OBJECT(glarea_), "leave_notify_event",
1382 G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
1383 g_signal_connect(G_OBJECT(glarea_), "motion_notify_event",
1384 G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
1385 g_signal_connect(G_OBJECT(glarea_), "scroll_event",
1386 G_CALLBACK(&BrowserWindowOsrGtk::ScrollEvent), this);
1387 g_signal_connect(G_OBJECT(glarea_), "focus_in_event",
1388 G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
1389 g_signal_connect(G_OBJECT(glarea_), "focus_out_event",
1390 G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
1391 g_signal_connect(G_OBJECT(glarea_), "touch-event",
1392 G_CALLBACK(&BrowserWindowOsrGtk::TouchEvent), this);
1393
1394 RegisterDragDrop();
1395
1396 gtk_widget_set_vexpand(glarea_, TRUE);
1397 gtk_grid_attach(GTK_GRID(parent_handle), glarea_, 0, 3, 1, 1);
1398
1399 // Make the GlArea visible in the parent container.
1400 gtk_widget_show_all(parent_handle);
1401 }
1402
1403 // static
SizeAllocation(GtkWidget * widget,GtkAllocation * allocation,BrowserWindowOsrGtk * self)1404 gint BrowserWindowOsrGtk::SizeAllocation(GtkWidget* widget,
1405 GtkAllocation* allocation,
1406 BrowserWindowOsrGtk* self) {
1407 REQUIRE_MAIN_THREAD();
1408 if (self->browser_.get()) {
1409 // Results in a call to GetViewRect().
1410 self->browser_->GetHost()->WasResized();
1411 }
1412 return TRUE;
1413 }
1414
1415 // static
ClickEvent(GtkWidget * widget,GdkEventButton * event,BrowserWindowOsrGtk * self)1416 gint BrowserWindowOsrGtk::ClickEvent(GtkWidget* widget,
1417 GdkEventButton* event,
1418 BrowserWindowOsrGtk* self) {
1419 REQUIRE_MAIN_THREAD();
1420
1421 if (!self->browser_.get())
1422 return TRUE;
1423
1424 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1425
1426 CefBrowserHost::MouseButtonType button_type = MBT_LEFT;
1427 switch (event->button) {
1428 case 1:
1429 break;
1430 case 2:
1431 button_type = MBT_MIDDLE;
1432 break;
1433 case 3:
1434 button_type = MBT_RIGHT;
1435 break;
1436 default:
1437 // Other mouse buttons are not handled here.
1438 return FALSE;
1439 }
1440
1441 float device_scale_factor;
1442 {
1443 base::AutoLock lock_scope(self->lock_);
1444 device_scale_factor = self->device_scale_factor_;
1445 }
1446
1447 CefMouseEvent mouse_event;
1448 mouse_event.x = event->x;
1449 mouse_event.y = event->y;
1450 self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
1451 DeviceToLogical(mouse_event, device_scale_factor);
1452 mouse_event.modifiers = GetCefStateModifiers(event->state);
1453
1454 bool mouse_up = (event->type == GDK_BUTTON_RELEASE);
1455 if (!mouse_up) {
1456 gtk_widget_grab_focus(widget);
1457 }
1458
1459 int click_count = 1;
1460 switch (event->type) {
1461 case GDK_2BUTTON_PRESS:
1462 click_count = 2;
1463 break;
1464 case GDK_3BUTTON_PRESS:
1465 click_count = 3;
1466 break;
1467 default:
1468 break;
1469 }
1470
1471 host->SendMouseClickEvent(mouse_event, button_type, mouse_up, click_count);
1472
1473 // Save mouse event that can be a possible trigger for drag.
1474 if (!self->drag_context_ && button_type == MBT_LEFT) {
1475 if (self->drag_trigger_event_) {
1476 gdk_event_free(self->drag_trigger_event_);
1477 }
1478 self->drag_trigger_event_ =
1479 gdk_event_copy(reinterpret_cast<GdkEvent*>(event));
1480 }
1481
1482 return TRUE;
1483 }
1484
1485 // static
KeyEvent(GtkWidget * widget,GdkEventKey * event,BrowserWindowOsrGtk * self)1486 gint BrowserWindowOsrGtk::KeyEvent(GtkWidget* widget,
1487 GdkEventKey* event,
1488 BrowserWindowOsrGtk* self) {
1489 REQUIRE_MAIN_THREAD();
1490
1491 if (!self->browser_.get())
1492 return TRUE;
1493
1494 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1495
1496 // Based on WebKeyboardEventBuilder::Build from
1497 // content/browser/renderer_host/input/web_input_event_builders_gtk.cc.
1498 CefKeyEvent key_event;
1499 KeyboardCode windows_key_code = GdkEventToWindowsKeyCode(event);
1500 key_event.windows_key_code =
1501 GetWindowsKeyCodeWithoutLocation(windows_key_code);
1502 key_event.native_key_code = event->hardware_keycode;
1503
1504 key_event.modifiers = GetCefStateModifiers(event->state);
1505 if (event->keyval >= GDK_KP_Space && event->keyval <= GDK_KP_9)
1506 key_event.modifiers |= EVENTFLAG_IS_KEY_PAD;
1507 if (key_event.modifiers & EVENTFLAG_ALT_DOWN)
1508 key_event.is_system_key = true;
1509
1510 if (windows_key_code == VKEY_RETURN) {
1511 // We need to treat the enter key as a key press of character \r. This
1512 // is apparently just how webkit handles it and what it expects.
1513 key_event.unmodified_character = '\r';
1514 } else {
1515 // FIXME: fix for non BMP chars
1516 key_event.unmodified_character =
1517 static_cast<int>(gdk_keyval_to_unicode(event->keyval));
1518 }
1519
1520 // If ctrl key is pressed down, then control character shall be input.
1521 if (key_event.modifiers & EVENTFLAG_CONTROL_DOWN) {
1522 key_event.character = GetControlCharacter(
1523 windows_key_code, key_event.modifiers & EVENTFLAG_SHIFT_DOWN);
1524 } else {
1525 key_event.character = key_event.unmodified_character;
1526 }
1527
1528 if (event->type == GDK_KEY_PRESS) {
1529 key_event.type = KEYEVENT_RAWKEYDOWN;
1530 host->SendKeyEvent(key_event);
1531 key_event.type = KEYEVENT_CHAR;
1532 host->SendKeyEvent(key_event);
1533 } else {
1534 key_event.type = KEYEVENT_KEYUP;
1535 host->SendKeyEvent(key_event);
1536 }
1537
1538 return TRUE;
1539 }
1540
1541 // static
MoveEvent(GtkWidget * widget,GdkEventMotion * event,BrowserWindowOsrGtk * self)1542 gint BrowserWindowOsrGtk::MoveEvent(GtkWidget* widget,
1543 GdkEventMotion* event,
1544 BrowserWindowOsrGtk* self) {
1545 if (!self->browser_.get())
1546 return TRUE;
1547
1548 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1549
1550 gint x, y;
1551 GdkModifierType state;
1552
1553 if (event->is_hint) {
1554 gdk_window_get_pointer(event->window, &x, &y, &state);
1555 } else {
1556 x = (gint)event->x;
1557 y = (gint)event->y;
1558 state = (GdkModifierType)event->state;
1559 if (x == 0 && y == 0) {
1560 // Invalid coordinates of (0,0) appear from time to time in
1561 // enter-notify-event and leave-notify-event events. Sending them may
1562 // cause StartDragging to never get called, so just ignore these.
1563 return TRUE;
1564 }
1565 }
1566
1567 float device_scale_factor;
1568 {
1569 base::AutoLock lock_scope(self->lock_);
1570 device_scale_factor = self->device_scale_factor_;
1571 }
1572
1573 CefMouseEvent mouse_event;
1574 mouse_event.x = x;
1575 mouse_event.y = y;
1576 self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
1577 DeviceToLogical(mouse_event, device_scale_factor);
1578 mouse_event.modifiers = GetCefStateModifiers(state);
1579
1580 bool mouse_leave = (event->type == GDK_LEAVE_NOTIFY);
1581 host->SendMouseMoveEvent(mouse_event, mouse_leave);
1582
1583 // Save mouse event that can be a possible trigger for drag.
1584 if (!self->drag_context_ &&
1585 (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)) {
1586 if (self->drag_trigger_event_) {
1587 gdk_event_free(self->drag_trigger_event_);
1588 }
1589 self->drag_trigger_event_ =
1590 gdk_event_copy(reinterpret_cast<GdkEvent*>(event));
1591 }
1592
1593 return TRUE;
1594 }
1595
1596 // static
ScrollEvent(GtkWidget * widget,GdkEventScroll * event,BrowserWindowOsrGtk * self)1597 gint BrowserWindowOsrGtk::ScrollEvent(GtkWidget* widget,
1598 GdkEventScroll* event,
1599 BrowserWindowOsrGtk* self) {
1600 REQUIRE_MAIN_THREAD();
1601
1602 if (!self->browser_.get())
1603 return TRUE;
1604
1605 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1606
1607 float device_scale_factor;
1608 {
1609 base::AutoLock lock_scope(self->lock_);
1610 device_scale_factor = self->device_scale_factor_;
1611 }
1612
1613 CefMouseEvent mouse_event;
1614 mouse_event.x = event->x;
1615 mouse_event.y = event->y;
1616 self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
1617 DeviceToLogical(mouse_event, device_scale_factor);
1618 mouse_event.modifiers = GetCefStateModifiers(event->state);
1619
1620 static const int scrollbarPixelsPerGtkTick = 40;
1621 int deltaX = 0;
1622 int deltaY = 0;
1623 switch (event->direction) {
1624 case GDK_SCROLL_UP:
1625 deltaY = scrollbarPixelsPerGtkTick;
1626 break;
1627 case GDK_SCROLL_DOWN:
1628 deltaY = -scrollbarPixelsPerGtkTick;
1629 break;
1630 case GDK_SCROLL_LEFT:
1631 deltaX = scrollbarPixelsPerGtkTick;
1632 break;
1633 case GDK_SCROLL_RIGHT:
1634 deltaX = -scrollbarPixelsPerGtkTick;
1635 break;
1636 case GDK_SCROLL_SMOOTH:
1637 NOTIMPLEMENTED();
1638 break;
1639 }
1640
1641 host->SendMouseWheelEvent(mouse_event, deltaX, deltaY);
1642 return TRUE;
1643 }
1644
1645 // static
FocusEvent(GtkWidget * widget,GdkEventFocus * event,BrowserWindowOsrGtk * self)1646 gint BrowserWindowOsrGtk::FocusEvent(GtkWidget* widget,
1647 GdkEventFocus* event,
1648 BrowserWindowOsrGtk* self) {
1649 // May be called on the main thread and the UI thread.
1650 if (self->browser_.get())
1651 self->browser_->GetHost()->SetFocus(event->in == TRUE);
1652 return TRUE;
1653 }
1654
1655 // static
TouchEvent(GtkWidget * widget,GdkEventTouch * event,BrowserWindowOsrGtk * self)1656 gboolean BrowserWindowOsrGtk::TouchEvent(GtkWidget* widget,
1657 GdkEventTouch* event,
1658 BrowserWindowOsrGtk* self) {
1659 REQUIRE_MAIN_THREAD();
1660
1661 if (!self->browser_.get())
1662 return TRUE;
1663
1664 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1665
1666 float device_scale_factor;
1667 {
1668 base::AutoLock lock_scope(self->lock_);
1669 device_scale_factor = self->device_scale_factor_;
1670 }
1671
1672 CefTouchEvent touch_event;
1673 switch (event->type) {
1674 case GDK_TOUCH_BEGIN:
1675 touch_event.type = CEF_TET_PRESSED;
1676 break;
1677 case GDK_TOUCH_UPDATE:
1678 touch_event.type = CEF_TET_MOVED;
1679 break;
1680 case GDK_TOUCH_END:
1681 touch_event.type = CEF_TET_RELEASED;
1682 break;
1683 default:
1684 return TRUE;
1685 }
1686
1687 touch_event.x = event->x;
1688 touch_event.y = event->y;
1689 touch_event.radius_x = 0;
1690 touch_event.radius_y = 0;
1691 touch_event.rotation_angle = 0;
1692 touch_event.pressure = 0;
1693 DeviceToLogical(touch_event, device_scale_factor);
1694 touch_event.modifiers = GetCefStateModifiers(event->state);
1695
1696 host->SendTouchEvent(touch_event);
1697 return TRUE;
1698 }
1699
IsOverPopupWidget(int x,int y) const1700 bool BrowserWindowOsrGtk::IsOverPopupWidget(int x, int y) const {
1701 const CefRect& rc = renderer_.popup_rect();
1702 int popup_right = rc.x + rc.width;
1703 int popup_bottom = rc.y + rc.height;
1704 return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom);
1705 }
1706
GetPopupXOffset() const1707 int BrowserWindowOsrGtk::GetPopupXOffset() const {
1708 return renderer_.original_popup_rect().x - renderer_.popup_rect().x;
1709 }
1710
GetPopupYOffset() const1711 int BrowserWindowOsrGtk::GetPopupYOffset() const {
1712 return renderer_.original_popup_rect().y - renderer_.popup_rect().y;
1713 }
1714
ApplyPopupOffset(int & x,int & y) const1715 void BrowserWindowOsrGtk::ApplyPopupOffset(int& x, int& y) const {
1716 if (IsOverPopupWidget(x, y)) {
1717 x += GetPopupXOffset();
1718 y += GetPopupYOffset();
1719 }
1720 }
1721
EnableGL()1722 void BrowserWindowOsrGtk::EnableGL() {
1723 CEF_REQUIRE_UI_THREAD();
1724
1725 if (gl_enabled_)
1726 return;
1727
1728 ScopedGLContext scoped_gl_context(glarea_, false);
1729 if (!scoped_gl_context.IsValid())
1730 return;
1731
1732 renderer_.Initialize();
1733
1734 gl_enabled_ = true;
1735 }
1736
DisableGL()1737 void BrowserWindowOsrGtk::DisableGL() {
1738 CEF_REQUIRE_UI_THREAD();
1739
1740 if (!gl_enabled_)
1741 return;
1742
1743 ScopedGLContext scoped_gl_context(glarea_, false);
1744 if (!scoped_gl_context.IsValid())
1745 return;
1746
1747 renderer_.Cleanup();
1748
1749 gl_enabled_ = false;
1750 }
1751
RegisterDragDrop()1752 void BrowserWindowOsrGtk::RegisterDragDrop() {
1753 REQUIRE_MAIN_THREAD();
1754
1755 ScopedGdkThreadsEnter scoped_gdk_threads;
1756
1757 // Succession of CEF d&d calls:
1758 // 1. DragTargetDragEnter
1759 // 2. DragTargetDragOver
1760 // 3. DragTargetDragLeave - optional
1761 // 4. DragSourceSystemDragEnded - optional, to cancel dragging
1762 // 5. DragTargetDrop
1763 // 6. DragSourceEndedAt
1764 // 7. DragSourceSystemDragEnded
1765
1766 // Succession of GTK d&d events:
1767 // 1. drag-begin-event, drag-data-get
1768 // 2. drag-motion
1769 // 3. drag-leave
1770 // 4. drag-failed
1771 // 5. drag-drop, drag-data-received
1772 // 6. 7. drag-end-event
1773
1774 // Using gtk_drag_begin in StartDragging instead of calling
1775 // gtk_drag_source_set here. Doing so because when using gtk_drag_source_set
1776 // then StartDragging is being called very late, about ten DragMotion events
1777 // after DragBegin, and drag icon can be set only when beginning drag.
1778 // Default values for drag threshold are set to 8 pixels in both GTK and
1779 // Chromium, but doesn't work as expected.
1780 // --OFF--
1781 // gtk_drag_source_set(glarea_, GDK_BUTTON1_MASK, nullptr, 0,
1782 // GDK_ACTION_COPY);
1783
1784 // Source widget events.
1785 g_signal_connect(G_OBJECT(glarea_), "drag_begin",
1786 G_CALLBACK(&BrowserWindowOsrGtk::DragBegin), this);
1787 g_signal_connect(G_OBJECT(glarea_), "drag_data_get",
1788 G_CALLBACK(&BrowserWindowOsrGtk::DragDataGet), this);
1789 g_signal_connect(G_OBJECT(glarea_), "drag_end",
1790 G_CALLBACK(&BrowserWindowOsrGtk::DragEnd), this);
1791
1792 // Destination widget and its events.
1793 gtk_drag_dest_set(glarea_, (GtkDestDefaults)0, (GtkTargetEntry*)nullptr, 0,
1794 (GdkDragAction)GDK_ACTION_COPY);
1795 g_signal_connect(G_OBJECT(glarea_), "drag_motion",
1796 G_CALLBACK(&BrowserWindowOsrGtk::DragMotion), this);
1797 g_signal_connect(G_OBJECT(glarea_), "drag_leave",
1798 G_CALLBACK(&BrowserWindowOsrGtk::DragLeave), this);
1799 g_signal_connect(G_OBJECT(glarea_), "drag_failed",
1800 G_CALLBACK(&BrowserWindowOsrGtk::DragFailed), this);
1801 g_signal_connect(G_OBJECT(glarea_), "drag_drop",
1802 G_CALLBACK(&BrowserWindowOsrGtk::DragDrop), this);
1803 g_signal_connect(G_OBJECT(glarea_), "drag_data_received",
1804 G_CALLBACK(&BrowserWindowOsrGtk::DragDataReceived), this);
1805 }
1806
UnregisterDragDrop()1807 void BrowserWindowOsrGtk::UnregisterDragDrop() {
1808 ScopedGdkThreadsEnter scoped_gdk_threads;
1809 gtk_drag_dest_unset(glarea_);
1810 // Drag events are unregistered in OnBeforeClose by calling
1811 // g_signal_handlers_disconnect_matched.
1812 }
1813
DragReset()1814 void BrowserWindowOsrGtk::DragReset() {
1815 if (drag_trigger_event_) {
1816 gdk_event_free(drag_trigger_event_);
1817 drag_trigger_event_ = nullptr;
1818 }
1819 drag_data_ = nullptr;
1820 drag_operation_ = DRAG_OPERATION_NONE;
1821 if (drag_context_) {
1822 g_object_unref(drag_context_);
1823 drag_context_ = nullptr;
1824 }
1825 drag_leave_ = false;
1826 drag_drop_ = false;
1827 }
1828
1829 // static
DragBegin(GtkWidget * widget,GdkDragContext * drag_context,BrowserWindowOsrGtk * self)1830 void BrowserWindowOsrGtk::DragBegin(GtkWidget* widget,
1831 GdkDragContext* drag_context,
1832 BrowserWindowOsrGtk* self) {
1833 // Load drag icon.
1834 if (!self->drag_data_->HasImage()) {
1835 LOG(ERROR) << "Failed to set drag icon, drag image not available";
1836 return;
1837 }
1838
1839 float device_scale_factor;
1840 {
1841 base::AutoLock lock_scope(self->lock_);
1842 device_scale_factor = self->device_scale_factor_;
1843 }
1844
1845 int pixel_width = 0;
1846 int pixel_height = 0;
1847 CefRefPtr<CefBinaryValue> image_binary =
1848 self->drag_data_->GetImage()->GetAsPNG(device_scale_factor, true,
1849 pixel_width, pixel_height);
1850 if (!image_binary) {
1851 LOG(ERROR) << "Failed to set drag icon, drag image error";
1852 return;
1853 }
1854
1855 ScopedGdkThreadsEnter scoped_gdk_threads;
1856
1857 size_t image_size = image_binary->GetSize();
1858 guint8* image_buffer = (guint8*)malloc(image_size); // must free
1859 image_binary->GetData((void*)image_buffer, image_size, 0);
1860 GdkPixbufLoader* loader = nullptr; // must unref
1861 GError* error = nullptr; // must free
1862 GdkPixbuf* pixbuf = nullptr; // owned by loader
1863 gboolean success = FALSE;
1864 loader = gdk_pixbuf_loader_new_with_type("png", &error);
1865 if (error == nullptr && loader) {
1866 success =
1867 gdk_pixbuf_loader_write(loader, image_buffer, image_size, nullptr);
1868 if (success) {
1869 success = gdk_pixbuf_loader_close(loader, nullptr);
1870 if (success) {
1871 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1872 if (pixbuf) {
1873 CefPoint image_hotspot = self->drag_data_->GetImageHotspot();
1874 int hotspot_x = image_hotspot.x;
1875 int hotspot_y = image_hotspot.y;
1876 gtk_drag_set_icon_pixbuf(drag_context, pixbuf, hotspot_x, hotspot_y);
1877 } else {
1878 LOG(ERROR) << "Failed to set drag icon, pixbuf error";
1879 }
1880 } else {
1881 LOG(ERROR) << "Failed to set drag icon, loader close error";
1882 }
1883 } else {
1884 LOG(ERROR) << "Failed to set drag icon, loader write error";
1885 }
1886 } else {
1887 LOG(ERROR) << "Failed to set drag icon, loader creation error";
1888 }
1889 if (loader) {
1890 g_object_unref(loader); // unref
1891 }
1892 if (error) {
1893 g_error_free(error); // free
1894 }
1895 free(image_buffer); // free
1896 }
1897
1898 // static
DragDataGet(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * data,guint info,guint time,BrowserWindowOsrGtk * self)1899 void BrowserWindowOsrGtk::DragDataGet(GtkWidget* widget,
1900 GdkDragContext* drag_context,
1901 GtkSelectionData* data,
1902 guint info,
1903 guint time,
1904 BrowserWindowOsrGtk* self) {
1905 REQUIRE_MAIN_THREAD();
1906 // No drag targets are set so this callback is never called.
1907 }
1908
1909 // static
DragEnd(GtkWidget * widget,GdkDragContext * drag_context,BrowserWindowOsrGtk * self)1910 void BrowserWindowOsrGtk::DragEnd(GtkWidget* widget,
1911 GdkDragContext* drag_context,
1912 BrowserWindowOsrGtk* self) {
1913 REQUIRE_MAIN_THREAD();
1914
1915 if (self->browser_) {
1916 // Sometimes there is DragEnd event generated without prior DragDrop.
1917 // Maybe related to drag-leave bug described in comments in DragLeave.
1918 if (!self->drag_drop_) {
1919 // Real coordinates not available.
1920 self->browser_->GetHost()->DragSourceEndedAt(-1, -1,
1921 self->drag_operation_);
1922 }
1923 self->browser_->GetHost()->DragSourceSystemDragEnded();
1924 }
1925
1926 self->DragReset();
1927 }
1928
1929 // static
DragMotion(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,guint time,BrowserWindowOsrGtk * self)1930 gboolean BrowserWindowOsrGtk::DragMotion(GtkWidget* widget,
1931 GdkDragContext* drag_context,
1932 gint x,
1933 gint y,
1934 guint time,
1935 BrowserWindowOsrGtk* self) {
1936 REQUIRE_MAIN_THREAD();
1937
1938 float device_scale_factor;
1939 {
1940 base::AutoLock lock_scope(self->lock_);
1941 device_scale_factor = self->device_scale_factor_;
1942 }
1943
1944 // MoveEvent is never called during drag & drop, so must call
1945 // SendMouseMoveEvent here.
1946 CefMouseEvent mouse_event;
1947 mouse_event.x = x;
1948 mouse_event.y = y;
1949 mouse_event.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
1950 self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
1951 DeviceToLogical(mouse_event, device_scale_factor);
1952 if (self->browser_) {
1953 bool mouse_leave = self->drag_leave_;
1954 self->browser_->GetHost()->SendMouseMoveEvent(mouse_event, mouse_leave);
1955 }
1956
1957 // Mouse event.
1958 CefMouseEvent ev;
1959 ev.x = x;
1960 ev.y = y;
1961 ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
1962
1963 CefBrowserHost::DragOperationsMask allowed_ops =
1964 GetDragOperationsMask(drag_context);
1965
1966 // Send drag enter event if needed.
1967 if (self->drag_leave_ && self->browser_) {
1968 self->browser_->GetHost()->DragTargetDragEnter(self->drag_data_, ev,
1969 allowed_ops);
1970 }
1971
1972 // Send drag over event.
1973 if (self->browser_) {
1974 self->browser_->GetHost()->DragTargetDragOver(ev, allowed_ops);
1975 }
1976
1977 // Update GTK drag status.
1978 if (widget == self->glarea_) {
1979 gdk_drag_status(drag_context, GDK_ACTION_COPY, time);
1980 if (self->drag_leave_) {
1981 self->drag_leave_ = false;
1982 }
1983 return TRUE;
1984 } else {
1985 LOG(WARNING) << "Invalid drag destination widget";
1986 gdk_drag_status(drag_context, (GdkDragAction)0, time);
1987 return FALSE;
1988 }
1989 }
1990
1991 // static
DragLeave(GtkWidget * widget,GdkDragContext * drag_context,guint time,BrowserWindowOsrGtk * self)1992 void BrowserWindowOsrGtk::DragLeave(GtkWidget* widget,
1993 GdkDragContext* drag_context,
1994 guint time,
1995 BrowserWindowOsrGtk* self) {
1996 REQUIRE_MAIN_THREAD();
1997
1998 // There is no drag-enter event in GTK. The first drag-motion event
1999 // after drag-leave will be a drag-enter event.
2000
2001 // There seems to be a bug during GTK drop, drag-leave event is generated
2002 // just before drag-drop. A solution is to call DragTargetDragEnter
2003 // and DragTargetDragOver in DragDrop when drag_leave_ is true.
2004
2005 // Send drag leave event.
2006 if (self->browser_) {
2007 self->browser_->GetHost()->DragTargetDragLeave();
2008 }
2009
2010 self->drag_leave_ = true;
2011 }
2012
2013 // static
DragFailed(GtkWidget * widget,GdkDragContext * drag_context,GtkDragResult result,BrowserWindowOsrGtk * self)2014 gboolean BrowserWindowOsrGtk::DragFailed(GtkWidget* widget,
2015 GdkDragContext* drag_context,
2016 GtkDragResult result,
2017 BrowserWindowOsrGtk* self) {
2018 REQUIRE_MAIN_THREAD();
2019
2020 // Send drag end coordinates and system drag ended event.
2021 if (self->browser_) {
2022 // Real coordinates not available.
2023 self->browser_->GetHost()->DragSourceEndedAt(-1, -1, self->drag_operation_);
2024 self->browser_->GetHost()->DragSourceSystemDragEnded();
2025 }
2026
2027 self->DragReset();
2028 return TRUE;
2029 }
2030
2031 // static
DragDrop(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,guint time,BrowserWindowOsrGtk * self)2032 gboolean BrowserWindowOsrGtk::DragDrop(GtkWidget* widget,
2033 GdkDragContext* drag_context,
2034 gint x,
2035 gint y,
2036 guint time,
2037 BrowserWindowOsrGtk* self) {
2038 REQUIRE_MAIN_THREAD();
2039
2040 // Finish GTK drag.
2041 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2042
2043 // Mouse event.
2044 CefMouseEvent ev;
2045 ev.x = x;
2046 ev.y = y;
2047 ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
2048
2049 CefBrowserHost::DragOperationsMask allowed_ops =
2050 GetDragOperationsMask(drag_context);
2051
2052 // Send drag enter/over events if needed (read comment in DragLeave).
2053 if (self->drag_leave_ && self->browser_) {
2054 self->browser_->GetHost()->DragTargetDragEnter(self->drag_data_, ev,
2055 allowed_ops);
2056 self->browser_->GetHost()->DragTargetDragOver(ev, allowed_ops);
2057 }
2058
2059 // Send drag drop event.
2060 if (self->browser_) {
2061 self->browser_->GetHost()->DragTargetDrop(ev);
2062 }
2063
2064 // Send drag end coordinates.
2065 if (self->browser_) {
2066 self->browser_->GetHost()->DragSourceEndedAt(x, y, self->drag_operation_);
2067 }
2068
2069 self->drag_drop_ = true;
2070 return TRUE;
2071 }
2072
2073 // static
DragDataReceived(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint time,BrowserWindowOsrGtk * self)2074 void BrowserWindowOsrGtk::DragDataReceived(GtkWidget* widget,
2075 GdkDragContext* drag_context,
2076 gint x,
2077 gint y,
2078 GtkSelectionData* data,
2079 guint info,
2080 guint time,
2081 BrowserWindowOsrGtk* self) {
2082 REQUIRE_MAIN_THREAD();
2083 // This callback is never called because DragDrop does not call
2084 // gtk_drag_get_data, as only dragging inside web view is supported.
2085 }
2086
2087 } // namespace client
2088