• 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 "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