Home | History | Annotate | Download | only in renderer_host
      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 
     51 namespace {
     52 
     53 const int kMaxWindowWidth = 4000;
     54 const int kMaxWindowHeight = 4000;
     55 const char* kRenderWidgetHostViewKey = "__RENDER_WIDGET_HOST_VIEW__";
     56 
     57 // The duration of the fade-out animation. See |overlay_animation_|.
     58 const 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.
     64 const float kDefaultScrollPixelsPerTick = 20;
     65 #else
     66 // See WebInputEventFactor.cpp for a reason for this being the default
     67 // scroll size for linux.
     68 const float kDefaultScrollPixelsPerTick = 160.0f / 3.0f;
     69 #endif
     70 
     71 // Returns the spinning cursor used for loading state.
     72 GdkCursor* 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 
     91 using WebKit::WebInputEventFactory;
     92 using WebKit::WebMouseWheelEvent;
     93 
     94 // This class is a simple convenience wrapper for Gtk functions. It has only
     95 // static methods.
     96 class 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
    495 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
    496     RenderWidgetHost* widget) {
    497   return new RenderWidgetHostViewGtk(widget);
    498 }
    499 
    500 RenderWidgetHostViewGtk::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 
    521 RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() {
    522   set_last_mouse_down(NULL);
    523   view_.Destroy();
    524 }
    525 
    526 void RenderWidgetHostViewGtk::InitAsChild() {
    527   DoSharedInit();
    528   overlay_animation_.SetDuration(kFadeEffectDuration);
    529   overlay_animation_.SetSlideDuration(kFadeEffectDuration);
    530   gtk_widget_show(view_.get());
    531 }
    532 
    533 void 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 
    577 void 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 
    602 RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const {
    603   return host_;
    604 }
    605 
    606 void 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 
    616 void 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 
    630 void 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 
    653 void 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 
    663 gfx::NativeView RenderWidgetHostViewGtk::GetNativeView() {
    664   return view_.get();
    665 }
    666 
    667 void 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 
    674 void RenderWidgetHostViewGtk::Focus() {
    675   gtk_widget_grab_focus(view_.get());
    676 }
    677 
    678 void 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 
    684 bool RenderWidgetHostViewGtk::HasFocus() {
    685   return gtk_widget_is_focus(view_.get());
    686 }
    687 
    688 void RenderWidgetHostViewGtk::Show() {
    689   gtk_widget_show(view_.get());
    690 }
    691 
    692 void RenderWidgetHostViewGtk::Hide() {
    693   gtk_widget_hide(view_.get());
    694 }
    695 
    696 bool 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 
    701 gfx::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 
    708 void 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 
    721 void 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 
    728 void RenderWidgetHostViewGtk::ImeUpdateTextInputState(
    729     WebKit::WebTextInputType type,
    730     const gfx::Rect& caret_rect) {
    731   im_context_->UpdateInputMethodState(type, caret_rect);
    732 }
    733 
    734 void RenderWidgetHostViewGtk::ImeCancelComposition() {
    735   im_context_->CancelComposition();
    736 }
    737 
    738 void 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 
    766 void RenderWidgetHostViewGtk::RenderViewGone(base::TerminationStatus status,
    767                                              int error_code) {
    768   Destroy();
    769   plugin_container_manager_.set_host_widget(NULL);
    770 }
    771 
    772 void 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 
    811 void 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 
    833 void 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 
    840 void RenderWidgetHostViewGtk::ShowingContextMenu(bool showing) {
    841   is_showing_context_menu_ = showing;
    842 }
    843 
    844 #if !defined(TOOLKIT_VIEWS)
    845 void RenderWidgetHostViewGtk::AppendInputMethodsContextMenu(MenuGtk* menu) {
    846   im_context_->AppendInputMethodsContextMenu(menu);
    847 }
    848 #endif
    849 
    850 gboolean 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 
    868 void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) {
    869   DCHECK(is_fullscreen_);
    870   host_->Shutdown();
    871 }
    872 
    873 bool RenderWidgetHostViewGtk::NeedsInputGrab() {
    874   return popup_type_ == WebKit::WebPopupTypeSelect;
    875 }
    876 
    877 bool RenderWidgetHostViewGtk::IsPopup() const {
    878   return popup_type_ != WebKit::WebPopupTypeNone;
    879 }
    880 
    881 void 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 
    891 void 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 
    909 BackingStore* 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 
    916 void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) {
    917   RenderWidgetHostView::SetBackground(background);
    918   host_->Send(new ViewMsg_SetBackground(host_->routing_id(), background));
    919 }
    920 
    921 void 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 
    973 void 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 
   1066 void 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 
   1086 void RenderWidgetHostViewGtk::CreatePluginContainer(
   1087     gfx::PluginWindowHandle id) {
   1088   plugin_container_manager_.CreatePluginContainer(id);
   1089 }
   1090 
   1091 void RenderWidgetHostViewGtk::DestroyPluginContainer(
   1092     gfx::PluginWindowHandle id) {
   1093   plugin_container_manager_.DestroyPluginContainer(id);
   1094 }
   1095 
   1096 void 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 
   1114 bool RenderWidgetHostViewGtk::ContainsNativeView(
   1115     gfx::NativeView native_view) const {
   1116   // TODO(port)
   1117   NOTREACHED() <<
   1118     "RenderWidgetHostViewGtk::ContainsNativeView not implemented.";
   1119   return false;
   1120 }
   1121 
   1122 void RenderWidgetHostViewGtk::AcceleratedCompositingActivated(bool activated) {
   1123   GtkPreserveWindow* widget =
   1124     reinterpret_cast<GtkPreserveWindow*>(view_.get());
   1125 
   1126   gtk_preserve_window_delegate_resize(widget, activated);
   1127 }
   1128 
   1129 gfx::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 
   1141 void 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 
   1159 void RenderWidgetHostViewGtk::AnimationEnded(const ui::Animation* animation) {
   1160   gtk_widget_queue_draw(view_.get());
   1161 }
   1162 
   1163 void RenderWidgetHostViewGtk::AnimationProgressed(
   1164     const ui::Animation* animation) {
   1165   gtk_widget_queue_draw(view_.get());
   1166 }
   1167 
   1168 void RenderWidgetHostViewGtk::AnimationCanceled(
   1169     const ui::Animation* animation) {
   1170   gtk_widget_queue_draw(view_.get());
   1171 }
   1172 
   1173 void 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
   1187 RenderWidgetHostView*
   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