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/gpu/gpu_messages.h" 36 #include "content/common/input_messages.h" 37 #include "content/common/view_messages.h" 38 #include "content/common/webplugin_geometry.h" 39 #include "content/public/browser/native_web_keyboard_event.h" 40 #include "content/public/common/content_switches.h" 41 #include "skia/ext/platform_canvas.h" 42 #include "third_party/WebKit/public/platform/WebScreenInfo.h" 43 #include "third_party/WebKit/public/web/WebInputEvent.h" 44 #include "ui/base/clipboard/scoped_clipboard_writer.h" 45 #include "ui/base/x/active_window_watcher_x.h" 46 #include "ui/base/x/x11_util.h" 47 #include "ui/gfx/gtk_compat.h" 48 #include "ui/gfx/gtk_native_view_id_manager.h" 49 #include "ui/gfx/gtk_preserve_window.h" 50 #include "ui/gfx/text_elider.h" 51 #include "webkit/common/cursors/webcursor_gtk_data.h" 52 53 using blink::WebMouseWheelEvent; 54 using blink::WebScreenInfo; 55 56 namespace content { 57 namespace { 58 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 61 // (i.e. to 256MB). 62 // 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. 65 // 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; 71 72 const GdkColor kBGColor = 73 #if defined(NDEBUG) 74 { 0, 0xff * 257, 0xff * 257, 0xff * 257 }; 75 #else 76 { 0, 0x00 * 257, 0xff * 257, 0x00 * 257 }; 77 #endif 78 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); 89 moz_spinning_cursor = 90 gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 2, 2); 91 g_object_unref(source); 92 g_object_unref(mask); 93 } 94 return moz_spinning_cursor; 95 } 96 97 bool MovedToPoint(const blink::WebMouseEvent& mouse_event, 98 const gfx::Point& center) { 99 return mouse_event.globalX == center.x() && 100 mouse_event.globalY == center.y(); 101 } 102 103 } // namespace 104 105 // This class is a simple convenience wrapper for Gtk functions. It has only 106 // static methods. 107 class RenderWidgetHostViewGtkWidget { 108 public: 109 static AtkObject* GetAccessible(void* userdata) { 110 return (static_cast<RenderWidgetHostViewGtk*>(userdata))-> 111 GetAccessible(); 112 } 113 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); 124 125 gtk_widget_add_events(widget, GDK_EXPOSURE_MASK | 126 GDK_STRUCTURE_MASK | 127 GDK_POINTER_MOTION_MASK | 128 GDK_BUTTON_PRESS_MASK | 129 GDK_BUTTON_RELEASE_MASK | 130 GDK_KEY_PRESS_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); 136 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); 167 168 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); 173 174 // Route calls to get_accessible to the view. 175 gtk_preserve_window_set_accessible_factory( 176 GTK_PRESERVE_WINDOW(widget), GetAccessible, host_view); 177 178 return widget; 179 } 180 181 private: 182 static gboolean OnExposeEvent(GtkWidget* widget, 183 GdkEventExpose* expose, 184 RenderWidgetHostViewGtk* host_view) { 185 if (host_view->host_->is_hidden()) 186 return FALSE; 187 const gfx::Rect damage_rect(expose->area); 188 host_view->Paint(damage_rect); 189 return FALSE; 190 } 191 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), 197 "configure-event", 198 G_CALLBACK(OnConfigureEvent), host_view); 199 return FALSE; 200 } 201 202 static gboolean OnConfigureEvent(GtkWidget* widget, 203 GdkEventConfigure* event, 204 RenderWidgetHostViewGtk* host_view) { 205 host_view->MarkCachedWidgetCenterStale(); 206 host_view->UpdateScreenInfo(host_view->GetNativeView()); 207 return FALSE; 208 } 209 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)); 215 return FALSE; 216 } 217 218 static gboolean OnKeyPressReleaseEvent(GtkWidget* widget, 219 GdkEventKey* event, 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(); 230 } else { 231 // Send key event to input method. 232 host_view->im_context_->ProcessKeyEvent(event); 233 } 234 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(). 238 return TRUE; 239 } 240 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()); 247 host->GotFocus(); 248 host->SetActive(true); 249 250 // The only way to enable a GtkIMContext object is to call its focus in 251 // handler. 252 host_view->im_context_->OnFocusIn(); 253 254 return TRUE; 255 } 256 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 264 // focus. 265 if (!host_view->IsShowingContextMenu()) { 266 RenderWidgetHostImpl* host = 267 RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); 268 host->SetActive(false); 269 host->Blur(); 270 } 271 272 // Prevents us from stealing input context focus in OnGrabNotify() handler. 273 host_view->was_imcontext_focused_before_grab_ = false; 274 275 // Disable the GtkIMContext object. 276 host_view->im_context_->OnFocusOut(); 277 278 host_view->set_last_mouse_down(NULL); 279 280 return TRUE; 281 } 282 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) { 289 if (was_grabbed) { 290 if (host_view->was_imcontext_focused_before_grab_) 291 host_view->im_context_->OnFocusIn(); 292 } else { 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(); 298 } 299 } 300 } 301 302 static gboolean OnButtonPressReleaseEvent( 303 GtkWidget* widget, 304 GdkEventButton* event, 305 RenderWidgetHostViewGtk* host_view) { 306 TRACE_EVENT0("browser", 307 "RenderWidgetHostViewGtkWidget::OnButtonPressReleaseEvent"); 308 309 if (event->type != GDK_BUTTON_RELEASE) 310 host_view->set_last_mouse_down(event); 311 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) 315 return FALSE; 316 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(); 320 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(); 325 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) { 331 int x = 0; 332 int y = 0; 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(); 347 return FALSE; 348 } 349 event->x = x; 350 event->y = y; 351 } 352 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); 357 358 host_view->is_popup_first_mouse_release_ = false; 359 RenderWidgetHostImpl* widget_host = 360 RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); 361 if (widget_host) 362 widget_host->ForwardMouseEvent(WebMouseEventBuilder::Build(event)); 363 364 // Although we did handle the mouse event, we need to let other handlers 365 // run (in particular the one installed by WebContentsViewGtk). 366 return FALSE; 367 } 368 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) { 379 int x = 0; 380 int y = 0; 381 gtk_widget_get_pointer(widget, &x, &y); 382 event->x = x; 383 event->y = y; 384 } 385 386 host_view->ModifyEventForEdgeDragging(widget, event); 387 388 blink::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event); 389 390 if (host_view->mouse_locked_) { 391 gfx::Point center = host_view->GetWidgetCenter(); 392 393 bool moved_to_center = MovedToPoint(mouse_event, center); 394 if (moved_to_center) 395 host_view->mouse_has_been_warped_to_new_center_ = true; 396 397 host_view->ModifyEventMovementAndCoords(&mouse_event); 398 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); 407 } 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); 415 } 416 } 417 return FALSE; 418 } 419 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 = 426 GDK_BUTTON1_MASK | 427 GDK_BUTTON2_MASK | 428 GDK_BUTTON3_MASK | 429 GDK_BUTTON4_MASK | 430 GDK_BUTTON5_MASK; 431 432 // Only forward crossing events if the mouse button is not down. 433 // (When the mouse button is down, the proper events are already being 434 // sent by ButtonPressReleaseEvent and MouseMoveEvent, above, and if we 435 // additionally send this crossing event with the state indicating the 436 // button is down, it causes problems with drag and drop in WebKit.) 437 if (!(event->state & any_button_mask)) { 438 blink::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event); 439 host_view->ModifyEventMovementAndCoords(&mouse_event); 440 // When crossing out and back into a render view the movement values 441 // must represent the instantaneous movement of the mouse, not the jump 442 // from the exit to re-entry point. 443 mouse_event.movementX = 0; 444 mouse_event.movementY = 0; 445 RenderWidgetHostImpl::From( 446 host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event); 447 } 448 449 return FALSE; 450 } 451 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; 458 return TRUE; 459 } 460 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) { 467 int num_clicks = 0; 468 GdkEvent* event; 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; 479 } 480 if (vert) { 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; 487 } 488 } 489 } else { 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; 496 } 497 } 498 } 499 } 500 } 501 // If we have an event left we put it back on the queue. 502 if (event) { 503 gdk_event_put(event); 504 gdk_event_free(event); 505 } 506 return num_clicks * WebMouseWheelEventBuilder::ScrollbarPixelsPerTick(); 507 } 508 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; 522 } 523 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 528 // scroll events. 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; 533 else 534 web_event.deltaY = -pixelsPerTick; 535 web_event.deltaY += GetPendingScrollDelta(true, event->state); 536 } else { 537 if (event->direction == GDK_SCROLL_LEFT) 538 web_event.deltaX = pixelsPerTick; 539 else 540 web_event.deltaX = -pixelsPerTick; 541 web_event.deltaX += GetPendingScrollDelta(false, event->state); 542 } 543 RenderWidgetHostImpl::From( 544 host_view->GetRenderWidgetHost())->ForwardWheelEvent(web_event); 545 return FALSE; 546 } 547 548 DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget); 549 }; 550 551 RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) 552 : host_(RenderWidgetHostImpl::From(widget_host)), 553 about_to_validate_and_paint_(false), 554 is_loading_(false), 555 parent_(NULL), 556 is_popup_first_mouse_release_(true), 557 was_imcontext_focused_before_grab_(false), 558 do_x_grab_(false), 559 is_fullscreen_(false), 560 made_active_(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); 568 } 569 570 RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() { 571 UnlockMouse(); 572 set_last_mouse_down(NULL); 573 view_.Destroy(); 574 } 575 576 bool RenderWidgetHostViewGtk::OnMessageReceived(const IPC::Message& message) { 577 bool handled = true; 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() 585 return handled; 586 } 587 588 void RenderWidgetHostViewGtk::InitAsChild( 589 gfx::NativeView parent_view) { 590 DoSharedInit(); 591 gtk_widget_show(view_.get()); 592 } 593 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. 597 DCHECK(IsPopup()); 598 599 DoSharedInit(); 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); 604 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 608 // devices. 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 612 // covers it. 613 GtkWidget* toplevel = gtk_widget_get_toplevel(parent_); 614 if (toplevel && 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); 619 } 620 621 // Install an application-level GTK grab to make sure that we receive all of 622 // the app's input. 623 gtk_grab_add(view_.get()); 624 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(); 628 if (do_x_grab_) { 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()); 635 636 gdk_pointer_grab( 637 grab_window, 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), 642 NULL, 643 NULL, 644 GDK_CURRENT_TIME); 645 // We grab keyboard events too so things like alt+tab are eaten. 646 gdk_keyboard_grab(grab_window, TRUE, GDK_CURRENT_TIME); 647 } 648 } 649 } 650 651 void RenderWidgetHostViewGtk::InitAsFullscreen( 652 RenderWidgetHostView* reference_host_view) { 653 DCHECK(reference_host_view); 654 DoSharedInit(); 655 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), 660 "destroy", 661 G_CALLBACK(OnDestroyThunk), 662 this); 663 gtk_container_add(GTK_CONTAINER(window), view_.get()); 664 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()); 670 671 gfx::Rect bounds; 672 if (ref_gdk_window) { 673 const int monitor_id = gdk_screen_get_monitor_at_window(screen, 674 ref_gdk_window); 675 GdkRectangle monitor_rect; 676 gdk_screen_get_monitor_geometry(screen, monitor_id, &monitor_rect); 677 bounds = gfx::Rect(monitor_rect); 678 } else { 679 bounds = gfx::Rect( 680 0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen)); 681 } 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); 686 } 687 688 RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const { 689 return host_; 690 } 691 692 void RenderWidgetHostViewGtk::WasShown() { 693 if (!host_ || !host_->is_hidden()) 694 return; 695 696 if (web_contents_switch_paint_time_.is_null()) 697 web_contents_switch_paint_time_ = base::TimeTicks::Now(); 698 699 host_->WasShown(); 700 } 701 702 void RenderWidgetHostViewGtk::WasHidden() { 703 if (!host_ || host_->is_hidden()) 704 return; 705 706 // If we have a renderer, then inform it that we are being hidden so it can 707 // reduce its resource utilization. 708 host_->WasHidden(); 709 710 web_contents_switch_paint_time_ = base::TimeTicks(); 711 } 712 713 void RenderWidgetHostViewGtk::SetSize(const gfx::Size& size) { 714 int width = std::min(size.width(), kMaxWindowWidth); 715 int height = std::min(size.height(), kMaxWindowHeight); 716 if (IsPopup()) { 717 // We're a popup, honor the size request. 718 gtk_widget_set_size_request(view_.get(), width, height); 719 } 720 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(); 726 host_->WasResized(); 727 } 728 } 729 730 void RenderWidgetHostViewGtk::SetBounds(const gfx::Rect& rect) { 731 // This is called when webkit has sent us a Move message. 732 if (IsPopup()) { 733 gtk_window_move(GTK_WINDOW(gtk_widget_get_toplevel(view_.get())), 734 rect.x(), rect.y()); 735 } 736 737 SetSize(rect.size()); 738 } 739 740 gfx::NativeView RenderWidgetHostViewGtk::GetNativeView() const { 741 return view_.get(); 742 } 743 744 gfx::NativeViewId RenderWidgetHostViewGtk::GetNativeViewId() const { 745 return GtkNativeViewManager::GetInstance()->GetIdForWidget(view_.get()); 746 } 747 748 gfx::NativeViewAccessible RenderWidgetHostViewGtk::GetNativeViewAccessible() { 749 NOTIMPLEMENTED(); 750 return NULL; 751 } 752 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]); 758 } 759 } 760 761 void RenderWidgetHostViewGtk::Focus() { 762 gtk_widget_grab_focus(view_.get()); 763 } 764 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. 768 host_->Blur(); 769 } 770 771 bool RenderWidgetHostViewGtk::HasFocus() const { 772 return gtk_widget_has_focus(view_.get()); 773 } 774 775 void RenderWidgetHostViewGtk::ActiveWindowChanged(GdkWindow* window) { 776 GdkWindow* our_window = gtk_widget_get_parent_window(view_.get()); 777 778 if (our_window == window) 779 made_active_ = true; 780 781 // If the window was previously active, but isn't active anymore, shut it 782 // down. 783 if (is_fullscreen_ && our_window != window && made_active_) 784 host_->Shutdown(); 785 } 786 787 bool RenderWidgetHostViewGtk::Send(IPC::Message* message) { 788 return host_->Send(message); 789 } 790 791 bool RenderWidgetHostViewGtk::IsSurfaceAvailableForCopy() const { 792 return true; 793 } 794 795 void RenderWidgetHostViewGtk::Show() { 796 gtk_widget_show(view_.get()); 797 WasShown(); 798 } 799 800 void RenderWidgetHostViewGtk::Hide() { 801 gtk_widget_hide(view_.get()); 802 WasHidden(); 803 } 804 805 bool RenderWidgetHostViewGtk::IsShowing() { 806 return gtk_widget_get_visible(view_.get()); 807 } 808 809 gfx::Rect RenderWidgetHostViewGtk::GetViewBounds() const { 810 GdkWindow* gdk_window = gtk_widget_get_window(view_.get()); 811 if (!gdk_window) 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()); 817 } 818 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()) { 825 return; 826 } 827 828 current_cursor_ = cursor; 829 ShowCurrentCursor(); 830 } 831 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) 836 ShowCurrentCursor(); 837 } 838 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); 844 } 845 846 void RenderWidgetHostViewGtk::ImeCancelComposition() { 847 im_context_->CancelComposition(); 848 } 849 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); 857 858 if (host_->is_hidden()) 859 return; 860 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); 866 else 867 Paint(scroll_rect); 868 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); 873 if (rect.IsEmpty()) 874 continue; 875 876 if (about_to_validate_and_paint_) 877 invalid_rect_.Union(rect); 878 else 879 Paint(rect); 880 } 881 } 882 883 void RenderWidgetHostViewGtk::RenderProcessGone(base::TerminationStatus status, 884 int error_code) { 885 Destroy(); 886 plugin_container_manager_.set_host_widget(NULL); 887 } 888 889 void RenderWidgetHostViewGtk::Destroy() { 890 if (compositing_surface_ != gfx::kNullPluginWindow) { 891 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); 892 manager->ReleasePermanentXID(compositing_surface_); 893 } 894 895 if (do_x_grab_) { 896 // Undo the X grab. 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); 900 } 901 902 if (view_.get()) { 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()); 907 908 ui::ActiveWindowWatcherX::RemoveObserver(this); 909 910 // Disconnect the destroy handler so that we don't try to shutdown twice. 911 if (is_fullscreen_) 912 g_signal_handler_disconnect(window, destroy_handler_id_); 913 914 gtk_widget_destroy(window); 915 } 916 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()); 924 } 925 926 // The RenderWidgetHost's destruction led here, so don't call it. 927 host_ = NULL; 928 929 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 930 } 931 932 void RenderWidgetHostViewGtk::SetTooltipText( 933 const base::string16& tooltip_text) { 934 // Maximum number of characters we allow in a tooltip. 935 const int kMaxTooltipLength = 8 << 10; 936 // Clamp the tooltip length to kMaxTooltipLength so that we don't 937 // accidentally DOS the user with a mega tooltip (since GTK doesn't do 938 // this itself). 939 // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream. 940 const base::string16 clamped_tooltip = 941 gfx::TruncateString(tooltip_text, kMaxTooltipLength); 942 943 if (clamped_tooltip.empty()) { 944 gtk_widget_set_has_tooltip(view_.get(), FALSE); 945 } else { 946 gtk_widget_set_tooltip_text(view_.get(), 947 UTF16ToUTF8(clamped_tooltip).c_str()); 948 } 949 } 950 951 void RenderWidgetHostViewGtk::SelectionChanged(const base::string16& text, 952 size_t offset, 953 const gfx::Range& range) { 954 RenderWidgetHostViewBase::SelectionChanged(text, offset, range); 955 956 if (text.empty() || range.is_empty()) 957 return; 958 size_t pos = range.GetMin() - offset; 959 size_t n = range.length(); 960 961 DCHECK(pos + n <= text.length()) << "The text can not fully cover range."; 962 if (pos >= text.length()) { 963 NOTREACHED() << "The text can not cover range."; 964 return; 965 } 966 967 // Set the CLIPBOARD_TYPE SELECTION to the ui::Clipboard. 968 ui::ScopedClipboardWriter clipboard_writer( 969 ui::Clipboard::GetForCurrentThread(), 970 ui::CLIPBOARD_TYPE_SELECTION); 971 clipboard_writer.WriteText(text.substr(pos, n)); 972 } 973 974 void RenderWidgetHostViewGtk::SelectionBoundsChanged( 975 const ViewHostMsg_SelectionBounds_Params& params) { 976 im_context_->UpdateCaretBounds( 977 gfx::UnionRects(params.anchor_rect, params.focus_rect)); 978 } 979 980 void RenderWidgetHostViewGtk::ScrollOffsetChanged() { 981 } 982 983 GdkEventButton* RenderWidgetHostViewGtk::GetLastMouseDown() { 984 return last_mouse_down_; 985 } 986 987 gfx::NativeView RenderWidgetHostViewGtk::BuildInputMethodsGtkMenu() { 988 return im_context_->BuildInputMethodsGtkMenu(); 989 } 990 991 void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) { 992 DCHECK(is_fullscreen_); 993 host_->Shutdown(); 994 } 995 996 bool RenderWidgetHostViewGtk::NeedsInputGrab() { 997 return popup_type_ == blink::WebPopupTypeSelect; 998 } 999 1000 bool RenderWidgetHostViewGtk::IsPopup() const { 1001 return popup_type_ != blink::WebPopupTypeNone; 1002 } 1003 1004 void RenderWidgetHostViewGtk::DoSharedInit() { 1005 view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this)); 1006 im_context_.reset(new GtkIMContextWrapper(this)); 1007 plugin_container_manager_.set_host_widget(view_.get()); 1008 key_bindings_handler_.reset(new GtkKeyBindingsHandler(view_.get())); 1009 } 1010 1011 void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow* window, 1012 const gfx::Rect& bounds) { 1013 requested_size_.SetSize(std::min(bounds.width(), kMaxWindowWidth), 1014 std::min(bounds.height(), kMaxWindowHeight)); 1015 host_->WasResized(); 1016 1017 ui::ActiveWindowWatcherX::AddObserver(this); 1018 1019 // Don't set the size when we're going fullscreen. This can confuse the 1020 // window manager into thinking we're resizing a fullscreen window and 1021 // therefore not fullscreen anymore. 1022 if (!is_fullscreen_) { 1023 gtk_widget_set_size_request( 1024 view_.get(), requested_size_.width(), requested_size_.height()); 1025 1026 // Don't allow the window to be resized. This also forces the window to 1027 // shrink down to the size of its child contents. 1028 gtk_window_set_resizable(window, FALSE); 1029 gtk_window_set_default_size(window, -1, -1); 1030 gtk_window_move(window, bounds.x(), bounds.y()); 1031 } 1032 1033 gtk_widget_show_all(GTK_WIDGET(window)); 1034 } 1035 1036 BackingStore* RenderWidgetHostViewGtk::AllocBackingStore( 1037 const gfx::Size& size) { 1038 gint depth = gdk_visual_get_depth(gtk_widget_get_visual(view_.get())); 1039 return new BackingStoreGtk(host_, size, 1040 ui::GetVisualFromGtkWidget(view_.get()), 1041 depth); 1042 } 1043 1044 // NOTE: |output| is initialized with the size of |src_subrect|, and |dst_size| 1045 // is ignored on GTK. 1046 void RenderWidgetHostViewGtk::CopyFromCompositingSurface( 1047 const gfx::Rect& src_subrect, 1048 const gfx::Size& /* dst_size */, 1049 const base::Callback<void(bool, const SkBitmap&)>& callback) { 1050 // Grab the snapshot from the renderer as that's the only reliable way to 1051 // readback from the GPU for this platform right now. 1052 GetRenderWidgetHost()->GetSnapshotFromRenderer(src_subrect, callback); 1053 } 1054 1055 void RenderWidgetHostViewGtk::CopyFromCompositingSurfaceToVideoFrame( 1056 const gfx::Rect& src_subrect, 1057 const scoped_refptr<media::VideoFrame>& target, 1058 const base::Callback<void(bool)>& callback) { 1059 NOTIMPLEMENTED(); 1060 callback.Run(false); 1061 } 1062 1063 bool RenderWidgetHostViewGtk::CanCopyToVideoFrame() const { 1064 return false; 1065 } 1066 1067 void RenderWidgetHostViewGtk::AcceleratedSurfaceInitialized(int host_id, 1068 int route_id) { 1069 } 1070 1071 void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped( 1072 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, 1073 int gpu_host_id) { 1074 AcceleratedSurfaceMsg_BufferPresented_Params ack_params; 1075 ack_params.sync_point = 0; 1076 RenderWidgetHostImpl::AcknowledgeBufferPresent( 1077 params.route_id, gpu_host_id, ack_params); 1078 RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info); 1079 } 1080 1081 void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer( 1082 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, 1083 int gpu_host_id) { 1084 AcceleratedSurfaceMsg_BufferPresented_Params ack_params; 1085 ack_params.sync_point = 0; 1086 RenderWidgetHostImpl::AcknowledgeBufferPresent( 1087 params.route_id, gpu_host_id, ack_params); 1088 RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info); 1089 } 1090 1091 void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() { 1092 } 1093 1094 void RenderWidgetHostViewGtk::AcceleratedSurfaceRelease() { 1095 } 1096 1097 bool RenderWidgetHostViewGtk::HasAcceleratedSurface( 1098 const gfx::Size& desired_size) { 1099 // TODO(jbates) Implement this so this view can use GetBackingStore for both 1100 // software and GPU frames. Defaulting to false just makes GetBackingStore 1101 // only useable for software frames. 1102 return false; 1103 } 1104 1105 void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) { 1106 RenderWidgetHostViewBase::SetBackground(background); 1107 Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background)); 1108 } 1109 1110 void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging( 1111 GtkWidget* widget, GdkEventMotion* event) { 1112 // If the widget is aligned with an edge of the monitor its on and the user 1113 // attempts to drag past that edge we track the number of times it has 1114 // occurred, so that we can force the widget to scroll when it otherwise 1115 // would be unable to, by modifying the (x,y) position in the drag 1116 // event that we forward on to webkit. If we get a move that's no longer a 1117 // drag or a drag indicating the user is no longer at that edge we stop 1118 // altering the drag events. 1119 int new_dragged_at_horizontal_edge = 0; 1120 int new_dragged_at_vertical_edge = 0; 1121 // Used for checking the edges of the monitor. We cache the values to save 1122 // roundtrips to the X server. 1123 CR_DEFINE_STATIC_LOCAL(gfx::Size, drag_monitor_size, ()); 1124 if (event->state & GDK_BUTTON1_MASK) { 1125 if (drag_monitor_size.IsEmpty()) { 1126 // We can safely cache the monitor size for the duration of a drag. 1127 GdkScreen* screen = gtk_widget_get_screen(widget); 1128 int monitor = 1129 gdk_screen_get_monitor_at_point(screen, event->x_root, event->y_root); 1130 GdkRectangle geometry; 1131 gdk_screen_get_monitor_geometry(screen, monitor, &geometry); 1132 drag_monitor_size.SetSize(geometry.width, geometry.height); 1133 } 1134 GtkAllocation allocation; 1135 gtk_widget_get_allocation(widget, &allocation); 1136 // Check X and Y independently, as the user could be dragging into a corner. 1137 if (event->x == 0 && event->x_root == 0) { 1138 new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ - 1; 1139 } else if (allocation.width - 1 == static_cast<gint>(event->x) && 1140 drag_monitor_size.width() - 1 == static_cast<gint>(event->x_root)) { 1141 new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ + 1; 1142 } 1143 1144 if (event->y == 0 && event->y_root == 0) { 1145 new_dragged_at_vertical_edge = dragged_at_vertical_edge_ - 1; 1146 } else if (allocation.height - 1 == static_cast<gint>(event->y) && 1147 drag_monitor_size.height() - 1 == static_cast<gint>(event->y_root)) { 1148 new_dragged_at_vertical_edge = dragged_at_vertical_edge_ + 1; 1149 } 1150 1151 event->x_root += new_dragged_at_horizontal_edge; 1152 event->x += new_dragged_at_horizontal_edge; 1153 event->y_root += new_dragged_at_vertical_edge; 1154 event->y += new_dragged_at_vertical_edge; 1155 } else { 1156 // Clear whenever we get a non-drag mouse move. 1157 drag_monitor_size.SetSize(0, 0); 1158 } 1159 dragged_at_horizontal_edge_ = new_dragged_at_horizontal_edge; 1160 dragged_at_vertical_edge_ = new_dragged_at_vertical_edge; 1161 } 1162 1163 void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) { 1164 TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::Paint"); 1165 1166 // If the GPU process is rendering directly into the View, 1167 // call the compositor directly. 1168 RenderWidgetHostImpl* render_widget_host = 1169 RenderWidgetHostImpl::From(GetRenderWidgetHost()); 1170 if (render_widget_host->is_accelerated_compositing_active()) { 1171 host_->ScheduleComposite(); 1172 return; 1173 } 1174 1175 GdkWindow* window = gtk_widget_get_window(view_.get()); 1176 DCHECK(!about_to_validate_and_paint_); 1177 1178 invalid_rect_ = damage_rect; 1179 about_to_validate_and_paint_ = true; 1180 1181 // If the size of our canvas is (0,0), then we don't want to block here. We 1182 // are doing one of our first paints and probably have animations going on. 1183 bool force_create = !host_->empty(); 1184 BackingStoreGtk* backing_store = static_cast<BackingStoreGtk*>( 1185 host_->GetBackingStore(force_create)); 1186 // Calling GetBackingStore maybe have changed |invalid_rect_|... 1187 about_to_validate_and_paint_ = false; 1188 1189 gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight); 1190 paint_rect.Intersect(invalid_rect_); 1191 1192 if (backing_store) { 1193 // Only render the widget if it is attached to a window; there's a short 1194 // period where this object isn't attached to a window but hasn't been 1195 // Destroy()ed yet and it receives paint messages... 1196 if (window) { 1197 backing_store->XShowRect(gfx::Point(0, 0), 1198 paint_rect, ui::GetX11WindowFromGtkWidget(view_.get())); 1199 } 1200 if (!whiteout_start_time_.is_null()) { 1201 base::TimeDelta whiteout_duration = base::TimeTicks::Now() - 1202 whiteout_start_time_; 1203 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration); 1204 1205 // Reset the start time to 0 so that we start recording again the next 1206 // time the backing store is NULL... 1207 whiteout_start_time_ = base::TimeTicks(); 1208 } 1209 if (!web_contents_switch_paint_time_.is_null()) { 1210 base::TimeDelta web_contents_switch_paint_duration = 1211 base::TimeTicks::Now() - web_contents_switch_paint_time_; 1212 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", 1213 web_contents_switch_paint_duration); 1214 // Reset web_contents_switch_paint_time_ to 0 so future tab selections are 1215 // recorded. 1216 web_contents_switch_paint_time_ = base::TimeTicks(); 1217 } 1218 software_latency_info_.AddLatencyNumber( 1219 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0); 1220 render_widget_host->FrameSwapped(software_latency_info_); 1221 software_latency_info_.Clear(); 1222 } else { 1223 if (window) 1224 gdk_window_clear(window); 1225 if (whiteout_start_time_.is_null()) 1226 whiteout_start_time_ = base::TimeTicks::Now(); 1227 } 1228 } 1229 1230 void RenderWidgetHostViewGtk::ShowCurrentCursor() { 1231 // The widget may not have a window. If that's the case, abort mission. This 1232 // is the same issue as that explained above in Paint(). 1233 if (!gtk_widget_get_window(view_.get())) 1234 return; 1235 1236 // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is 1237 // that calling gdk_window_set_cursor repeatedly is expensive. We should 1238 // avoid it here where possible. 1239 GdkCursor* gdk_cursor; 1240 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) { 1241 // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and 1242 // the page is loading. 1243 gdk_cursor = is_loading_ ? GetMozSpinningCursor() : NULL; 1244 } else { 1245 gdk_cursor = current_cursor_.GetNativeCursor(); 1246 } 1247 gdk_window_set_cursor(gtk_widget_get_window(view_.get()), gdk_cursor); 1248 } 1249 1250 void RenderWidgetHostViewGtk::SetHasHorizontalScrollbar( 1251 bool has_horizontal_scrollbar) { 1252 } 1253 1254 void RenderWidgetHostViewGtk::SetScrollOffsetPinning( 1255 bool is_pinned_to_left, bool is_pinned_to_right) { 1256 } 1257 1258 1259 void RenderWidgetHostViewGtk::OnAcceleratedCompositingStateChange() { 1260 bool activated = host_->is_accelerated_compositing_active(); 1261 GtkPreserveWindow* widget = reinterpret_cast<GtkPreserveWindow*>(view_.get()); 1262 1263 gtk_preserve_window_delegate_resize(widget, activated); 1264 } 1265 1266 void RenderWidgetHostViewGtk::GetScreenInfo(WebScreenInfo* results) { 1267 GdkWindow* gdk_window = gtk_widget_get_window(view_.get()); 1268 if (!gdk_window) { 1269 GdkDisplay* display = gdk_display_get_default(); 1270 gdk_window = gdk_display_get_default_group(display); 1271 } 1272 if (!gdk_window) 1273 return; 1274 GetScreenInfoFromNativeWindow(gdk_window, results); 1275 } 1276 1277 gfx::Rect RenderWidgetHostViewGtk::GetBoundsInRootWindow() { 1278 GtkWidget* toplevel = gtk_widget_get_toplevel(view_.get()); 1279 if (!toplevel) 1280 return GetViewBounds(); 1281 1282 GdkRectangle frame_extents; 1283 GdkWindow* gdk_window = gtk_widget_get_window(toplevel); 1284 if (!gdk_window) 1285 return GetViewBounds(); 1286 1287 gdk_window_get_frame_extents(gdk_window, &frame_extents); 1288 return gfx::Rect(frame_extents.x, frame_extents.y, 1289 frame_extents.width, frame_extents.height); 1290 } 1291 1292 gfx::GLSurfaceHandle RenderWidgetHostViewGtk::GetCompositingSurface() { 1293 if (compositing_surface_ == gfx::kNullPluginWindow) { 1294 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); 1295 gfx::NativeViewId view_id = GetNativeViewId(); 1296 1297 if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) { 1298 DLOG(ERROR) << "Can't find XID for view id " << view_id; 1299 } 1300 } 1301 return gfx::GLSurfaceHandle(compositing_surface_, gfx::NATIVE_TRANSPORT); 1302 } 1303 1304 void RenderWidgetHostViewGtk::ResizeCompositingSurface(const gfx::Size& size) { 1305 GtkWidget* widget = view_.get(); 1306 GdkWindow* window = gtk_widget_get_window(widget); 1307 if (window) { 1308 Display* display = GDK_WINDOW_XDISPLAY(window); 1309 gdk_window_resize(window, size.width(), size.height()); 1310 XSync(display, False); 1311 } 1312 } 1313 1314 bool RenderWidgetHostViewGtk::LockMouse() { 1315 if (mouse_locked_) 1316 return true; 1317 1318 mouse_locked_ = true; 1319 1320 // Release any current grab. 1321 GtkWidget* current_grab_window = gtk_grab_get_current(); 1322 if (current_grab_window) { 1323 gtk_grab_remove(current_grab_window); 1324 LOG(WARNING) << "Locking Mouse with gdk_pointer_grab, " 1325 << "but had to steal grab from another window"; 1326 } 1327 1328 GtkWidget* widget = view_.get(); 1329 GdkWindow* window = gtk_widget_get_window(widget); 1330 GdkCursor* cursor = gdk_cursor_new(GDK_BLANK_CURSOR); 1331 1332 GdkGrabStatus grab_status = 1333 gdk_pointer_grab(window, 1334 FALSE, // owner_events 1335 static_cast<GdkEventMask>( 1336 GDK_POINTER_MOTION_MASK | 1337 GDK_BUTTON_PRESS_MASK | 1338 GDK_BUTTON_RELEASE_MASK), 1339 window, // confine_to 1340 cursor, 1341 GDK_CURRENT_TIME); 1342 1343 if (grab_status != GDK_GRAB_SUCCESS) { 1344 LOG(WARNING) << "Failed to grab pointer for LockMouse. " 1345 << "gdk_pointer_grab returned: " << grab_status; 1346 mouse_locked_ = false; 1347 return false; 1348 } 1349 1350 // Clear the tooltip window. 1351 SetTooltipText(base::string16()); 1352 1353 // Ensure that the widget center location will be relevant for this mouse 1354 // lock session. It is updated whenever the window geometry moves 1355 // but may be out of date due to switching tabs. 1356 MarkCachedWidgetCenterStale(); 1357 1358 // Ensure that if we were previously warping the cursor to a specific point 1359 // that we no longer track doing so when entering lock. It should be cleared 1360 // by the cursor moving to the warp point, and this shouldn't be necessary. 1361 // But, this is a small effort to ensure robustness in the event a warp isn't 1362 // completed. 1363 mouse_is_being_warped_to_unlocked_position_ = false; 1364 1365 return true; 1366 } 1367 1368 void RenderWidgetHostViewGtk::UnlockMouse() { 1369 if (!mouse_locked_) 1370 return; 1371 1372 mouse_locked_ = false; 1373 1374 GtkWidget* widget = view_.get(); 1375 GdkDisplay* display = gtk_widget_get_display(widget); 1376 GdkScreen* screen = gtk_widget_get_screen(widget); 1377 gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME); 1378 gdk_display_warp_pointer(display, screen, 1379 unlocked_global_mouse_position_.x(), 1380 unlocked_global_mouse_position_.y()); 1381 mouse_is_being_warped_to_unlocked_position_ = true; 1382 1383 if (host_) 1384 host_->LostMouseLock(); 1385 } 1386 1387 void RenderWidgetHostViewGtk::ForwardKeyboardEvent( 1388 const NativeWebKeyboardEvent& event) { 1389 if (!host_) 1390 return; 1391 1392 EditCommands edit_commands; 1393 if (!event.skip_in_browser && 1394 key_bindings_handler_->Match(event, &edit_commands)) { 1395 Send(new InputMsg_SetEditCommandsForNextKeyEvent( 1396 host_->GetRoutingID(), edit_commands)); 1397 NativeWebKeyboardEvent copy_event(event); 1398 copy_event.match_edit_command = true; 1399 host_->ForwardKeyboardEvent(copy_event); 1400 return; 1401 } 1402 1403 host_->ForwardKeyboardEvent(event); 1404 } 1405 1406 bool RenderWidgetHostViewGtk::RetrieveSurrounding(std::string* text, 1407 size_t* cursor_index) { 1408 if (!selection_range_.IsValid()) 1409 return false; 1410 1411 size_t offset = selection_range_.GetMin() - selection_text_offset_; 1412 DCHECK(offset <= selection_text_.length()); 1413 1414 if (offset == selection_text_.length()) { 1415 *text = UTF16ToUTF8(selection_text_); 1416 *cursor_index = text->length(); 1417 return true; 1418 } 1419 1420 *text = base::UTF16ToUTF8AndAdjustOffset( 1421 base::StringPiece16(selection_text_), &offset); 1422 if (offset == base::string16::npos) { 1423 NOTREACHED() << "Invalid offset in UTF16 string."; 1424 return false; 1425 } 1426 *cursor_index = offset; 1427 return true; 1428 } 1429 1430 void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton* event) { 1431 GdkEventButton* temp = NULL; 1432 if (event) { 1433 temp = reinterpret_cast<GdkEventButton*>( 1434 gdk_event_copy(reinterpret_cast<GdkEvent*>(event))); 1435 } 1436 1437 if (last_mouse_down_) 1438 gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_)); 1439 1440 last_mouse_down_ = temp; 1441 } 1442 1443 void RenderWidgetHostViewGtk::MarkCachedWidgetCenterStale() { 1444 widget_center_valid_ = false; 1445 mouse_has_been_warped_to_new_center_ = false; 1446 } 1447 1448 gfx::Point RenderWidgetHostViewGtk::GetWidgetCenter() { 1449 if (widget_center_valid_) 1450 return widget_center_; 1451 1452 GdkWindow* window = gtk_widget_get_window(view_.get()); 1453 gint window_x = 0; 1454 gint window_y = 0; 1455 gdk_window_get_origin(window, &window_x, &window_y); 1456 gint window_w = gdk_window_get_width(window); 1457 gint window_h = gdk_window_get_height(window); 1458 widget_center_.SetPoint(window_x + window_w / 2, 1459 window_y + window_h / 2); 1460 widget_center_valid_ = true; 1461 return widget_center_; 1462 } 1463 1464 void RenderWidgetHostViewGtk::ModifyEventMovementAndCoords( 1465 blink::WebMouseEvent* event) { 1466 // Movement is computed by taking the difference of the new cursor position 1467 // and the previous. Under mouse lock the cursor will be warped back to the 1468 // center so that we are not limited by clipping boundaries. 1469 // We do not measure movement as the delta from cursor to center because 1470 // we may receive more mouse movement events before our warp has taken 1471 // effect. 1472 event->movementX = event->globalX - global_mouse_position_.x(); 1473 event->movementY = event->globalY - global_mouse_position_.y(); 1474 1475 // While the cursor is being warped back to the unlocked position, suppress 1476 // the movement member data. 1477 if (mouse_is_being_warped_to_unlocked_position_) { 1478 event->movementX = 0; 1479 event->movementY = 0; 1480 if (MovedToPoint(*event, unlocked_global_mouse_position_)) 1481 mouse_is_being_warped_to_unlocked_position_ = false; 1482 } 1483 1484 global_mouse_position_.SetPoint(event->globalX, event->globalY); 1485 1486 // Under mouse lock, coordinates of mouse are locked to what they were when 1487 // mouse lock was entered. 1488 if (mouse_locked_) { 1489 event->x = unlocked_mouse_position_.x(); 1490 event->y = unlocked_mouse_position_.y(); 1491 event->windowX = unlocked_mouse_position_.x(); 1492 event->windowY = unlocked_mouse_position_.y(); 1493 event->globalX = unlocked_global_mouse_position_.x(); 1494 event->globalY = unlocked_global_mouse_position_.y(); 1495 } else if (!mouse_is_being_warped_to_unlocked_position_) { 1496 unlocked_mouse_position_.SetPoint(event->windowX, event->windowY); 1497 unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY); 1498 } 1499 } 1500 1501 //////////////////////////////////////////////////////////////////////////////// 1502 // RenderWidgetHostView, public: 1503 1504 // static 1505 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( 1506 RenderWidgetHost* widget) { 1507 return new RenderWidgetHostViewGtk(widget); 1508 } 1509 1510 // static 1511 void RenderWidgetHostViewPort::GetDefaultScreenInfo(WebScreenInfo* results) { 1512 GdkWindow* gdk_window = 1513 gdk_display_get_default_group(gdk_display_get_default()); 1514 GetScreenInfoFromNativeWindow(gdk_window, results); 1515 } 1516 1517 void RenderWidgetHostViewGtk::SetAccessibilityFocus(int acc_obj_id) { 1518 if (!host_) 1519 return; 1520 1521 host_->AccessibilitySetFocus(acc_obj_id); 1522 } 1523 1524 void RenderWidgetHostViewGtk::AccessibilityDoDefaultAction(int acc_obj_id) { 1525 if (!host_) 1526 return; 1527 1528 host_->AccessibilityDoDefaultAction(acc_obj_id); 1529 } 1530 1531 void RenderWidgetHostViewGtk::AccessibilityScrollToMakeVisible( 1532 int acc_obj_id, gfx::Rect subfocus) { 1533 if (!host_) 1534 return; 1535 1536 host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus); 1537 } 1538 1539 void RenderWidgetHostViewGtk::AccessibilityScrollToPoint( 1540 int acc_obj_id, gfx::Point point) { 1541 if (!host_) 1542 return; 1543 1544 host_->AccessibilityScrollToPoint(acc_obj_id, point); 1545 } 1546 1547 void RenderWidgetHostViewGtk::AccessibilitySetTextSelection( 1548 int acc_obj_id, int start_offset, int end_offset) { 1549 if (!host_) 1550 return; 1551 1552 host_->AccessibilitySetTextSelection(acc_obj_id, start_offset, end_offset); 1553 } 1554 1555 gfx::Point RenderWidgetHostViewGtk::GetLastTouchEventLocation() const { 1556 // Not needed on Linux. 1557 return gfx::Point(); 1558 } 1559 1560 void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() { 1561 if (host_) { 1562 host_->FatalAccessibilityTreeError(); 1563 SetBrowserAccessibilityManager(NULL); 1564 } else { 1565 CHECK(FALSE); 1566 } 1567 } 1568 1569 void RenderWidgetHostViewGtk::OnAccessibilityEvents( 1570 const std::vector<AccessibilityHostMsg_EventParams>& params) { 1571 if (!GetBrowserAccessibilityManager()) { 1572 GtkWidget* parent = gtk_widget_get_parent(view_.get()); 1573 SetBrowserAccessibilityManager( 1574 new BrowserAccessibilityManagerGtk( 1575 parent, 1576 BrowserAccessibilityManagerGtk::GetEmptyDocument(), 1577 this)); 1578 } 1579 GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); 1580 } 1581 1582 AtkObject* RenderWidgetHostViewGtk::GetAccessible() { 1583 if (!GetBrowserAccessibilityManager()) { 1584 GtkWidget* parent = gtk_widget_get_parent(view_.get()); 1585 SetBrowserAccessibilityManager( 1586 new BrowserAccessibilityManagerGtk( 1587 parent, 1588 BrowserAccessibilityManagerGtk::GetEmptyDocument(), 1589 this)); 1590 } 1591 BrowserAccessibilityGtk* root = 1592 GetBrowserAccessibilityManager()->GetRoot()->ToBrowserAccessibilityGtk(); 1593 1594 atk_object_set_role(root->GetAtkObject(), ATK_ROLE_HTML_CONTAINER); 1595 return root->GetAtkObject(); 1596 } 1597 1598 void RenderWidgetHostViewGtk::OnCreatePluginContainer( 1599 gfx::PluginWindowHandle id) { 1600 plugin_container_manager_.CreatePluginContainer(id); 1601 } 1602 1603 void RenderWidgetHostViewGtk::OnDestroyPluginContainer( 1604 gfx::PluginWindowHandle id) { 1605 plugin_container_manager_.DestroyPluginContainer(id); 1606 } 1607 1608 } // namespace content 1609