- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / render_widget_host_view_gtk.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/render_widget_host_view_gtk.h"
6
7 #include <cairo/cairo.h>
8 #include <gdk/gdk.h>
9 #include <gdk/gdkkeysyms.h>
10 #include <gdk/gdkx.h>
11 #include <gtk/gtk.h>
12
13 #include <algorithm>
14 #include <string>
15
16 #include "base/bind_helpers.h"
17 #include "base/command_line.h"
18 #include "base/debug/trace_event.h"
19 #include "base/logging.h"
20 #include "base/message_loop/message_loop.h"
21 #include "base/metrics/histogram.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/utf_offset_string_conversions.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/time/time.h"
26 #include "content/browser/accessibility/browser_accessibility_gtk.h"
27 #include "content/browser/accessibility/browser_accessibility_manager_gtk.h"
28 #include "content/browser/renderer_host/backing_store_gtk.h"
29 #include "content/browser/renderer_host/gtk_im_context_wrapper.h"
30 #include "content/browser/renderer_host/gtk_key_bindings_handler.h"
31 #include "content/browser/renderer_host/gtk_window_utils.h"
32 #include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
33 #include "content/browser/renderer_host/render_view_host_delegate.h"
34 #include "content/browser/renderer_host/render_view_host_impl.h"
35 #include "content/common/gpu/gpu_messages.h"
36 #include "content/common/input_messages.h"
37 #include "content/common/view_messages.h"
38 #include "content/common/webplugin_geometry.h"
39 #include "content/public/browser/native_web_keyboard_event.h"
40 #include "content/public/common/content_switches.h"
41 #include "skia/ext/platform_canvas.h"
42 #include "third_party/WebKit/public/web/WebInputEvent.h"
43 #include "third_party/WebKit/public/web/WebScreenInfo.h"
44 #include "ui/base/clipboard/scoped_clipboard_writer.h"
45 #include "ui/base/x/active_window_watcher_x.h"
46 #include "ui/base/x/x11_util.h"
47 #include "ui/gfx/gtk_compat.h"
48 #include "ui/gfx/gtk_native_view_id_manager.h"
49 #include "ui/gfx/gtk_preserve_window.h"
50 #include "ui/gfx/text_elider.h"
51 #include "webkit/common/cursors/webcursor_gtk_data.h"
52
53 using WebKit::WebMouseWheelEvent;
54 using WebKit::WebScreenInfo;
55
56 namespace content {
57 namespace {
58
59 // Paint rects on Linux are bounded by the maximum size of a shared memory
60 // region. By default that's 32MB, but many distros increase it significantly
61 // (i.e. to 256MB).
62 //
63 // We fetch the maximum value from /proc/sys/kernel/shmmax at runtime and, if
64 // we exceed that, then we limit the height of the paint rect in the renderer.
65 //
66 // These constants are here to ensure that, in the event that we exceed it, we
67 // end up with something a little more square. Previously we had 4000x4000, but
68 // people's monitor setups are actually exceeding that these days.
69 const int kMaxWindowWidth = 10000;
70 const int kMaxWindowHeight = 10000;
71
72 const GdkColor kBGColor =
73 #if defined(NDEBUG)
74     { 0, 0xff * 257, 0xff * 257, 0xff * 257 };
75 #else
76     { 0, 0x00 * 257, 0xff * 257, 0x00 * 257 };
77 #endif
78
79 // Returns the spinning cursor used for loading state.
80 GdkCursor* GetMozSpinningCursor() {
81   static GdkCursor* moz_spinning_cursor = NULL;
82   if (!moz_spinning_cursor) {
83     const GdkColor fg = { 0, 0, 0, 0 };
84     const GdkColor bg = { 65535, 65535, 65535, 65535 };
85     GdkPixmap* source = gdk_bitmap_create_from_data(
86         NULL, reinterpret_cast<const gchar*>(moz_spinning_bits), 32, 32);
87     GdkPixmap* mask = gdk_bitmap_create_from_data(
88         NULL, reinterpret_cast<const gchar*>(moz_spinning_mask_bits), 32, 32);
89     moz_spinning_cursor =
90         gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 2, 2);
91     g_object_unref(source);
92     g_object_unref(mask);
93   }
94   return moz_spinning_cursor;
95 }
96
97 bool MovedToPoint(const WebKit::WebMouseEvent& mouse_event,
98                    const gfx::Point& center) {
99   return mouse_event.globalX == center.x() &&
100          mouse_event.globalY == center.y();
101 }
102
103 }  // namespace
104
105 // This class is a simple convenience wrapper for Gtk functions. It has only
106 // static methods.
107 class RenderWidgetHostViewGtkWidget {
108  public:
109   static AtkObject* GetAccessible(void* userdata) {
110     return (static_cast<RenderWidgetHostViewGtk*>(userdata))->
111         GetAccessible();
112   }
113
114   static GtkWidget* CreateNewWidget(RenderWidgetHostViewGtk* host_view) {
115     GtkWidget* widget = gtk_preserve_window_new();
116     gtk_widget_set_name(widget, "chrome-render-widget-host-view");
117     // We manually double-buffer in Paint() because Paint() may or may not be
118     // called in repsonse to an "expose-event" signal.
119     gtk_widget_set_double_buffered(widget, FALSE);
120     gtk_widget_set_redraw_on_allocate(widget, FALSE);
121     gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &kBGColor);
122     // Allow the browser window to be resized freely.
123     gtk_widget_set_size_request(widget, 0, 0);
124
125     gtk_widget_add_events(widget, GDK_EXPOSURE_MASK |
126                                   GDK_STRUCTURE_MASK |
127                                   GDK_POINTER_MOTION_MASK |
128                                   GDK_BUTTON_PRESS_MASK |
129                                   GDK_BUTTON_RELEASE_MASK |
130                                   GDK_KEY_PRESS_MASK |
131                                   GDK_KEY_RELEASE_MASK |
132                                   GDK_FOCUS_CHANGE_MASK |
133                                   GDK_ENTER_NOTIFY_MASK |
134                                   GDK_LEAVE_NOTIFY_MASK);
135     gtk_widget_set_can_focus(widget, TRUE);
136
137     g_signal_connect(widget, "expose-event",
138                      G_CALLBACK(OnExposeEvent), host_view);
139     g_signal_connect(widget, "realize",
140                      G_CALLBACK(OnRealize), host_view);
141     g_signal_connect(widget, "configure-event",
142                      G_CALLBACK(OnConfigureEvent), host_view);
143     g_signal_connect(widget, "size-allocate",
144                      G_CALLBACK(OnSizeAllocate), host_view);
145     g_signal_connect(widget, "key-press-event",
146                      G_CALLBACK(OnKeyPressReleaseEvent), host_view);
147     g_signal_connect(widget, "key-release-event",
148                      G_CALLBACK(OnKeyPressReleaseEvent), host_view);
149     g_signal_connect(widget, "focus-in-event",
150                      G_CALLBACK(OnFocusIn), host_view);
151     g_signal_connect(widget, "focus-out-event",
152                      G_CALLBACK(OnFocusOut), host_view);
153     g_signal_connect(widget, "grab-notify",
154                      G_CALLBACK(OnGrabNotify), host_view);
155     g_signal_connect(widget, "button-press-event",
156                      G_CALLBACK(OnButtonPressReleaseEvent), host_view);
157     g_signal_connect(widget, "button-release-event",
158                      G_CALLBACK(OnButtonPressReleaseEvent), host_view);
159     g_signal_connect(widget, "motion-notify-event",
160                      G_CALLBACK(OnMouseMoveEvent), host_view);
161     g_signal_connect(widget, "enter-notify-event",
162                      G_CALLBACK(OnCrossingEvent), host_view);
163     g_signal_connect(widget, "leave-notify-event",
164                      G_CALLBACK(OnCrossingEvent), host_view);
165     g_signal_connect(widget, "client-event",
166                      G_CALLBACK(OnClientEvent), host_view);
167
168
169     // Connect after so that we are called after the handler installed by the
170     // WebContentsView which handles zoom events.
171     g_signal_connect_after(widget, "scroll-event",
172                            G_CALLBACK(OnMouseScrollEvent), host_view);
173
174     // Route calls to get_accessible to the view.
175     gtk_preserve_window_set_accessible_factory(
176         GTK_PRESERVE_WINDOW(widget), GetAccessible, host_view);
177
178     return widget;
179   }
180
181  private:
182   static gboolean OnExposeEvent(GtkWidget* widget,
183                                 GdkEventExpose* expose,
184                                 RenderWidgetHostViewGtk* host_view) {
185     if (host_view->host_->is_hidden())
186       return FALSE;
187     const gfx::Rect damage_rect(expose->area);
188     host_view->Paint(damage_rect);
189     return FALSE;
190   }
191
192   static gboolean OnRealize(GtkWidget* widget,
193                             RenderWidgetHostViewGtk* host_view) {
194     // Use GtkSignalRegistrar to register events on a widget we don't
195     // control the lifetime of, auto disconnecting at our end of our life.
196     host_view->signals_.Connect(gtk_widget_get_toplevel(widget),
197                                 "configure-event",
198                                 G_CALLBACK(OnConfigureEvent), host_view);
199     return FALSE;
200   }
201
202   static gboolean OnConfigureEvent(GtkWidget* widget,
203                                    GdkEventConfigure* event,
204                                    RenderWidgetHostViewGtk* host_view) {
205     host_view->MarkCachedWidgetCenterStale();
206     host_view->UpdateScreenInfo(host_view->GetNativeView());
207     return FALSE;
208   }
209
210   static gboolean OnSizeAllocate(GtkWidget* widget,
211                                  GdkRectangle* allocation,
212                                  RenderWidgetHostViewGtk* host_view) {
213     if (!host_view->IsPopup() && !host_view->is_fullscreen_)
214       host_view->SetSize(gfx::Size(allocation->width, allocation->height));
215     return FALSE;
216   }
217
218   static gboolean OnKeyPressReleaseEvent(GtkWidget* widget,
219                                          GdkEventKey* event,
220                                          RenderWidgetHostViewGtk* host_view) {
221     TRACE_EVENT0("browser",
222                  "RenderWidgetHostViewGtkWidget::OnKeyPressReleaseEvent");
223     // Force popups or fullscreen windows to close on Escape so they won't keep
224     // the keyboard grabbed or be stuck onscreen if the renderer is hanging.
225     bool should_close_on_escape =
226         (host_view->IsPopup() && host_view->NeedsInputGrab()) ||
227         host_view->is_fullscreen_;
228     if (should_close_on_escape && GDK_Escape == event->keyval) {
229       host_view->host_->Shutdown();
230     } else {
231       // Send key event to input method.
232       host_view->im_context_->ProcessKeyEvent(event);
233     }
234
235     // We return TRUE because we did handle the event. If it turns out webkit
236     // can't handle the event, we'll deal with it in
237     // RenderView::UnhandledKeyboardEvent().
238     return TRUE;
239   }
240
241   static gboolean OnFocusIn(GtkWidget* widget,
242                             GdkEventFocus* focus,
243                             RenderWidgetHostViewGtk* host_view) {
244     host_view->ShowCurrentCursor();
245     RenderWidgetHostImpl* host =
246         RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
247     host->GotFocus();
248     host->SetActive(true);
249
250     // The only way to enable a GtkIMContext object is to call its focus in
251     // handler.
252     host_view->im_context_->OnFocusIn();
253
254     return TRUE;
255   }
256
257   static gboolean OnFocusOut(GtkWidget* widget,
258                              GdkEventFocus* focus,
259                              RenderWidgetHostViewGtk* host_view) {
260     // Whenever we lose focus, set the cursor back to that of our parent window,
261     // which should be the default arrow.
262     gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
263     // If we are showing a context menu, maintain the illusion that webkit has
264     // focus.
265     if (!host_view->IsShowingContextMenu()) {
266       RenderWidgetHostImpl* host =
267           RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
268       host->SetActive(false);
269       host->Blur();
270     }
271
272     // Prevents us from stealing input context focus in OnGrabNotify() handler.
273     host_view->was_imcontext_focused_before_grab_ = false;
274
275     // Disable the GtkIMContext object.
276     host_view->im_context_->OnFocusOut();
277
278     host_view->set_last_mouse_down(NULL);
279
280     return TRUE;
281   }
282
283   // Called when we are shadowed or unshadowed by a keyboard grab (which will
284   // occur for activatable popups, such as dropdown menus). Popup windows do not
285   // take focus, so we never get a focus out or focus in event when they are
286   // shown, and must rely on this signal instead.
287   static void OnGrabNotify(GtkWidget* widget, gboolean was_grabbed,
288                            RenderWidgetHostViewGtk* host_view) {
289     if (was_grabbed) {
290       if (host_view->was_imcontext_focused_before_grab_)
291         host_view->im_context_->OnFocusIn();
292     } else {
293       host_view->was_imcontext_focused_before_grab_ =
294           host_view->im_context_->is_focused();
295       if (host_view->was_imcontext_focused_before_grab_) {
296         gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
297         host_view->im_context_->OnFocusOut();
298       }
299     }
300   }
301
302   static gboolean OnButtonPressReleaseEvent(
303       GtkWidget* widget,
304       GdkEventButton* event,
305       RenderWidgetHostViewGtk* host_view) {
306     TRACE_EVENT0("browser",
307                  "RenderWidgetHostViewGtkWidget::OnButtonPressReleaseEvent");
308
309     if (event->type != GDK_BUTTON_RELEASE)
310       host_view->set_last_mouse_down(event);
311
312     if (!(event->button == 1 || event->button == 2 || event->button == 3))
313       return FALSE;  // We do not forward any other buttons to the renderer.
314     if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
315       return FALSE;
316
317     // If we don't have focus already, this mouse click will focus us.
318     if (!gtk_widget_is_focus(widget))
319       host_view->host_->OnPointerEventActivate();
320
321     // Confirm existing composition text on mouse click events, to make sure
322     // the input caret won't be moved with an ongoing composition session.
323     if (event->type != GDK_BUTTON_RELEASE)
324       host_view->im_context_->ConfirmComposition();
325
326     // We want to translate the coordinates of events that do not originate
327     // from this widget to be relative to the top left of the widget.
328     GtkWidget* event_widget = gtk_get_event_widget(
329         reinterpret_cast<GdkEvent*>(event));
330     if (event_widget != widget) {
331       int x = 0;
332       int y = 0;
333       gtk_widget_get_pointer(widget, &x, &y);
334       // If the mouse event happens outside our popup, force the popup to
335       // close.  We do this so a hung renderer doesn't prevent us from
336       // releasing the x pointer grab.
337       GtkAllocation allocation;
338       gtk_widget_get_allocation(widget, &allocation);
339       bool click_in_popup = x >= 0 && y >= 0 && x < allocation.width &&
340           y < allocation.height;
341       // Only Shutdown on mouse downs. Mouse ups can occur outside the render
342       // view if the user drags for DnD or while using the scrollbar on a select
343       // dropdown. Don't shutdown if we are not a popup.
344       if (event->type != GDK_BUTTON_RELEASE && host_view->IsPopup() &&
345           !host_view->is_popup_first_mouse_release_ && !click_in_popup) {
346         host_view->host_->Shutdown();
347         return FALSE;
348       }
349       event->x = x;
350       event->y = y;
351     }
352
353     // TODO(evanm): why is this necessary here but not in test shell?
354     // This logic is the same as GtkButton.
355     if (event->type == GDK_BUTTON_PRESS && !gtk_widget_has_focus(widget))
356       gtk_widget_grab_focus(widget);
357
358     host_view->is_popup_first_mouse_release_ = false;
359     RenderWidgetHostImpl* widget_host =
360         RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
361     if (widget_host)
362       widget_host->ForwardMouseEvent(WebMouseEventBuilder::Build(event));
363
364     // Although we did handle the mouse event, we need to let other handlers
365     // run (in particular the one installed by WebContentsViewGtk).
366     return FALSE;
367   }
368
369   static gboolean OnMouseMoveEvent(GtkWidget* widget,
370                                    GdkEventMotion* event,
371                                    RenderWidgetHostViewGtk* host_view) {
372     TRACE_EVENT0("browser",
373                  "RenderWidgetHostViewGtkWidget::OnMouseMoveEvent");
374     // We want to translate the coordinates of events that do not originate
375     // from this widget to be relative to the top left of the widget.
376     GtkWidget* event_widget = gtk_get_event_widget(
377         reinterpret_cast<GdkEvent*>(event));
378     if (event_widget != widget) {
379       int x = 0;
380       int y = 0;
381       gtk_widget_get_pointer(widget, &x, &y);
382       event->x = x;
383       event->y = y;
384     }
385
386     host_view->ModifyEventForEdgeDragging(widget, event);
387
388     WebKit::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event);
389
390     if (host_view->mouse_locked_) {
391       gfx::Point center = host_view->GetWidgetCenter();
392
393       bool moved_to_center = MovedToPoint(mouse_event, center);
394       if (moved_to_center)
395         host_view->mouse_has_been_warped_to_new_center_ = true;
396
397       host_view->ModifyEventMovementAndCoords(&mouse_event);
398
399       if (!moved_to_center &&
400           (mouse_event.movementX || mouse_event.movementY)) {
401         GdkDisplay* display = gtk_widget_get_display(widget);
402         GdkScreen* screen = gtk_widget_get_screen(widget);
403         gdk_display_warp_pointer(display, screen, center.x(), center.y());
404         if (host_view->mouse_has_been_warped_to_new_center_)
405           RenderWidgetHostImpl::From(
406               host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
407       }
408     } else {  // Mouse is not locked.
409       host_view->ModifyEventMovementAndCoords(&mouse_event);
410       // Do not send mouse events while the mouse cursor is being warped back
411       // to the unlocked location.
412       if (!host_view->mouse_is_being_warped_to_unlocked_position_) {
413         RenderWidgetHostImpl::From(
414             host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
415       }
416     }
417     return FALSE;
418   }
419
420   static gboolean OnCrossingEvent(GtkWidget* widget,
421                                   GdkEventCrossing* event,
422                                   RenderWidgetHostViewGtk* host_view) {
423     TRACE_EVENT0("browser",
424                  "RenderWidgetHostViewGtkWidget::OnCrossingEvent");
425     const int any_button_mask =
426         GDK_BUTTON1_MASK |
427         GDK_BUTTON2_MASK |
428         GDK_BUTTON3_MASK |
429         GDK_BUTTON4_MASK |
430         GDK_BUTTON5_MASK;
431
432     // Only forward crossing events if the mouse button is not down.
433     // (When the mouse button is down, the proper events are already being
434     // sent by ButtonPressReleaseEvent and MouseMoveEvent, above, and if we
435     // additionally send this crossing event with the state indicating the
436     // button is down, it causes problems with drag and drop in WebKit.)
437     if (!(event->state & any_button_mask)) {
438       WebKit::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event);
439       host_view->ModifyEventMovementAndCoords(&mouse_event);
440       // When crossing out and back into a render view the movement values
441       // must represent the instantaneous movement of the mouse, not the jump
442       // from the exit to re-entry point.
443       mouse_event.movementX = 0;
444       mouse_event.movementY = 0;
445       RenderWidgetHostImpl::From(
446           host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
447     }
448
449     return FALSE;
450   }
451
452   static gboolean OnClientEvent(GtkWidget* widget,
453                                 GdkEventClient* event,
454                                 RenderWidgetHostViewGtk* host_view) {
455     VLOG(1) << "client event type: " << event->message_type
456             << " data_format: " << event->data_format
457             << " data: " << event->data.l;
458     return TRUE;
459   }
460
461   // Return the net up / down (or left / right) distance represented by events
462   // in the  events will be removed from the queue. We only look at the top of
463   // queue...any other type of event will cause us not to look farther.
464   // If there is a change to the set of modifier keys or scroll axis
465   // in the events we will stop looking as well.
466   static int GetPendingScrollDelta(bool vert, guint current_event_state) {
467     int num_clicks = 0;
468     GdkEvent* event;
469     bool event_coalesced = true;
470     while ((event = gdk_event_get()) && event_coalesced) {
471       event_coalesced = false;
472       if (event->type == GDK_SCROLL) {
473         GdkEventScroll scroll = event->scroll;
474         if (scroll.state & GDK_SHIFT_MASK) {
475           if (scroll.direction == GDK_SCROLL_UP)
476             scroll.direction = GDK_SCROLL_LEFT;
477           else if (scroll.direction == GDK_SCROLL_DOWN)
478             scroll.direction = GDK_SCROLL_RIGHT;
479         }
480         if (vert) {
481           if (scroll.direction == GDK_SCROLL_UP ||
482               scroll.direction == GDK_SCROLL_DOWN) {
483             if (scroll.state == current_event_state) {
484               num_clicks += (scroll.direction == GDK_SCROLL_UP ? 1 : -1);
485               gdk_event_free(event);
486               event_coalesced = true;
487             }
488           }
489         } else {
490           if (scroll.direction == GDK_SCROLL_LEFT ||
491               scroll.direction == GDK_SCROLL_RIGHT) {
492             if (scroll.state == current_event_state) {
493               num_clicks += (scroll.direction == GDK_SCROLL_LEFT ? 1 : -1);
494               gdk_event_free(event);
495               event_coalesced = true;
496             }
497           }
498         }
499       }
500     }
501     // If we have an event left we put it back on the queue.
502     if (event) {
503       gdk_event_put(event);
504       gdk_event_free(event);
505     }
506     return num_clicks * WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
507   }
508
509   static gboolean OnMouseScrollEvent(GtkWidget* widget,
510                                      GdkEventScroll* event,
511                                      RenderWidgetHostViewGtk* host_view) {
512     TRACE_EVENT0("browser",
513                  "RenderWidgetHostViewGtkWidget::OnMouseScrollEvent");
514     // If the user is holding shift, translate it into a horizontal scroll. We
515     // don't care what other modifiers the user may be holding (zooming is
516     // handled at the WebContentsView level).
517     if (event->state & GDK_SHIFT_MASK) {
518       if (event->direction == GDK_SCROLL_UP)
519         event->direction = GDK_SCROLL_LEFT;
520       else if (event->direction == GDK_SCROLL_DOWN)
521         event->direction = GDK_SCROLL_RIGHT;
522     }
523
524     WebMouseWheelEvent web_event = WebMouseWheelEventBuilder::Build(event);
525     const float pixelsPerTick =
526         WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
527     // We  peek ahead at the top of the queue to look for additional pending
528     // scroll events.
529     if (event->direction == GDK_SCROLL_UP ||
530         event->direction == GDK_SCROLL_DOWN) {
531       if (event->direction == GDK_SCROLL_UP)
532         web_event.deltaY = pixelsPerTick;
533       else
534         web_event.deltaY = -pixelsPerTick;
535       web_event.deltaY += GetPendingScrollDelta(true, event->state);
536     } else {
537       if (event->direction == GDK_SCROLL_LEFT)
538         web_event.deltaX = pixelsPerTick;
539       else
540         web_event.deltaX = -pixelsPerTick;
541       web_event.deltaX += GetPendingScrollDelta(false, event->state);
542     }
543     RenderWidgetHostImpl::From(
544         host_view->GetRenderWidgetHost())->ForwardWheelEvent(web_event);
545     return FALSE;
546   }
547
548   DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget);
549 };
550
551 RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host)
552     : host_(RenderWidgetHostImpl::From(widget_host)),
553       about_to_validate_and_paint_(false),
554       is_loading_(false),
555       parent_(NULL),
556       is_popup_first_mouse_release_(true),
557       was_imcontext_focused_before_grab_(false),
558       do_x_grab_(false),
559       is_fullscreen_(false),
560       made_active_(false),
561       mouse_is_being_warped_to_unlocked_position_(false),
562       destroy_handler_id_(0),
563       dragged_at_horizontal_edge_(0),
564       dragged_at_vertical_edge_(0),
565       compositing_surface_(gfx::kNullPluginWindow),
566       last_mouse_down_(NULL) {
567   host_->SetView(this);
568 }
569
570 RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() {
571   UnlockMouse();
572   set_last_mouse_down(NULL);
573   view_.Destroy();
574 }
575
576 bool RenderWidgetHostViewGtk::OnMessageReceived(const IPC::Message& message) {
577   bool handled = true;
578   IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewGtk, message)
579     IPC_MESSAGE_HANDLER(ViewHostMsg_CreatePluginContainer,
580                         OnCreatePluginContainer)
581     IPC_MESSAGE_HANDLER(ViewHostMsg_DestroyPluginContainer,
582                         OnDestroyPluginContainer)
583     IPC_MESSAGE_UNHANDLED(handled = false)
584   IPC_END_MESSAGE_MAP()
585   return handled;
586 }
587
588 void RenderWidgetHostViewGtk::InitAsChild(
589     gfx::NativeView parent_view) {
590   DoSharedInit();
591   gtk_widget_show(view_.get());
592 }
593
594 void RenderWidgetHostViewGtk::InitAsPopup(
595     RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
596   // If we aren't a popup, then |window| will be leaked.
597   DCHECK(IsPopup());
598
599   DoSharedInit();
600   parent_ = parent_host_view->GetNativeView();
601   GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP));
602   gtk_container_add(GTK_CONTAINER(window), view_.get());
603   DoPopupOrFullscreenInit(window, pos);
604
605   // Grab all input for the app. If a click lands outside the bounds of the
606   // popup, WebKit will notice and destroy us. The underlying X window needs to
607   // be created and mapped by the above code before we can grab the input
608   // devices.
609   if (NeedsInputGrab()) {
610     // If our parent is in a widget hierarchy that ends with a window, add
611     // ourselves to the same window group to make sure that our GTK grab
612     // covers it.
613     GtkWidget* toplevel = gtk_widget_get_toplevel(parent_);
614     if (toplevel &&
615         GTK_WIDGET_TOPLEVEL(toplevel) &&
616         GTK_IS_WINDOW(toplevel)) {
617       gtk_window_group_add_window(
618           gtk_window_get_group(GTK_WINDOW(toplevel)), window);
619     }
620
621     // Install an application-level GTK grab to make sure that we receive all of
622     // the app's input.
623     gtk_grab_add(view_.get());
624
625     // We need to install an X grab as well. However if the app already has an X
626     // grab (as in the case of extension popup), an app grab will suffice.
627     do_x_grab_ = !gdk_pointer_is_grabbed();
628     if (do_x_grab_) {
629       // Install the grab on behalf our parent window if it and all of its
630       // ancestors are mapped; otherwise, just use ourselves (maybe we're being
631       // shown on behalf of an inactive tab).
632       GdkWindow* grab_window = gtk_widget_get_window(parent_);
633       if (!grab_window || !gdk_window_is_viewable(grab_window))
634         grab_window = gtk_widget_get_window(view_.get());
635
636       gdk_pointer_grab(
637           grab_window,
638           TRUE,  // Only events outside of the window are reported with
639                  // respect to |parent_->window|.
640           static_cast<GdkEventMask>(GDK_BUTTON_PRESS_MASK |
641               GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK),
642           NULL,
643           NULL,
644           GDK_CURRENT_TIME);
645       // We grab keyboard events too so things like alt+tab are eaten.
646       gdk_keyboard_grab(grab_window, TRUE, GDK_CURRENT_TIME);
647     }
648   }
649 }
650
651 void RenderWidgetHostViewGtk::InitAsFullscreen(
652     RenderWidgetHostView* reference_host_view) {
653   DCHECK(reference_host_view);
654   DoSharedInit();
655
656   is_fullscreen_ = true;
657   GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
658   gtk_window_set_decorated(window, FALSE);
659   destroy_handler_id_ = g_signal_connect(GTK_WIDGET(window),
660                                          "destroy",
661                                          G_CALLBACK(OnDestroyThunk),
662                                          this);
663   gtk_container_add(GTK_CONTAINER(window), view_.get());
664
665   // Try to move and resize the window to cover the screen in case the window
666   // manager doesn't support _NET_WM_STATE_FULLSCREEN.
667   GdkScreen* screen = gtk_window_get_screen(window);
668   GdkWindow* ref_gdk_window = gtk_widget_get_window(
669       reference_host_view->GetNativeView());
670
671   gfx::Rect bounds;
672   if (ref_gdk_window) {
673     const int monitor_id = gdk_screen_get_monitor_at_window(screen,
674                                                             ref_gdk_window);
675     GdkRectangle monitor_rect;
676     gdk_screen_get_monitor_geometry(screen, monitor_id, &monitor_rect);
677     bounds = gfx::Rect(monitor_rect);
678   } else {
679     bounds = gfx::Rect(
680         0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen));
681   }
682   gtk_window_move(window, bounds.x(), bounds.y());
683   gtk_window_resize(window, bounds.width(), bounds.height());
684   gtk_window_fullscreen(window);
685   DoPopupOrFullscreenInit(window, bounds);
686 }
687
688 RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const {
689   return host_;
690 }
691
692 void RenderWidgetHostViewGtk::WasShown() {
693   if (!host_ || !host_->is_hidden())
694     return;
695
696   if (web_contents_switch_paint_time_.is_null())
697     web_contents_switch_paint_time_ = base::TimeTicks::Now();
698
699   host_->WasShown();
700 }
701
702 void RenderWidgetHostViewGtk::WasHidden() {
703   if (!host_ || host_->is_hidden())
704     return;
705
706   // If we have a renderer, then inform it that we are being hidden so it can
707   // reduce its resource utilization.
708   host_->WasHidden();
709
710   web_contents_switch_paint_time_ = base::TimeTicks();
711 }
712
713 void RenderWidgetHostViewGtk::SetSize(const gfx::Size& size) {
714   int width = std::min(size.width(), kMaxWindowWidth);
715   int height = std::min(size.height(), kMaxWindowHeight);
716   if (IsPopup()) {
717     // We're a popup, honor the size request.
718     gtk_widget_set_size_request(view_.get(), width, height);
719   }
720
721   // Update the size of the RWH.
722   if (requested_size_.width() != width ||
723       requested_size_.height() != height) {
724     requested_size_ = gfx::Size(width, height);
725     host_->SendScreenRects();
726     host_->WasResized();
727   }
728 }
729
730 void RenderWidgetHostViewGtk::SetBounds(const gfx::Rect& rect) {
731   // This is called when webkit has sent us a Move message.
732   if (IsPopup()) {
733     gtk_window_move(GTK_WINDOW(gtk_widget_get_toplevel(view_.get())),
734                     rect.x(), rect.y());
735   }
736
737   SetSize(rect.size());
738 }
739
740 gfx::NativeView RenderWidgetHostViewGtk::GetNativeView() const {
741   return view_.get();
742 }
743
744 gfx::NativeViewId RenderWidgetHostViewGtk::GetNativeViewId() const {
745   return GtkNativeViewManager::GetInstance()->GetIdForWidget(view_.get());
746 }
747
748 gfx::NativeViewAccessible RenderWidgetHostViewGtk::GetNativeViewAccessible() {
749   NOTIMPLEMENTED();
750   return NULL;
751 }
752
753 void RenderWidgetHostViewGtk::MovePluginWindows(
754     const gfx::Vector2d& scroll_offset,
755     const std::vector<WebPluginGeometry>& moves) {
756   for (size_t i = 0; i < moves.size(); ++i) {
757     plugin_container_manager_.MovePluginContainer(moves[i]);
758   }
759 }
760
761 void RenderWidgetHostViewGtk::Focus() {
762   gtk_widget_grab_focus(view_.get());
763 }
764
765 void RenderWidgetHostViewGtk::Blur() {
766   // TODO(estade): We should be clearing native focus as well, but I know of no
767   // way to do that without focusing another widget.
768   host_->Blur();
769 }
770
771 bool RenderWidgetHostViewGtk::HasFocus() const {
772   return gtk_widget_has_focus(view_.get());
773 }
774
775 void RenderWidgetHostViewGtk::ActiveWindowChanged(GdkWindow* window) {
776   GdkWindow* our_window = gtk_widget_get_parent_window(view_.get());
777
778   if (our_window == window)
779     made_active_ = true;
780
781   // If the window was previously active, but isn't active anymore, shut it
782   // down.
783   if (is_fullscreen_ && our_window != window && made_active_)
784     host_->Shutdown();
785 }
786
787 bool RenderWidgetHostViewGtk::Send(IPC::Message* message) {
788   return host_->Send(message);
789 }
790
791 bool RenderWidgetHostViewGtk::IsSurfaceAvailableForCopy() const {
792   return true;
793 }
794
795 void RenderWidgetHostViewGtk::Show() {
796   gtk_widget_show(view_.get());
797   WasShown();
798 }
799
800 void RenderWidgetHostViewGtk::Hide() {
801   gtk_widget_hide(view_.get());
802   WasHidden();
803 }
804
805 bool RenderWidgetHostViewGtk::IsShowing() {
806   return gtk_widget_get_visible(view_.get());
807 }
808
809 gfx::Rect RenderWidgetHostViewGtk::GetViewBounds() const {
810   GdkWindow* gdk_window = gtk_widget_get_window(view_.get());
811   if (!gdk_window)
812     return gfx::Rect(requested_size_);
813   GdkRectangle window_rect;
814   gdk_window_get_origin(gdk_window, &window_rect.x, &window_rect.y);
815   return gfx::Rect(window_rect.x, window_rect.y,
816                    requested_size_.width(), requested_size_.height());
817 }
818
819 void RenderWidgetHostViewGtk::UpdateCursor(const WebCursor& cursor) {
820   // Optimize the common case, where the cursor hasn't changed.
821   // However, we can switch between different pixmaps, so only on the
822   // non-pixmap branch.
823   if (current_cursor_.GetCursorType() != GDK_CURSOR_IS_PIXMAP &&
824       current_cursor_.GetCursorType() == cursor.GetCursorType()) {
825     return;
826   }
827
828   current_cursor_ = cursor;
829   ShowCurrentCursor();
830 }
831
832 void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) {
833   is_loading_ = is_loading;
834   // Only call ShowCurrentCursor() when it will actually change the cursor.
835   if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR)
836     ShowCurrentCursor();
837 }
838
839 void RenderWidgetHostViewGtk::TextInputTypeChanged(
840     ui::TextInputType type,
841     ui::TextInputMode input_mode,
842     bool can_compose_inline) {
843   im_context_->UpdateInputMethodState(type, can_compose_inline);
844 }
845
846 void RenderWidgetHostViewGtk::ImeCancelComposition() {
847   im_context_->CancelComposition();
848 }
849
850 void RenderWidgetHostViewGtk::DidUpdateBackingStore(
851     const gfx::Rect& scroll_rect,
852     const gfx::Vector2d& scroll_delta,
853     const std::vector<gfx::Rect>& copy_rects,
854     const ui::LatencyInfo& latency_info) {
855   TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::DidUpdateBackingStore");
856   software_latency_info_.MergeWith(latency_info);
857
858   if (host_->is_hidden())
859     return;
860
861   // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX.  Can that
862   // be done using XCopyArea?  Perhaps similar to
863   // BackingStore::ScrollBackingStore?
864   if (about_to_validate_and_paint_)
865     invalid_rect_.Union(scroll_rect);
866   else
867     Paint(scroll_rect);
868
869   for (size_t i = 0; i < copy_rects.size(); ++i) {
870     // Avoid double painting.  NOTE: This is only relevant given the call to
871     // Paint(scroll_rect) above.
872     gfx::Rect rect = gfx::SubtractRects(copy_rects[i], scroll_rect);
873     if (rect.IsEmpty())
874       continue;
875
876     if (about_to_validate_and_paint_)
877       invalid_rect_.Union(rect);
878     else
879       Paint(rect);
880   }
881 }
882
883 void RenderWidgetHostViewGtk::RenderProcessGone(base::TerminationStatus status,
884                                                 int error_code) {
885   Destroy();
886   plugin_container_manager_.set_host_widget(NULL);
887 }
888
889 void RenderWidgetHostViewGtk::Destroy() {
890   if (compositing_surface_ != gfx::kNullPluginWindow) {
891     GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
892     manager->ReleasePermanentXID(compositing_surface_);
893   }
894
895   if (do_x_grab_) {
896     // Undo the X grab.
897     GdkDisplay* display = gtk_widget_get_display(parent_);
898     gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
899     gdk_display_keyboard_ungrab(display, GDK_CURRENT_TIME);
900   }
901
902   if (view_.get()) {
903     // If this is a popup or fullscreen widget, then we need to destroy the
904     // window that we created to hold it.
905     if (IsPopup() || is_fullscreen_) {
906       GtkWidget* window = gtk_widget_get_parent(view_.get());
907
908       ui::ActiveWindowWatcherX::RemoveObserver(this);
909
910       // Disconnect the destroy handler so that we don't try to shutdown twice.
911       if (is_fullscreen_)
912         g_signal_handler_disconnect(window, destroy_handler_id_);
913
914       gtk_widget_destroy(window);
915     }
916
917     // Remove |view_| from all containers now, so nothing else can hold a
918     // reference to |view_|'s widget except possibly a gtk signal handler if
919     // this code is currently executing within the context of a gtk signal
920     // handler.  Note that |view_| is still alive after this call.  It will be
921     // deallocated in the destructor.
922     // See http://crbug.com/11847 for details.
923     gtk_widget_destroy(view_.get());
924   }
925
926   // The RenderWidgetHost's destruction led here, so don't call it.
927   host_ = NULL;
928
929   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
930 }
931
932 void RenderWidgetHostViewGtk::SetTooltipText(const string16& tooltip_text) {
933   // Maximum number of characters we allow in a tooltip.
934   const int kMaxTooltipLength = 8 << 10;
935   // Clamp the tooltip length to kMaxTooltipLength so that we don't
936   // accidentally DOS the user with a mega tooltip (since GTK doesn't do
937   // this itself).
938   // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream.
939   const string16 clamped_tooltip =
940       gfx::TruncateString(tooltip_text, kMaxTooltipLength);
941
942   if (clamped_tooltip.empty()) {
943     gtk_widget_set_has_tooltip(view_.get(), FALSE);
944   } else {
945     gtk_widget_set_tooltip_text(view_.get(),
946                                 UTF16ToUTF8(clamped_tooltip).c_str());
947   }
948 }
949
950 void RenderWidgetHostViewGtk::SelectionChanged(const string16& text,
951                                                size_t offset,
952                                                const gfx::Range& range) {
953   RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
954
955   if (text.empty() || range.is_empty())
956     return;
957   size_t pos = range.GetMin() - offset;
958   size_t n = range.length();
959
960   DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
961   if (pos >= text.length()) {
962     NOTREACHED() << "The text can not cover range.";
963     return;
964   }
965
966   // Set the CLIPBOARD_TYPE SELECTION to the ui::Clipboard.
967   ui::ScopedClipboardWriter clipboard_writer(
968       ui::Clipboard::GetForCurrentThread(),
969       ui::CLIPBOARD_TYPE_SELECTION);
970   clipboard_writer.WriteText(text.substr(pos, n));
971 }
972
973 void RenderWidgetHostViewGtk::SelectionBoundsChanged(
974     const ViewHostMsg_SelectionBounds_Params& params) {
975   im_context_->UpdateCaretBounds(
976       gfx::UnionRects(params.anchor_rect, params.focus_rect));
977 }
978
979 void RenderWidgetHostViewGtk::ScrollOffsetChanged() {
980 }
981
982 GdkEventButton* RenderWidgetHostViewGtk::GetLastMouseDown() {
983   return last_mouse_down_;
984 }
985
986 gfx::NativeView RenderWidgetHostViewGtk::BuildInputMethodsGtkMenu() {
987   return im_context_->BuildInputMethodsGtkMenu();
988 }
989
990 void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) {
991   DCHECK(is_fullscreen_);
992   host_->Shutdown();
993 }
994
995 bool RenderWidgetHostViewGtk::NeedsInputGrab() {
996   return popup_type_ == WebKit::WebPopupTypeSelect;
997 }
998
999 bool RenderWidgetHostViewGtk::IsPopup() const {
1000   return popup_type_ != WebKit::WebPopupTypeNone;
1001 }
1002
1003 void RenderWidgetHostViewGtk::DoSharedInit() {
1004   view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this));
1005   im_context_.reset(new GtkIMContextWrapper(this));
1006   plugin_container_manager_.set_host_widget(view_.get());
1007   key_bindings_handler_.reset(new GtkKeyBindingsHandler(view_.get()));
1008 }
1009
1010 void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow* window,
1011                                                       const gfx::Rect& bounds) {
1012   requested_size_.SetSize(std::min(bounds.width(), kMaxWindowWidth),
1013                           std::min(bounds.height(), kMaxWindowHeight));
1014   host_->WasResized();
1015
1016   ui::ActiveWindowWatcherX::AddObserver(this);
1017
1018   // Don't set the size when we're going fullscreen. This can confuse the
1019   // window manager into thinking we're resizing a fullscreen window and
1020   // therefore not fullscreen anymore.
1021   if (!is_fullscreen_) {
1022     gtk_widget_set_size_request(
1023         view_.get(), requested_size_.width(), requested_size_.height());
1024
1025     // Don't allow the window to be resized. This also forces the window to
1026     // shrink down to the size of its child contents.
1027     gtk_window_set_resizable(window, FALSE);
1028     gtk_window_set_default_size(window, -1, -1);
1029     gtk_window_move(window, bounds.x(), bounds.y());
1030   }
1031
1032   gtk_widget_show_all(GTK_WIDGET(window));
1033 }
1034
1035 BackingStore* RenderWidgetHostViewGtk::AllocBackingStore(
1036     const gfx::Size& size) {
1037   gint depth = gdk_visual_get_depth(gtk_widget_get_visual(view_.get()));
1038   return new BackingStoreGtk(host_, size,
1039                              ui::GetVisualFromGtkWidget(view_.get()),
1040                              depth);
1041 }
1042
1043 // NOTE: |output| is initialized with the size of |src_subrect|, and |dst_size|
1044 // is ignored on GTK.
1045 void RenderWidgetHostViewGtk::CopyFromCompositingSurface(
1046     const gfx::Rect& src_subrect,
1047     const gfx::Size& /* dst_size */,
1048     const base::Callback<void(bool, const SkBitmap&)>& callback) {
1049   // Grab the snapshot from the renderer as that's the only reliable way to
1050   // readback from the GPU for this platform right now.
1051   GetRenderWidgetHost()->GetSnapshotFromRenderer(src_subrect, callback);
1052 }
1053
1054 void RenderWidgetHostViewGtk::CopyFromCompositingSurfaceToVideoFrame(
1055       const gfx::Rect& src_subrect,
1056       const scoped_refptr<media::VideoFrame>& target,
1057       const base::Callback<void(bool)>& callback) {
1058   NOTIMPLEMENTED();
1059   callback.Run(false);
1060 }
1061
1062 bool RenderWidgetHostViewGtk::CanCopyToVideoFrame() const {
1063   return false;
1064 }
1065
1066 void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped(
1067     const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
1068     int gpu_host_id) {
1069   AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
1070   ack_params.sync_point = 0;
1071   RenderWidgetHostImpl::AcknowledgeBufferPresent(
1072       params.route_id, gpu_host_id, ack_params);
1073   RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info);
1074 }
1075
1076 void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer(
1077     const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
1078     int gpu_host_id) {
1079   AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
1080   ack_params.sync_point = 0;
1081   RenderWidgetHostImpl::AcknowledgeBufferPresent(
1082       params.route_id, gpu_host_id, ack_params);
1083   RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info);
1084 }
1085
1086 void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() {
1087 }
1088
1089 void RenderWidgetHostViewGtk::AcceleratedSurfaceRelease() {
1090 }
1091
1092 bool RenderWidgetHostViewGtk::HasAcceleratedSurface(
1093       const gfx::Size& desired_size) {
1094   // TODO(jbates) Implement this so this view can use GetBackingStore for both
1095   // software and GPU frames. Defaulting to false just makes GetBackingStore
1096   // only useable for software frames.
1097   return false;
1098 }
1099
1100 void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) {
1101   RenderWidgetHostViewBase::SetBackground(background);
1102   Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background));
1103 }
1104
1105 void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging(
1106     GtkWidget* widget, GdkEventMotion* event) {
1107   // If the widget is aligned with an edge of the monitor its on and the user
1108   // attempts to drag past that edge we track the number of times it has
1109   // occurred, so that we can force the widget to scroll when it otherwise
1110   // would be unable to, by modifying the (x,y) position in the drag
1111   // event that we forward on to webkit. If we get a move that's no longer a
1112   // drag or a drag indicating the user is no longer at that edge we stop
1113   // altering the drag events.
1114   int new_dragged_at_horizontal_edge = 0;
1115   int new_dragged_at_vertical_edge = 0;
1116   // Used for checking the edges of the monitor. We cache the values to save
1117   // roundtrips to the X server.
1118   CR_DEFINE_STATIC_LOCAL(gfx::Size, drag_monitor_size, ());
1119   if (event->state & GDK_BUTTON1_MASK) {
1120     if (drag_monitor_size.IsEmpty()) {
1121       // We can safely cache the monitor size for the duration of a drag.
1122       GdkScreen* screen = gtk_widget_get_screen(widget);
1123       int monitor =
1124           gdk_screen_get_monitor_at_point(screen, event->x_root, event->y_root);
1125       GdkRectangle geometry;
1126       gdk_screen_get_monitor_geometry(screen, monitor, &geometry);
1127       drag_monitor_size.SetSize(geometry.width, geometry.height);
1128     }
1129     GtkAllocation allocation;
1130     gtk_widget_get_allocation(widget, &allocation);
1131     // Check X and Y independently, as the user could be dragging into a corner.
1132     if (event->x == 0 && event->x_root == 0) {
1133       new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ - 1;
1134     } else if (allocation.width - 1 == static_cast<gint>(event->x) &&
1135         drag_monitor_size.width() - 1 == static_cast<gint>(event->x_root)) {
1136       new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ + 1;
1137     }
1138
1139     if (event->y == 0 && event->y_root == 0) {
1140       new_dragged_at_vertical_edge = dragged_at_vertical_edge_ - 1;
1141     } else if (allocation.height - 1 == static_cast<gint>(event->y) &&
1142         drag_monitor_size.height() - 1 == static_cast<gint>(event->y_root)) {
1143       new_dragged_at_vertical_edge = dragged_at_vertical_edge_ + 1;
1144     }
1145
1146     event->x_root += new_dragged_at_horizontal_edge;
1147     event->x += new_dragged_at_horizontal_edge;
1148     event->y_root += new_dragged_at_vertical_edge;
1149     event->y += new_dragged_at_vertical_edge;
1150   } else {
1151     // Clear whenever we get a non-drag mouse move.
1152     drag_monitor_size.SetSize(0, 0);
1153   }
1154   dragged_at_horizontal_edge_ = new_dragged_at_horizontal_edge;
1155   dragged_at_vertical_edge_ = new_dragged_at_vertical_edge;
1156 }
1157
1158 void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) {
1159   TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::Paint");
1160
1161   // If the GPU process is rendering directly into the View,
1162   // call the compositor directly.
1163   RenderWidgetHostImpl* render_widget_host =
1164       RenderWidgetHostImpl::From(GetRenderWidgetHost());
1165   if (render_widget_host->is_accelerated_compositing_active()) {
1166     host_->ScheduleComposite();
1167     return;
1168   }
1169
1170   GdkWindow* window = gtk_widget_get_window(view_.get());
1171   DCHECK(!about_to_validate_and_paint_);
1172
1173   invalid_rect_ = damage_rect;
1174   about_to_validate_and_paint_ = true;
1175
1176   // If the size of our canvas is (0,0), then we don't want to block here. We
1177   // are doing one of our first paints and probably have animations going on.
1178   bool force_create = !host_->empty();
1179   BackingStoreGtk* backing_store = static_cast<BackingStoreGtk*>(
1180       host_->GetBackingStore(force_create));
1181   // Calling GetBackingStore maybe have changed |invalid_rect_|...
1182   about_to_validate_and_paint_ = false;
1183
1184   gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight);
1185   paint_rect.Intersect(invalid_rect_);
1186
1187   if (backing_store) {
1188     // Only render the widget if it is attached to a window; there's a short
1189     // period where this object isn't attached to a window but hasn't been
1190     // Destroy()ed yet and it receives paint messages...
1191     if (window) {
1192       backing_store->XShowRect(gfx::Point(0, 0),
1193           paint_rect, ui::GetX11WindowFromGtkWidget(view_.get()));
1194     }
1195     if (!whiteout_start_time_.is_null()) {
1196       base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
1197           whiteout_start_time_;
1198       UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
1199
1200       // Reset the start time to 0 so that we start recording again the next
1201       // time the backing store is NULL...
1202       whiteout_start_time_ = base::TimeTicks();
1203     }
1204     if (!web_contents_switch_paint_time_.is_null()) {
1205       base::TimeDelta web_contents_switch_paint_duration =
1206           base::TimeTicks::Now() - web_contents_switch_paint_time_;
1207       UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
1208           web_contents_switch_paint_duration);
1209       // Reset web_contents_switch_paint_time_ to 0 so future tab selections are
1210       // recorded.
1211       web_contents_switch_paint_time_ = base::TimeTicks();
1212     }
1213     software_latency_info_.AddLatencyNumber(
1214         ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
1215     render_widget_host->FrameSwapped(software_latency_info_);
1216     software_latency_info_.Clear();
1217   } else {
1218     if (window)
1219       gdk_window_clear(window);
1220     if (whiteout_start_time_.is_null())
1221       whiteout_start_time_ = base::TimeTicks::Now();
1222   }
1223 }
1224
1225 void RenderWidgetHostViewGtk::ShowCurrentCursor() {
1226   // The widget may not have a window. If that's the case, abort mission. This
1227   // is the same issue as that explained above in Paint().
1228   if (!gtk_widget_get_window(view_.get()))
1229     return;
1230
1231   // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is
1232   // that calling gdk_window_set_cursor repeatedly is expensive.  We should
1233   // avoid it here where possible.
1234   GdkCursor* gdk_cursor;
1235   if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) {
1236     // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and
1237     // the page is loading.
1238     gdk_cursor = is_loading_ ? GetMozSpinningCursor() : NULL;
1239   } else {
1240     gdk_cursor = current_cursor_.GetNativeCursor();
1241   }
1242   gdk_window_set_cursor(gtk_widget_get_window(view_.get()), gdk_cursor);
1243 }
1244
1245 void RenderWidgetHostViewGtk::SetHasHorizontalScrollbar(
1246     bool has_horizontal_scrollbar) {
1247 }
1248
1249 void RenderWidgetHostViewGtk::SetScrollOffsetPinning(
1250     bool is_pinned_to_left, bool is_pinned_to_right) {
1251 }
1252
1253
1254 void RenderWidgetHostViewGtk::OnAcceleratedCompositingStateChange() {
1255   bool activated = host_->is_accelerated_compositing_active();
1256   GtkPreserveWindow* widget = reinterpret_cast<GtkPreserveWindow*>(view_.get());
1257
1258   gtk_preserve_window_delegate_resize(widget, activated);
1259 }
1260
1261 void RenderWidgetHostViewGtk::GetScreenInfo(WebScreenInfo* results) {
1262   GdkWindow* gdk_window = gtk_widget_get_window(view_.get());
1263   if (!gdk_window) {
1264     GdkDisplay* display = gdk_display_get_default();
1265     gdk_window = gdk_display_get_default_group(display);
1266   }
1267   if (!gdk_window)
1268     return;
1269   GetScreenInfoFromNativeWindow(gdk_window, results);
1270 }
1271
1272 gfx::Rect RenderWidgetHostViewGtk::GetBoundsInRootWindow() {
1273   GtkWidget* toplevel = gtk_widget_get_toplevel(view_.get());
1274   if (!toplevel)
1275     return GetViewBounds();
1276
1277   GdkRectangle frame_extents;
1278   GdkWindow* gdk_window = gtk_widget_get_window(toplevel);
1279   if (!gdk_window)
1280     return GetViewBounds();
1281
1282   gdk_window_get_frame_extents(gdk_window, &frame_extents);
1283   return gfx::Rect(frame_extents.x, frame_extents.y,
1284                    frame_extents.width, frame_extents.height);
1285 }
1286
1287 gfx::GLSurfaceHandle RenderWidgetHostViewGtk::GetCompositingSurface() {
1288   if (compositing_surface_ == gfx::kNullPluginWindow) {
1289     GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
1290     gfx::NativeViewId view_id = GetNativeViewId();
1291
1292     if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) {
1293       DLOG(ERROR) << "Can't find XID for view id " << view_id;
1294     }
1295   }
1296   return gfx::GLSurfaceHandle(compositing_surface_, gfx::NATIVE_TRANSPORT);
1297 }
1298
1299 void RenderWidgetHostViewGtk::ResizeCompositingSurface(const gfx::Size& size) {
1300   GtkWidget* widget = view_.get();
1301   GdkWindow* window = gtk_widget_get_window(widget);
1302   if (window) {
1303     Display* display = GDK_WINDOW_XDISPLAY(window);
1304     gdk_window_resize(window, size.width(), size.height());
1305     XSync(display, False);
1306   }
1307 }
1308
1309 bool RenderWidgetHostViewGtk::LockMouse() {
1310   if (mouse_locked_)
1311     return true;
1312
1313   mouse_locked_ = true;
1314
1315   // Release any current grab.
1316   GtkWidget* current_grab_window = gtk_grab_get_current();
1317   if (current_grab_window) {
1318     gtk_grab_remove(current_grab_window);
1319     LOG(WARNING) << "Locking Mouse with gdk_pointer_grab, "
1320                  << "but had to steal grab from another window";
1321   }
1322
1323   GtkWidget* widget = view_.get();
1324   GdkWindow* window = gtk_widget_get_window(widget);
1325   GdkCursor* cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
1326
1327   GdkGrabStatus grab_status =
1328       gdk_pointer_grab(window,
1329                        FALSE,  // owner_events
1330                        static_cast<GdkEventMask>(
1331                            GDK_POINTER_MOTION_MASK |
1332                            GDK_BUTTON_PRESS_MASK |
1333                            GDK_BUTTON_RELEASE_MASK),
1334                        window,  // confine_to
1335                        cursor,
1336                        GDK_CURRENT_TIME);
1337
1338   if (grab_status != GDK_GRAB_SUCCESS) {
1339     LOG(WARNING) << "Failed to grab pointer for LockMouse. "
1340                  << "gdk_pointer_grab returned: " << grab_status;
1341     mouse_locked_ = false;
1342     return false;
1343   }
1344
1345   // Clear the tooltip window.
1346   SetTooltipText(string16());
1347
1348   // Ensure that the widget center location will be relevant for this mouse
1349   // lock session. It is updated whenever the window geometry moves
1350   // but may be out of date due to switching tabs.
1351   MarkCachedWidgetCenterStale();
1352
1353   return true;
1354 }
1355
1356 void RenderWidgetHostViewGtk::UnlockMouse() {
1357   if (!mouse_locked_)
1358     return;
1359
1360   mouse_locked_ = false;
1361
1362   GtkWidget* widget = view_.get();
1363   GdkDisplay* display = gtk_widget_get_display(widget);
1364   GdkScreen* screen = gtk_widget_get_screen(widget);
1365   gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
1366   gdk_display_warp_pointer(display, screen,
1367                            unlocked_global_mouse_position_.x(),
1368                            unlocked_global_mouse_position_.y());
1369   mouse_is_being_warped_to_unlocked_position_ = true;
1370
1371   if (host_)
1372     host_->LostMouseLock();
1373 }
1374
1375 void RenderWidgetHostViewGtk::ForwardKeyboardEvent(
1376     const NativeWebKeyboardEvent& event) {
1377   if (!host_)
1378     return;
1379
1380   EditCommands edit_commands;
1381   if (!event.skip_in_browser &&
1382       key_bindings_handler_->Match(event, &edit_commands)) {
1383     Send(new InputMsg_SetEditCommandsForNextKeyEvent(
1384         host_->GetRoutingID(), edit_commands));
1385     NativeWebKeyboardEvent copy_event(event);
1386     copy_event.match_edit_command = true;
1387     host_->ForwardKeyboardEvent(copy_event);
1388     return;
1389   }
1390
1391   host_->ForwardKeyboardEvent(event);
1392 }
1393
1394 bool RenderWidgetHostViewGtk::RetrieveSurrounding(std::string* text,
1395                                                   size_t* cursor_index) {
1396   if (!selection_range_.IsValid())
1397     return false;
1398
1399   size_t offset = selection_range_.GetMin() - selection_text_offset_;
1400   DCHECK(offset <= selection_text_.length());
1401
1402   if (offset == selection_text_.length()) {
1403     *text = UTF16ToUTF8(selection_text_);
1404     *cursor_index = text->length();
1405     return true;
1406   }
1407
1408   *text = base::UTF16ToUTF8AndAdjustOffset(
1409       base::StringPiece16(selection_text_), &offset);
1410   if (offset == string16::npos) {
1411     NOTREACHED() << "Invalid offset in UTF16 string.";
1412     return false;
1413   }
1414   *cursor_index = offset;
1415   return true;
1416 }
1417
1418 void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton* event) {
1419   GdkEventButton* temp = NULL;
1420   if (event) {
1421     temp = reinterpret_cast<GdkEventButton*>(
1422         gdk_event_copy(reinterpret_cast<GdkEvent*>(event)));
1423   }
1424
1425   if (last_mouse_down_)
1426     gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_));
1427
1428   last_mouse_down_ = temp;
1429 }
1430
1431 void RenderWidgetHostViewGtk::MarkCachedWidgetCenterStale() {
1432   widget_center_valid_ = false;
1433   mouse_has_been_warped_to_new_center_ = false;
1434 }
1435
1436 gfx::Point RenderWidgetHostViewGtk::GetWidgetCenter() {
1437   if (widget_center_valid_)
1438     return widget_center_;
1439
1440   GdkWindow* window = gtk_widget_get_window(view_.get());
1441   gint window_x = 0;
1442   gint window_y = 0;
1443   gdk_window_get_origin(window, &window_x, &window_y);
1444   gint window_w = gdk_window_get_width(window);
1445   gint window_h = gdk_window_get_height(window);
1446   widget_center_.SetPoint(window_x + window_w / 2,
1447                           window_y + window_h / 2);
1448   widget_center_valid_ = true;
1449   return widget_center_;
1450 }
1451
1452 void RenderWidgetHostViewGtk::ModifyEventMovementAndCoords(
1453     WebKit::WebMouseEvent* event) {
1454   // Movement is computed by taking the difference of the new cursor position
1455   // and the previous. Under mouse lock the cursor will be warped back to the
1456   // center so that we are not limited by clipping boundaries.
1457   // We do not measure movement as the delta from cursor to center because
1458   // we may receive more mouse movement events before our warp has taken
1459   // effect.
1460   event->movementX = event->globalX - global_mouse_position_.x();
1461   event->movementY = event->globalY - global_mouse_position_.y();
1462
1463   // While the cursor is being warped back to the unlocked position, suppress
1464   // the movement member data.
1465   if (mouse_is_being_warped_to_unlocked_position_) {
1466     event->movementX = 0;
1467     event->movementY = 0;
1468     if (MovedToPoint(*event, unlocked_global_mouse_position_))
1469       mouse_is_being_warped_to_unlocked_position_ = false;
1470   }
1471
1472   global_mouse_position_.SetPoint(event->globalX, event->globalY);
1473
1474   // Under mouse lock, coordinates of mouse are locked to what they were when
1475   // mouse lock was entered.
1476   if (mouse_locked_) {
1477     event->x = unlocked_mouse_position_.x();
1478     event->y = unlocked_mouse_position_.y();
1479     event->windowX = unlocked_mouse_position_.x();
1480     event->windowY = unlocked_mouse_position_.y();
1481     event->globalX = unlocked_global_mouse_position_.x();
1482     event->globalY = unlocked_global_mouse_position_.y();
1483   } else {
1484     unlocked_mouse_position_.SetPoint(event->windowX, event->windowY);
1485     unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY);
1486   }
1487 }
1488
1489 ////////////////////////////////////////////////////////////////////////////////
1490 // RenderWidgetHostView, public:
1491
1492 // static
1493 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
1494     RenderWidgetHost* widget) {
1495   return new RenderWidgetHostViewGtk(widget);
1496 }
1497
1498 // static
1499 void RenderWidgetHostViewPort::GetDefaultScreenInfo(WebScreenInfo* results) {
1500   GdkWindow* gdk_window =
1501       gdk_display_get_default_group(gdk_display_get_default());
1502   GetScreenInfoFromNativeWindow(gdk_window, results);
1503 }
1504
1505 void RenderWidgetHostViewGtk::SetAccessibilityFocus(int acc_obj_id) {
1506   if (!host_)
1507     return;
1508
1509   host_->AccessibilitySetFocus(acc_obj_id);
1510 }
1511
1512 void RenderWidgetHostViewGtk::AccessibilityDoDefaultAction(int acc_obj_id) {
1513   if (!host_)
1514     return;
1515
1516   host_->AccessibilityDoDefaultAction(acc_obj_id);
1517 }
1518
1519 void RenderWidgetHostViewGtk::AccessibilityScrollToMakeVisible(
1520     int acc_obj_id, gfx::Rect subfocus) {
1521   if (!host_)
1522     return;
1523
1524   host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
1525 }
1526
1527 void RenderWidgetHostViewGtk::AccessibilityScrollToPoint(
1528     int acc_obj_id, gfx::Point point) {
1529   if (!host_)
1530     return;
1531
1532   host_->AccessibilityScrollToPoint(acc_obj_id, point);
1533 }
1534
1535 void RenderWidgetHostViewGtk::AccessibilitySetTextSelection(
1536     int acc_obj_id, int start_offset, int end_offset) {
1537   if (!host_)
1538     return;
1539
1540   host_->AccessibilitySetTextSelection(acc_obj_id, start_offset, end_offset);
1541 }
1542
1543 gfx::Point RenderWidgetHostViewGtk::GetLastTouchEventLocation() const {
1544   // Not needed on Linux.
1545   return gfx::Point();
1546 }
1547
1548 void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() {
1549   if (host_) {
1550     host_->FatalAccessibilityTreeError();
1551     SetBrowserAccessibilityManager(NULL);
1552   } else {
1553     CHECK(FALSE);
1554   }
1555 }
1556
1557 void RenderWidgetHostViewGtk::OnAccessibilityEvents(
1558     const std::vector<AccessibilityHostMsg_EventParams>& params) {
1559   if (!GetBrowserAccessibilityManager()) {
1560     GtkWidget* parent = gtk_widget_get_parent(view_.get());
1561     SetBrowserAccessibilityManager(
1562         new BrowserAccessibilityManagerGtk(
1563             parent,
1564             BrowserAccessibilityManagerGtk::GetEmptyDocument(),
1565             this));
1566   }
1567   GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
1568 }
1569
1570 AtkObject* RenderWidgetHostViewGtk::GetAccessible() {
1571   if (!GetBrowserAccessibilityManager()) {
1572     GtkWidget* parent = gtk_widget_get_parent(view_.get());
1573     SetBrowserAccessibilityManager(
1574         new BrowserAccessibilityManagerGtk(
1575             parent,
1576             BrowserAccessibilityManagerGtk::GetEmptyDocument(),
1577             this));
1578   }
1579   BrowserAccessibilityGtk* root =
1580       GetBrowserAccessibilityManager()->GetRoot()->ToBrowserAccessibilityGtk();
1581
1582   atk_object_set_role(root->GetAtkObject(), ATK_ROLE_HTML_CONTAINER);
1583   return root->GetAtkObject();
1584 }
1585
1586 void RenderWidgetHostViewGtk::OnCreatePluginContainer(
1587     gfx::PluginWindowHandle id) {
1588   plugin_container_manager_.CreatePluginContainer(id);
1589 }
1590
1591 void RenderWidgetHostViewGtk::OnDestroyPluginContainer(
1592     gfx::PluginWindowHandle id) {
1593   plugin_container_manager_.DestroyPluginContainer(id);
1594 }
1595
1596 }  // namespace content