root/content/browser/renderer_host/render_widget_host_view_gtk.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetMozSpinningCursor
  2. MovedToPoint
  3. GetAccessible
  4. CreateNewWidget
  5. OnExposeEvent
  6. OnRealize
  7. OnConfigureEvent
  8. OnSizeAllocate
  9. OnKeyPressReleaseEvent
  10. OnFocusIn
  11. OnFocusOut
  12. OnGrabNotify
  13. OnButtonPressReleaseEvent
  14. OnMouseMoveEvent
  15. OnCrossingEvent
  16. OnClientEvent
  17. GetPendingScrollDelta
  18. OnMouseScrollEvent
  19. last_mouse_down_
  20. OnMessageReceived
  21. InitAsChild
  22. InitAsPopup
  23. InitAsFullscreen
  24. GetRenderWidgetHost
  25. WasShown
  26. WasHidden
  27. SetSize
  28. SetBounds
  29. GetNativeView
  30. GetNativeViewId
  31. GetNativeViewAccessible
  32. MovePluginWindows
  33. Focus
  34. Blur
  35. HasFocus
  36. ActiveWindowChanged
  37. Send
  38. IsSurfaceAvailableForCopy
  39. Show
  40. Hide
  41. IsShowing
  42. GetViewBounds
  43. UpdateCursor
  44. SetIsLoading
  45. TextInputTypeChanged
  46. ImeCancelComposition
  47. DidUpdateBackingStore
  48. RenderProcessGone
  49. Destroy
  50. SetTooltipText
  51. SelectionChanged
  52. SelectionBoundsChanged
  53. ScrollOffsetChanged
  54. GetLastMouseDown
  55. BuildInputMethodsGtkMenu
  56. OnDestroy
  57. NeedsInputGrab
  58. IsPopup
  59. DoSharedInit
  60. DoPopupOrFullscreenInit
  61. AllocBackingStore
  62. CopyFromCompositingSurface
  63. CopyFromCompositingSurfaceToVideoFrame
  64. CanCopyToVideoFrame
  65. AcceleratedSurfaceInitialized
  66. AcceleratedSurfaceBuffersSwapped
  67. AcceleratedSurfacePostSubBuffer
  68. AcceleratedSurfaceSuspend
  69. AcceleratedSurfaceRelease
  70. HasAcceleratedSurface
  71. SetBackground
  72. ModifyEventForEdgeDragging
  73. Paint
  74. ShowCurrentCursor
  75. SetHasHorizontalScrollbar
  76. SetScrollOffsetPinning
  77. OnAcceleratedCompositingStateChange
  78. GetScreenInfo
  79. GetBoundsInRootWindow
  80. GetCompositingSurface
  81. ResizeCompositingSurface
  82. LockMouse
  83. UnlockMouse
  84. ForwardKeyboardEvent
  85. RetrieveSurrounding
  86. set_last_mouse_down
  87. MarkCachedWidgetCenterStale
  88. GetWidgetCenter
  89. ModifyEventMovementAndCoords
  90. CreateViewForWidget
  91. GetDefaultScreenInfo
  92. SetAccessibilityFocus
  93. AccessibilityDoDefaultAction
  94. AccessibilityScrollToMakeVisible
  95. AccessibilityScrollToPoint
  96. AccessibilitySetTextSelection
  97. GetLastTouchEventLocation
  98. FatalAccessibilityTreeError
  99. CreateBrowserAccessibilityManagerIfNeeded
  100. GetAccessible
  101. OnCreatePluginContainer
  102. OnDestroyPluginContainer
  103. PreferredReadbackFormat

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/render_widget_host_view_gtk.h"

#include <cairo/cairo.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>

#include <algorithm>
#include <string>

#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_offset_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "content/browser/accessibility/browser_accessibility_gtk.h"
#include "content/browser/accessibility/browser_accessibility_manager_gtk.h"
#include "content/browser/renderer_host/backing_store_gtk.h"
#include "content/browser/renderer_host/gtk_im_context_wrapper.h"
#include "content/browser/renderer_host/gtk_key_bindings_handler.h"
#include "content/browser/renderer_host/gtk_window_utils.h"
#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/common/cursors/webcursor_gtk_data.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/common/webplugin_geometry.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/common/content_switches.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/public/platform/WebScreenInfo.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/x/active_window_watcher_x.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/gtk_compat.h"
#include "ui/gfx/gtk_native_view_id_manager.h"
#include "ui/gfx/gtk_preserve_window.h"
#include "ui/gfx/text_elider.h"

using blink::WebMouseWheelEvent;
using blink::WebScreenInfo;

namespace content {
namespace {

// Paint rects on Linux are bounded by the maximum size of a shared memory
// region. By default that's 32MB, but many distros increase it significantly
// (i.e. to 256MB).
//
// We fetch the maximum value from /proc/sys/kernel/shmmax at runtime and, if
// we exceed that, then we limit the height of the paint rect in the renderer.
//
// These constants are here to ensure that, in the event that we exceed it, we
// end up with something a little more square. Previously we had 4000x4000, but
// people's monitor setups are actually exceeding that these days.
const int kMaxWindowWidth = 10000;
const int kMaxWindowHeight = 10000;

const GdkColor kBGColor =
#if defined(NDEBUG)
    { 0, 0xff * 257, 0xff * 257, 0xff * 257 };
#else
    { 0, 0x00 * 257, 0xff * 257, 0x00 * 257 };
#endif

// Returns the spinning cursor used for loading state.
GdkCursor* GetMozSpinningCursor() {
  static GdkCursor* moz_spinning_cursor = NULL;
  if (!moz_spinning_cursor) {
    const GdkColor fg = { 0, 0, 0, 0 };
    const GdkColor bg = { 65535, 65535, 65535, 65535 };
    GdkPixmap* source = gdk_bitmap_create_from_data(
        NULL, reinterpret_cast<const gchar*>(moz_spinning_bits), 32, 32);
    GdkPixmap* mask = gdk_bitmap_create_from_data(
        NULL, reinterpret_cast<const gchar*>(moz_spinning_mask_bits), 32, 32);
    moz_spinning_cursor =
        gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 2, 2);
    g_object_unref(source);
    g_object_unref(mask);
  }
  return moz_spinning_cursor;
}

bool MovedToPoint(const blink::WebMouseEvent& mouse_event,
                   const gfx::Point& center) {
  return mouse_event.globalX == center.x() &&
         mouse_event.globalY == center.y();
}

}  // namespace

// This class is a simple convenience wrapper for Gtk functions. It has only
// static methods.
class RenderWidgetHostViewGtkWidget {
 public:
  static AtkObject* GetAccessible(void* userdata) {
    return (static_cast<RenderWidgetHostViewGtk*>(userdata))->
        GetAccessible();
  }

  static GtkWidget* CreateNewWidget(RenderWidgetHostViewGtk* host_view) {
    GtkWidget* widget = gtk_preserve_window_new();
    gtk_widget_set_name(widget, "chrome-render-widget-host-view");
    // We manually double-buffer in Paint() because Paint() may or may not be
    // called in repsonse to an "expose-event" signal.
    gtk_widget_set_double_buffered(widget, FALSE);
    gtk_widget_set_redraw_on_allocate(widget, FALSE);
    gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &kBGColor);
    // Allow the browser window to be resized freely.
    gtk_widget_set_size_request(widget, 0, 0);

    gtk_widget_add_events(widget, GDK_EXPOSURE_MASK |
                                  GDK_STRUCTURE_MASK |
                                  GDK_POINTER_MOTION_MASK |
                                  GDK_BUTTON_PRESS_MASK |
                                  GDK_BUTTON_RELEASE_MASK |
                                  GDK_KEY_PRESS_MASK |
                                  GDK_KEY_RELEASE_MASK |
                                  GDK_FOCUS_CHANGE_MASK |
                                  GDK_ENTER_NOTIFY_MASK |
                                  GDK_LEAVE_NOTIFY_MASK);
    gtk_widget_set_can_focus(widget, TRUE);

