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