Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(726)

Side by Side Diff: content/browser/renderer_host/render_widget_host_view_gtk.cc

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

Powered by Google App Engine
This is Rietveld 408576698