    g_signal_connect(widget, "expose-event",
                     G_CALLBACK(OnExposeEvent), host_view);
    g_signal_connect(widget, "realize",
                     G_CALLBACK(OnRealize), host_view);
    g_signal_connect(widget, "configure-event",
                     G_CALLBACK(OnConfigureEvent), host_view);
    g_signal_connect(widget, "size-allocate",
                     G_CALLBACK(OnSizeAllocate), host_view);
    g_signal_connect(widget, "key-press-event",
                     G_CALLBACK(OnKeyPressReleaseEvent), host_view);
    g_signal_connect(widget, "key-release-event",
                     G_CALLBACK(OnKeyPressReleaseEvent), host_view);
    g_signal_connect(widget, "focus-in-event",
                     G_CALLBACK(OnFocusIn), host_view);
    g_signal_connect(widget, "focus-out-event",
                     G_CALLBACK(OnFocusOut), host_view);
    g_signal_connect(widget, "grab-notify",
                     G_CALLBACK(OnGrabNotify), host_view);
    g_signal_connect(widget, "button-press-event",
                     G_CALLBACK(OnButtonPressReleaseEvent), host_view);
    g_signal_connect(widget, "button-release-event",
                     G_CALLBACK(OnButtonPressReleaseEvent), host_view);
    g_signal_connect(widget, "motion-notify-event",
                     G_CALLBACK(OnMouseMoveEvent), host_view);
    g_signal_connect(widget, "enter-notify-event",
                     G_CALLBACK(OnCrossingEvent), host_view);
    g_signal_connect(widget, "leave-notify-event",
                     G_CALLBACK(OnCrossingEvent), host_view);
    g_signal_connect(widget, "client-event",
                     G_CALLBACK(OnClientEvent), host_view);


    // Connect after so that we are called after the handler installed by the
    // WebContentsView which handles zoom events.
    g_signal_connect_after(widget, "scroll-event",
                           G_CALLBACK(OnMouseScrollEvent), host_view);

    // Route calls to get_accessible to the view.
    gtk_preserve_window_set_accessible_factory(
        GTK_PRESERVE_WINDOW(widget), GetAccessible, host_view);

