| OLD | NEW |
| (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 | |
| OLD | NEW |