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