    return widget;
  }

 private:
  static gboolean OnExposeEvent(GtkWidget* widget,
                                GdkEventExpose* expose,
                                RenderWidgetHostViewGtk* host_view) {
    if (host_view->host_->is_hidden())
      return FALSE;
    const gfx::Rect damage_rect(expose->area);
    host_view->Paint(damage_rect);
    return FALSE;
  }

  static gboolean OnRealize(GtkWidget* widget,
                            RenderWidgetHostViewGtk* host_view) {
    // Use GtkSignalRegistrar to register events on a widget we don't
    // control the lifetime of, auto disconnecting at our end of our life.
    host_view->signals_.Connect(gtk_widget_get_toplevel(widget),
                                "configure-event",
                                G_CALLBACK(OnConfigureEvent), host_view);
    return FALSE;
  }

  static gboolean OnConfigureEvent(GtkWidget* widget,
                                   GdkEventConfigure* event,
                                   RenderWidgetHostViewGtk* host_view) {
    host_view->MarkCachedWidgetCenterStale();
    host_view->UpdateScreenInfo(host_view->GetNativeView());
    return FALSE;
  }

  static gboolean OnSizeAllocate(GtkWidget* widget,
                                 GdkRectangle* allocation,
                                 RenderWidgetHostViewGtk* host_view) {
    if (!host_view->IsPopup() && !host_view->is_fullscreen_)
      host_view->SetSize(gfx::Size(allocation->width, allocation->height));
    return FALSE;
  }

  static gboolean OnKeyPressReleaseEvent(GtkWidget* widget,
                                         GdkEventKey* event,
                                         RenderWidgetHostViewGtk* host_view) {
    TRACE_EVENT0("browser",
                 "RenderWidgetHostViewGtkWidget::OnKeyPressReleaseEvent");
    // Force popups or fullscreen windows to close on Escape so they won't keep
    // the keyboard grabbed or be stuck onscreen if the renderer is hanging.
    bool should_close_on_escape =
        (host_view->IsPopup() && host_view->NeedsInputGrab()) ||
        host_view->is_fullscreen_;
    if (should_close_on_escape && GDK_Escape == event->keyval) {
      host_view->host_->Shutdown();
    } else {
      // Send key event to input method.
      host_view->im_context_->ProcessKeyEvent(event);
    }

    // We return TRUE because we did handle the event. If it turns out webkit
    // can't handle the event, we'll deal with it in
    // RenderView::UnhandledKeyboardEvent().
    return TRUE;
  }

  static gboolean OnFocusIn(GtkWidget* widget,
                            GdkEventFocus* focus,
                            RenderWidgetHostViewGtk* host_view) {
    host_view->ShowCurrentCursor();
    RenderWidgetHostImpl* host =
        RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
    host->GotFocus();
    host->SetActive(true);

    // The only way to enable a GtkIMContext object is to call its focus in
    // handler.
    host_view->im_context_->OnFocusIn();

    return TRUE;
  }

  static gboolean OnFocusOut(GtkWidget* widget,
                             GdkEventFocus* focus,
                             RenderWidgetHostViewGtk* host_view) {
    // Whenever we lose focus, set the cursor back to that of our parent window,
    // which should be the default arrow.
    gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
    // If we are showing a context menu, maintain the illusion that webkit has
    // focus.
    if (!host_view->IsShowingContextMenu()) {
      RenderWidgetHostImpl* host =
          RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
      host->SetActive(false);
      host->Blur();
    }

    // Prevents us from stealing input context focus in OnGrabNotify() handler.
    host_view->was_imcontext_focused_before_grab_ = false;

    // Disable the GtkIMContext object.
    host_view->im_context_->OnFocusOut();

    host_view->set_last_mouse_down(NULL);

    return TRUE;
  }

  // Called when we are shadowed or unshadowed by a keyboard grab (which will
  // occur for activatable popups, such as dropdown menus). Popup windows do not
  // take focus, so we never get a focus out or focus in event when they are
  // shown, and must rely on this signal instead.
  static void OnGrabNotify(GtkWidget* widget, gboolean was_grabbed,
                           RenderWidgetHostViewGtk* host_view) {
    if (was_grabbed) {
      if (host_view->was_imcontext_focused_before_grab_)
        host_view->im_context_->OnFocusIn();
    } else {
      host_view->was_imcontext_focused_before_grab_ =
          host_view->im_context_->is_focused();
      if (host_view->was_imcontext_focused_before_grab_) {
        gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
        host_view->im_context_->OnFocusOut();
      }
    }
  }

  static gboolean OnButtonPressReleaseEvent(
      GtkWidget* widget,
      GdkEventButton* event,
      RenderWidgetHostViewGtk* host_view) {
    TRACE_EVENT0("browser",
                 "RenderWidgetHostViewGtkWidget::OnButtonPressReleaseEvent");

    if (event->type != GDK_BUTTON_RELEASE)
      host_view->set_last_mouse_down(event);

    if (!(event->button == 1 || event->button == 2 || event->button == 3))
      return FALSE;  // We do not forward any other buttons to the renderer.
    if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
      return FALSE;

    // If we don't have focus already, this mouse click will focus us.
    if (!gtk_widget_is_focus(widget))
      host_view->host_->OnPointerEventActivate();

    // Confirm existing composition text on mouse click events, to make sure
    // the input caret won't be moved with an ongoing composition session.
    if (event->type != GDK_BUTTON_RELEASE)
      host_view->im_context_->ConfirmComposition();

    // We want to translate the coordinates of events that do not originate
    // from this widget to be relative to the top left of the widget.
    GtkWidget* event_widget = gtk_get_event_widget(
        reinterpret_cast<GdkEvent*>(event));
    if (event_widget != widget) {
      int x = 0;
      int y = 0;
      gtk_widget_get_pointer(widget, &x, &y);
      // If the mouse event happens outside our popup, force the popup to
      // close.  We do this so a hung renderer doesn't prevent us from
      // releasing the x pointer grab.
      GtkAllocation allocation;
      gtk_widget_get_allocation(widget, &allocation);
      bool click_in_popup = x >= 0 && y >= 0 && x < allocation.width &&
          y < allocation.height;
      // Only Shutdown on mouse downs. Mouse ups can occur outside the render
      // view if the user drags for DnD or while using the scrollbar on a select
      // dropdown. Don't shutdown if we are not a popup.
      if (event->type != GDK_BUTTON_RELEASE && host_view->IsPopup() &&
          !host_view->is_popup_first_mouse_release_ && !click_in_popup) {
        host_view->host_->Shutdown();
        return FALSE;
      }
      event->x = x;
      event->y = y;
    }

    // TODO(evanm): why is this necessary here but not in test shell?
    // This logic is the same as GtkButton.
    if (event->type == GDK_BUTTON_PRESS && !gtk_widget_has_focus(widget))
      gtk_widget_grab_focus(widget);

    host_view->is_popup_first_mouse_release_ = false;
    RenderWidgetHostImpl* widget_host =
        RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
    if (widget_host)
      widget_host->ForwardMouseEvent(WebMouseEventBuilder::Build(event));

    // Although we did handle the mouse event, we need to let other handlers
    // run (in particular the one installed by WebContentsViewGtk).
    return FALSE;
  }

  static gboolean OnMouseMoveEvent(GtkWidget* widget,
                                   GdkEventMotion* event,
                                   RenderWidgetHostViewGtk* host_view) {
    TRACE_EVENT0("browser",
                 "RenderWidgetHostViewGtkWidget::OnMouseMoveEvent");
    // We want to translate the coordinates of events that do not originate
    // from this widget to be relative to the top left of the widget.
    GtkWidget* event_widget = gtk_get_event_widget(
        reinterpret_cast<GdkEvent*>(event));
    if (event_widget != widget) {
      int x = 0;
      int y = 0;
      gtk_widget_get_pointer(widget, &x, &y);
      event->x = x;
      event->y = y;
    }

    host_view->ModifyEventForEdgeDragging(widget, event);

    blink::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event);

    if (host_view->mouse_locked_) {
      gfx::Point center = host_view->GetWidgetCenter();

      bool moved_to_center = MovedToPoint(mouse_event, center);
      if (moved_to_center)
        host_view->mouse_has_been_warped_to_new_center_ = true;

      host_view->ModifyEventMovementAndCoords(&mouse_event);

      if (!moved_to_center &&
          (mouse_event.movementX || mouse_event.movementY)) {
        GdkDisplay* display = gtk_widget_get_display(widget);
        GdkScreen* screen = gtk_widget_get_screen(widget);
        gdk_display_warp_pointer(display, screen, center.x(), center.y());
        if (host_view->mouse_has_been_warped_to_new_center_)
          RenderWidgetHostImpl::From(
              host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
      }
    } else {  // Mouse is not locked.
      host_view->ModifyEventMovementAndCoords(&mouse_event);
      // Do not send mouse events while the mouse cursor is being warped back
      // to the unlocked location.
      if (!host_view->mouse_is_being_warped_to_unlocked_position_) {
        RenderWidgetHostImpl::From(
            host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
      }
    }
    return FALSE;
  }

  static gboolean OnCrossingEvent(GtkWidget* widget,
                                  GdkEventCrossing* event,
                                  RenderWidgetHostViewGtk* host_view) {
    TRACE_EVENT0("browser",
                 "RenderWidgetHostViewGtkWidget::OnCrossingEvent");
    const int any_button_mask =
        GDK_BUTTON1_MASK |
        GDK_BUTTON2_MASK |
        GDK_BUTTON3_MASK |
        GDK_BUTTON4_MASK |
        GDK_BUTTON5_MASK;

    // Only forward crossing events if the mouse button is not down.
    // (When the mouse button is down, the proper events are already being
    // sent by ButtonPressReleaseEvent and MouseMoveEvent, above, and if we
    // additionally send this crossing event with the state indicating the
    // button is down, it causes problems with drag and drop in WebKit.)
    if (!(event->state & any_button_mask)) {
      blink::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event);
      host_view->ModifyEventMovementAndCoords(&mouse_event);
      // When crossing out and back into a render view the movement values
      // must represent the instantaneous movement of the mouse, not the jump
      // from the exit to re-entry point.
      mouse_event.movementX = 0;
      mouse_event.movementY = 0;
      RenderWidgetHostImpl::From(
          host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
    }

    return FALSE;
  }

  static gboolean OnClientEvent(GtkWidget* widget,
                                GdkEventClient* event,
                                RenderWidgetHostViewGtk* host_view) {
    VLOG(1) << "client event type: " << event->message_type
            << " data_format: " << event->data_format
            << " data: " << event->data.l;
    return TRUE;
  }

  // Return the net up / down (or left / right) distance represented by events
  // in the  events will be removed from the queue. We only look at the top of
  // queue...any other type of event will cause us not to look farther.
  // If there is a change to the set of modifier keys or scroll axis
  // in the events we will stop looking as well.
  static int GetPendingScrollDelta(bool vert, guint current_event_state) {
    int num_clicks = 0;
    GdkEvent* event;
    bool event_coalesced = true;
    while ((event = gdk_event_get()) && event_coalesced) {
      event_coalesced = false;
      if (event->type == GDK_SCROLL) {
        GdkEventScroll scroll = event->scroll;
        if (scroll.state & GDK_SHIFT_MASK) {
          if (scroll.direction == GDK_SCROLL_UP)
            scroll.direction = GDK_SCROLL_LEFT;
          else if (scroll.direction == GDK_SCROLL_DOWN)
            scroll.direction = GDK_SCROLL_RIGHT;
        }
        if (vert) {
          if (scroll.direction == GDK_SCROLL_UP ||
              scroll.direction == GDK_SCROLL_DOWN) {
            if (scroll.state == current_event_state) {
              num_clicks += (scroll.direction == GDK_SCROLL_UP ? 1 : -1);
              gdk_event_free(event);
              event_coalesced = true;
            }
          }
        } else {
          if (scroll.direction == GDK_SCROLL_LEFT ||
              scroll.direction == GDK_SCROLL_RIGHT) {
            if (scroll.state == current_event_state) {
              num_clicks += (scroll.direction == GDK_SCROLL_LEFT ? 1 : -1);
              gdk_event_free(event);
              event_coalesced = true;
            }
          }
        }
      }
    }
    // If we have an event left we put it back on the queue.
    if (event) {
      gdk_event_put(event);
      gdk_event_free(event);
    }
    return num_clicks * WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
  }

  static gboolean OnMouseScrollEvent(GtkWidget* widget,
                                     GdkEventScroll* event,
                                     RenderWidgetHostViewGtk* host_view) {
    TRACE_EVENT0("browser",
                 "RenderWidgetHostViewGtkWidget::OnMouseScrollEvent");
    // If the user is holding shift, translate it into a horizontal scroll. We
    // don't care what other modifiers the user may be holding (zooming is
    // handled at the WebContentsView level).
    if (event->state & GDK_SHIFT_MASK) {
      if (event->direction == GDK_SCROLL_UP)
        event->direction = GDK_SCROLL_LEFT;
      else if (event->direction == GDK_SCROLL_DOWN)
        event->direction = GDK_SCROLL_RIGHT;
    }

    WebMouseWheelEvent web_event = WebMouseWheelEventBuilder::Build(event);
    const float pixelsPerTick =
        WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
    // We  peek ahead at the top of the queue to look for additional pending
    // scroll events.
    if (event->direction == GDK_SCROLL_UP ||
        event->direction == GDK_SCROLL_DOWN) {
      if (event->direction == GDK_SCROLL_UP)
        web_event.deltaY = pixelsPerTick;
      else
        web_event.deltaY = -pixelsPerTick;
      web_event.deltaY += GetPendingScrollDelta(true, event->state);
    } else {
      if (event->direction == GDK_SCROLL_LEFT)
        web_event.deltaX = pixelsPerTick;
      else
        web_event.deltaX = -pixelsPerTick;
      web_event.deltaX += GetPendingScrollDelta(false, event->state);
    }
    RenderWidgetHostImpl::From(
        host_view->GetRenderWidgetHost())->ForwardWheelEvent(web_event);
    return FALSE;
  }

  DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget);
};

RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host)
    : host_(RenderWidgetHostImpl::From(widget_host)),
      about_to_validate_and_paint_(false),
      is_loading_(false),
      parent_(NULL),
      is_popup_first_mouse_release_(true),
      was_imcontext_focused_before_grab_(false),
      do_x_grab_(false),
      is_fullscreen_(false),
      made_active_(false),
      mouse_is_being_warped_to_unlocked_position_(false),
      destroy_handler_id_(0),
      dragged_at_horizontal_edge_(0),
      dragged_at_vertical_edge_(0),
      compositing_surface_(gfx::kNullPluginWindow),
      last_mouse_down_(NULL) {
  host_->SetView(this);
}

RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() {
  UnlockMouse();
  set_last_mouse_down(NULL);
  view_.Destroy();
}

bool RenderWidgetHostViewGtk::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewGtk, message)
    IPC_MESSAGE_HANDLER(ViewHostMsg_CreatePluginContainer,
                        OnCreatePluginContainer)
    IPC_MESSAGE_HANDLER(ViewHostMsg_DestroyPluginContainer,
                        OnDestroyPluginContainer)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void RenderWidgetHostViewGtk::InitAsChild(
    gfx::NativeView parent_view) {
  DoSharedInit();
  gtk_widget_show(view_.get());
}

void RenderWidgetHostViewGtk::InitAsPopup(
    RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
  // If we aren't a popup, then |window| will be leaked.
  DCHECK(IsPopup());

  DoSharedInit();
  parent_ = parent_host_view->GetNativeView();
  GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP));
  gtk_container_add(GTK_CONTAINER(window), view_.get());
  DoPopupOrFullscreenInit(window, pos);

  // Grab all input for the app. If a click lands outside the bounds of the
  // popup, WebKit will notice and destroy us. The underlying X window needs to
  // be created and mapped by the above code before we can grab the input
  // devices.
  if (NeedsInputGrab()) {
    // If our parent is in a widget hierarchy that ends with a window, add
    // ourselves to the same window group to make sure that our GTK grab
    // covers it.
    GtkWidget* toplevel = gtk_widget_get_toplevel(parent_);
    if (toplevel &&
        GTK_WIDGET_TOPLEVEL(toplevel) &&
        GTK_IS_WINDOW(toplevel)) {
      gtk_window_group_add_window(
          gtk_window_get_group(GTK_WINDOW(toplevel)), window);
    }

    // Install an application-level GTK grab to make sure that we receive all of
    // the app's input.
    gtk_grab_add(view_.get());

    // We need to install an X grab as well. However if the app already has an X
    // grab (as in the case of extension popup), an app grab will suffice.
    do_x_grab_ = !gdk_pointer_is_grabbed();
    if (do_x_grab_) {
      // Install the grab on behalf our parent window if it and all of its
      // ancestors are mapped; otherwise, just use ourselves (maybe we're being
      // shown on behalf of an inactive tab).
      GdkWindow* grab_window = gtk_widget_get_window(parent_);
      if (!grab_window || !gdk_window_is_viewable(grab_window))
        grab_window = gtk_widget_get_window(view_.get());

      gdk_pointer_grab(
          grab_window,
          TRUE,  // Only events outside of the window are reported with
                 // respect to |parent_->window|.
          static_cast<GdkEventMask>(GDK_BUTTON_PRESS_MASK |
              GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK),
          NULL,
          NULL,
          GDK_CURRENT_TIME);
      // We grab keyboard events too so things like alt+tab are eaten.
      gdk_keyboard_grab(grab_window, TRUE, GDK_CURRENT_TIME);
    }
  }
}

void RenderWidgetHostViewGtk::InitAsFullscreen(
    RenderWidgetHostView* reference_host_view) {
  DCHECK(reference_host_view);
  DoSharedInit();

  is_fullscreen_ = true;
  GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
  gtk_window_set_decorated(window, FALSE);
  destroy_handler_id_ = g_signal_connect(GTK_WIDGET(window),
                                         "destroy",
                                         G_CALLBACK(OnDestroyThunk),
                                         this);
  gtk_container_add(GTK_CONTAINER(window), view_.get());

  // Try to move and resize the window to cover the screen in case the window
  // manager doesn't support _NET_WM_STATE_FULLSCREEN.
  GdkScreen* screen = gtk_window_get_screen(window);
  GdkWindow* ref_gdk_window = gtk_widget_get_window(
      reference_host_view->GetNativeView());

  gfx::Rect bounds;
  if (ref_gdk_window) {
    const int monitor_id = gdk_screen_get_monitor_at_window(screen,
                                                            ref_gdk_window);
    GdkRectangle monitor_rect;
    gdk_screen_get_monitor_geometry(screen, monitor_id, &monitor_rect);
    bounds = gfx::Rect(monitor_rect);
  } else {
    bounds = gfx::Rect(
        0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen));
  }
  gtk_window_move(window, bounds.x(), bounds.y());
  gtk_window_resize(window, bounds.width(), bounds.height());
  gtk_window_fullscreen(window);
  DoPopupOrFullscreenInit(window, bounds);
}

RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const {
  return host_;
}

void RenderWidgetHostViewGtk::WasShown() {
  if (!host_ || !host_->is_hidden())
    return;

  if (web_contents_switch_paint_time_.is_null())
    web_contents_switch_paint_time_ = base::TimeTicks::Now();

  host_->WasShown();
}

void RenderWidgetHostViewGtk::WasHidden() {
  if (!host_ || host_->is_hidden())
    return;

  // If we have a renderer, then inform it that we are being hidden so it can
  // reduce its resource utilization.
  host_->WasHidden();

  web_contents_switch_paint_time_ = base::TimeTicks();
}

void RenderWidgetHostViewGtk::SetSize(const gfx::Size& size) {
  int width = std::min(size.width(), kMaxWindowWidth);
  int height = std::min(size.height(), kMaxWindowHeight);
  if (IsPopup()) {
    // We're a popup, honor the size request.
    gtk_widget_set_size_request(view_.get(), width, height);
  }

  // Update the size of the RWH.
  if (requested_size_.width() != width ||
      requested_size_.height() != height) {
    requested_size_ = gfx::Size(width, height);
    host_->SendScreenRects();
    host_->WasResized();
  }
}

void RenderWidgetHostViewGtk::SetBounds(const gfx::Rect& rect) {
  // This is called when webkit has sent us a Move message.
  if (IsPopup()) {
    gtk_window_move(GTK_WINDOW(gtk_widget_get_toplevel(view_.get())),
                    rect.x(), rect.y());
  }

  SetSize(rect.size());
}

gfx::NativeView RenderWidgetHostViewGtk::GetNativeView() const {
  return view_.get();
}

