• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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