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.
5 #include "content/browser/renderer_host/render_widget_host_view_gtk.h"
7 #include <cairo/cairo.h>
9 #include <gdk/gdkkeysyms.h>
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/gpu/gpu_messages.h"
36 #include "content/common/input_messages.h"
37 #include "content/common/view_messages.h"
38 #include "content/common/webplugin_geometry.h"
39 #include "content/public/browser/native_web_keyboard_event.h"
40 #include "content/public/common/content_switches.h"
41 #include "skia/ext/platform_canvas.h"
42 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
43 #include "third_party/WebKit/public/web/WebInputEvent.h"
44 #include "ui/base/clipboard/scoped_clipboard_writer.h"
45 #include "ui/base/x/active_window_watcher_x.h"
46 #include "ui/base/x/x11_util.h"
47 #include "ui/gfx/gtk_compat.h"
48 #include "ui/gfx/gtk_native_view_id_manager.h"
49 #include "ui/gfx/gtk_preserve_window.h"
50 #include "ui/gfx/text_elider.h"
51 #include "webkit/common/cursors/webcursor_gtk_data.h"
53 using blink::WebMouseWheelEvent
;
54 using blink::WebScreenInfo
;
59 // Paint rects on Linux are bounded by the maximum size of a shared memory
60 // region. By default that's 32MB, but many distros increase it significantly
63 // We fetch the maximum value from /proc/sys/kernel/shmmax at runtime and, if
64 // we exceed that, then we limit the height of the paint rect in the renderer.
66 // These constants are here to ensure that, in the event that we exceed it, we
67 // end up with something a little more square. Previously we had 4000x4000, but
68 // people's monitor setups are actually exceeding that these days.
69 const int kMaxWindowWidth
= 10000;
70 const int kMaxWindowHeight
= 10000;
72 const GdkColor kBGColor
=
74 { 0, 0xff * 257, 0xff * 257, 0xff * 257 };
76 { 0, 0x00 * 257, 0xff * 257, 0x00 * 257 };
79 // Returns the spinning cursor used for loading state.
80 GdkCursor
* GetMozSpinningCursor() {
81 static GdkCursor
* moz_spinning_cursor
= NULL
;
82 if (!moz_spinning_cursor
) {
83 const GdkColor fg
= { 0, 0, 0, 0 };
84 const GdkColor bg
= { 65535, 65535, 65535, 65535 };
85 GdkPixmap
* source
= gdk_bitmap_create_from_data(
86 NULL
, reinterpret_cast<const gchar
*>(moz_spinning_bits
), 32, 32);
87 GdkPixmap
* mask
= gdk_bitmap_create_from_data(
88 NULL
, reinterpret_cast<const gchar
*>(moz_spinning_mask_bits
), 32, 32);
90 gdk_cursor_new_from_pixmap(source
, mask
, &fg
, &bg
, 2, 2);
91 g_object_unref(source
);
94 return moz_spinning_cursor
;
97 bool MovedToPoint(const blink::WebMouseEvent
& mouse_event
,
98 const gfx::Point
& center
) {
99 return mouse_event
.globalX
== center
.x() &&
100 mouse_event
.globalY
== center
.y();
105 // This class is a simple convenience wrapper for Gtk functions. It has only
107 class RenderWidgetHostViewGtkWidget
{
109 static AtkObject
* GetAccessible(void* userdata
) {
110 return (static_cast<RenderWidgetHostViewGtk
*>(userdata
))->
114 static GtkWidget
* CreateNewWidget(RenderWidgetHostViewGtk
* host_view
) {
115 GtkWidget
* widget
= gtk_preserve_window_new();
116 gtk_widget_set_name(widget
, "chrome-render-widget-host-view");
117 // We manually double-buffer in Paint() because Paint() may or may not be
118 // called in repsonse to an "expose-event" signal.
119 gtk_widget_set_double_buffered(widget
, FALSE
);
120 gtk_widget_set_redraw_on_allocate(widget
, FALSE
);
121 gtk_widget_modify_bg(widget
, GTK_STATE_NORMAL
, &kBGColor
);
122 // Allow the browser window to be resized freely.
123 gtk_widget_set_size_request(widget
, 0, 0);
125 gtk_widget_add_events(widget
, GDK_EXPOSURE_MASK
|
127 GDK_POINTER_MOTION_MASK
|
128 GDK_BUTTON_PRESS_MASK
|
129 GDK_BUTTON_RELEASE_MASK
|
131 GDK_KEY_RELEASE_MASK
|
132 GDK_FOCUS_CHANGE_MASK
|
133 GDK_ENTER_NOTIFY_MASK
|
134 GDK_LEAVE_NOTIFY_MASK
);
135 gtk_widget_set_can_focus(widget
, TRUE
);
137 g_signal_connect(widget
, "expose-event",
138 G_CALLBACK(OnExposeEvent
), host_view
);
139 g_signal_connect(widget
, "realize",
140 G_CALLBACK(OnRealize
), host_view
);
141 g_signal_connect(widget
, "configure-event",
142 G_CALLBACK(OnConfigureEvent
), host_view
);
143 g_signal_connect(widget
, "size-allocate",
144 G_CALLBACK(OnSizeAllocate
), host_view
);
145 g_signal_connect(widget
, "key-press-event",
146 G_CALLBACK(OnKeyPressReleaseEvent
), host_view
);
147 g_signal_connect(widget
, "key-release-event",
148 G_CALLBACK(OnKeyPressReleaseEvent
), host_view
);
149 g_signal_connect(widget
, "focus-in-event",
150 G_CALLBACK(OnFocusIn
), host_view
);
151 g_signal_connect(widget
, "focus-out-event",
152 G_CALLBACK(OnFocusOut
), host_view
);
153 g_signal_connect(widget
, "grab-notify",
154 G_CALLBACK(OnGrabNotify
), host_view
);
155 g_signal_connect(widget
, "button-press-event",
156 G_CALLBACK(OnButtonPressReleaseEvent
), host_view
);
157 g_signal_connect(widget
, "button-release-event",
158 G_CALLBACK(OnButtonPressReleaseEvent
), host_view
);
159 g_signal_connect(widget
, "motion-notify-event",
160 G_CALLBACK(OnMouseMoveEvent
), host_view
);
161 g_signal_connect(widget
, "enter-notify-event",
162 G_CALLBACK(OnCrossingEvent
), host_view
);
163 g_signal_connect(widget
, "leave-notify-event",
164 G_CALLBACK(OnCrossingEvent
), host_view
);
165 g_signal_connect(widget
, "client-event",
166 G_CALLBACK(OnClientEvent
), host_view
);
169 // Connect after so that we are called after the handler installed by the
170 // WebContentsView which handles zoom events.
171 g_signal_connect_after(widget
, "scroll-event",
172 G_CALLBACK(OnMouseScrollEvent
), host_view
);
174 // Route calls to get_accessible to the view.
175 gtk_preserve_window_set_accessible_factory(
176 GTK_PRESERVE_WINDOW(widget
), GetAccessible
, host_view
);
182 static gboolean
OnExposeEvent(GtkWidget
* widget
,
183 GdkEventExpose
* expose
,
184 RenderWidgetHostViewGtk
* host_view
) {
185 if (host_view
->host_
->is_hidden())
187 const gfx::Rect
damage_rect(expose
->area
);
188 host_view
->Paint(damage_rect
);
192 static gboolean
OnRealize(GtkWidget
* widget
,
193 RenderWidgetHostViewGtk
* host_view
) {
194 // Use GtkSignalRegistrar to register events on a widget we don't
195 // control the lifetime of, auto disconnecting at our end of our life.
196 host_view
->signals_
.Connect(gtk_widget_get_toplevel(widget
),
198 G_CALLBACK(OnConfigureEvent
), host_view
);
202 static gboolean
OnConfigureEvent(GtkWidget
* widget
,
203 GdkEventConfigure
* event
,
204 RenderWidgetHostViewGtk
* host_view
) {
205 host_view
->MarkCachedWidgetCenterStale();
206 host_view
->UpdateScreenInfo(host_view
->GetNativeView());
210 static gboolean
OnSizeAllocate(GtkWidget
* widget
,
211 GdkRectangle
* allocation
,
212 RenderWidgetHostViewGtk
* host_view
) {
213 if (!host_view
->IsPopup() && !host_view
->is_fullscreen_
)
214 host_view
->SetSize(gfx::Size(allocation
->width
, allocation
->height
));
218 static gboolean
OnKeyPressReleaseEvent(GtkWidget
* widget
,
220 RenderWidgetHostViewGtk
* host_view
) {
221 TRACE_EVENT0("browser",
222 "RenderWidgetHostViewGtkWidget::OnKeyPressReleaseEvent");
223 // Force popups or fullscreen windows to close on Escape so they won't keep
224 // the keyboard grabbed or be stuck onscreen if the renderer is hanging.
225 bool should_close_on_escape
=
226 (host_view
->IsPopup() && host_view
->NeedsInputGrab()) ||
227 host_view
->is_fullscreen_
;
228 if (should_close_on_escape
&& GDK_Escape
== event
->keyval
) {
229 host_view
->host_
->Shutdown();
231 // Send key event to input method.
232 host_view
->im_context_
->ProcessKeyEvent(event
);
235 // We return TRUE because we did handle the event. If it turns out webkit
236 // can't handle the event, we'll deal with it in
237 // RenderView::UnhandledKeyboardEvent().
241 static gboolean
OnFocusIn(GtkWidget
* widget
,
242 GdkEventFocus
* focus
,
243 RenderWidgetHostViewGtk
* host_view
) {
244 host_view
->ShowCurrentCursor();
245 RenderWidgetHostImpl
* host
=
246 RenderWidgetHostImpl::From(host_view
->GetRenderWidgetHost());
248 host
->SetActive(true);
250 // The only way to enable a GtkIMContext object is to call its focus in
252 host_view
->im_context_
->OnFocusIn();
257 static gboolean
OnFocusOut(GtkWidget
* widget
,
258 GdkEventFocus
* focus
,
259 RenderWidgetHostViewGtk
* host_view
) {
260 // Whenever we lose focus, set the cursor back to that of our parent window,
261 // which should be the default arrow.
262 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
263 // If we are showing a context menu, maintain the illusion that webkit has
265 if (!host_view
->IsShowingContextMenu()) {
266 RenderWidgetHostImpl
* host
=
267 RenderWidgetHostImpl::From(host_view
->GetRenderWidgetHost());
268 host
->SetActive(false);
272 // Prevents us from stealing input context focus in OnGrabNotify() handler.
273 host_view
->was_imcontext_focused_before_grab_
= false;
275 // Disable the GtkIMContext object.
276 host_view
->im_context_
->OnFocusOut();
278 host_view
->set_last_mouse_down(NULL
);
283 // Called when we are shadowed or unshadowed by a keyboard grab (which will
284 // occur for activatable popups, such as dropdown menus). Popup windows do not
285 // take focus, so we never get a focus out or focus in event when they are
286 // shown, and must rely on this signal instead.
287 static void OnGrabNotify(GtkWidget
* widget
, gboolean was_grabbed
,
288 RenderWidgetHostViewGtk
* host_view
) {
290 if (host_view
->was_imcontext_focused_before_grab_
)
291 host_view
->im_context_
->OnFocusIn();
293 host_view
->was_imcontext_focused_before_grab_
=
294 host_view
->im_context_
->is_focused();
295 if (host_view
->was_imcontext_focused_before_grab_
) {
296 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
297 host_view
->im_context_
->OnFocusOut();
302 static gboolean
OnButtonPressReleaseEvent(
304 GdkEventButton
* event
,
305 RenderWidgetHostViewGtk
* host_view
) {
306 TRACE_EVENT0("browser",
307 "RenderWidgetHostViewGtkWidget::OnButtonPressReleaseEvent");
309 if (event
->type
!= GDK_BUTTON_RELEASE
)
310 host_view
->set_last_mouse_down(event
);
312 if (!(event
->button
== 1 || event
->button
== 2 || event
->button
== 3))
313 return FALSE
; // We do not forward any other buttons to the renderer.
314 if (event
->type
== GDK_2BUTTON_PRESS
|| event
->type
== GDK_3BUTTON_PRESS
)
317 // If we don't have focus already, this mouse click will focus us.
318 if (!gtk_widget_is_focus(widget
))
319 host_view
->host_
->OnPointerEventActivate();
321 // Confirm existing composition text on mouse click events, to make sure
322 // the input caret won't be moved with an ongoing composition session.
323 if (event
->type
!= GDK_BUTTON_RELEASE
)
324 host_view
->im_context_
->ConfirmComposition();
326 // We want to translate the coordinates of events that do not originate
327 // from this widget to be relative to the top left of the widget.
328 GtkWidget
* event_widget
= gtk_get_event_widget(
329 reinterpret_cast<GdkEvent
*>(event
));
330 if (event_widget
!= widget
) {
333 gtk_widget_get_pointer(widget
, &x
, &y
);
334 // If the mouse event happens outside our popup, force the popup to
335 // close. We do this so a hung renderer doesn't prevent us from
336 // releasing the x pointer grab.
337 GtkAllocation allocation
;
338 gtk_widget_get_allocation(widget
, &allocation
);
339 bool click_in_popup
= x
>= 0 && y
>= 0 && x
< allocation
.width
&&
340 y
< allocation
.height
;
341 // Only Shutdown on mouse downs. Mouse ups can occur outside the render
342 // view if the user drags for DnD or while using the scrollbar on a select
343 // dropdown. Don't shutdown if we are not a popup.
344 if (event
->type
!= GDK_BUTTON_RELEASE
&& host_view
->IsPopup() &&
345 !host_view
->is_popup_first_mouse_release_
&& !click_in_popup
) {
346 host_view
->host_
->Shutdown();
353 // TODO(evanm): why is this necessary here but not in test shell?
354 // This logic is the same as GtkButton.
355 if (event
->type
== GDK_BUTTON_PRESS
&& !gtk_widget_has_focus(widget
))
356 gtk_widget_grab_focus(widget
);
358 host_view
->is_popup_first_mouse_release_
= false;
359 RenderWidgetHostImpl
* widget_host
=
360 RenderWidgetHostImpl::From(host_view
->GetRenderWidgetHost());
362 widget_host
->ForwardMouseEvent(WebMouseEventBuilder::Build(event
));
364 // Although we did handle the mouse event, we need to let other handlers
365 // run (in particular the one installed by WebContentsViewGtk).
369 static gboolean
OnMouseMoveEvent(GtkWidget
* widget
,
370 GdkEventMotion
* event
,
371 RenderWidgetHostViewGtk
* host_view
) {
372 TRACE_EVENT0("browser",
373 "RenderWidgetHostViewGtkWidget::OnMouseMoveEvent");
374 // We want to translate the coordinates of events that do not originate
375 // from this widget to be relative to the top left of the widget.
376 GtkWidget
* event_widget
= gtk_get_event_widget(
377 reinterpret_cast<GdkEvent
*>(event
));
378 if (event_widget
!= widget
) {
381 gtk_widget_get_pointer(widget
, &x
, &y
);
386 host_view
->ModifyEventForEdgeDragging(widget
, event
);
388 blink::WebMouseEvent mouse_event
= WebMouseEventBuilder::Build(event
);
390 if (host_view
->mouse_locked_
) {
391 gfx::Point center
= host_view
->GetWidgetCenter();
393 bool moved_to_center
= MovedToPoint(mouse_event
, center
);
395 host_view
->mouse_has_been_warped_to_new_center_
= true;
397 host_view
->ModifyEventMovementAndCoords(&mouse_event
);
399 if (!moved_to_center
&&
400 (mouse_event
.movementX
|| mouse_event
.movementY
)) {
401 GdkDisplay
* display
= gtk_widget_get_display(widget
);
402 GdkScreen
* screen
= gtk_widget_get_screen(widget
);
403 gdk_display_warp_pointer(display
, screen
, center
.x(), center
.y());
404 if (host_view
->mouse_has_been_warped_to_new_center_
)
405 RenderWidgetHostImpl::From(
406 host_view
->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event
);
408 } else { // Mouse is not locked.
409 host_view
->ModifyEventMovementAndCoords(&mouse_event
);
410 // Do not send mouse events while the mouse cursor is being warped back
411 // to the unlocked location.
412 if (!host_view
->mouse_is_being_warped_to_unlocked_position_
) {
413 RenderWidgetHostImpl::From(
414 host_view
->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event
);
420 static gboolean
OnCrossingEvent(GtkWidget
* widget
,
421 GdkEventCrossing
* event
,
422 RenderWidgetHostViewGtk
* host_view
) {
423 TRACE_EVENT0("browser",
424 "RenderWidgetHostViewGtkWidget::OnCrossingEvent");
425 const int any_button_mask
=
432 // Only forward crossing events if the mouse button is not down.
433 // (When the mouse button is down, the proper events are already being
434 // sent by ButtonPressReleaseEvent and MouseMoveEvent, above, and if we
435 // additionally send this crossing event with the state indicating the
436 // button is down, it causes problems with drag and drop in WebKit.)
437 if (!(event
->state
& any_button_mask
)) {
438 blink::WebMouseEvent mouse_event
= WebMouseEventBuilder::Build(event
);
439 host_view
->ModifyEventMovementAndCoords(&mouse_event
);
440 // When crossing out and back into a render view the movement values
441 // must represent the instantaneous movement of the mouse, not the jump
442 // from the exit to re-entry point.
443 mouse_event
.movementX
= 0;
444 mouse_event
.movementY
= 0;
445 RenderWidgetHostImpl::From(
446 host_view
->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event
);
452 static gboolean
OnClientEvent(GtkWidget
* widget
,
453 GdkEventClient
* event
,
454 RenderWidgetHostViewGtk
* host_view
) {
455 VLOG(1) << "client event type: " << event
->message_type
456 << " data_format: " << event
->data_format
457 << " data: " << event
->data
.l
;
461 // Return the net up / down (or left / right) distance represented by events
462 // in the events will be removed from the queue. We only look at the top of
463 // queue...any other type of event will cause us not to look farther.
464 // If there is a change to the set of modifier keys or scroll axis
465 // in the events we will stop looking as well.
466 static int GetPendingScrollDelta(bool vert
, guint current_event_state
) {
469 bool event_coalesced
= true;
470 while ((event
= gdk_event_get()) && event_coalesced
) {
471 event_coalesced
= false;
472 if (event
->type
== GDK_SCROLL
) {
473 GdkEventScroll scroll
= event
->scroll
;
474 if (scroll
.state
& GDK_SHIFT_MASK
) {
475 if (scroll
.direction
== GDK_SCROLL_UP
)
476 scroll
.direction
= GDK_SCROLL_LEFT
;
477 else if (scroll
.direction
== GDK_SCROLL_DOWN
)
478 scroll
.direction
= GDK_SCROLL_RIGHT
;
481 if (scroll
.direction
== GDK_SCROLL_UP
||
482 scroll
.direction
== GDK_SCROLL_DOWN
) {
483 if (scroll
.state
== current_event_state
) {
484 num_clicks
+= (scroll
.direction
== GDK_SCROLL_UP
? 1 : -1);
485 gdk_event_free(event
);
486 event_coalesced
= true;
490 if (scroll
.direction
== GDK_SCROLL_LEFT
||
491 scroll
.direction
== GDK_SCROLL_RIGHT
) {
492 if (scroll
.state
== current_event_state
) {
493 num_clicks
+= (scroll
.direction
== GDK_SCROLL_LEFT
? 1 : -1);
494 gdk_event_free(event
);
495 event_coalesced
= true;
501 // If we have an event left we put it back on the queue.
503 gdk_event_put(event
);
504 gdk_event_free(event
);
506 return num_clicks
* WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
509 static gboolean
OnMouseScrollEvent(GtkWidget
* widget
,
510 GdkEventScroll
* event
,
511 RenderWidgetHostViewGtk
* host_view
) {
512 TRACE_EVENT0("browser",
513 "RenderWidgetHostViewGtkWidget::OnMouseScrollEvent");
514 // If the user is holding shift, translate it into a horizontal scroll. We
515 // don't care what other modifiers the user may be holding (zooming is
516 // handled at the WebContentsView level).
517 if (event
->state
& GDK_SHIFT_MASK
) {
518 if (event
->direction
== GDK_SCROLL_UP
)
519 event
->direction
= GDK_SCROLL_LEFT
;
520 else if (event
->direction
== GDK_SCROLL_DOWN
)
521 event
->direction
= GDK_SCROLL_RIGHT
;
524 WebMouseWheelEvent web_event
= WebMouseWheelEventBuilder::Build(event
);
525 const float pixelsPerTick
=
526 WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
527 // We peek ahead at the top of the queue to look for additional pending
529 if (event
->direction
== GDK_SCROLL_UP
||
530 event
->direction
== GDK_SCROLL_DOWN
) {
531 if (event
->direction
== GDK_SCROLL_UP
)
532 web_event
.deltaY
= pixelsPerTick
;
534 web_event
.deltaY
= -pixelsPerTick
;
535 web_event
.deltaY
+= GetPendingScrollDelta(true, event
->state
);
537 if (event
->direction
== GDK_SCROLL_LEFT
)
538 web_event
.deltaX
= pixelsPerTick
;
540 web_event
.deltaX
= -pixelsPerTick
;
541 web_event
.deltaX
+= GetPendingScrollDelta(false, event
->state
);
543 RenderWidgetHostImpl::From(
544 host_view
->GetRenderWidgetHost())->ForwardWheelEvent(web_event
);
548 DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget
);
551 RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost
* widget_host
)
552 : host_(RenderWidgetHostImpl::From(widget_host
)),
553 about_to_validate_and_paint_(false),
556 is_popup_first_mouse_release_(true),
557 was_imcontext_focused_before_grab_(false),
559 is_fullscreen_(false),
561 mouse_is_being_warped_to_unlocked_position_(false),
562 destroy_handler_id_(0),
563 dragged_at_horizontal_edge_(0),
564 dragged_at_vertical_edge_(0),
565 compositing_surface_(gfx::kNullPluginWindow
),
566 last_mouse_down_(NULL
) {
567 host_
->SetView(this);
570 RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() {
572 set_last_mouse_down(NULL
);
576 bool RenderWidgetHostViewGtk::OnMessageReceived(const IPC::Message
& message
) {
578 IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewGtk
, message
)
579 IPC_MESSAGE_HANDLER(ViewHostMsg_CreatePluginContainer
,
580 OnCreatePluginContainer
)
581 IPC_MESSAGE_HANDLER(ViewHostMsg_DestroyPluginContainer
,
582 OnDestroyPluginContainer
)
583 IPC_MESSAGE_UNHANDLED(handled
= false)
584 IPC_END_MESSAGE_MAP()
588 void RenderWidgetHostViewGtk::InitAsChild(
589 gfx::NativeView parent_view
) {
591 gtk_widget_show(view_
.get());
594 void RenderWidgetHostViewGtk::InitAsPopup(
595 RenderWidgetHostView
* parent_host_view
, const gfx::Rect
& pos
) {
596 // If we aren't a popup, then |window| will be leaked.
600 parent_
= parent_host_view
->GetNativeView();
601 GtkWindow
* window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP
));
602 gtk_container_add(GTK_CONTAINER(window
), view_
.get());
603 DoPopupOrFullscreenInit(window
, pos
);
605 // Grab all input for the app. If a click lands outside the bounds of the
606 // popup, WebKit will notice and destroy us. The underlying X window needs to
607 // be created and mapped by the above code before we can grab the input
609 if (NeedsInputGrab()) {
610 // If our parent is in a widget hierarchy that ends with a window, add
611 // ourselves to the same window group to make sure that our GTK grab
613 GtkWidget
* toplevel
= gtk_widget_get_toplevel(parent_
);
615 GTK_WIDGET_TOPLEVEL(toplevel
) &&
616 GTK_IS_WINDOW(toplevel
)) {
617 gtk_window_group_add_window(
618 gtk_window_get_group(GTK_WINDOW(toplevel
)), window
);
621 // Install an application-level GTK grab to make sure that we receive all of
623 gtk_grab_add(view_
.get());
625 // We need to install an X grab as well. However if the app already has an X
626 // grab (as in the case of extension popup), an app grab will suffice.
627 do_x_grab_
= !gdk_pointer_is_grabbed();
629 // Install the grab on behalf our parent window if it and all of its
630 // ancestors are mapped; otherwise, just use ourselves (maybe we're being
631 // shown on behalf of an inactive tab).
632 GdkWindow
* grab_window
= gtk_widget_get_window(parent_
);
633 if (!grab_window
|| !gdk_window_is_viewable(grab_window
))
634 grab_window
= gtk_widget_get_window(view_
.get());
638 TRUE
, // Only events outside of the window are reported with
639 // respect to |parent_->window|.
640 static_cast<GdkEventMask
>(GDK_BUTTON_PRESS_MASK
|
641 GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
),
645 // We grab keyboard events too so things like alt+tab are eaten.
646 gdk_keyboard_grab(grab_window
, TRUE
, GDK_CURRENT_TIME
);
651 void RenderWidgetHostViewGtk::InitAsFullscreen(
652 RenderWidgetHostView
* reference_host_view
) {
653 DCHECK(reference_host_view
);
656 is_fullscreen_
= true;
657 GtkWindow
* window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
658 gtk_window_set_decorated(window
, FALSE
);
659 destroy_handler_id_
= g_signal_connect(GTK_WIDGET(window
),
661 G_CALLBACK(OnDestroyThunk
),
663 gtk_container_add(GTK_CONTAINER(window
), view_
.get());
665 // Try to move and resize the window to cover the screen in case the window
666 // manager doesn't support _NET_WM_STATE_FULLSCREEN.
667 GdkScreen
* screen
= gtk_window_get_screen(window
);
668 GdkWindow
* ref_gdk_window
= gtk_widget_get_window(
669 reference_host_view
->GetNativeView());
672 if (ref_gdk_window
) {
673 const int monitor_id
= gdk_screen_get_monitor_at_window(screen
,
675 GdkRectangle monitor_rect
;
676 gdk_screen_get_monitor_geometry(screen
, monitor_id
, &monitor_rect
);
677 bounds
= gfx::Rect(monitor_rect
);
680 0, 0, gdk_screen_get_width(screen
), gdk_screen_get_height(screen
));
682 gtk_window_move(window
, bounds
.x(), bounds
.y());
683 gtk_window_resize(window
, bounds
.width(), bounds
.height());
684 gtk_window_fullscreen(window
);
685 DoPopupOrFullscreenInit(window
, bounds
);
688 RenderWidgetHost
* RenderWidgetHostViewGtk::GetRenderWidgetHost() const {
692 void RenderWidgetHostViewGtk::WasShown() {
693 if (!host_
|| !host_
->is_hidden())
696 if (web_contents_switch_paint_time_
.is_null())
697 web_contents_switch_paint_time_
= base::TimeTicks::Now();
702 void RenderWidgetHostViewGtk::WasHidden() {
703 if (!host_
|| host_
->is_hidden())
706 // If we have a renderer, then inform it that we are being hidden so it can
707 // reduce its resource utilization.
710 web_contents_switch_paint_time_
= base::TimeTicks();
713 void RenderWidgetHostViewGtk::SetSize(const gfx::Size
& size
) {
714 int width
= std::min(size
.width(), kMaxWindowWidth
);
715 int height
= std::min(size
.height(), kMaxWindowHeight
);
717 // We're a popup, honor the size request.
718 gtk_widget_set_size_request(view_
.get(), width
, height
);
721 // Update the size of the RWH.
722 if (requested_size_
.width() != width
||
723 requested_size_
.height() != height
) {
724 requested_size_
= gfx::Size(width
, height
);
725 host_
->SendScreenRects();
730 void RenderWidgetHostViewGtk::SetBounds(const gfx::Rect
& rect
) {
731 // This is called when webkit has sent us a Move message.
733 gtk_window_move(GTK_WINDOW(gtk_widget_get_toplevel(view_
.get())),
737 SetSize(rect
.size());
740 gfx::NativeView
RenderWidgetHostViewGtk::GetNativeView() const {
744 gfx::NativeViewId
RenderWidgetHostViewGtk::GetNativeViewId() const {
745 return GtkNativeViewManager::GetInstance()->GetIdForWidget(view_
.get());
748 gfx::NativeViewAccessible
RenderWidgetHostViewGtk::GetNativeViewAccessible() {
753 void RenderWidgetHostViewGtk::MovePluginWindows(
754 const gfx::Vector2d
& scroll_offset
,
755 const std::vector
<WebPluginGeometry
>& moves
) {
756 for (size_t i
= 0; i
< moves
.size(); ++i
) {
757 plugin_container_manager_
.MovePluginContainer(moves
[i
]);
761 void RenderWidgetHostViewGtk::Focus() {
762 gtk_widget_grab_focus(view_
.get());
765 void RenderWidgetHostViewGtk::Blur() {
766 // TODO(estade): We should be clearing native focus as well, but I know of no
767 // way to do that without focusing another widget.
771 bool RenderWidgetHostViewGtk::HasFocus() const {
772 return gtk_widget_has_focus(view_
.get());
775 void RenderWidgetHostViewGtk::ActiveWindowChanged(GdkWindow
* window
) {
776 GdkWindow
* our_window
= gtk_widget_get_parent_window(view_
.get());
778 if (our_window
== window
)
781 // If the window was previously active, but isn't active anymore, shut it
783 if (is_fullscreen_
&& our_window
!= window
&& made_active_
)
787 bool RenderWidgetHostViewGtk::Send(IPC::Message
* message
) {
788 return host_
->Send(message
);
791 bool RenderWidgetHostViewGtk::IsSurfaceAvailableForCopy() const {
795 void RenderWidgetHostViewGtk::Show() {
796 gtk_widget_show(view_
.get());
800 void RenderWidgetHostViewGtk::Hide() {
801 gtk_widget_hide(view_
.get());
805 bool RenderWidgetHostViewGtk::IsShowing() {
806 return gtk_widget_get_visible(view_
.get());
809 gfx::Rect
RenderWidgetHostViewGtk::GetViewBounds() const {
810 GdkWindow
* gdk_window
= gtk_widget_get_window(view_
.get());
812 return gfx::Rect(requested_size_
);
813 GdkRectangle window_rect
;
814 gdk_window_get_origin(gdk_window
, &window_rect
.x
, &window_rect
.y
);
815 return gfx::Rect(window_rect
.x
, window_rect
.y
,
816 requested_size_
.width(), requested_size_
.height());
819 void RenderWidgetHostViewGtk::UpdateCursor(const WebCursor
& cursor
) {
820 // Optimize the common case, where the cursor hasn't changed.
821 // However, we can switch between different pixmaps, so only on the
822 // non-pixmap branch.
823 if (current_cursor_
.GetCursorType() != GDK_CURSOR_IS_PIXMAP
&&
824 current_cursor_
.GetCursorType() == cursor
.GetCursorType()) {
828 current_cursor_
= cursor
;
832 void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading
) {
833 is_loading_
= is_loading
;
834 // Only call ShowCurrentCursor() when it will actually change the cursor.
835 if (current_cursor_
.GetCursorType() == GDK_LAST_CURSOR
)
839 void RenderWidgetHostViewGtk::TextInputTypeChanged(
840 ui::TextInputType type
,
841 ui::TextInputMode input_mode
,
842 bool can_compose_inline
) {
843 im_context_
->UpdateInputMethodState(type
, can_compose_inline
);
846 void RenderWidgetHostViewGtk::ImeCancelComposition() {
847 im_context_
->CancelComposition();
850 void RenderWidgetHostViewGtk::DidUpdateBackingStore(
851 const gfx::Rect
& scroll_rect
,
852 const gfx::Vector2d
& scroll_delta
,
853 const std::vector
<gfx::Rect
>& copy_rects
,
854 const std::vector
<ui::LatencyInfo
>& latency_info
) {
855 TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::DidUpdateBackingStore");
856 for (size_t i
= 0; i
< latency_info
.size(); i
++)
857 software_latency_info_
.push_back(latency_info
[i
]);
859 if (host_
->is_hidden())
862 // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX. Can that
863 // be done using XCopyArea? Perhaps similar to
864 // BackingStore::ScrollBackingStore?
865 if (about_to_validate_and_paint_
)
866 invalid_rect_
.Union(scroll_rect
);
870 for (size_t i
= 0; i
< copy_rects
.size(); ++i
) {
871 // Avoid double painting. NOTE: This is only relevant given the call to
872 // Paint(scroll_rect) above.
873 gfx::Rect rect
= gfx::SubtractRects(copy_rects
[i
], scroll_rect
);
877 if (about_to_validate_and_paint_
)
878 invalid_rect_
.Union(rect
);
884 void RenderWidgetHostViewGtk::RenderProcessGone(base::TerminationStatus status
,
887 plugin_container_manager_
.set_host_widget(NULL
);
890 void RenderWidgetHostViewGtk::Destroy() {
891 if (compositing_surface_
!= gfx::kNullPluginWindow
) {
892 GtkNativeViewManager
* manager
= GtkNativeViewManager::GetInstance();
893 manager
->ReleasePermanentXID(compositing_surface_
);
898 GdkDisplay
* display
= gtk_widget_get_display(parent_
);
899 gdk_display_pointer_ungrab(display
, GDK_CURRENT_TIME
);
900 gdk_display_keyboard_ungrab(display
, GDK_CURRENT_TIME
);
904 // If this is a popup or fullscreen widget, then we need to destroy the
905 // window that we created to hold it.
906 if (IsPopup() || is_fullscreen_
) {
907 GtkWidget
* window
= gtk_widget_get_parent(view_
.get());
909 ui::ActiveWindowWatcherX::RemoveObserver(this);
911 // Disconnect the destroy handler so that we don't try to shutdown twice.
913 g_signal_handler_disconnect(window
, destroy_handler_id_
);
915 gtk_widget_destroy(window
);
918 // Remove |view_| from all containers now, so nothing else can hold a
919 // reference to |view_|'s widget except possibly a gtk signal handler if
920 // this code is currently executing within the context of a gtk signal
921 // handler. Note that |view_| is still alive after this call. It will be
922 // deallocated in the destructor.
923 // See http://crbug.com/11847 for details.
924 gtk_widget_destroy(view_
.get());
927 // The RenderWidgetHost's destruction led here, so don't call it.
930 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
933 void RenderWidgetHostViewGtk::SetTooltipText(
934 const base::string16
& tooltip_text
) {
935 // Maximum number of characters we allow in a tooltip.
936 const int kMaxTooltipLength
= 8 << 10;
937 // Clamp the tooltip length to kMaxTooltipLength so that we don't
938 // accidentally DOS the user with a mega tooltip (since GTK doesn't do
940 // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream.
941 const base::string16 clamped_tooltip
=
942 gfx::TruncateString(tooltip_text
, kMaxTooltipLength
);
944 if (clamped_tooltip
.empty()) {
945 gtk_widget_set_has_tooltip(view_
.get(), FALSE
);
947 gtk_widget_set_tooltip_text(view_
.get(),
948 base::UTF16ToUTF8(clamped_tooltip
).c_str());
952 void RenderWidgetHostViewGtk::SelectionChanged(const base::string16
& text
,
954 const gfx::Range
& range
) {
955 RenderWidgetHostViewBase::SelectionChanged(text
, offset
, range
);
957 if (text
.empty() || range
.is_empty())
959 size_t pos
= range
.GetMin() - offset
;
960 size_t n
= range
.length();
962 DCHECK(pos
+ n
<= text
.length()) << "The text can not fully cover range.";
963 if (pos
>= text
.length()) {
964 NOTREACHED() << "The text can not cover range.";
968 // Set the CLIPBOARD_TYPE SELECTION to the ui::Clipboard.
969 ui::ScopedClipboardWriter
clipboard_writer(
970 ui::Clipboard::GetForCurrentThread(),
971 ui::CLIPBOARD_TYPE_SELECTION
);
972 clipboard_writer
.WriteText(text
.substr(pos
, n
));
975 void RenderWidgetHostViewGtk::SelectionBoundsChanged(
976 const ViewHostMsg_SelectionBounds_Params
& params
) {
977 im_context_
->UpdateCaretBounds(
978 gfx::UnionRects(params
.anchor_rect
, params
.focus_rect
));
981 void RenderWidgetHostViewGtk::ScrollOffsetChanged() {
984 GdkEventButton
* RenderWidgetHostViewGtk::GetLastMouseDown() {
985 return last_mouse_down_
;
988 gfx::NativeView
RenderWidgetHostViewGtk::BuildInputMethodsGtkMenu() {
989 return im_context_
->BuildInputMethodsGtkMenu();
992 void RenderWidgetHostViewGtk::OnDestroy(GtkWidget
* widget
) {
993 DCHECK(is_fullscreen_
);
997 bool RenderWidgetHostViewGtk::NeedsInputGrab() {
998 return popup_type_
== blink::WebPopupTypeSelect
;
1001 bool RenderWidgetHostViewGtk::IsPopup() const {
1002 return popup_type_
!= blink::WebPopupTypeNone
;
1005 void RenderWidgetHostViewGtk::DoSharedInit() {
1006 view_
.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this));
1007 im_context_
.reset(new GtkIMContextWrapper(this));
1008 plugin_container_manager_
.set_host_widget(view_
.get());
1009 key_bindings_handler_
.reset(new GtkKeyBindingsHandler(view_
.get()));
1012 void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow
* window
,
1013 const gfx::Rect
& bounds
) {
1014 requested_size_
.SetSize(std::min(bounds
.width(), kMaxWindowWidth
),
1015 std::min(bounds
.height(), kMaxWindowHeight
));
1016 host_
->WasResized();
1018 ui::ActiveWindowWatcherX::AddObserver(this);
1020 // Don't set the size when we're going fullscreen. This can confuse the
1021 // window manager into thinking we're resizing a fullscreen window and
1022 // therefore not fullscreen anymore.
1023 if (!is_fullscreen_
) {
1024 gtk_widget_set_size_request(
1025 view_
.get(), requested_size_
.width(), requested_size_
.height());
1027 // Don't allow the window to be resized. This also forces the window to
1028 // shrink down to the size of its child contents.
1029 gtk_window_set_resizable(window
, FALSE
);
1030 gtk_window_set_default_size(window
, -1, -1);
1031 gtk_window_move(window
, bounds
.x(), bounds
.y());
1034 gtk_widget_show_all(GTK_WIDGET(window
));
1037 BackingStore
* RenderWidgetHostViewGtk::AllocBackingStore(
1038 const gfx::Size
& size
) {
1039 gint depth
= gdk_visual_get_depth(gtk_widget_get_visual(view_
.get()));
1040 return new BackingStoreGtk(host_
, size
,
1041 ui::GetVisualFromGtkWidget(view_
.get()),
1045 // NOTE: |output| is initialized with the size of |src_subrect|, and |dst_size|
1046 // is ignored on GTK.
1047 void RenderWidgetHostViewGtk::CopyFromCompositingSurface(
1048 const gfx::Rect
& src_subrect
,
1049 const gfx::Size
& /* dst_size */,
1050 const base::Callback
<void(bool, const SkBitmap
&)>& callback
,
1051 SkBitmap::Config config
) {
1052 if (config
!= SkBitmap::kARGB_8888_Config
) {
1054 callback
.Run(false, SkBitmap());
1056 // Grab the snapshot from the renderer as that's the only reliable way to
1057 // readback from the GPU for this platform right now.
1058 GetRenderWidgetHost()->GetSnapshotFromRenderer(src_subrect
, callback
);
1061 void RenderWidgetHostViewGtk::CopyFromCompositingSurfaceToVideoFrame(
1062 const gfx::Rect
& src_subrect
,
1063 const scoped_refptr
<media::VideoFrame
>& target
,
1064 const base::Callback
<void(bool)>& callback
) {
1066 callback
.Run(false);
1069 bool RenderWidgetHostViewGtk::CanCopyToVideoFrame() const {
1073 void RenderWidgetHostViewGtk::AcceleratedSurfaceInitialized(int host_id
,
1077 void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped(
1078 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params
& params
,
1080 AcceleratedSurfaceMsg_BufferPresented_Params ack_params
;
1081 ack_params
.sync_point
= 0;
1082 RenderWidgetHostImpl::AcknowledgeBufferPresent(
1083 params
.route_id
, gpu_host_id
, ack_params
);
1084 RenderWidgetHostImpl::CompositorFrameDrawn(params
.latency_info
);
1087 void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer(
1088 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params
& params
,
1090 AcceleratedSurfaceMsg_BufferPresented_Params ack_params
;
1091 ack_params
.sync_point
= 0;
1092 RenderWidgetHostImpl::AcknowledgeBufferPresent(
1093 params
.route_id
, gpu_host_id
, ack_params
);
1094 RenderWidgetHostImpl::CompositorFrameDrawn(params
.latency_info
);
1097 void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() {
1100 void RenderWidgetHostViewGtk::AcceleratedSurfaceRelease() {
1103 bool RenderWidgetHostViewGtk::HasAcceleratedSurface(
1104 const gfx::Size
& desired_size
) {
1105 // TODO(jbates) Implement this so this view can use GetBackingStore for both
1106 // software and GPU frames. Defaulting to false just makes GetBackingStore
1107 // only useable for software frames.
1111 void RenderWidgetHostViewGtk::SetBackground(const SkBitmap
& background
) {
1112 RenderWidgetHostViewBase::SetBackground(background
);
1113 Send(new ViewMsg_SetBackground(host_
->GetRoutingID(), background
));
1116 void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging(
1117 GtkWidget
* widget
, GdkEventMotion
* event
) {
1118 // If the widget is aligned with an edge of the monitor its on and the user
1119 // attempts to drag past that edge we track the number of times it has
1120 // occurred, so that we can force the widget to scroll when it otherwise
1121 // would be unable to, by modifying the (x,y) position in the drag
1122 // event that we forward on to webkit. If we get a move that's no longer a
1123 // drag or a drag indicating the user is no longer at that edge we stop
1124 // altering the drag events.
1125 int new_dragged_at_horizontal_edge
= 0;
1126 int new_dragged_at_vertical_edge
= 0;
1127 // Used for checking the edges of the monitor. We cache the values to save
1128 // roundtrips to the X server.
1129 CR_DEFINE_STATIC_LOCAL(gfx::Size
, drag_monitor_size
, ());
1130 if (event
->state
& GDK_BUTTON1_MASK
) {
1131 if (drag_monitor_size
.IsEmpty()) {
1132 // We can safely cache the monitor size for the duration of a drag.
1133 GdkScreen
* screen
= gtk_widget_get_screen(widget
);
1135 gdk_screen_get_monitor_at_point(screen
, event
->x_root
, event
->y_root
);
1136 GdkRectangle geometry
;
1137 gdk_screen_get_monitor_geometry(screen
, monitor
, &geometry
);
1138 drag_monitor_size
.SetSize(geometry
.width
, geometry
.height
);
1140 GtkAllocation allocation
;
1141 gtk_widget_get_allocation(widget
, &allocation
);
1142 // Check X and Y independently, as the user could be dragging into a corner.
1143 if (event
->x
== 0 && event
->x_root
== 0) {
1144 new_dragged_at_horizontal_edge
= dragged_at_horizontal_edge_
- 1;
1145 } else if (allocation
.width
- 1 == static_cast<gint
>(event
->x
) &&
1146 drag_monitor_size
.width() - 1 == static_cast<gint
>(event
->x_root
)) {
1147 new_dragged_at_horizontal_edge
= dragged_at_horizontal_edge_
+ 1;
1150 if (event
->y
== 0 && event
->y_root
== 0) {
1151 new_dragged_at_vertical_edge
= dragged_at_vertical_edge_
- 1;
1152 } else if (allocation
.height
- 1 == static_cast<gint
>(event
->y
) &&
1153 drag_monitor_size
.height() - 1 == static_cast<gint
>(event
->y_root
)) {
1154 new_dragged_at_vertical_edge
= dragged_at_vertical_edge_
+ 1;
1157 event
->x_root
+= new_dragged_at_horizontal_edge
;
1158 event
->x
+= new_dragged_at_horizontal_edge
;
1159 event
->y_root
+= new_dragged_at_vertical_edge
;
1160 event
->y
+= new_dragged_at_vertical_edge
;
1162 // Clear whenever we get a non-drag mouse move.
1163 drag_monitor_size
.SetSize(0, 0);
1165 dragged_at_horizontal_edge_
= new_dragged_at_horizontal_edge
;
1166 dragged_at_vertical_edge_
= new_dragged_at_vertical_edge
;
1169 void RenderWidgetHostViewGtk::Paint(const gfx::Rect
& damage_rect
) {
1170 TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::Paint");
1172 // If the GPU process is rendering directly into the View,
1173 // call the compositor directly.
1174 RenderWidgetHostImpl
* render_widget_host
=
1175 RenderWidgetHostImpl::From(GetRenderWidgetHost());
1176 if (render_widget_host
->is_accelerated_compositing_active()) {
1177 host_
->ScheduleComposite();
1181 GdkWindow
* window
= gtk_widget_get_window(view_
.get());
1182 DCHECK(!about_to_validate_and_paint_
);
1184 invalid_rect_
= damage_rect
;
1185 about_to_validate_and_paint_
= true;
1187 // If the size of our canvas is (0,0), then we don't want to block here. We
1188 // are doing one of our first paints and probably have animations going on.
1189 bool force_create
= !host_
->empty();
1190 BackingStoreGtk
* backing_store
= static_cast<BackingStoreGtk
*>(
1191 host_
->GetBackingStore(force_create
));
1192 // Calling GetBackingStore maybe have changed |invalid_rect_|...
1193 about_to_validate_and_paint_
= false;
1195 gfx::Rect paint_rect
= gfx::Rect(0, 0, kMaxWindowWidth
, kMaxWindowHeight
);
1196 paint_rect
.Intersect(invalid_rect_
);
1198 if (backing_store
) {
1199 // Only render the widget if it is attached to a window; there's a short
1200 // period where this object isn't attached to a window but hasn't been
1201 // Destroy()ed yet and it receives paint messages...
1203 backing_store
->XShowRect(gfx::Point(0, 0),
1204 paint_rect
, ui::GetX11WindowFromGtkWidget(view_
.get()));
1206 if (!whiteout_start_time_
.is_null()) {
1207 base::TimeDelta whiteout_duration
= base::TimeTicks::Now() -
1208 whiteout_start_time_
;
1209 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration
);
1211 // Reset the start time to 0 so that we start recording again the next
1212 // time the backing store is NULL...
1213 whiteout_start_time_
= base::TimeTicks();
1215 if (!web_contents_switch_paint_time_
.is_null()) {
1216 base::TimeDelta web_contents_switch_paint_duration
=
1217 base::TimeTicks::Now() - web_contents_switch_paint_time_
;
1218 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
1219 web_contents_switch_paint_duration
);
1220 // Reset web_contents_switch_paint_time_ to 0 so future tab selections are
1222 web_contents_switch_paint_time_
= base::TimeTicks();
1225 for (size_t i
= 0; i
< software_latency_info_
.size(); i
++) {
1226 software_latency_info_
[i
].AddLatencyNumber(
1227 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT
, 0, 0);
1228 render_widget_host
->FrameSwapped(software_latency_info_
[i
]);
1230 software_latency_info_
.clear();
1233 gdk_window_clear(window
);
1234 if (whiteout_start_time_
.is_null())
1235 whiteout_start_time_
= base::TimeTicks::Now();
1239 void RenderWidgetHostViewGtk::ShowCurrentCursor() {
1240 // The widget may not have a window. If that's the case, abort mission. This
1241 // is the same issue as that explained above in Paint().
1242 if (!gtk_widget_get_window(view_
.get()))
1245 // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is
1246 // that calling gdk_window_set_cursor repeatedly is expensive. We should
1247 // avoid it here where possible.
1248 GdkCursor
* gdk_cursor
;
1249 if (current_cursor_
.GetCursorType() == GDK_LAST_CURSOR
) {
1250 // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and
1251 // the page is loading.
1252 gdk_cursor
= is_loading_
? GetMozSpinningCursor() : NULL
;
1254 gdk_cursor
= current_cursor_
.GetNativeCursor();
1256 gdk_window_set_cursor(gtk_widget_get_window(view_
.get()), gdk_cursor
);
1259 void RenderWidgetHostViewGtk::SetHasHorizontalScrollbar(
1260 bool has_horizontal_scrollbar
) {
1263 void RenderWidgetHostViewGtk::SetScrollOffsetPinning(
1264 bool is_pinned_to_left
, bool is_pinned_to_right
) {
1268 void RenderWidgetHostViewGtk::OnAcceleratedCompositingStateChange() {
1269 bool activated
= host_
->is_accelerated_compositing_active();
1270 GtkPreserveWindow
* widget
= reinterpret_cast<GtkPreserveWindow
*>(view_
.get());
1272 gtk_preserve_window_delegate_resize(widget
, activated
);
1275 void RenderWidgetHostViewGtk::GetScreenInfo(WebScreenInfo
* results
) {
1276 GdkWindow
* gdk_window
= gtk_widget_get_window(view_
.get());
1278 GdkDisplay
* display
= gdk_display_get_default();
1279 gdk_window
= gdk_display_get_default_group(display
);
1283 GetScreenInfoFromNativeWindow(gdk_window
, results
);
1286 gfx::Rect
RenderWidgetHostViewGtk::GetBoundsInRootWindow() {
1287 GtkWidget
* toplevel
= gtk_widget_get_toplevel(view_
.get());
1289 return GetViewBounds();
1291 GdkRectangle frame_extents
;
1292 GdkWindow
* gdk_window
= gtk_widget_get_window(toplevel
);
1294 return GetViewBounds();
1296 gdk_window_get_frame_extents(gdk_window
, &frame_extents
);
1297 return gfx::Rect(frame_extents
.x
, frame_extents
.y
,
1298 frame_extents
.width
, frame_extents
.height
);
1301 gfx::GLSurfaceHandle
RenderWidgetHostViewGtk::GetCompositingSurface() {
1302 if (compositing_surface_
== gfx::kNullPluginWindow
) {
1303 GtkNativeViewManager
* manager
= GtkNativeViewManager::GetInstance();
1304 gfx::NativeViewId view_id
= GetNativeViewId();
1306 if (!manager
->GetPermanentXIDForId(&compositing_surface_
, view_id
)) {
1307 DLOG(ERROR
) << "Can't find XID for view id " << view_id
;
1310 return gfx::GLSurfaceHandle(compositing_surface_
, gfx::NATIVE_TRANSPORT
);
1313 void RenderWidgetHostViewGtk::ResizeCompositingSurface(const gfx::Size
& size
) {
1314 GtkWidget
* widget
= view_
.get();
1315 GdkWindow
* window
= gtk_widget_get_window(widget
);
1317 Display
* display
= GDK_WINDOW_XDISPLAY(window
);
1318 gdk_window_resize(window
, size
.width(), size
.height());
1319 XSync(display
, False
);
1323 bool RenderWidgetHostViewGtk::LockMouse() {
1327 mouse_locked_
= true;
1329 // Release any current grab.
1330 GtkWidget
* current_grab_window
= gtk_grab_get_current();
1331 if (current_grab_window
) {
1332 gtk_grab_remove(current_grab_window
);
1333 LOG(WARNING
) << "Locking Mouse with gdk_pointer_grab, "
1334 << "but had to steal grab from another window";
1337 GtkWidget
* widget
= view_
.get();
1338 GdkWindow
* window
= gtk_widget_get_window(widget
);
1339 GdkCursor
* cursor
= gdk_cursor_new(GDK_BLANK_CURSOR
);
1341 GdkGrabStatus grab_status
=
1342 gdk_pointer_grab(window
,
1343 FALSE
, // owner_events
1344 static_cast<GdkEventMask
>(
1345 GDK_POINTER_MOTION_MASK
|
1346 GDK_BUTTON_PRESS_MASK
|
1347 GDK_BUTTON_RELEASE_MASK
),
1348 window
, // confine_to
1352 if (grab_status
!= GDK_GRAB_SUCCESS
) {
1353 LOG(WARNING
) << "Failed to grab pointer for LockMouse. "
1354 << "gdk_pointer_grab returned: " << grab_status
;
1355 mouse_locked_
= false;
1359 // Clear the tooltip window.
1360 SetTooltipText(base::string16());
1362 // Ensure that the widget center location will be relevant for this mouse
1363 // lock session. It is updated whenever the window geometry moves
1364 // but may be out of date due to switching tabs.
1365 MarkCachedWidgetCenterStale();
1367 // Ensure that if we were previously warping the cursor to a specific point
1368 // that we no longer track doing so when entering lock. It should be cleared
1369 // by the cursor moving to the warp point, and this shouldn't be necessary.
1370 // But, this is a small effort to ensure robustness in the event a warp isn't
1372 mouse_is_being_warped_to_unlocked_position_
= false;
1377 void RenderWidgetHostViewGtk::UnlockMouse() {
1381 mouse_locked_
= false;
1383 GtkWidget
* widget
= view_
.get();
1384 GdkDisplay
* display
= gtk_widget_get_display(widget
);
1385 GdkScreen
* screen
= gtk_widget_get_screen(widget
);
1386 gdk_display_pointer_ungrab(display
, GDK_CURRENT_TIME
);
1387 gdk_display_warp_pointer(display
, screen
,
1388 unlocked_global_mouse_position_
.x(),
1389 unlocked_global_mouse_position_
.y());
1390 mouse_is_being_warped_to_unlocked_position_
= true;
1393 host_
->LostMouseLock();
1396 void RenderWidgetHostViewGtk::ForwardKeyboardEvent(
1397 const NativeWebKeyboardEvent
& event
) {
1401 EditCommands edit_commands
;
1402 if (!event
.skip_in_browser
&&
1403 key_bindings_handler_
->Match(event
, &edit_commands
)) {
1404 Send(new InputMsg_SetEditCommandsForNextKeyEvent(
1405 host_
->GetRoutingID(), edit_commands
));
1406 NativeWebKeyboardEvent
copy_event(event
);
1407 copy_event
.match_edit_command
= true;
1408 host_
->ForwardKeyboardEvent(copy_event
);
1412 host_
->ForwardKeyboardEvent(event
);
1415 bool RenderWidgetHostViewGtk::RetrieveSurrounding(std::string
* text
,
1416 size_t* cursor_index
) {
1417 if (!selection_range_
.IsValid())
1420 size_t offset
= selection_range_
.GetMin() - selection_text_offset_
;
1421 DCHECK(offset
<= selection_text_
.length());
1423 if (offset
== selection_text_
.length()) {
1424 *text
= base::UTF16ToUTF8(selection_text_
);
1425 *cursor_index
= text
->length();
1429 *text
= base::UTF16ToUTF8AndAdjustOffset(
1430 base::StringPiece16(selection_text_
), &offset
);
1431 if (offset
== base::string16::npos
) {
1432 NOTREACHED() << "Invalid offset in UTF16 string.";
1435 *cursor_index
= offset
;
1439 void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton
* event
) {
1440 GdkEventButton
* temp
= NULL
;
1442 temp
= reinterpret_cast<GdkEventButton
*>(
1443 gdk_event_copy(reinterpret_cast<GdkEvent
*>(event
)));
1446 if (last_mouse_down_
)
1447 gdk_event_free(reinterpret_cast<GdkEvent
*>(last_mouse_down_
));
1449 last_mouse_down_
= temp
;
1452 void RenderWidgetHostViewGtk::MarkCachedWidgetCenterStale() {
1453 widget_center_valid_
= false;
1454 mouse_has_been_warped_to_new_center_
= false;
1457 gfx::Point
RenderWidgetHostViewGtk::GetWidgetCenter() {
1458 if (widget_center_valid_
)
1459 return widget_center_
;
1461 GdkWindow
* window
= gtk_widget_get_window(view_
.get());
1464 gdk_window_get_origin(window
, &window_x
, &window_y
);
1465 gint window_w
= gdk_window_get_width(window
);
1466 gint window_h
= gdk_window_get_height(window
);
1467 widget_center_
.SetPoint(window_x
+ window_w
/ 2,
1468 window_y
+ window_h
/ 2);
1469 widget_center_valid_
= true;
1470 return widget_center_
;
1473 void RenderWidgetHostViewGtk::ModifyEventMovementAndCoords(
1474 blink::WebMouseEvent
* event
) {
1475 // Movement is computed by taking the difference of the new cursor position
1476 // and the previous. Under mouse lock the cursor will be warped back to the
1477 // center so that we are not limited by clipping boundaries.
1478 // We do not measure movement as the delta from cursor to center because
1479 // we may receive more mouse movement events before our warp has taken
1481 event
->movementX
= event
->globalX
- global_mouse_position_
.x();
1482 event
->movementY
= event
->globalY
- global_mouse_position_
.y();
1484 // While the cursor is being warped back to the unlocked position, suppress
1485 // the movement member data.
1486 if (mouse_is_being_warped_to_unlocked_position_
) {
1487 event
->movementX
= 0;
1488 event
->movementY
= 0;
1489 if (MovedToPoint(*event
, unlocked_global_mouse_position_
))
1490 mouse_is_being_warped_to_unlocked_position_
= false;
1493 global_mouse_position_
.SetPoint(event
->globalX
, event
->globalY
);
1495 // Under mouse lock, coordinates of mouse are locked to what they were when
1496 // mouse lock was entered.
1497 if (mouse_locked_
) {
1498 event
->x
= unlocked_mouse_position_
.x();
1499 event
->y
= unlocked_mouse_position_
.y();
1500 event
->windowX
= unlocked_mouse_position_
.x();
1501 event
->windowY
= unlocked_mouse_position_
.y();
1502 event
->globalX
= unlocked_global_mouse_position_
.x();
1503 event
->globalY
= unlocked_global_mouse_position_
.y();
1504 } else if (!mouse_is_being_warped_to_unlocked_position_
) {
1505 unlocked_mouse_position_
.SetPoint(event
->windowX
, event
->windowY
);
1506 unlocked_global_mouse_position_
.SetPoint(event
->globalX
, event
->globalY
);
1510 ////////////////////////////////////////////////////////////////////////////////
1511 // RenderWidgetHostView, public:
1514 RenderWidgetHostView
* RenderWidgetHostView::CreateViewForWidget(
1515 RenderWidgetHost
* widget
) {
1516 return new RenderWidgetHostViewGtk(widget
);
1520 void RenderWidgetHostViewPort::GetDefaultScreenInfo(WebScreenInfo
* results
) {
1521 GdkWindow
* gdk_window
=
1522 gdk_display_get_default_group(gdk_display_get_default());
1523 GetScreenInfoFromNativeWindow(gdk_window
, results
);
1526 void RenderWidgetHostViewGtk::SetAccessibilityFocus(int acc_obj_id
) {
1530 host_
->AccessibilitySetFocus(acc_obj_id
);
1533 void RenderWidgetHostViewGtk::AccessibilityDoDefaultAction(int acc_obj_id
) {
1537 host_
->AccessibilityDoDefaultAction(acc_obj_id
);
1540 void RenderWidgetHostViewGtk::AccessibilityScrollToMakeVisible(
1541 int acc_obj_id
, gfx::Rect subfocus
) {
1545 host_
->AccessibilityScrollToMakeVisible(acc_obj_id
, subfocus
);
1548 void RenderWidgetHostViewGtk::AccessibilityScrollToPoint(
1549 int acc_obj_id
, gfx::Point point
) {
1553 host_
->AccessibilityScrollToPoint(acc_obj_id
, point
);
1556 void RenderWidgetHostViewGtk::AccessibilitySetTextSelection(
1557 int acc_obj_id
, int start_offset
, int end_offset
) {
1561 host_
->AccessibilitySetTextSelection(acc_obj_id
, start_offset
, end_offset
);
1564 gfx::Point
RenderWidgetHostViewGtk::GetLastTouchEventLocation() const {
1565 // Not needed on Linux.
1566 return gfx::Point();
1569 void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() {
1571 host_
->FatalAccessibilityTreeError();
1572 SetBrowserAccessibilityManager(NULL
);
1578 void RenderWidgetHostViewGtk::CreateBrowserAccessibilityManagerIfNeeded() {
1579 if (!GetBrowserAccessibilityManager()) {
1580 GtkWidget
* parent
= gtk_widget_get_parent(view_
.get());
1581 SetBrowserAccessibilityManager(
1582 new BrowserAccessibilityManagerGtk(
1584 BrowserAccessibilityManagerGtk::GetEmptyDocument(),
1589 AtkObject
* RenderWidgetHostViewGtk::GetAccessible() {
1590 if (!GetBrowserAccessibilityManager()) {
1591 GtkWidget
* parent
= gtk_widget_get_parent(view_
.get());
1592 SetBrowserAccessibilityManager(
1593 new BrowserAccessibilityManagerGtk(
1595 BrowserAccessibilityManagerGtk::GetEmptyDocument(),
1598 BrowserAccessibilityGtk
* root
=
1599 GetBrowserAccessibilityManager()->GetRoot()->ToBrowserAccessibilityGtk();
1601 atk_object_set_role(root
->GetAtkObject(), ATK_ROLE_HTML_CONTAINER
);
1602 return root
->GetAtkObject();
1605 void RenderWidgetHostViewGtk::OnCreatePluginContainer(
1606 gfx::PluginWindowHandle id
) {
1607 plugin_container_manager_
.CreatePluginContainer(id
);
1610 void RenderWidgetHostViewGtk::OnDestroyPluginContainer(
1611 gfx::PluginWindowHandle id
) {
1612 plugin_container_manager_
.DestroyPluginContainer(id
);
1615 } // namespace content