gfx::NativeViewId RenderWidgetHostViewGtk::GetNativeViewId() const {
  return GtkNativeViewManager::GetInstance()->GetIdForWidget(view_.get());
}

gfx::NativeViewAccessible RenderWidgetHostViewGtk::GetNativeViewAccessible() {
  NOTIMPLEMENTED();
  return NULL;
}

void RenderWidgetHostViewGtk::MovePluginWindows(
    const gfx::Vector2d& scroll_offset,
    const std::vector<WebPluginGeometry>& moves) {
  for (size_t i = 0; i < moves.size(); ++i) {
    plugin_container_manager_.MovePluginContainer(moves[i]);
  }
}

void RenderWidgetHostViewGtk::Focus() {
  gtk_widget_grab_focus(view_.get());
}

void RenderWidgetHostViewGtk::Blur() {
  // TODO(estade): We should be clearing native focus as well, but I know of no
  // way to do that without focusing another widget.
  host_->Blur();
}

bool RenderWidgetHostViewGtk::HasFocus() const {
  return gtk_widget_has_focus(view_.get());
}

void RenderWidgetHostViewGtk::ActiveWindowChanged(GdkWindow* window) {
  GdkWindow* our_window = gtk_widget_get_parent_window(view_.get());

  if (our_window == window)
    made_active_ = true;

  // If the window was previously active, but isn't active anymore, shut it
  // down.
  if (is_fullscreen_ && our_window != window && made_active_)
    host_->Shutdown();
}

bool RenderWidgetHostViewGtk::Send(IPC::Message* message) {
  return host_->Send(message);
}

bool RenderWidgetHostViewGtk::IsSurfaceAvailableForCopy() const {
  return true;
}

void RenderWidgetHostViewGtk::Show() {
  gtk_widget_show(view_.get());
  WasShown();
}

void RenderWidgetHostViewGtk::Hide() {
  gtk_widget_hide(view_.get());
  WasHidden();
}

bool RenderWidgetHostViewGtk::IsShowing() {
  return gtk_widget_get_visible(view_.get());
}

gfx::Rect RenderWidgetHostViewGtk::GetViewBounds() const {
  GdkWindow* gdk_window = gtk_widget_get_window(view_.get());
  if (!gdk_window)
    return gfx::Rect(requested_size_);
  GdkRectangle window_rect;
  gdk_window_get_origin(gdk_window, &window_rect.x, &window_rect.y);
  return gfx::Rect(window_rect.x, window_rect.y,
                   requested_size_.width(), requested_size_.height());
}

void RenderWidgetHostViewGtk::UpdateCursor(const WebCursor& cursor) {
  // Optimize the common case, where the cursor hasn't changed.
  // However, we can switch between different pixmaps, so only on the
  // non-pixmap branch.
  if (current_cursor_.GetCursorType() != GDK_CURSOR_IS_PIXMAP &&
      current_cursor_.GetCursorType() == cursor.GetCursorType()) {
    return;
  }

  current_cursor_ = cursor;
  ShowCurrentCursor();
}

void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) {
  is_loading_ = is_loading;
  // Only call ShowCurrentCursor() when it will actually change the cursor.
  if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR)
    ShowCurrentCursor();
}

void RenderWidgetHostViewGtk::TextInputTypeChanged(
    ui::TextInputType type,
    ui::TextInputMode input_mode,
    bool can_compose_inline) {
  im_context_->UpdateInputMethodState(type, can_compose_inline);
}

void RenderWidgetHostViewGtk::ImeCancelComposition() {
  im_context_->CancelComposition();
}

void RenderWidgetHostViewGtk::DidUpdateBackingStore(
    const gfx::Rect& scroll_rect,
    const gfx::Vector2d& scroll_delta,
    const std::vector<gfx::Rect>& copy_rects,
    const std::vector<ui::LatencyInfo>& latency_info) {
  TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::DidUpdateBackingStore");
  for (size_t i = 0; i < latency_info.size(); i++)
    software_latency_info_.push_back(latency_info[i]);

  if (host_->is_hidden())
    return;

  // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX.  Can that
  // be done using XCopyArea?  Perhaps similar to
  // BackingStore::ScrollBackingStore?
  if (about_to_validate_and_paint_)
    invalid_rect_.Union(scroll_rect);
  else
    Paint(scroll_rect);

  for (size_t i = 0; i < copy_rects.size(); ++i) {
    // Avoid double painting.  NOTE: This is only relevant given the call to
    // Paint(scroll_rect) above.
    gfx::Rect rect = gfx::SubtractRects(copy_rects[i], scroll_rect);
    if (rect.IsEmpty())
      continue;

    if (about_to_validate_and_paint_)
      invalid_rect_.Union(rect);
    else
      Paint(rect);
  }
}

void RenderWidgetHostViewGtk::RenderProcessGone(base::TerminationStatus status,
                                                int error_code) {
  Destroy();
  plugin_container_manager_.set_host_widget(NULL);
}

void RenderWidgetHostViewGtk::Destroy() {
  if (compositing_surface_ != gfx::kNullPluginWindow) {
    GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
    manager->ReleasePermanentXID(compositing_surface_);
  }

  if (do_x_grab_) {
    // Undo the X grab.
    GdkDisplay* display = gtk_widget_get_display(parent_);
    gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
    gdk_display_keyboard_ungrab(display, GDK_CURRENT_TIME);
  }

  if (view_.get()) {
    // If this is a popup or fullscreen widget, then we need to destroy the
    // window that we created to hold it.
    if (IsPopup() || is_fullscreen_) {
      GtkWidget* window = gtk_widget_get_parent(view_.get());

      ui::ActiveWindowWatcherX::RemoveObserver(this);

      // Disconnect the destroy handler so that we don't try to shutdown twice.
      if (is_fullscreen_)
        g_signal_handler_disconnect(window, destroy_handler_id_);

      gtk_widget_destroy(window);
    }

    // Remove |view_| from all containers now, so nothing else can hold a
    // reference to |view_|'s widget except possibly a gtk signal handler if
    // this code is currently executing within the context of a gtk signal
    // handler.  Note that |view_| is still alive after this call.  It will be
    // deallocated in the destructor.
    // See http://crbug.com/11847 for details.
    gtk_widget_destroy(view_.get());
  }

  // The RenderWidgetHost's destruction led here, so don't call it.
  host_ = NULL;

  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}

void RenderWidgetHostViewGtk::SetTooltipText(
    const base::string16& tooltip_text) {
  // Maximum number of characters we allow in a tooltip.
  const int kMaxTooltipLength = 8 << 10;
  // Clamp the tooltip length to kMaxTooltipLength so that we don't
  // accidentally DOS the user with a mega tooltip (since GTK doesn't do
  // this itself).
  // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream.
  const base::string16 clamped_tooltip =
      gfx::TruncateString(tooltip_text, kMaxTooltipLength);

  if (clamped_tooltip.empty()) {
    gtk_widget_set_has_tooltip(view_.get(), FALSE);
  } else {
    gtk_widget_set_tooltip_text(view_.get(),
                                base::UTF16ToUTF8(clamped_tooltip).c_str());
  }
}

void RenderWidgetHostViewGtk::SelectionChanged(const base::string16& text,
                                               size_t offset,
                                               const gfx::Range& range) {
  RenderWidgetHostViewBase::SelectionChanged(text, offset, range);

  if (text.empty() || range.is_empty())
    return;
  size_t pos = range.GetMin() - offset;
  size_t n = range.length();

  DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
  if (pos >= text.length()) {
    NOTREACHED() << "The text can not cover range.";
    return;
  }

  // Set the CLIPBOARD_TYPE SELECTION to the ui::Clipboard.
  ui::ScopedClipboardWriter clipboard_writer(
      ui::Clipboard::GetForCurrentThread(),
      ui::CLIPBOARD_TYPE_SELECTION);
  clipboard_writer.WriteText(text.substr(pos, n));
}

