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