1// Copyright (c) 2011 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 "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
6
7// If this gets included after the gtk headers, then a bunch of compiler
8// errors happen because of a "#define Status int" in Xlib.h, which interacts
9// badly with net::URLRequestStatus::Status.
10#include "chrome/common/render_messages.h"
11#include "content/common/view_messages.h"
12
13#include <cairo/cairo.h>
14#include <gdk/gdk.h>
15#include <gdk/gdkkeysyms.h>
16#include <gdk/gdkx.h>
17#include <gtk/gtk.h>
18
19#include <algorithm>
20#include <string>
21
22#include "base/command_line.h"
23#include "base/logging.h"
24#include "base/message_loop.h"
25#include "base/metrics/histogram.h"
26#include "base/string_number_conversions.h"
27#include "base/time.h"
28#include "base/utf_string_conversions.h"
29#include "chrome/browser/renderer_host/gtk_im_context_wrapper.h"
30#include "chrome/browser/renderer_host/gtk_key_bindings_handler.h"
31#include "chrome/browser/ui/gtk/gtk_util.h"
32#include "chrome/common/chrome_switches.h"
33#include "content/browser/renderer_host/backing_store_x.h"
34#include "content/browser/renderer_host/render_view_host.h"
35#include "content/browser/renderer_host/render_view_host_delegate.h"
36#include "content/browser/renderer_host/render_widget_host.h"
37#include "content/common/native_web_keyboard_event.h"
38#include "third_party/WebKit/Source/WebKit/chromium/public/gtk/WebInputEventFactory.h"
39#include "ui/base/l10n/l10n_util.h"
40#include "ui/base/x/x11_util.h"
41#include "ui/gfx/gtk_preserve_window.h"
42#include "ui/gfx/gtk_native_view_id_manager.h"
43#include "webkit/glue/webaccessibility.h"
44#include "webkit/glue/webcursor_gtk_data.h"
45#include "webkit/plugins/npapi/webplugin.h"
46
47#if defined(OS_CHROMEOS)
48#include "views/widget/tooltip_window_gtk.h"
49#endif  // defined(OS_CHROMEOS)
50
51namespace {
52
53const int kMaxWindowWidth = 4000;
54const int kMaxWindowHeight = 4000;
55const char* kRenderWidgetHostViewKey = "__RENDER_WIDGET_HOST_VIEW__";
56
57// The duration of the fade-out animation. See |overlay_animation_|.
58const int kFadeEffectDuration = 300;
59
60#if defined(OS_CHROMEOS)
61// TODO(davemoore) Under Chromeos we are increasing the rate that the trackpad
62// generates events to get better precisions. Eventually we will coordinate the
63// driver and this setting to ensure they match.
64const float kDefaultScrollPixelsPerTick = 20;
65#else
66// See WebInputEventFactor.cpp for a reason for this being the default
67// scroll size for linux.
68const float kDefaultScrollPixelsPerTick = 160.0f / 3.0f;
69#endif
70
71// Returns the spinning cursor used for loading state.
72GdkCursor* GetMozSpinningCursor() {
73  static GdkCursor* moz_spinning_cursor = NULL;
74  if (!moz_spinning_cursor) {
75    const GdkColor fg = { 0, 0, 0, 0 };
76    const GdkColor bg = { 65535, 65535, 65535, 65535 };
77    GdkPixmap* source =
78        gdk_bitmap_create_from_data(NULL, moz_spinning_bits, 32, 32);
79    GdkPixmap* mask =
80        gdk_bitmap_create_from_data(NULL, moz_spinning_mask_bits, 32, 32);
81    moz_spinning_cursor =
82        gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 2, 2);
83    g_object_unref(source);
84    g_object_unref(mask);
85  }
86  return moz_spinning_cursor;
87}
88
89}  // namespace
90
91using WebKit::WebInputEventFactory;
92using WebKit::WebMouseWheelEvent;
93
94// This class is a simple convenience wrapper for Gtk functions. It has only
95// static methods.
96class RenderWidgetHostViewGtkWidget {
97 public:
98  static GtkWidget* CreateNewWidget(RenderWidgetHostViewGtk* host_view) {
99    GtkWidget* widget = gtk_preserve_window_new();
100    gtk_widget_set_name(widget, "chrome-render-widget-host-view");
101    // We manually double-buffer in Paint() because Paint() may or may not be
102    // called in repsonse to an "expose-event" signal.
103    gtk_widget_set_double_buffered(widget, FALSE);
104    gtk_widget_set_redraw_on_allocate(widget, FALSE);
105#if defined(NDEBUG)
106    gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gtk_util::kGdkWhite);
107#else
108    gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gtk_util::kGdkGreen);
109#endif
110    // Allow the browser window to be resized freely.
111    gtk_widget_set_size_request(widget, 0, 0);
112
113    gtk_widget_add_events(widget, GDK_EXPOSURE_MASK |
114                                  GDK_POINTER_MOTION_MASK |
115                                  GDK_BUTTON_PRESS_MASK |
116                                  GDK_BUTTON_RELEASE_MASK |
117                                  GDK_KEY_PRESS_MASK |
118                                  GDK_KEY_RELEASE_MASK |
119                                  GDK_FOCUS_CHANGE_MASK |
120                                  GDK_ENTER_NOTIFY_MASK |
121                                  GDK_LEAVE_NOTIFY_MASK);
122    GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);
123
124    g_signal_connect(widget, "expose-event",
125                     G_CALLBACK(OnExposeEvent), host_view);
126    g_signal_connect(widget, "key-press-event",
127                     G_CALLBACK(OnKeyPressReleaseEvent), host_view);
128    g_signal_connect(widget, "key-release-event",
129                     G_CALLBACK(OnKeyPressReleaseEvent), host_view);
130    g_signal_connect(widget, "focus-in-event",
131                     G_CALLBACK(OnFocusIn), host_view);
132    g_signal_connect(widget, "focus-out-event",
133                     G_CALLBACK(OnFocusOut), host_view);
134    g_signal_connect(widget, "grab-notify",
135                     G_CALLBACK(OnGrabNotify), host_view);
136    g_signal_connect(widget, "button-press-event",
137                     G_CALLBACK(OnButtonPressReleaseEvent), host_view);
138    g_signal_connect(widget, "button-release-event",
139                     G_CALLBACK(OnButtonPressReleaseEvent), host_view);
140    g_signal_connect(widget, "motion-notify-event",
141                     G_CALLBACK(OnMouseMoveEvent), host_view);
142    g_signal_connect(widget, "enter-notify-event",
143                     G_CALLBACK(OnCrossingEvent), host_view);
144    g_signal_connect(widget, "leave-notify-event",
145                     G_CALLBACK(OnCrossingEvent), host_view);
146    g_signal_connect(widget, "client-event",
147                     G_CALLBACK(OnClientEvent), host_view);
148
149
150    // Connect after so that we are called after the handler installed by the
151    // TabContentsView which handles zoom events.
152    g_signal_connect_after(widget, "scroll-event",
153                           G_CALLBACK(OnMouseScrollEvent), host_view);
154
155    g_object_set_data(G_OBJECT(widget), kRenderWidgetHostViewKey,
156                      static_cast<RenderWidgetHostView*>(host_view));
157
158    return widget;
159  }
160
161 private:
162  static gboolean OnExposeEvent(GtkWidget* widget,
163                                GdkEventExpose* expose,
164                                RenderWidgetHostViewGtk* host_view) {
165    if (host_view->is_hidden_)
166      return FALSE;
167    const gfx::Rect damage_rect(expose->area);
168    host_view->Paint(damage_rect);
169    return FALSE;
170  }
171
172  static gboolean OnKeyPressReleaseEvent(GtkWidget* widget,
173                                         GdkEventKey* event,
174                                         RenderWidgetHostViewGtk* host_view) {
175    // Force popups or fullscreen windows to close on Escape so they won't keep
176    // the keyboard grabbed or be stuck onscreen if the renderer is hanging.
177    bool should_close_on_escape =
178        (host_view->IsPopup() && host_view->NeedsInputGrab()) ||
179        host_view->is_fullscreen_;
180    if (should_close_on_escape && GDK_Escape == event->keyval) {
181      host_view->host_->Shutdown();
182    } else {
183      // Send key event to input method.
184      host_view->im_context_->ProcessKeyEvent(event);
185    }
186
187    // We return TRUE because we did handle the event. If it turns out webkit
188    // can't handle the event, we'll deal with it in
189    // RenderView::UnhandledKeyboardEvent().
190    return TRUE;
191  }
192
193  static gboolean OnFocusIn(GtkWidget* widget,
194                            GdkEventFocus* focus,
195                            RenderWidgetHostViewGtk* host_view) {
196    host_view->ShowCurrentCursor();
197    host_view->GetRenderWidgetHost()->GotFocus();
198
199    // The only way to enable a GtkIMContext object is to call its focus in
200    // handler.
201    host_view->im_context_->OnFocusIn();
202
203    return TRUE;
204  }
205
206  static gboolean OnFocusOut(GtkWidget* widget,
207                             GdkEventFocus* focus,
208                             RenderWidgetHostViewGtk* host_view) {
209    // Whenever we lose focus, set the cursor back to that of our parent window,
210    // which should be the default arrow.
211    gdk_window_set_cursor(widget->window, NULL);
212    // If we are showing a context menu, maintain the illusion that webkit has
213    // focus.
214    if (!host_view->is_showing_context_menu_)
215      host_view->GetRenderWidgetHost()->Blur();
216
217    // Prevents us from stealing input context focus in OnGrabNotify() handler.
218    host_view->was_focused_before_grab_ = false;
219
220    // Disable the GtkIMContext object.
221    host_view->im_context_->OnFocusOut();
222
223    return TRUE;
224  }
225
226  // Called when we are shadowed or unshadowed by a keyboard grab (which will
227  // occur for activatable popups, such as dropdown menus). Popup windows do not
228  // take focus, so we never get a focus out or focus in event when they are
229  // shown, and must rely on this signal instead.
230  static void OnGrabNotify(GtkWidget* widget, gboolean was_grabbed,
231                           RenderWidgetHostViewGtk* host_view) {
232    if (was_grabbed) {
233      if (host_view->was_focused_before_grab_)
234        host_view->im_context_->OnFocusIn();
235    } else {
236      host_view->was_focused_before_grab_ = host_view->HasFocus();
237      if (host_view->was_focused_before_grab_) {
238        gdk_window_set_cursor(widget->window, NULL);
239        host_view->im_context_->OnFocusOut();
240      }
241    }
242  }
243
244  static gboolean OnButtonPressReleaseEvent(
245      GtkWidget* widget,
246      GdkEventButton* event,
247      RenderWidgetHostViewGtk* host_view) {
248#if defined (OS_CHROMEOS)
249    // We support buttons 8 & 9 for scrolling with an attached USB mouse
250    // in ChromeOS. We do this separately from the builtin scrolling support
251    // because we want to support the user's expectations about the amount
252    // scrolled on each event. xorg.conf on chromeos specifies buttons
253    // 8 & 9 for the scroll wheel for the attached USB mouse.
254    if (event->type == GDK_BUTTON_RELEASE &&
255        (event->button == 8 || event->button == 9)) {
256      GdkEventScroll scroll_event;
257      scroll_event.type = GDK_SCROLL;
258      scroll_event.window = event->window;
259      scroll_event.send_event = event->send_event;
260      scroll_event.time = event->time;
261      scroll_event.x = event->x;
262      scroll_event.y = event->y;
263      scroll_event.state = event->state;
264      if (event->state & GDK_SHIFT_MASK) {
265        scroll_event.direction =
266            event->button == 8 ? GDK_SCROLL_LEFT : GDK_SCROLL_RIGHT;
267      } else {
268        scroll_event.direction =
269            event->button == 8 ? GDK_SCROLL_UP : GDK_SCROLL_DOWN;
270      }
271      scroll_event.device = event->device;
272      scroll_event.x_root = event->x_root;
273      scroll_event.y_root = event->y_root;
274      WebMouseWheelEvent web_event =
275          WebInputEventFactory::mouseWheelEvent(&scroll_event);
276      host_view->GetRenderWidgetHost()->ForwardWheelEvent(web_event);
277    }
278#endif
279
280    if (event->type != GDK_BUTTON_RELEASE)
281      host_view->set_last_mouse_down(event);
282
283    if (!(event->button == 1 || event->button == 2 || event->button == 3))
284      return FALSE;  // We do not forward any other buttons to the renderer.
285    if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
286      return FALSE;
287
288    // If we don't have focus already, this mouse click will focus us.
289    if (!gtk_widget_is_focus(widget))
290      host_view->host_->OnMouseActivate();
291
292    // Confirm existing composition text on mouse click events, to make sure
293    // the input caret won't be moved with an ongoing composition session.
294    host_view->im_context_->ConfirmComposition();
295
296    // We want to translate the coordinates of events that do not originate
297    // from this widget to be relative to the top left of the widget.
298    GtkWidget* event_widget = gtk_get_event_widget(
299        reinterpret_cast<GdkEvent*>(event));
300    if (event_widget != widget) {
301      int x = 0;
302      int y = 0;
303      gtk_widget_get_pointer(widget, &x, &y);
304      // If the mouse event happens outside our popup, force the popup to
305      // close.  We do this so a hung renderer doesn't prevent us from
306      // releasing the x pointer grab.
307      bool click_in_popup = x >= 0 && y >= 0 && x < widget->allocation.width &&
308          y < widget->allocation.height;
309      // Only Shutdown on mouse downs. Mouse ups can occur outside the render
310      // view if the user drags for DnD or while using the scrollbar on a select
311      // dropdown. Don't shutdown if we are not a popup.
312      if (event->type != GDK_BUTTON_RELEASE && host_view->IsPopup() &&
313          !host_view->is_popup_first_mouse_release_ && !click_in_popup) {
314        host_view->host_->Shutdown();
315        return FALSE;
316      }
317      event->x = x;
318      event->y = y;
319    }
320
321    // TODO(evanm): why is this necessary here but not in test shell?
322    // This logic is the same as GtkButton.
323    if (event->type == GDK_BUTTON_PRESS && !GTK_WIDGET_HAS_FOCUS(widget))
324      gtk_widget_grab_focus(widget);
325
326    host_view->is_popup_first_mouse_release_ = false;
327    host_view->GetRenderWidgetHost()->ForwardMouseEvent(
328        WebInputEventFactory::mouseEvent(event));
329
330    // Although we did handle the mouse event, we need to let other handlers
331    // run (in particular the one installed by TabContentsViewGtk).
332    return FALSE;
333  }
334
335  static gboolean OnMouseMoveEvent(GtkWidget* widget,
336                                   GdkEventMotion* event,
337                                   RenderWidgetHostViewGtk* host_view) {
338    // We want to translate the coordinates of events that do not originate
339    // from this widget to be relative to the top left of the widget.
340    GtkWidget* event_widget = gtk_get_event_widget(
341        reinterpret_cast<GdkEvent*>(event));
342    if (event_widget != widget) {
343      int x = 0;
344      int y = 0;
345      gtk_widget_get_pointer(widget, &x, &y);
346      event->x = x;
347      event->y = y;
348    }
349
350    host_view->ModifyEventForEdgeDragging(widget, event);
351    host_view->GetRenderWidgetHost()->ForwardMouseEvent(
352        WebInputEventFactory::mouseEvent(event));
353    return FALSE;
354  }
355
356  static gboolean OnCrossingEvent(GtkWidget* widget,
357                                  GdkEventCrossing* event,
358                                  RenderWidgetHostViewGtk* host_view) {
359    const int any_button_mask =
360        GDK_BUTTON1_MASK |
361        GDK_BUTTON2_MASK |
362        GDK_BUTTON3_MASK |
363        GDK_BUTTON4_MASK |
364        GDK_BUTTON5_MASK;
365
366    // Only forward crossing events if the mouse button is not down.
367    // (When the mouse button is down, the proper events are already being
368    // sent by ButtonPressReleaseEvent and MouseMoveEvent, above, and if we
369    // additionally send this crossing event with the state indicating the
370    // button is down, it causes problems with drag and drop in WebKit.)
371    if (!(event->state & any_button_mask)) {
372      host_view->GetRenderWidgetHost()->ForwardMouseEvent(
373          WebInputEventFactory::mouseEvent(event));
374    }
375
376    return FALSE;
377  }
378
379  static gboolean OnClientEvent(GtkWidget* widget,
380                                GdkEventClient* event,
381                                RenderWidgetHostViewGtk* host_view) {
382    VLOG(1) << "client event type: " << event->message_type
383            << " data_format: " << event->data_format
384            << " data: " << event->data.l;
385    return TRUE;
386  }
387
388  // Allow the vertical scroll delta to be overridden from the command line.
389  // This will allow us to test more easily to discover the amount
390  // (either hard coded or computed) that's best.
391  static float GetScrollPixelsPerTick() {
392    static float scroll_pixels = -1;
393    if (scroll_pixels < 0) {
394      // TODO(brettw): Remove the command line switch (crbug.com/63525)
395      scroll_pixels = kDefaultScrollPixelsPerTick;
396      CommandLine* command_line = CommandLine::ForCurrentProcess();
397      std::string scroll_pixels_option =
398          command_line->GetSwitchValueASCII(switches::kScrollPixels);
399      if (!scroll_pixels_option.empty()) {
400        double v;
401        if (base::StringToDouble(scroll_pixels_option, &v))
402          scroll_pixels = static_cast<float>(v);
403      }
404      DCHECK_GT(scroll_pixels, 0);
405    }
406    return scroll_pixels;
407  }
408
409  // Return the net up / down (or left / right) distance represented by events
410  // in the  events will be removed from the queue. We only look at the top of
411  // queue...any other type of event will cause us not to look farther.
412  // If there is a change to the set of modifier keys or scroll axis
413  // in the events we will stop looking as well.
414  static int GetPendingScrollDelta(bool vert, guint current_event_state) {
415    int num_clicks = 0;
416    GdkEvent* event;
417    bool event_coalesced = true;
418    while ((event = gdk_event_get()) && event_coalesced) {
419      event_coalesced = false;
420      if (event->type == GDK_SCROLL) {
421        GdkEventScroll scroll = event->scroll;
422        if (scroll.state & GDK_SHIFT_MASK) {
423          if (scroll.direction == GDK_SCROLL_UP)
424            scroll.direction = GDK_SCROLL_LEFT;
425          else if (scroll.direction == GDK_SCROLL_DOWN)
426            scroll.direction = GDK_SCROLL_RIGHT;
427        }
428        if (vert) {
429          if (scroll.direction == GDK_SCROLL_UP ||
430              scroll.direction == GDK_SCROLL_DOWN) {
431            if (scroll.state == current_event_state) {
432              num_clicks += (scroll.direction == GDK_SCROLL_UP ? 1 : -1);
433              gdk_event_free(event);
434              event_coalesced = true;
435            }
436          }
437        } else {
438          if (scroll.direction == GDK_SCROLL_LEFT ||
439              scroll.direction == GDK_SCROLL_RIGHT) {
440            if (scroll.state == current_event_state) {
441              num_clicks += (scroll.direction == GDK_SCROLL_LEFT ? 1 : -1);
442              gdk_event_free(event);
443              event_coalesced = true;
444            }
445          }
446        }
447      }
448    }
449    // If we have an event left we put it back on the queue.
450    if (event) {
451      gdk_event_put(event);
452      gdk_event_free(event);
453    }
454    return num_clicks * GetScrollPixelsPerTick();
455  }
456
457  static gboolean OnMouseScrollEvent(GtkWidget* widget,
458                                     GdkEventScroll* event,
459                                     RenderWidgetHostViewGtk* host_view) {
460    // If the user is holding shift, translate it into a horizontal scroll. We
461    // don't care what other modifiers the user may be holding (zooming is
462    // handled at the TabContentsView level).
463    if (event->state & GDK_SHIFT_MASK) {
464      if (event->direction == GDK_SCROLL_UP)
465        event->direction = GDK_SCROLL_LEFT;
466      else if (event->direction == GDK_SCROLL_DOWN)
467        event->direction = GDK_SCROLL_RIGHT;
468    }
469
470    WebMouseWheelEvent web_event = WebInputEventFactory::mouseWheelEvent(event);
471    // We  peek ahead at the top of the queue to look for additional pending
472    // scroll events.
473    if (event->direction == GDK_SCROLL_UP ||
474        event->direction == GDK_SCROLL_DOWN) {
475      if (event->direction == GDK_SCROLL_UP)
476        web_event.deltaY = GetScrollPixelsPerTick();
477      else
478        web_event.deltaY = -GetScrollPixelsPerTick();
479      web_event.deltaY += GetPendingScrollDelta(true, event->state);
480    } else {
481      if (event->direction == GDK_SCROLL_LEFT)
482        web_event.deltaX = GetScrollPixelsPerTick();
483      else
484        web_event.deltaX = -GetScrollPixelsPerTick();
485      web_event.deltaX += GetPendingScrollDelta(false, event->state);
486    }
487    host_view->GetRenderWidgetHost()->ForwardWheelEvent(web_event);
488    return FALSE;
489  }
490
491  DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget);
492};
493
494// static
495RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
496    RenderWidgetHost* widget) {
497  return new RenderWidgetHostViewGtk(widget);
498}
499
500RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host)
501    : host_(widget_host),
502      about_to_validate_and_paint_(false),
503      is_hidden_(false),
504      is_loading_(false),
505      is_showing_context_menu_(false),
506      overlay_color_(0),
507      overlay_animation_(this),
508      parent_(NULL),
509      is_popup_first_mouse_release_(true),
510      was_focused_before_grab_(false),
511      do_x_grab_(false),
512      is_fullscreen_(false),
513      destroy_handler_id_(0),
514      dragged_at_horizontal_edge_(0),
515      dragged_at_vertical_edge_(0),
516      compositing_surface_(gfx::kNullPluginWindow),
517      last_mouse_down_(NULL) {
518  host_->set_view(this);
519}
520
521RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() {
522  set_last_mouse_down(NULL);
523  view_.Destroy();
524}
525
526void RenderWidgetHostViewGtk::InitAsChild() {
527  DoSharedInit();
528  overlay_animation_.SetDuration(kFadeEffectDuration);
529  overlay_animation_.SetSlideDuration(kFadeEffectDuration);
530  gtk_widget_show(view_.get());
531}
532
533void RenderWidgetHostViewGtk::InitAsPopup(
534    RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
535  // If we aren't a popup, then |window| will be leaked.
536  DCHECK(IsPopup());
537
538  DoSharedInit();
539  parent_ = parent_host_view->GetNativeView();
540  GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP));
541  gtk_container_add(GTK_CONTAINER(window), view_.get());
542  DoPopupOrFullscreenInit(window, pos);
543
544  // The underlying X window needs to be created and mapped by the above code
545  // before we can grab the input devices.
546  if (NeedsInputGrab()) {
547    // Grab all input for the app. If a click lands outside the bounds of the
548    // popup, WebKit will notice and destroy us. Before doing this we need
549    // to ensure that the the popup is added to the browser's window group,
550    // to allow for the grabs to work correctly.
551    gtk_window_group_add_window(gtk_window_get_group(
552        GTK_WINDOW(gtk_widget_get_toplevel(parent_))), window);
553    gtk_grab_add(view_.get());
554
555    // We need for the application to do an X grab as well. However if the app
556    // already has an X grab (as in the case of extension popup), an app grab
557    // will suffice.
558    do_x_grab_ = !gdk_pointer_is_grabbed();
559
560    // Now grab all of X's input.
561    if (do_x_grab_) {
562      gdk_pointer_grab(
563          parent_->window,
564          TRUE,  // Only events outside of the window are reported with respect
565                 // to |parent_->window|.
566          static_cast<GdkEventMask>(GDK_BUTTON_PRESS_MASK |
567              GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK),
568          NULL,
569          NULL,
570          GDK_CURRENT_TIME);
571      // We grab keyboard events too so things like alt+tab are eaten.
572      gdk_keyboard_grab(parent_->window, TRUE, GDK_CURRENT_TIME);
573    }
574  }
575}
576
577void RenderWidgetHostViewGtk::InitAsFullscreen() {
578  DoSharedInit();
579
580  is_fullscreen_ = true;
581  GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
582  gtk_window_set_decorated(window, FALSE);
583  gtk_window_fullscreen(window);
584  g_signal_connect(GTK_WIDGET(window),
585                   "window-state-event",
586                   G_CALLBACK(&OnWindowStateEventThunk),
587                   this);
588  destroy_handler_id_ = g_signal_connect(GTK_WIDGET(window),
589                                         "destroy",
590                                         G_CALLBACK(OnDestroyThunk),
591                                         this);
592  gtk_container_add(GTK_CONTAINER(window), view_.get());
593
594  // Try to move and resize the window to cover the screen in case the window
595  // manager doesn't support _NET_WM_STATE_FULLSCREEN.
596  GdkScreen* screen = gtk_window_get_screen(window);
597  gfx::Rect bounds(
598      0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen));
599  DoPopupOrFullscreenInit(window, bounds);
600}
601
602RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const {
603  return host_;
604}
605
606void RenderWidgetHostViewGtk::DidBecomeSelected() {
607  if (!is_hidden_)
608    return;
609
610  if (tab_switch_paint_time_.is_null())
611    tab_switch_paint_time_ = base::TimeTicks::Now();
612  is_hidden_ = false;
613  host_->WasRestored();
614}
615
616void RenderWidgetHostViewGtk::WasHidden() {
617  if (is_hidden_)
618    return;
619
620  // If we receive any more paint messages while we are hidden, we want to
621  // ignore them so we don't re-allocate the backing store.  We will paint
622  // everything again when we become selected again.
623  is_hidden_ = true;
624
625  // If we have a renderer, then inform it that we are being hidden so it can
626  // reduce its resource utilization.
627  GetRenderWidgetHost()->WasHidden();
628}
629
630void RenderWidgetHostViewGtk::SetSize(const gfx::Size& size) {
631  int width = std::min(size.width(), kMaxWindowWidth);
632  int height = std::min(size.height(), kMaxWindowHeight);
633  if (IsPopup()) {
634    // We're a popup, honor the size request.
635    gtk_widget_set_size_request(view_.get(), width, height);
636  } else {
637#if defined(TOOLKIT_VIEWS)
638    // TOOLKIT_VIEWS' resize logic flow matches windows. so we go ahead and
639    // size the widget.  In GTK+, the size of the widget is determined by its
640    // children.
641    gtk_widget_set_size_request(view_.get(), width, height);
642#endif
643  }
644
645  // Update the size of the RWH.
646  if (requested_size_.width() != width ||
647      requested_size_.height() != height) {
648    requested_size_ = gfx::Size(width, height);
649    host_->WasResized();
650  }
651}
652
653void RenderWidgetHostViewGtk::SetBounds(const gfx::Rect& rect) {
654  // This is called when webkit has sent us a Move message.
655  if (IsPopup()) {
656    gtk_window_move(GTK_WINDOW(gtk_widget_get_toplevel(view_.get())),
657                    rect.x(), rect.y());
658  }
659
660  SetSize(rect.size());
661}
662
663gfx::NativeView RenderWidgetHostViewGtk::GetNativeView() {
664  return view_.get();
665}
666
667void RenderWidgetHostViewGtk::MovePluginWindows(
668    const std::vector<webkit::npapi::WebPluginGeometry>& moves) {
669  for (size_t i = 0; i < moves.size(); ++i) {
670    plugin_container_manager_.MovePluginContainer(moves[i]);
671  }
672}
673
674void RenderWidgetHostViewGtk::Focus() {
675  gtk_widget_grab_focus(view_.get());
676}
677
678void RenderWidgetHostViewGtk::Blur() {
679  // TODO(estade): We should be clearing native focus as well, but I know of no
680  // way to do that without focusing another widget.
681  host_->Blur();
682}
683
684bool RenderWidgetHostViewGtk::HasFocus() {
685  return gtk_widget_is_focus(view_.get());
686}
687
688void RenderWidgetHostViewGtk::Show() {
689  gtk_widget_show(view_.get());
690}
691
692void RenderWidgetHostViewGtk::Hide() {
693  gtk_widget_hide(view_.get());
694}
695
696bool RenderWidgetHostViewGtk::IsShowing() {
697  // TODO(jcivelli): use gtk_widget_get_visible once we build with GTK 2.18.
698  return (GTK_WIDGET_FLAGS(view_.get()) & GTK_VISIBLE) != 0;
699}
700
701gfx::Rect RenderWidgetHostViewGtk::GetViewBounds() const {
702  GtkAllocation* alloc = &view_.get()->allocation;
703  return gfx::Rect(alloc->x, alloc->y,
704                   requested_size_.width(),
705                   requested_size_.height());
706}
707
708void RenderWidgetHostViewGtk::UpdateCursor(const WebCursor& cursor) {
709  // Optimize the common case, where the cursor hasn't changed.
710  // However, we can switch between different pixmaps, so only on the
711  // non-pixmap branch.
712  if (current_cursor_.GetCursorType() != GDK_CURSOR_IS_PIXMAP &&
713      current_cursor_.GetCursorType() == cursor.GetCursorType()) {
714    return;
715  }
716
717  current_cursor_ = cursor;
718  ShowCurrentCursor();
719}
720
721void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) {
722  is_loading_ = is_loading;
723  // Only call ShowCurrentCursor() when it will actually change the cursor.
724  if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR)
725    ShowCurrentCursor();
726}
727
728void RenderWidgetHostViewGtk::ImeUpdateTextInputState(
729    WebKit::WebTextInputType type,
730    const gfx::Rect& caret_rect) {
731  im_context_->UpdateInputMethodState(type, caret_rect);
732}
733
734void RenderWidgetHostViewGtk::ImeCancelComposition() {
735  im_context_->CancelComposition();
736}
737
738void RenderWidgetHostViewGtk::DidUpdateBackingStore(
739    const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
740    const std::vector<gfx::Rect>& copy_rects) {
741  if (is_hidden_)
742    return;
743
744  // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX.  Can that
745  // be done using XCopyArea?  Perhaps similar to
746  // BackingStore::ScrollBackingStore?
747  if (about_to_validate_and_paint_)
748    invalid_rect_ = invalid_rect_.Union(scroll_rect);
749  else
750    Paint(scroll_rect);
751
752  for (size_t i = 0; i < copy_rects.size(); ++i) {
753    // Avoid double painting.  NOTE: This is only relevant given the call to
754    // Paint(scroll_rect) above.
755    gfx::Rect rect = copy_rects[i].Subtract(scroll_rect);
756    if (rect.IsEmpty())
757      continue;
758
759    if (about_to_validate_and_paint_)
760      invalid_rect_ = invalid_rect_.Union(rect);
761    else
762      Paint(rect);
763  }
764}
765
766void RenderWidgetHostViewGtk::RenderViewGone(base::TerminationStatus status,
767                                             int error_code) {
768  Destroy();
769  plugin_container_manager_.set_host_widget(NULL);
770}
771
772void RenderWidgetHostViewGtk::Destroy() {
773  if (compositing_surface_ != gfx::kNullPluginWindow) {
774    GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
775    manager->ReleasePermanentXID(compositing_surface_);
776  }
777
778  if (do_x_grab_) {
779    // Undo the X grab.
780    GdkDisplay* display = gtk_widget_get_display(parent_);
781    gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
782    gdk_display_keyboard_ungrab(display, GDK_CURRENT_TIME);
783  }
784
785  // If this is a popup or fullscreen widget, then we need to destroy the window
786  // that we created to hold it.
787  if (IsPopup() || is_fullscreen_) {
788    GtkWidget* window = gtk_widget_get_parent(view_.get());
789
790    // Disconnect the destroy handler so that we don't try to shutdown twice.
791    if (is_fullscreen_)
792      g_signal_handler_disconnect(window, destroy_handler_id_);
793
794    gtk_widget_destroy(window);
795  }
796
797  // Remove |view_| from all containers now, so nothing else can hold a
798  // reference to |view_|'s widget except possibly a gtk signal handler if
799  // this code is currently executing within the context of a gtk signal
800  // handler.  Note that |view_| is still alive after this call.  It will be
801  // deallocated in the destructor.
802  // See http://www.crbug.com/11847 for details.
803  gtk_widget_destroy(view_.get());
804
805  // The RenderWidgetHost's destruction led here, so don't call it.
806  host_ = NULL;
807
808  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
809}
810
811void RenderWidgetHostViewGtk::SetTooltipText(const std::wstring& tooltip_text) {
812  // Maximum number of characters we allow in a tooltip.
813  const int kMaxTooltipLength = 8 << 10;
814  // Clamp the tooltip length to kMaxTooltipLength so that we don't
815  // accidentally DOS the user with a mega tooltip (since GTK doesn't do
816  // this itself).
817  // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream.
818  const string16 clamped_tooltip =
819      l10n_util::TruncateString(WideToUTF16Hack(tooltip_text),
820                                kMaxTooltipLength);
821
822  if (clamped_tooltip.empty()) {
823    gtk_widget_set_has_tooltip(view_.get(), FALSE);
824  } else {
825    gtk_widget_set_tooltip_text(view_.get(),
826                                UTF16ToUTF8(clamped_tooltip).c_str());
827#if defined(OS_CHROMEOS)
828    tooltip_window_->SetTooltipText(UTF16ToWideHack(clamped_tooltip));
829#endif  // defined(OS_CHROMEOS)
830  }
831}
832
833void RenderWidgetHostViewGtk::SelectionChanged(const std::string& text) {
834  if (!text.empty()) {
835    GtkClipboard* x_clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
836    gtk_clipboard_set_text(x_clipboard, text.c_str(), text.length());
837  }
838}
839
840void RenderWidgetHostViewGtk::ShowingContextMenu(bool showing) {
841  is_showing_context_menu_ = showing;
842}
843
844#if !defined(TOOLKIT_VIEWS)
845void RenderWidgetHostViewGtk::AppendInputMethodsContextMenu(MenuGtk* menu) {
846  im_context_->AppendInputMethodsContextMenu(menu);
847}
848#endif
849
850gboolean RenderWidgetHostViewGtk::OnWindowStateEvent(
851    GtkWidget* widget,
852    GdkEventWindowState* event) {
853  if (is_fullscreen_) {
854    // If a fullscreen widget got unfullscreened (e.g. by the window manager),
855    // close it.
856    bool unfullscreened =
857        (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
858        !(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
859    if (unfullscreened) {
860      host_->Shutdown();
861      return TRUE;
862    }
863  }
864
865  return FALSE;
866}
867
868void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) {
869  DCHECK(is_fullscreen_);
870  host_->Shutdown();
871}
872
873bool RenderWidgetHostViewGtk::NeedsInputGrab() {
874  return popup_type_ == WebKit::WebPopupTypeSelect;
875}
876
877bool RenderWidgetHostViewGtk::IsPopup() const {
878  return popup_type_ != WebKit::WebPopupTypeNone;
879}
880
881void RenderWidgetHostViewGtk::DoSharedInit() {
882  view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this));
883  im_context_.reset(new GtkIMContextWrapper(this));
884  key_bindings_handler_.reset(new GtkKeyBindingsHandler(view_.get()));
885  plugin_container_manager_.set_host_widget(view_.get());
886#if defined(OS_CHROMEOS)
887  tooltip_window_.reset(new views::TooltipWindowGtk(view_.get()));
888#endif
889}
890
891void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow* window,
892                                                      const gfx::Rect& bounds) {
893  requested_size_.SetSize(std::min(bounds.width(), kMaxWindowWidth),
894                          std::min(bounds.height(), kMaxWindowHeight));
895  host_->WasResized();
896
897  gtk_widget_set_size_request(
898      view_.get(), requested_size_.width(), requested_size_.height());
899
900  // Don't allow the window to be resized. This also forces the window to
901  // shrink down to the size of its child contents.
902  gtk_window_set_resizable(window, FALSE);
903  gtk_window_set_default_size(window, -1, -1);
904  gtk_window_move(window, bounds.x(), bounds.y());
905
906  gtk_widget_show_all(GTK_WIDGET(window));
907}
908
909BackingStore* RenderWidgetHostViewGtk::AllocBackingStore(
910    const gfx::Size& size) {
911  return new BackingStoreX(host_, size,
912                           ui::GetVisualFromGtkWidget(view_.get()),
913                           gtk_widget_get_visual(view_.get())->depth);
914}
915
916void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) {
917  RenderWidgetHostView::SetBackground(background);
918  host_->Send(new ViewMsg_SetBackground(host_->routing_id(), background));
919}
920
921void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging(
922    GtkWidget* widget, GdkEventMotion* event) {
923  // If the widget is aligned with an edge of the monitor its on and the user
924  // attempts to drag past that edge we track the number of times it has
925  // occurred, so that we can force the widget to scroll when it otherwise
926  // would be unable to, by modifying the (x,y) position in the drag
927  // event that we forward on to webkit. If we get a move that's no longer a
928  // drag or a drag indicating the user is no longer at that edge we stop
929  // altering the drag events.
930  int new_dragged_at_horizontal_edge = 0;
931  int new_dragged_at_vertical_edge = 0;
932  // Used for checking the edges of the monitor. We cache the values to save
933  // roundtrips to the X server.
934  static gfx::Size drag_monitor_size;
935  if (event->state & GDK_BUTTON1_MASK) {
936    if (drag_monitor_size.IsEmpty()) {
937      // We can safely cache the monitor size for the duration of a drag.
938      GdkScreen* screen = gtk_widget_get_screen(widget);
939      int monitor =
940          gdk_screen_get_monitor_at_point(screen, event->x_root, event->y_root);
941      GdkRectangle geometry;
942      gdk_screen_get_monitor_geometry(screen, monitor, &geometry);
943      drag_monitor_size.SetSize(geometry.width, geometry.height);
944    }
945
946    // Check X and Y independently, as the user could be dragging into a corner.
947    if (event->x == 0 && event->x_root == 0) {
948      new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ - 1;
949    } else if (widget->allocation.width - 1 == static_cast<gint>(event->x) &&
950        drag_monitor_size.width() - 1 == static_cast<gint>(event->x_root)) {
951      new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ + 1;
952    }
953
954    if (event->y == 0 && event->y_root == 0) {
955      new_dragged_at_vertical_edge = dragged_at_vertical_edge_ - 1;
956    } else if (widget->allocation.height - 1 == static_cast<gint>(event->y) &&
957        drag_monitor_size.height() - 1 == static_cast<gint>(event->y_root)) {
958      new_dragged_at_vertical_edge = dragged_at_vertical_edge_ + 1;
959    }
960
961    event->x_root += new_dragged_at_horizontal_edge;
962    event->x += new_dragged_at_horizontal_edge;
963    event->y_root += new_dragged_at_vertical_edge;
964    event->y += new_dragged_at_vertical_edge;
965  } else {
966    // Clear whenever we get a non-drag mouse move.
967    drag_monitor_size.SetSize(0, 0);
968  }
969  dragged_at_horizontal_edge_ = new_dragged_at_horizontal_edge;
970  dragged_at_vertical_edge_ = new_dragged_at_vertical_edge;
971}
972
973void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) {
974  // If the GPU process is rendering directly into the View,
975  // call the compositor directly.
976  RenderWidgetHost* render_widget_host = GetRenderWidgetHost();
977  if (render_widget_host->is_accelerated_compositing_active()) {
978    host_->ScheduleComposite();
979    return;
980  }
981
982  GdkWindow* window = view_.get()->window;
983  DCHECK(!about_to_validate_and_paint_);
984
985  invalid_rect_ = damage_rect;
986  about_to_validate_and_paint_ = true;
987  BackingStoreX* backing_store = static_cast<BackingStoreX*>(
988      host_->GetBackingStore(true));
989  // Calling GetBackingStore maybe have changed |invalid_rect_|...
990  about_to_validate_and_paint_ = false;
991
992  gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight);
993  paint_rect = paint_rect.Intersect(invalid_rect_);
994
995  if (backing_store) {
996    // Only render the widget if it is attached to a window; there's a short
997    // period where this object isn't attached to a window but hasn't been
998    // Destroy()ed yet and it receives paint messages...
999    if (window) {
1000      if (SkColorGetA(overlay_color_) == 0) {
1001        // In the common case, use XCopyArea. We don't draw more than once, so
1002        // we don't need to double buffer.
1003        backing_store->XShowRect(gfx::Point(0, 0),
1004            paint_rect, ui::GetX11WindowFromGtkWidget(view_.get()));
1005      } else {
1006        // If the grey blend is showing, we make two drawing calls. Use double
1007        // buffering to prevent flicker. Use CairoShowRect because XShowRect
1008        // shortcuts GDK's double buffering. We won't be able to draw outside
1009        // of |damage_rect|, so invalidate the difference between |paint_rect|
1010        // and |damage_rect|.
1011        if (paint_rect != damage_rect) {
1012          GdkRectangle extra_gdk_rect =
1013              paint_rect.Subtract(damage_rect).ToGdkRectangle();
1014          gdk_window_invalidate_rect(window, &extra_gdk_rect, false);
1015        }
1016
1017        GdkRectangle rect = { damage_rect.x(), damage_rect.y(),
1018                              damage_rect.width(), damage_rect.height() };
1019        gdk_window_begin_paint_rect(window, &rect);
1020
1021        backing_store->CairoShowRect(damage_rect, GDK_DRAWABLE(window));
1022
1023        cairo_t* cr = gdk_cairo_create(window);
1024        gdk_cairo_rectangle(cr, &rect);
1025        SkColor overlay = SkColorSetA(
1026            overlay_color_,
1027            SkColorGetA(overlay_color_) *
1028                overlay_animation_.GetCurrentValue());
1029        float r = SkColorGetR(overlay) / 255.;
1030        float g = SkColorGetG(overlay) / 255.;
1031        float b = SkColorGetB(overlay) / 255.;
1032        float a = SkColorGetA(overlay) / 255.;
1033        cairo_set_source_rgba(cr, r, g, b, a);
1034        cairo_fill(cr);
1035        cairo_destroy(cr);
1036
1037        gdk_window_end_paint(window);
1038      }
1039    }
1040    if (!whiteout_start_time_.is_null()) {
1041      base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
1042          whiteout_start_time_;
1043      UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
1044
1045      // Reset the start time to 0 so that we start recording again the next
1046      // time the backing store is NULL...
1047      whiteout_start_time_ = base::TimeTicks();
1048    }
1049    if (!tab_switch_paint_time_.is_null()) {
1050      base::TimeDelta tab_switch_paint_duration = base::TimeTicks::Now() -
1051          tab_switch_paint_time_;
1052      UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
1053          tab_switch_paint_duration);
1054      // Reset tab_switch_paint_time_ to 0 so future tab selections are
1055      // recorded.
1056      tab_switch_paint_time_ = base::TimeTicks();
1057    }
1058  } else {
1059    if (window)
1060      gdk_window_clear(window);
1061    if (whiteout_start_time_.is_null())
1062      whiteout_start_time_ = base::TimeTicks::Now();
1063  }
1064}
1065
1066void RenderWidgetHostViewGtk::ShowCurrentCursor() {
1067  // The widget may not have a window. If that's the case, abort mission. This
1068  // is the same issue as that explained above in Paint().
1069  if (!view_.get()->window)
1070    return;
1071
1072  // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is
1073  // that calling gdk_window_set_cursor repeatedly is expensive.  We should
1074  // avoid it here where possible.
1075  GdkCursor* gdk_cursor;
1076  if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) {
1077    // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and
1078    // the page is loading.
1079    gdk_cursor = is_loading_ ? GetMozSpinningCursor() : NULL;
1080  } else {
1081    gdk_cursor = current_cursor_.GetNativeCursor();
1082  }
1083  gdk_window_set_cursor(view_.get()->window, gdk_cursor);
1084}
1085
1086void RenderWidgetHostViewGtk::CreatePluginContainer(
1087    gfx::PluginWindowHandle id) {
1088  plugin_container_manager_.CreatePluginContainer(id);
1089}
1090
1091void RenderWidgetHostViewGtk::DestroyPluginContainer(
1092    gfx::PluginWindowHandle id) {
1093  plugin_container_manager_.DestroyPluginContainer(id);
1094}
1095
1096void RenderWidgetHostViewGtk::SetVisuallyDeemphasized(
1097    const SkColor* color, bool animate) {
1098  // Do nothing unless |color| has changed, meaning |animate| is only
1099  // respected for the first call.
1100  if (color && (*color == overlay_color_))
1101    return;
1102
1103  overlay_color_ = color ? *color : 0;
1104
1105  if (animate) {
1106    overlay_animation_.Reset();
1107    overlay_animation_.Show();
1108  } else {
1109    overlay_animation_.Reset(1.0);
1110    gtk_widget_queue_draw(view_.get());
1111  }
1112}
1113
1114bool RenderWidgetHostViewGtk::ContainsNativeView(
1115    gfx::NativeView native_view) const {
1116  // TODO(port)
1117  NOTREACHED() <<
1118    "RenderWidgetHostViewGtk::ContainsNativeView not implemented.";
1119  return false;
1120}
1121
1122void RenderWidgetHostViewGtk::AcceleratedCompositingActivated(bool activated) {
1123  GtkPreserveWindow* widget =
1124    reinterpret_cast<GtkPreserveWindow*>(view_.get());
1125
1126  gtk_preserve_window_delegate_resize(widget, activated);
1127}
1128
1129gfx::PluginWindowHandle RenderWidgetHostViewGtk::GetCompositingSurface() {
1130  if (compositing_surface_ == gfx::kNullPluginWindow) {
1131    GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
1132    gfx::NativeViewId view_id = gfx::IdFromNativeView(GetNativeView());
1133
1134    if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) {
1135      DLOG(ERROR) << "Can't find XID for view id " << view_id;
1136    }
1137  }
1138  return compositing_surface_;
1139}
1140
1141void RenderWidgetHostViewGtk::ForwardKeyboardEvent(
1142    const NativeWebKeyboardEvent& event) {
1143  if (!host_)
1144    return;
1145
1146  EditCommands edit_commands;
1147  if (!event.skip_in_browser &&
1148      key_bindings_handler_->Match(event, &edit_commands)) {
1149    host_->ForwardEditCommandsForNextKeyEvent(edit_commands);
1150    NativeWebKeyboardEvent copy_event(event);
1151    copy_event.match_edit_command = true;
1152    host_->ForwardKeyboardEvent(copy_event);
1153    return;
1154  }
1155
1156  host_->ForwardKeyboardEvent(event);
1157}
1158
1159void RenderWidgetHostViewGtk::AnimationEnded(const ui::Animation* animation) {
1160  gtk_widget_queue_draw(view_.get());
1161}
1162
1163void RenderWidgetHostViewGtk::AnimationProgressed(
1164    const ui::Animation* animation) {
1165  gtk_widget_queue_draw(view_.get());
1166}
1167
1168void RenderWidgetHostViewGtk::AnimationCanceled(
1169    const ui::Animation* animation) {
1170  gtk_widget_queue_draw(view_.get());
1171}
1172
1173void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton* event) {
1174  GdkEventButton* temp = NULL;
1175  if (event) {
1176    temp = reinterpret_cast<GdkEventButton*>(
1177        gdk_event_copy(reinterpret_cast<GdkEvent*>(event)));
1178  }
1179
1180  if (last_mouse_down_)
1181    gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_));
1182
1183  last_mouse_down_ = temp;
1184}
1185
1186// static
1187RenderWidgetHostView*
1188    RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView(
1189        gfx::NativeView widget) {
1190  gpointer user_data = g_object_get_data(G_OBJECT(widget),
1191                                         kRenderWidgetHostViewKey);
1192  return reinterpret_cast<RenderWidgetHostView*>(user_data);
1193}
1194