void RenderWidgetHostViewGtk::SelectionBoundsChanged(
    const ViewHostMsg_SelectionBounds_Params& params) {
  im_context_->UpdateCaretBounds(
      gfx::UnionRects(params.anchor_rect, params.focus_rect));
}

void RenderWidgetHostViewGtk::ScrollOffsetChanged() {
}

GdkEventButton* RenderWidgetHostViewGtk::GetLastMouseDown() {
  return last_mouse_down_;
}

gfx::NativeView RenderWidgetHostViewGtk::BuildInputMethodsGtkMenu() {
  return im_context_->BuildInputMethodsGtkMenu();
}

void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) {
  DCHECK(is_fullscreen_);
  host_->Shutdown();
}

bool RenderWidgetHostViewGtk::NeedsInputGrab() {
  return popup_type_ == blink::WebPopupTypeSelect;
}

bool RenderWidgetHostViewGtk::IsPopup() const {
  return popup_type_ != blink::WebPopupTypeNone;
}

void RenderWidgetHostViewGtk::DoSharedInit() {
  view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this));
  im_context_.reset(new GtkIMContextWrapper(this));
  plugin_container_manager_.set_host_widget(view_.get());
  key_bindings_handler_.reset(new GtkKeyBindingsHandler(view_.get()));
}

void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow* window,
                                                      const gfx::Rect& bounds) {
  requested_size_.SetSize(std::min(bounds.width(), kMaxWindowWidth),
                          std::min(bounds.height(), kMaxWindowHeight));
  host_->WasResized();

  ui::ActiveWindowWatcherX::AddObserver(this);

  // Don't set the size when we're going fullscreen. This can confuse the
  // window manager into thinking we're resizing a fullscreen window and
  // therefore not fullscreen anymore.
  if (!is_fullscreen_) {
    gtk_widget_set_size_request(
        view_.get(), requested_size_.width(), requested_size_.height());

    // Don't allow the window to be resized. This also forces the window to
    // shrink down to the size of its child contents.
    gtk_window_set_resizable(window, FALSE);
    gtk_window_set_default_size(window, -1, -1);
    gtk_window_move(window, bounds.x(), bounds.y());
  }

  gtk_widget_show_all(GTK_WIDGET(window));
}

BackingStore* RenderWidgetHostViewGtk::AllocBackingStore(
    const gfx::Size& size) {
  gint depth = gdk_visual_get_depth(gtk_widget_get_visual(view_.get()));
  return new BackingStoreGtk(host_, size,
                             ui::GetVisualFromGtkWidget(view_.get()),
                             depth);
}

// NOTE: |output| is initialized with the size of |src_subrect|, and |dst_size|
// is ignored on GTK.
void RenderWidgetHostViewGtk::CopyFromCompositingSurface(
    const gfx::Rect& src_subrect,
    const gfx::Size& /* dst_size */,
    const base::Callback<void(bool, const SkBitmap&)>& callback,
    SkBitmap::Config config) {
  if (config != SkBitmap::kARGB_8888_Config) {
    NOTIMPLEMENTED();
    callback.Run(false, SkBitmap());
  }
  // Grab the snapshot from the renderer as that's the only reliable way to
  // readback from the GPU for this platform right now.
  GetRenderWidgetHost()->GetSnapshotFromRenderer(src_subrect, callback);
}

void RenderWidgetHostViewGtk::CopyFromCompositingSurfaceToVideoFrame(
      const gfx::Rect& src_subrect,
      const scoped_refptr<media::VideoFrame>& target,
      const base::Callback<void(bool)>& callback) {
  NOTIMPLEMENTED();
  callback.Run(false);
}

bool RenderWidgetHostViewGtk::CanCopyToVideoFrame() const {
  return false;
}

void RenderWidgetHostViewGtk::AcceleratedSurfaceInitialized(int host_id,
                                                            int route_id) {
}

void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped(
    const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
    int gpu_host_id) {
  AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
  ack_params.sync_point = 0;
  RenderWidgetHostImpl::AcknowledgeBufferPresent(
      params.route_id, gpu_host_id, ack_params);
  RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info);
}

void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer(
    const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
    int gpu_host_id) {
  AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
  ack_params.sync_point = 0;
  RenderWidgetHostImpl::AcknowledgeBufferPresent(
      params.route_id, gpu_host_id, ack_params);
  RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info);
}

void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() {
}

void RenderWidgetHostViewGtk::AcceleratedSurfaceRelease() {
}

bool RenderWidgetHostViewGtk::HasAcceleratedSurface(
      const gfx::Size& desired_size) {
  // TODO(jbates) Implement this so this view can use GetBackingStore for both
  // software and GPU frames. Defaulting to false just makes GetBackingStore
  // only useable for software frames.
  return false;
}

void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) {
  RenderWidgetHostViewBase::SetBackground(background);
  Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background));
}

void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging(
    GtkWidget* widget, GdkEventMotion* event) {
  // If the widget is aligned with an edge of the monitor its on and the user
  // attempts to drag past that edge we track the number of times it has
  // occurred, so that we can force the widget to scroll when it otherwise
  // would be unable to, by modifying the (x,y) position in the drag
  // event that we forward on to webkit. If we get a move that's no longer a
  // drag or a drag indicating the user is no longer at that edge we stop
  // altering the drag events.
  int new_dragged_at_horizontal_edge = 0;
  int new_dragged_at_vertical_edge = 0;
  // Used for checking the edges of the monitor. We cache the values to save
  // roundtrips to the X server.
  CR_DEFINE_STATIC_LOCAL(gfx::Size, drag_monitor_size, ());
  if (event->state & GDK_BUTTON1_MASK) {
    if (drag_monitor_size.IsEmpty()) {
      // We can safely cache the monitor size for the duration of a drag.
      GdkScreen* screen = gtk_widget_get_screen(widget);
      int monitor =
          gdk_screen_get_monitor_at_point(screen, event->x_root, event->y_root);
      GdkRectangle geometry;
      gdk_screen_get_monitor_geometry(screen, monitor, &geometry);
      drag_monitor_size.SetSize(geometry.width, geometry.height);
    }
    GtkAllocation allocation;
    gtk_widget_get_allocation(widget, &allocation);
    // Check X and Y independently, as the user could be dragging into a corner.
    if (event->x == 0 && event->x_root == 0) {
      new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ - 1;
    } else if (allocation.width - 1 == static_cast<gint>(event->x) &&
        drag_monitor_size.width() - 1 == static_cast<gint>(event->x_root)) {
      new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ + 1;
    }

    if (event->y == 0 && event->y_root == 0) {
      new_dragged_at_vertical_edge = dragged_at_vertical_edge_ - 1;
    } else if (allocation.height - 1 == static_cast<gint>(event->y) &&
        drag_monitor_size.height() - 1 == static_cast<gint>(event->y_root)) {
      new_dragged_at_vertical_edge = dragged_at_vertical_edge_ + 1;
    }

    event->x_root += new_dragged_at_horizontal_edge;
    event->x += new_dragged_at_horizontal_edge;
    event->y_root += new_dragged_at_vertical_edge;
    event->y += new_dragged_at_vertical_edge;
  } else {
    // Clear whenever we get a non-drag mouse move.
    drag_monitor_size.SetSize(0, 0);
  }
  dragged_at_horizontal_edge_ = new_dragged_at_horizontal_edge;
  dragged_at_vertical_edge_ = new_dragged_at_vertical_edge;
}

