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/web/WebInputEvent.h"
43 #include "third_party/WebKit/public/web/WebScreenInfo.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 WebKit::WebMouseWheelEvent;
54 using WebKit::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 WebKit::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 WebKit::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 WebKit::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 ui::LatencyInfo& latency_info) {
855 TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::DidUpdateBackingStore");
856 software_latency_info_.MergeWith(latency_info);
858 if (host_->is_hidden())
861 // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX. Can that
862 // be done using XCopyArea? Perhaps similar to
863 // BackingStore::ScrollBackingStore?
864 if (about_to_validate_and_paint_)
865 invalid_rect_.Union(scroll_rect);
869 for (size_t i = 0; i < copy_rects.size(); ++i) {
870 // Avoid double painting. NOTE: This is only relevant given the call to
871 // Paint(scroll_rect) above.
872 gfx::Rect rect = gfx::SubtractRects(copy_rects[i], scroll_rect);
876 if (about_to_validate_and_paint_)
877 invalid_rect_.Union(rect);
883 void RenderWidgetHostViewGtk::RenderProcessGone(base::TerminationStatus status,
886 plugin_container_manager_.set_host_widget(NULL);
889 void RenderWidgetHostViewGtk::Destroy() {
890 if (compositing_surface_ != gfx::kNullPluginWindow) {
891 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
892 manager->ReleasePermanentXID(compositing_surface_);
897 GdkDisplay* display = gtk_widget_get_display(parent_);
898 gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
899 gdk_display_keyboard_ungrab(display, GDK_CURRENT_TIME);
903 // If this is a popup or fullscreen widget, then we need to destroy the
904 // window that we created to hold it.
905 if (IsPopup() || is_fullscreen_) {
906 GtkWidget* window = gtk_widget_get_parent(view_.get());
908 ui::ActiveWindowWatcherX::RemoveObserver(this);
910 // Disconnect the destroy handler so that we don't try to shutdown twice.
912 g_signal_handler_disconnect(window, destroy_handler_id_);
914 gtk_widget_destroy(window);
917 // Remove |view_| from all containers now, so nothing else can hold a
918 // reference to |view_|'s widget except possibly a gtk signal handler if
919 // this code is currently executing within the context of a gtk signal
920 // handler. Note that |view_| is still alive after this call. It will be
921 // deallocated in the destructor.
922 // See http://crbug.com/11847 for details.
923 gtk_widget_destroy(view_.get());
926 // The RenderWidgetHost's destruction led here, so don't call it.
929 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
932 void RenderWidgetHostViewGtk::SetTooltipText(const string16& tooltip_text) {
933 // Maximum number of characters we allow in a tooltip.
934 const int kMaxTooltipLength = 8 << 10;
935 // Clamp the tooltip length to kMaxTooltipLength so that we don't
936 // accidentally DOS the user with a mega tooltip (since GTK doesn't do
938 // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream.
939 const string16 clamped_tooltip =
940 gfx::TruncateString(tooltip_text, kMaxTooltipLength);
942 if (clamped_tooltip.empty()) {
943 gtk_widget_set_has_tooltip(view_.get(), FALSE);
945 gtk_widget_set_tooltip_text(view_.get(),
946 UTF16ToUTF8(clamped_tooltip).c_str());
950 void RenderWidgetHostViewGtk::SelectionChanged(const string16& text,
952 const gfx::Range& range) {
953 RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
955 if (text.empty() || range.is_empty())
957 size_t pos = range.GetMin() - offset;
958 size_t n = range.length();
960 DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
961 if (pos >= text.length()) {
962 NOTREACHED() << "The text can not cover range.";
966 // Set the CLIPBOARD_TYPE SELECTION to the ui::Clipboard.
967 ui::ScopedClipboardWriter clipboard_writer(
968 ui::Clipboard::GetForCurrentThread(),
969 ui::CLIPBOARD_TYPE_SELECTION);
970 clipboard_writer.WriteText(text.substr(pos, n));
973 void RenderWidgetHostViewGtk::SelectionBoundsChanged(
974 const ViewHostMsg_SelectionBounds_Params& params) {
975 im_context_->UpdateCaretBounds(
976 gfx::UnionRects(params.anchor_rect, params.focus_rect));
979 void RenderWidgetHostViewGtk::ScrollOffsetChanged() {
982 GdkEventButton* RenderWidgetHostViewGtk::GetLastMouseDown() {
983 return last_mouse_down_;
986 gfx::NativeView RenderWidgetHostViewGtk::BuildInputMethodsGtkMenu() {
987 return im_context_->BuildInputMethodsGtkMenu();
990 void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) {
991 DCHECK(is_fullscreen_);
995 bool RenderWidgetHostViewGtk::NeedsInputGrab() {
996 return popup_type_ == WebKit::WebPopupTypeSelect;
999 bool RenderWidgetHostViewGtk::IsPopup() const {
1000 return popup_type_ != WebKit::WebPopupTypeNone;
1003 void RenderWidgetHostViewGtk::DoSharedInit() {
1004 view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this));
1005 im_context_.reset(new GtkIMContextWrapper(this));
1006 plugin_container_manager_.set_host_widget(view_.get());
1007 key_bindings_handler_.reset(new GtkKeyBindingsHandler(view_.get()));
1010 void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow* window,
1011 const gfx::Rect& bounds) {
1012 requested_size_.SetSize(std::min(bounds.width(), kMaxWindowWidth),
1013 std::min(bounds.height(), kMaxWindowHeight));
1014 host_->WasResized();
1016 ui::ActiveWindowWatcherX::AddObserver(this);
1018 // Don't set the size when we're going fullscreen. This can confuse the
1019 // window manager into thinking we're resizing a fullscreen window and
1020 // therefore not fullscreen anymore.
1021 if (!is_fullscreen_) {
1022 gtk_widget_set_size_request(
1023 view_.get(), requested_size_.width(), requested_size_.height());
1025 // Don't allow the window to be resized. This also forces the window to
1026 // shrink down to the size of its child contents.
1027 gtk_window_set_resizable(window, FALSE);
1028 gtk_window_set_default_size(window, -1, -1);
1029 gtk_window_move(window, bounds.x(), bounds.y());
1032 gtk_widget_show_all(GTK_WIDGET(window));
1035 BackingStore* RenderWidgetHostViewGtk::AllocBackingStore(
1036 const gfx::Size& size) {
1037 gint depth = gdk_visual_get_depth(gtk_widget_get_visual(view_.get()));
1038 return new BackingStoreGtk(host_, size,
1039 ui::GetVisualFromGtkWidget(view_.get()),
1043 // NOTE: |output| is initialized with the size of |src_subrect|, and |dst_size|
1044 // is ignored on GTK.
1045 void RenderWidgetHostViewGtk::CopyFromCompositingSurface(
1046 const gfx::Rect& src_subrect,
1047 const gfx::Size& /* dst_size */,
1048 const base::Callback<void(bool, const SkBitmap&)>& callback) {
1049 // Grab the snapshot from the renderer as that's the only reliable way to
1050 // readback from the GPU for this platform right now.
1051 GetRenderWidgetHost()->GetSnapshotFromRenderer(src_subrect, callback);
1054 void RenderWidgetHostViewGtk::CopyFromCompositingSurfaceToVideoFrame(
1055 const gfx::Rect& src_subrect,
1056 const scoped_refptr<media::VideoFrame>& target,
1057 const base::Callback<void(bool)>& callback) {
1059 callback.Run(false);
1062 bool RenderWidgetHostViewGtk::CanCopyToVideoFrame() const {
1066 void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped(
1067 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
1069 AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
1070 ack_params.sync_point = 0;
1071 RenderWidgetHostImpl::AcknowledgeBufferPresent(
1072 params.route_id, gpu_host_id, ack_params);
1073 RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info);
1076 void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer(
1077 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
1079 AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
1080 ack_params.sync_point = 0;
1081 RenderWidgetHostImpl::AcknowledgeBufferPresent(
1082 params.route_id, gpu_host_id, ack_params);
1083 RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info);
1086 void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() {
1089 void RenderWidgetHostViewGtk::AcceleratedSurfaceRelease() {
1092 bool RenderWidgetHostViewGtk::HasAcceleratedSurface(
1093 const gfx::Size& desired_size) {
1094 // TODO(jbates) Implement this so this view can use GetBackingStore for both
1095 // software and GPU frames. Defaulting to false just makes GetBackingStore
1096 // only useable for software frames.
1100 void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) {
1101 RenderWidgetHostViewBase::SetBackground(background);
1102 Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background));
1105 void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging(
1106 GtkWidget* widget, GdkEventMotion* event) {
1107 // If the widget is aligned with an edge of the monitor its on and the user
1108 // attempts to drag past that edge we track the number of times it has
1109 // occurred, so that we can force the widget to scroll when it otherwise
1110 // would be unable to, by modifying the (x,y) position in the drag
1111 // event that we forward on to webkit. If we get a move that's no longer a
1112 // drag or a drag indicating the user is no longer at that edge we stop
1113 // altering the drag events.
1114 int new_dragged_at_horizontal_edge = 0;
1115 int new_dragged_at_vertical_edge = 0;
1116 // Used for checking the edges of the monitor. We cache the values to save
1117 // roundtrips to the X server.
1118 CR_DEFINE_STATIC_LOCAL(gfx::Size, drag_monitor_size, ());
1119 if (event->state & GDK_BUTTON1_MASK) {
1120 if (drag_monitor_size.IsEmpty()) {
1121 // We can safely cache the monitor size for the duration of a drag.
1122 GdkScreen* screen = gtk_widget_get_screen(widget);
1124 gdk_screen_get_monitor_at_point(screen, event->x_root, event->y_root);
1125 GdkRectangle geometry;
1126 gdk_screen_get_monitor_geometry(screen, monitor, &geometry);
1127 drag_monitor_size.SetSize(geometry.width, geometry.height);
1129 GtkAllocation allocation;
1130 gtk_widget_get_allocation(widget, &allocation);
1131 // Check X and Y independently, as the user could be dragging into a corner.
1132 if (event->x == 0 && event->x_root == 0) {
1133 new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ - 1;
1134 } else if (allocation.width - 1 == static_cast<gint>(event->x) &&
1135 drag_monitor_size.width() - 1 == static_cast<gint>(event->x_root)) {
1136 new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ + 1;
1139 if (event->y == 0 && event->y_root == 0) {
1140 new_dragged_at_vertical_edge = dragged_at_vertical_edge_ - 1;
1141 } else if (allocation.height - 1 == static_cast<gint>(event->y) &&
1142 drag_monitor_size.height() - 1 == static_cast<gint>(event->y_root)) {
1143 new_dragged_at_vertical_edge = dragged_at_vertical_edge_ + 1;
1146 event->x_root += new_dragged_at_horizontal_edge;
1147 event->x += new_dragged_at_horizontal_edge;
1148 event->y_root += new_dragged_at_vertical_edge;
1149 event->y += new_dragged_at_vertical_edge;
1151 // Clear whenever we get a non-drag mouse move.
1152 drag_monitor_size.SetSize(0, 0);
1154 dragged_at_horizontal_edge_ = new_dragged_at_horizontal_edge;
1155 dragged_at_vertical_edge_ = new_dragged_at_vertical_edge;
1158 void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) {
1159 TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::Paint");
1161 // If the GPU process is rendering directly into the View,
1162 // call the compositor directly.
1163 RenderWidgetHostImpl* render_widget_host =
1164 RenderWidgetHostImpl::From(GetRenderWidgetHost());
1165 if (render_widget_host->is_accelerated_compositing_active()) {
1166 host_->ScheduleComposite();
1170 GdkWindow* window = gtk_widget_get_window(view_.get());
1171 DCHECK(!about_to_validate_and_paint_);
1173 invalid_rect_ = damage_rect;
1174 about_to_validate_and_paint_ = true;
1176 // If the size of our canvas is (0,0), then we don't want to block here. We
1177 // are doing one of our first paints and probably have animations going on.
1178 bool force_create = !host_->empty();
1179 BackingStoreGtk* backing_store = static_cast<BackingStoreGtk*>(
1180 host_->GetBackingStore(force_create));
1181 // Calling GetBackingStore maybe have changed |invalid_rect_|...
1182 about_to_validate_and_paint_ = false;
1184 gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight);
1185 paint_rect.Intersect(invalid_rect_);
1187 if (backing_store) {
1188 // Only render the widget if it is attached to a window; there's a short
1189 // period where this object isn't attached to a window but hasn't been
1190 // Destroy()ed yet and it receives paint messages...
1192 backing_store->XShowRect(gfx::Point(0, 0),
1193 paint_rect, ui::GetX11WindowFromGtkWidget(view_.get()));
1195 if (!whiteout_start_time_.is_null()) {
1196 base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
1197 whiteout_start_time_;
1198 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
1200 // Reset the start time to 0 so that we start recording again the next
1201 // time the backing store is NULL...
1202 whiteout_start_time_ = base::TimeTicks();
1204 if (!web_contents_switch_paint_time_.is_null()) {
1205 base::TimeDelta web_contents_switch_paint_duration =
1206 base::TimeTicks::Now() - web_contents_switch_paint_time_;
1207 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
1208 web_contents_switch_paint_duration);
1209 // Reset web_contents_switch_paint_time_ to 0 so future tab selections are
1211 web_contents_switch_paint_time_ = base::TimeTicks();
1213 software_latency_info_.AddLatencyNumber(
1214 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
1215 render_widget_host->FrameSwapped(software_latency_info_);
1216 software_latency_info_.Clear();
1219 gdk_window_clear(window);
1220 if (whiteout_start_time_.is_null())
1221 whiteout_start_time_ = base::TimeTicks::Now();
1225 void RenderWidgetHostViewGtk::ShowCurrentCursor() {
1226 // The widget may not have a window. If that's the case, abort mission. This
1227 // is the same issue as that explained above in Paint().
1228 if (!gtk_widget_get_window(view_.get()))
1231 // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is
1232 // that calling gdk_window_set_cursor repeatedly is expensive. We should
1233 // avoid it here where possible.
1234 GdkCursor* gdk_cursor;
1235 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) {
1236 // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and
1237 // the page is loading.
1238 gdk_cursor = is_loading_ ? GetMozSpinningCursor() : NULL;
1240 gdk_cursor = current_cursor_.GetNativeCursor();
1242 gdk_window_set_cursor(gtk_widget_get_window(view_.get()), gdk_cursor);
1245 void RenderWidgetHostViewGtk::SetHasHorizontalScrollbar(
1246 bool has_horizontal_scrollbar) {
1249 void RenderWidgetHostViewGtk::SetScrollOffsetPinning(
1250 bool is_pinned_to_left, bool is_pinned_to_right) {
1254 void RenderWidgetHostViewGtk::OnAcceleratedCompositingStateChange() {
1255 bool activated = host_->is_accelerated_compositing_active();
1256 GtkPreserveWindow* widget = reinterpret_cast<GtkPreserveWindow*>(view_.get());
1258 gtk_preserve_window_delegate_resize(widget, activated);
1261 void RenderWidgetHostViewGtk::GetScreenInfo(WebScreenInfo* results) {
1262 GdkWindow* gdk_window = gtk_widget_get_window(view_.get());
1264 GdkDisplay* display = gdk_display_get_default();
1265 gdk_window = gdk_display_get_default_group(display);
1269 GetScreenInfoFromNativeWindow(gdk_window, results);
1272 gfx::Rect RenderWidgetHostViewGtk::GetBoundsInRootWindow() {
1273 GtkWidget* toplevel = gtk_widget_get_toplevel(view_.get());
1275 return GetViewBounds();
1277 GdkRectangle frame_extents;
1278 GdkWindow* gdk_window = gtk_widget_get_window(toplevel);
1280 return GetViewBounds();
1282 gdk_window_get_frame_extents(gdk_window, &frame_extents);
1283 return gfx::Rect(frame_extents.x, frame_extents.y,
1284 frame_extents.width, frame_extents.height);
1287 gfx::GLSurfaceHandle RenderWidgetHostViewGtk::GetCompositingSurface() {
1288 if (compositing_surface_ == gfx::kNullPluginWindow) {
1289 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
1290 gfx::NativeViewId view_id = GetNativeViewId();
1292 if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) {
1293 DLOG(ERROR) << "Can't find XID for view id " << view_id;
1296 return gfx::GLSurfaceHandle(compositing_surface_, gfx::NATIVE_TRANSPORT);
1299 void RenderWidgetHostViewGtk::ResizeCompositingSurface(const gfx::Size& size) {
1300 GtkWidget* widget = view_.get();
1301 GdkWindow* window = gtk_widget_get_window(widget);
1303 Display* display = GDK_WINDOW_XDISPLAY(window);
1304 gdk_window_resize(window, size.width(), size.height());
1305 XSync(display, False);
1309 bool RenderWidgetHostViewGtk::LockMouse() {
1313 mouse_locked_ = true;
1315 // Release any current grab.
1316 GtkWidget* current_grab_window = gtk_grab_get_current();
1317 if (current_grab_window) {
1318 gtk_grab_remove(current_grab_window);
1319 LOG(WARNING) << "Locking Mouse with gdk_pointer_grab, "
1320 << "but had to steal grab from another window";
1323 GtkWidget* widget = view_.get();
1324 GdkWindow* window = gtk_widget_get_window(widget);
1325 GdkCursor* cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
1327 GdkGrabStatus grab_status =
1328 gdk_pointer_grab(window,
1329 FALSE, // owner_events
1330 static_cast<GdkEventMask>(
1331 GDK_POINTER_MOTION_MASK |
1332 GDK_BUTTON_PRESS_MASK |
1333 GDK_BUTTON_RELEASE_MASK),
1334 window, // confine_to
1338 if (grab_status != GDK_GRAB_SUCCESS) {
1339 LOG(WARNING) << "Failed to grab pointer for LockMouse. "
1340 << "gdk_pointer_grab returned: " << grab_status;
1341 mouse_locked_ = false;
1345 // Clear the tooltip window.
1346 SetTooltipText(string16());
1348 // Ensure that the widget center location will be relevant for this mouse
1349 // lock session. It is updated whenever the window geometry moves
1350 // but may be out of date due to switching tabs.
1351 MarkCachedWidgetCenterStale();
1356 void RenderWidgetHostViewGtk::UnlockMouse() {
1360 mouse_locked_ = false;
1362 GtkWidget* widget = view_.get();
1363 GdkDisplay* display = gtk_widget_get_display(widget);
1364 GdkScreen* screen = gtk_widget_get_screen(widget);
1365 gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
1366 gdk_display_warp_pointer(display, screen,
1367 unlocked_global_mouse_position_.x(),
1368 unlocked_global_mouse_position_.y());
1369 mouse_is_being_warped_to_unlocked_position_ = true;
1372 host_->LostMouseLock();
1375 void RenderWidgetHostViewGtk::ForwardKeyboardEvent(
1376 const NativeWebKeyboardEvent& event) {
1380 EditCommands edit_commands;
1381 if (!event.skip_in_browser &&
1382 key_bindings_handler_->Match(event, &edit_commands)) {
1383 Send(new InputMsg_SetEditCommandsForNextKeyEvent(
1384 host_->GetRoutingID(), edit_commands));
1385 NativeWebKeyboardEvent copy_event(event);
1386 copy_event.match_edit_command = true;
1387 host_->ForwardKeyboardEvent(copy_event);
1391 host_->ForwardKeyboardEvent(event);
1394 bool RenderWidgetHostViewGtk::RetrieveSurrounding(std::string* text,
1395 size_t* cursor_index) {
1396 if (!selection_range_.IsValid())
1399 size_t offset = selection_range_.GetMin() - selection_text_offset_;
1400 DCHECK(offset <= selection_text_.length());
1402 if (offset == selection_text_.length()) {
1403 *text = UTF16ToUTF8(selection_text_);
1404 *cursor_index = text->length();
1408 *text = base::UTF16ToUTF8AndAdjustOffset(
1409 base::StringPiece16(selection_text_), &offset);
1410 if (offset == string16::npos) {
1411 NOTREACHED() << "Invalid offset in UTF16 string.";
1414 *cursor_index = offset;
1418 void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton* event) {
1419 GdkEventButton* temp = NULL;
1421 temp = reinterpret_cast<GdkEventButton*>(
1422 gdk_event_copy(reinterpret_cast<GdkEvent*>(event)));
1425 if (last_mouse_down_)
1426 gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_));
1428 last_mouse_down_ = temp;
1431 void RenderWidgetHostViewGtk::MarkCachedWidgetCenterStale() {
1432 widget_center_valid_ = false;
1433 mouse_has_been_warped_to_new_center_ = false;
1436 gfx::Point RenderWidgetHostViewGtk::GetWidgetCenter() {
1437 if (widget_center_valid_)
1438 return widget_center_;
1440 GdkWindow* window = gtk_widget_get_window(view_.get());
1443 gdk_window_get_origin(window, &window_x, &window_y);
1444 gint window_w = gdk_window_get_width(window);
1445 gint window_h = gdk_window_get_height(window);
1446 widget_center_.SetPoint(window_x + window_w / 2,
1447 window_y + window_h / 2);
1448 widget_center_valid_ = true;
1449 return widget_center_;
1452 void RenderWidgetHostViewGtk::ModifyEventMovementAndCoords(
1453 WebKit::WebMouseEvent* event) {
1454 // Movement is computed by taking the difference of the new cursor position
1455 // and the previous. Under mouse lock the cursor will be warped back to the
1456 // center so that we are not limited by clipping boundaries.
1457 // We do not measure movement as the delta from cursor to center because
1458 // we may receive more mouse movement events before our warp has taken
1460 event->movementX = event->globalX - global_mouse_position_.x();
1461 event->movementY = event->globalY - global_mouse_position_.y();
1463 // While the cursor is being warped back to the unlocked position, suppress
1464 // the movement member data.
1465 if (mouse_is_being_warped_to_unlocked_position_) {
1466 event->movementX = 0;
1467 event->movementY = 0;
1468 if (MovedToPoint(*event, unlocked_global_mouse_position_))
1469 mouse_is_being_warped_to_unlocked_position_ = false;
1472 global_mouse_position_.SetPoint(event->globalX, event->globalY);
1474 // Under mouse lock, coordinates of mouse are locked to what they were when
1475 // mouse lock was entered.
1476 if (mouse_locked_) {
1477 event->x = unlocked_mouse_position_.x();
1478 event->y = unlocked_mouse_position_.y();
1479 event->windowX = unlocked_mouse_position_.x();
1480 event->windowY = unlocked_mouse_position_.y();
1481 event->globalX = unlocked_global_mouse_position_.x();
1482 event->globalY = unlocked_global_mouse_position_.y();
1484 unlocked_mouse_position_.SetPoint(event->windowX, event->windowY);
1485 unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY);
1489 ////////////////////////////////////////////////////////////////////////////////
1490 // RenderWidgetHostView, public:
1493 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
1494 RenderWidgetHost* widget) {
1495 return new RenderWidgetHostViewGtk(widget);
1499 void RenderWidgetHostViewPort::GetDefaultScreenInfo(WebScreenInfo* results) {
1500 GdkWindow* gdk_window =
1501 gdk_display_get_default_group(gdk_display_get_default());
1502 GetScreenInfoFromNativeWindow(gdk_window, results);
1505 void RenderWidgetHostViewGtk::SetAccessibilityFocus(int acc_obj_id) {
1509 host_->AccessibilitySetFocus(acc_obj_id);
1512 void RenderWidgetHostViewGtk::AccessibilityDoDefaultAction(int acc_obj_id) {
1516 host_->AccessibilityDoDefaultAction(acc_obj_id);
1519 void RenderWidgetHostViewGtk::AccessibilityScrollToMakeVisible(
1520 int acc_obj_id, gfx::Rect subfocus) {
1524 host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
1527 void RenderWidgetHostViewGtk::AccessibilityScrollToPoint(
1528 int acc_obj_id, gfx::Point point) {
1532 host_->AccessibilityScrollToPoint(acc_obj_id, point);
1535 void RenderWidgetHostViewGtk::AccessibilitySetTextSelection(
1536 int acc_obj_id, int start_offset, int end_offset) {
1540 host_->AccessibilitySetTextSelection(acc_obj_id, start_offset, end_offset);
1543 gfx::Point RenderWidgetHostViewGtk::GetLastTouchEventLocation() const {
1544 // Not needed on Linux.
1545 return gfx::Point();
1548 void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() {
1550 host_->FatalAccessibilityTreeError();
1551 SetBrowserAccessibilityManager(NULL);
1557 void RenderWidgetHostViewGtk::OnAccessibilityEvents(
1558 const std::vector<AccessibilityHostMsg_EventParams>& params) {
1559 if (!GetBrowserAccessibilityManager()) {
1560 GtkWidget* parent = gtk_widget_get_parent(view_.get());
1561 SetBrowserAccessibilityManager(
1562 new BrowserAccessibilityManagerGtk(
1564 BrowserAccessibilityManagerGtk::GetEmptyDocument(),
1567 GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
1570 AtkObject* RenderWidgetHostViewGtk::GetAccessible() {
1571 if (!GetBrowserAccessibilityManager()) {
1572 GtkWidget* parent = gtk_widget_get_parent(view_.get());
1573 SetBrowserAccessibilityManager(
1574 new BrowserAccessibilityManagerGtk(
1576 BrowserAccessibilityManagerGtk::GetEmptyDocument(),
1579 BrowserAccessibilityGtk* root =
1580 GetBrowserAccessibilityManager()->GetRoot()->ToBrowserAccessibilityGtk();
1582 atk_object_set_role(root->GetAtkObject(), ATK_ROLE_HTML_CONTAINER);
1583 return root->GetAtkObject();
1586 void RenderWidgetHostViewGtk::OnCreatePluginContainer(
1587 gfx::PluginWindowHandle id) {
1588 plugin_container_manager_.CreatePluginContainer(id);
1591 void RenderWidgetHostViewGtk::OnDestroyPluginContainer(
1592 gfx::PluginWindowHandle id) {
1593 plugin_container_manager_.DestroyPluginContainer(id);
1596 } // namespace content