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