void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) {
  TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::Paint");

  // If the GPU process is rendering directly into the View,
  // call the compositor directly.
  RenderWidgetHostImpl* render_widget_host =
      RenderWidgetHostImpl::From(GetRenderWidgetHost());
  if (render_widget_host->is_accelerated_compositing_active()) {
    host_->ScheduleComposite();
    return;
  }

  GdkWindow* window = gtk_widget_get_window(view_.get());
  DCHECK(!about_to_validate_and_paint_);

  invalid_rect_ = damage_rect;
  about_to_validate_and_paint_ = true;

  // If the size of our canvas is (0,0), then we don't want to block here. We
  // are doing one of our first paints and probably have animations going on.
  bool force_create = !host_->empty();
  BackingStoreGtk* backing_store = static_cast<BackingStoreGtk*>(
      host_->GetBackingStore(force_create));
  // Calling GetBackingStore maybe have changed |invalid_rect_|...
  about_to_validate_and_paint_ = false;

  gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight);
  paint_rect.Intersect(invalid_rect_);

  if (backing_store) {
    // Only render the widget if it is attached to a window; there's a short
    // period where this object isn't attached to a window but hasn't been
    // Destroy()ed yet and it receives paint messages...
    if (window) {
      backing_store->XShowRect(gfx::Point(0, 0),
          paint_rect, ui::GetX11WindowFromGtkWidget(view_.get()));
    }
    if (!whiteout_start_time_.is_null()) {
      base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
          whiteout_start_time_;
      UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);

      // Reset the start time to 0 so that we start recording again the next
      // time the backing store is NULL...
      whiteout_start_time_ = base::TimeTicks();
    }
    if (!web_contents_switch_paint_time_.is_null()) {
      base::TimeDelta web_contents_switch_paint_duration =
          base::TimeTicks::Now() - web_contents_switch_paint_time_;
      UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
          web_contents_switch_paint_duration);
      // Reset web_contents_switch_paint_time_ to 0 so future tab selections are
      // recorded.
      web_contents_switch_paint_time_ = base::TimeTicks();
    }

    for (size_t i = 0; i < software_latency_info_.size(); i++) {
      software_latency_info_[i].AddLatencyNumber(
          ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
      render_widget_host->FrameSwapped(software_latency_info_[i]);
    }
    software_latency_info_.clear();
  } else {
    if (window)
      gdk_window_clear(window);
    if (whiteout_start_time_.is_null())
      whiteout_start_time_ = base::TimeTicks::Now();
  }
}

void RenderWidgetHostViewGtk::ShowCurrentCursor() {
  // The widget may not have a window. If that's the case, abort mission. This
  // is the same issue as that explained above in Paint().
  if (!gtk_widget_get_window(view_.get()))
    return;

  // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is
  // that calling gdk_window_set_cursor repeatedly is expensive.  We should
  // avoid it here where possible.
  GdkCursor* gdk_cursor;
  if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) {
    // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and
    // the page is loading.
    gdk_cursor = is_loading_ ? GetMozSpinningCursor() : NULL;
  } else {
    gdk_cursor = current_cursor_.GetNativeCursor();
  }
  gdk_window_set_cursor(gtk_widget_get_window(view_.get()), gdk_cursor);
}

void RenderWidgetHostViewGtk::SetHasHorizontalScrollbar(
    bool has_horizontal_scrollbar) {
}

void RenderWidgetHostViewGtk::SetScrollOffsetPinning(
    bool is_pinned_to_left, bool is_pinned_to_right) {
}


void RenderWidgetHostViewGtk::OnAcceleratedCompositingStateChange() {
  bool activated = host_->is_accelerated_compositing_active();
  GtkPreserveWindow* widget = reinterpret_cast<GtkPreserveWindow*>(view_.get());

  gtk_preserve_window_delegate_resize(widget, activated);
}

void RenderWidgetHostViewGtk::GetScreenInfo(WebScreenInfo* results) {
  GdkWindow* gdk_window = gtk_widget_get_window(view_.get());
  if (!gdk_window) {
    GdkDisplay* display = gdk_display_get_default();
    gdk_window = gdk_display_get_default_group(display);
  }
  if (!gdk_window)
    return;
  GetScreenInfoFromNativeWindow(gdk_window, results);
}

gfx::Rect RenderWidgetHostViewGtk::GetBoundsInRootWindow() {
  GtkWidget* toplevel = gtk_widget_get_toplevel(view_.get());
  if (!toplevel)
    return GetViewBounds();

  GdkRectangle frame_extents;
  GdkWindow* gdk_window = gtk_widget_get_window(toplevel);
  if (!gdk_window)
    return GetViewBounds();

  gdk_window_get_frame_extents(gdk_window, &frame_extents);
  return gfx::Rect(frame_extents.x, frame_extents.y,
                   frame_extents.width, frame_extents.height);
}

gfx::GLSurfaceHandle RenderWidgetHostViewGtk::GetCompositingSurface() {
  if (compositing_surface_ == gfx::kNullPluginWindow) {
    GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
    gfx::NativeViewId view_id = GetNativeViewId();

    if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) {
      DLOG(ERROR) << "Can't find XID for view id " << view_id;
    }
  }
  return gfx::GLSurfaceHandle(compositing_surface_, gfx::NATIVE_TRANSPORT);
}

void RenderWidgetHostViewGtk::ResizeCompositingSurface(const gfx::Size& size) {
  GtkWidget* widget = view_.get();
  GdkWindow* window = gtk_widget_get_window(widget);
  if (window) {
    Display* display = GDK_WINDOW_XDISPLAY(window);
    gdk_window_resize(window, size.width(), size.height());
    XSync(display, False);
  }
}

bool RenderWidgetHostViewGtk::LockMouse() {
  if (mouse_locked_)
    return true;

  mouse_locked_ = true;

  // Release any current grab.
  GtkWidget* current_grab_window = gtk_grab_get_current();
  if (current_grab_window) {
    gtk_grab_remove(current_grab_window);
    LOG(WARNING) << "Locking Mouse with gdk_pointer_grab, "
                 << "but had to steal grab from another window";
  }

  GtkWidget* widget = view_.get();
  GdkWindow* window = gtk_widget_get_window(widget);
  GdkCursor* cursor = gdk_cursor_new(GDK_BLANK_CURSOR);

  GdkGrabStatus grab_status =
      gdk_pointer_grab(window,
                       FALSE,  // owner_events
                       static_cast<GdkEventMask>(
                           GDK_POINTER_MOTION_MASK |
                           GDK_BUTTON_PRESS_MASK |
                           GDK_BUTTON_RELEASE_MASK),
                       window,  // confine_to
                       cursor,
                       GDK_CURRENT_TIME);

  if (grab_status != GDK_GRAB_SUCCESS) {
    LOG(WARNING) << "Failed to grab pointer for LockMouse. "
                 << "gdk_pointer_grab returned: " << grab_status;
    mouse_locked_ = false;
    return false;
  }

  // Clear the tooltip window.
  SetTooltipText(base::string16());

  // Ensure that the widget center location will be relevant for this mouse
  // lock session. It is updated whenever the window geometry moves
  // but may be out of date due to switching tabs.
  MarkCachedWidgetCenterStale();

  // Ensure that if we were previously warping the cursor to a specific point
  // that we no longer track doing so when entering lock. It should be cleared
  // by the cursor moving to the warp point, and this shouldn't be necessary.
  // But, this is a small effort to ensure robustness in the event a warp isn't
  // completed.
  mouse_is_being_warped_to_unlocked_position_ = false;

  return true;
}

void RenderWidgetHostViewGtk::UnlockMouse() {
  if (!mouse_locked_)
    return;

  mouse_locked_ = false;

  GtkWidget* widget = view_.get();
  GdkDisplay* display = gtk_widget_get_display(widget);
  GdkScreen* screen = gtk_widget_get_screen(widget);
  gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
  gdk_display_warp_pointer(display, screen,
                           unlocked_global_mouse_position_.x(),
                           unlocked_global_mouse_position_.y());
  mouse_is_being_warped_to_unlocked_position_ = true;

  if (host_)
    host_->LostMouseLock();
}

