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