void RenderWidgetHostViewGtk::ForwardKeyboardEvent(
    const NativeWebKeyboardEvent& event) {
  if (!host_)
    return;

  EditCommands edit_commands;
  if (!event.skip_in_browser &&
      key_bindings_handler_->Match(event, &edit_commands)) {
    Send(new InputMsg_SetEditCommandsForNextKeyEvent(
        host_->GetRoutingID(), edit_commands));
    NativeWebKeyboardEvent copy_event(event);
    copy_event.match_edit_command = true;
    host_->ForwardKeyboardEvent(copy_event);
    return;
  }

  host_->ForwardKeyboardEvent(event);
}

bool RenderWidgetHostViewGtk::RetrieveSurrounding(std::string* text,
                                                  size_t* cursor_index) {
  if (!selection_range_.IsValid())
    return false;

  size_t offset = selection_range_.GetMin() - selection_text_offset_;
  DCHECK(offset <= selection_text_.length());

  if (offset == selection_text_.length()) {
    *text = base::UTF16ToUTF8(selection_text_);
    *cursor_index = text->length();
    return true;
  }

  *text = base::UTF16ToUTF8AndAdjustOffset(
      base::StringPiece16(selection_text_), &offset);
  if (offset == base::string16::npos) {
    NOTREACHED() << "Invalid offset in UTF16 string.";
    return false;
  }
  *cursor_index = offset;
  return true;
}

void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton* event) {
  GdkEventButton* temp = NULL;
  if (event) {
    temp = reinterpret_cast<GdkEventButton*>(
        gdk_event_copy(reinterpret_cast<GdkEvent*>(event)));
  }

  if (last_mouse_down_)
    gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_));

  last_mouse_down_ = temp;
}

void RenderWidgetHostViewGtk::MarkCachedWidgetCenterStale() {
  widget_center_valid_ = false;
  mouse_has_been_warped_to_new_center_ = false;
}

gfx::Point RenderWidgetHostViewGtk::GetWidgetCenter() {
  if (widget_center_valid_)
    return widget_center_;

  GdkWindow* window = gtk_widget_get_window(view_.get());
  gint window_x = 0;
  gint window_y = 0;
  gdk_window_get_origin(window, &window_x, &window_y);
  gint window_w = gdk_window_get_width(window);
  gint window_h = gdk_window_get_height(window);
  widget_center_.SetPoint(window_x + window_w / 2,
                          window_y + window_h / 2);
  widget_center_valid_ = true;
  return widget_center_;
}

void RenderWidgetHostViewGtk::ModifyEventMovementAndCoords(
    blink::WebMouseEvent* event) {
  // Movement is computed by taking the difference of the new cursor position
  // and the previous. Under mouse lock the cursor will be warped back to the
  // center so that we are not limited by clipping boundaries.
  // We do not measure movement as the delta from cursor to center because
  // we may receive more mouse movement events before our warp has taken
  // effect.
  event->movementX = event->globalX - global_mouse_position_.x();
  event->movementY = event->globalY - global_mouse_position_.y();

  // While the cursor is being warped back to the unlocked position, suppress
  // the movement member data.
  if (mouse_is_being_warped_to_unlocked_position_) {
    event->movementX = 0;
    event->movementY = 0;
    if (MovedToPoint(*event, unlocked_global_mouse_position_))
      mouse_is_being_warped_to_unlocked_position_ = false;
  }

  global_mouse_position_.SetPoint(event->globalX, event->globalY);

  // Under mouse lock, coordinates of mouse are locked to what they were when
  // mouse lock was entered.
  if (mouse_locked_) {
    event->x = unlocked_mouse_position_.x();
    event->y = unlocked_mouse_position_.y();
    event->windowX = unlocked_mouse_position_.x();
    event->windowY = unlocked_mouse_position_.y();
    event->globalX = unlocked_global_mouse_position_.x();
    event->globalY = unlocked_global_mouse_position_.y();
  } else if (!mouse_is_being_warped_to_unlocked_position_) {
    unlocked_mouse_position_.SetPoint(event->windowX, event->windowY);
    unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY);
  }
}

////////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostView, public:

// static
RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
    RenderWidgetHost* widget) {
  return new RenderWidgetHostViewGtk(widget);
}

// static
void RenderWidgetHostViewPort::GetDefaultScreenInfo(WebScreenInfo* results) {
  GdkWindow* gdk_window =
      gdk_display_get_default_group(gdk_display_get_default());
  GetScreenInfoFromNativeWindow(gdk_window, results);
}

void RenderWidgetHostViewGtk::SetAccessibilityFocus(int acc_obj_id) {
  if (!host_)
    return;

  host_->AccessibilitySetFocus(acc_obj_id);
}

void RenderWidgetHostViewGtk::AccessibilityDoDefaultAction(int acc_obj_id) {
  if (!host_)
    return;

  host_->AccessibilityDoDefaultAction(acc_obj_id);
}

void RenderWidgetHostViewGtk::AccessibilityScrollToMakeVisible(
    int acc_obj_id, gfx::Rect subfocus) {
  if (!host_)
    return;

  host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
}

void RenderWidgetHostViewGtk::AccessibilityScrollToPoint(
    int acc_obj_id, gfx::Point point) {
  if (!host_)
    return;

  host_->AccessibilityScrollToPoint(acc_obj_id, point);
}

void RenderWidgetHostViewGtk::AccessibilitySetTextSelection(
    int acc_obj_id, int start_offset, int end_offset) {
  if (!host_)
    return;

  host_->AccessibilitySetTextSelection(acc_obj_id, start_offset, end_offset);
}

gfx::Point RenderWidgetHostViewGtk::GetLastTouchEventLocation() const {
  // Not needed on Linux.
  return gfx::Point();
}

void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() {
  if (host_) {
    host_->FatalAccessibilityTreeError();
    SetBrowserAccessibilityManager(NULL);
  } else {
    CHECK(FALSE);
  }
}

void RenderWidgetHostViewGtk::CreateBrowserAccessibilityManagerIfNeeded() {
  if (!GetBrowserAccessibilityManager()) {
    GtkWidget* parent = gtk_widget_get_parent(view_.get());
    SetBrowserAccessibilityManager(
        new BrowserAccessibilityManagerGtk(
            parent,
            BrowserAccessibilityManagerGtk::GetEmptyDocument(),
            this));
  }
}

AtkObject* RenderWidgetHostViewGtk::GetAccessible() {
  if (!GetBrowserAccessibilityManager()) {
    GtkWidget* parent = gtk_widget_get_parent(view_.get());
    SetBrowserAccessibilityManager(
        new BrowserAccessibilityManagerGtk(
            parent,
            BrowserAccessibilityManagerGtk::GetEmptyDocument(),
            this));
  }
  BrowserAccessibilityGtk* root =
      GetBrowserAccessibilityManager()->GetRoot()->ToBrowserAccessibilityGtk();

  atk_object_set_role(root->GetAtkObject(), ATK_ROLE_HTML_CONTAINER);
  return root->GetAtkObject();
}

void RenderWidgetHostViewGtk::OnCreatePluginContainer(
    gfx::PluginWindowHandle id) {
  plugin_container_manager_.CreatePluginContainer(id);
}

void RenderWidgetHostViewGtk::OnDestroyPluginContainer(
    gfx::PluginWindowHandle id) {
  plugin_container_manager_.DestroyPluginContainer(id);
}

SkBitmap::Config RenderWidgetHostViewGtk::PreferredReadbackFormat() {
  return SkBitmap::kARGB_8888_Config;
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */