1// Copyright (c) 2011 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 "chrome/browser/renderer_host/render_widget_host_view_gtk.h" 6 7// If this gets included after the gtk headers, then a bunch of compiler 8// errors happen because of a "#define Status int" in Xlib.h, which interacts 9// badly with net::URLRequestStatus::Status. 10#include "chrome/common/render_messages.h" 11#include "content/common/view_messages.h" 12 13#include <cairo/cairo.h> 14#include <gdk/gdk.h> 15#include <gdk/gdkkeysyms.h> 16#include <gdk/gdkx.h> 17#include <gtk/gtk.h> 18 19#include <algorithm> 20#include <string> 21 22#include "base/command_line.h" 23#include "base/logging.h" 24#include "base/message_loop.h" 25#include "base/metrics/histogram.h" 26#include "base/string_number_conversions.h" 27#include "base/time.h" 28#include "base/utf_string_conversions.h" 29#include "chrome/browser/renderer_host/gtk_im_context_wrapper.h" 30#include "chrome/browser/renderer_host/gtk_key_bindings_handler.h" 31#include "chrome/browser/ui/gtk/gtk_util.h" 32#include "chrome/common/chrome_switches.h" 33#include "content/browser/renderer_host/backing_store_x.h" 34#include "content/browser/renderer_host/render_view_host.h" 35#include "content/browser/renderer_host/render_view_host_delegate.h" 36#include "content/browser/renderer_host/render_widget_host.h" 37#include "content/common/native_web_keyboard_event.h" 38#include "third_party/WebKit/Source/WebKit/chromium/public/gtk/WebInputEventFactory.h" 39#include "ui/base/l10n/l10n_util.h" 40#include "ui/base/x/x11_util.h" 41#include "ui/gfx/gtk_preserve_window.h" 42#include "ui/gfx/gtk_native_view_id_manager.h" 43#include "webkit/glue/webaccessibility.h" 44#include "webkit/glue/webcursor_gtk_data.h" 45#include "webkit/plugins/npapi/webplugin.h" 46 47#if defined(OS_CHROMEOS) 48#include "views/widget/tooltip_window_gtk.h" 49#endif // defined(OS_CHROMEOS) 50 51namespace { 52 53const int kMaxWindowWidth = 4000; 54const int kMaxWindowHeight = 4000; 55const char* kRenderWidgetHostViewKey = "__RENDER_WIDGET_HOST_VIEW__"; 56 57// The duration of the fade-out animation. See |overlay_animation_|. 58const int kFadeEffectDuration = 300; 59 60#if defined(OS_CHROMEOS) 61// TODO(davemoore) Under Chromeos we are increasing the rate that the trackpad 62// generates events to get better precisions. Eventually we will coordinate the 63// driver and this setting to ensure they match. 64const float kDefaultScrollPixelsPerTick = 20; 65#else 66// See WebInputEventFactor.cpp for a reason for this being the default 67// scroll size for linux. 68const float kDefaultScrollPixelsPerTick = 160.0f / 3.0f; 69#endif 70 71// Returns the spinning cursor used for loading state. 72GdkCursor* GetMozSpinningCursor() { 73 static GdkCursor* moz_spinning_cursor = NULL; 74 if (!moz_spinning_cursor) { 75 const GdkColor fg = { 0, 0, 0, 0 }; 76 const GdkColor bg = { 65535, 65535, 65535, 65535 }; 77 GdkPixmap* source = 78 gdk_bitmap_create_from_data(NULL, moz_spinning_bits, 32, 32); 79 GdkPixmap* mask = 80 gdk_bitmap_create_from_data(NULL, moz_spinning_mask_bits, 32, 32); 81 moz_spinning_cursor = 82 gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 2, 2); 83 g_object_unref(source); 84 g_object_unref(mask); 85 } 86 return moz_spinning_cursor; 87} 88 89} // namespace 90 91using WebKit::WebInputEventFactory; 92using WebKit::WebMouseWheelEvent; 93 94// This class is a simple convenience wrapper for Gtk functions. It has only 95// static methods. 96class RenderWidgetHostViewGtkWidget { 97 public: 98 static GtkWidget* CreateNewWidget(RenderWidgetHostViewGtk* host_view) { 99 GtkWidget* widget = gtk_preserve_window_new(); 100 gtk_widget_set_name(widget, "chrome-render-widget-host-view"); 101 // We manually double-buffer in Paint() because Paint() may or may not be 102 // called in repsonse to an "expose-event" signal. 103 gtk_widget_set_double_buffered(widget, FALSE); 104 gtk_widget_set_redraw_on_allocate(widget, FALSE); 105#if defined(NDEBUG) 106 gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, >k_util::kGdkWhite); 107#else 108 gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, >k_util::kGdkGreen); 109#endif 110 // Allow the browser window to be resized freely. 111 gtk_widget_set_size_request(widget, 0, 0); 112 113 gtk_widget_add_events(widget, GDK_EXPOSURE_MASK | 114 GDK_POINTER_MOTION_MASK | 115 GDK_BUTTON_PRESS_MASK | 116 GDK_BUTTON_RELEASE_MASK | 117 GDK_KEY_PRESS_MASK | 118 GDK_KEY_RELEASE_MASK | 119 GDK_FOCUS_CHANGE_MASK | 120 GDK_ENTER_NOTIFY_MASK | 121 GDK_LEAVE_NOTIFY_MASK); 122 GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS); 123 124 g_signal_connect(widget, "expose-event", 125 G_CALLBACK(OnExposeEvent), host_view); 126 g_signal_connect(widget, "key-press-event", 127 G_CALLBACK(OnKeyPressReleaseEvent), host_view); 128 g_signal_connect(widget, "key-release-event", 129 G_CALLBACK(OnKeyPressReleaseEvent), host_view); 130 g_signal_connect(widget, "focus-in-event", 131 G_CALLBACK(OnFocusIn), host_view); 132 g_signal_connect(widget, "focus-out-event", 133 G_CALLBACK(OnFocusOut), host_view); 134 g_signal_connect(widget, "grab-notify", 135 G_CALLBACK(OnGrabNotify), host_view); 136 g_signal_connect(widget, "button-press-event", 137 G_CALLBACK(OnButtonPressReleaseEvent), host_view); 138 g_signal_connect(widget, "button-release-event", 139 G_CALLBACK(OnButtonPressReleaseEvent), host_view); 140 g_signal_connect(widget, "motion-notify-event", 141 G_CALLBACK(OnMouseMoveEvent), host_view); 142 g_signal_connect(widget, "enter-notify-event", 143 G_CALLBACK(OnCrossingEvent), host_view); 144 g_signal_connect(widget, "leave-notify-event", 145 G_CALLBACK(OnCrossingEvent), host_view); 146 g_signal_connect(widget, "client-event", 147 G_CALLBACK(OnClientEvent), host_view); 148 149 150 // Connect after so that we are called after the handler installed by the 151 // TabContentsView which handles zoom events. 152 g_signal_connect_after(widget, "scroll-event", 153 G_CALLBACK(OnMouseScrollEvent), host_view); 154 155 g_object_set_data(G_OBJECT(widget), kRenderWidgetHostViewKey, 156 static_cast<RenderWidgetHostView*>(host_view)); 157 158 return widget; 159 } 160 161 private: 162 static gboolean OnExposeEvent(GtkWidget* widget, 163 GdkEventExpose* expose, 164 RenderWidgetHostViewGtk* host_view) { 165 if (host_view->is_hidden_) 166 return FALSE; 167 const gfx::Rect damage_rect(expose->area); 168 host_view->Paint(damage_rect); 169 return FALSE; 170 } 171 172 static gboolean OnKeyPressReleaseEvent(GtkWidget* widget, 173 GdkEventKey* event, 174 RenderWidgetHostViewGtk* host_view) { 175 // Force popups or fullscreen windows to close on Escape so they won't keep 176 // the keyboard grabbed or be stuck onscreen if the renderer is hanging. 177 bool should_close_on_escape = 178 (host_view->IsPopup() && host_view->NeedsInputGrab()) || 179 host_view->is_fullscreen_; 180 if (should_close_on_escape && GDK_Escape == event->keyval) { 181 host_view->host_->Shutdown(); 182 } else { 183 // Send key event to input method. 184 host_view->im_context_->ProcessKeyEvent(event); 185 } 186 187 // We return TRUE because we did handle the event. If it turns out webkit 188 // can't handle the event, we'll deal with it in 189 // RenderView::UnhandledKeyboardEvent(). 190 return TRUE; 191 } 192 193 static gboolean OnFocusIn(GtkWidget* widget, 194 GdkEventFocus* focus, 195 RenderWidgetHostViewGtk* host_view) { 196 host_view->ShowCurrentCursor(); 197 host_view->GetRenderWidgetHost()->GotFocus(); 198 199 // The only way to enable a GtkIMContext object is to call its focus in 200 // handler. 201 host_view->im_context_->OnFocusIn(); 202 203 return TRUE; 204 } 205 206 static gboolean OnFocusOut(GtkWidget* widget, 207 GdkEventFocus* focus, 208 RenderWidgetHostViewGtk* host_view) { 209 // Whenever we lose focus, set the cursor back to that of our parent window, 210 // which should be the default arrow. 211 gdk_window_set_cursor(widget->window, NULL); 212 // If we are showing a context menu, maintain the illusion that webkit has 213 // focus. 214 if (!host_view->is_showing_context_menu_) 215 host_view->GetRenderWidgetHost()->Blur(); 216 217 // Prevents us from stealing input context focus in OnGrabNotify() handler. 218 host_view->was_focused_before_grab_ = false; 219 220 // Disable the GtkIMContext object. 221 host_view->im_context_->OnFocusOut(); 222 223 return TRUE; 224 } 225 226 // Called when we are shadowed or unshadowed by a keyboard grab (which will 227 // occur for activatable popups, such as dropdown menus). Popup windows do not 228 // take focus, so we never get a focus out or focus in event when they are 229 // shown, and must rely on this signal instead. 230 static void OnGrabNotify(GtkWidget* widget, gboolean was_grabbed, 231 RenderWidgetHostViewGtk* host_view) { 232 if (was_grabbed) { 233 if (host_view->was_focused_before_grab_) 234 host_view->im_context_->OnFocusIn(); 235 } else { 236 host_view->was_focused_before_grab_ = host_view->HasFocus(); 237 if (host_view->was_focused_before_grab_) { 238 gdk_window_set_cursor(widget->window, NULL); 239 host_view->im_context_->OnFocusOut(); 240 } 241 } 242 } 243 244 static gboolean OnButtonPressReleaseEvent( 245 GtkWidget* widget, 246 GdkEventButton* event, 247 RenderWidgetHostViewGtk* host_view) { 248#if defined (OS_CHROMEOS) 249 // We support buttons 8 & 9 for scrolling with an attached USB mouse 250 // in ChromeOS. We do this separately from the builtin scrolling support 251 // because we want to support the user's expectations about the amount 252 // scrolled on each event. xorg.conf on chromeos specifies buttons 253 // 8 & 9 for the scroll wheel for the attached USB mouse. 254 if (event->type == GDK_BUTTON_RELEASE && 255 (event->button == 8 || event->button == 9)) { 256 GdkEventScroll scroll_event; 257 scroll_event.type = GDK_SCROLL; 258 scroll_event.window = event->window; 259 scroll_event.send_event = event->send_event; 260 scroll_event.time = event->time; 261 scroll_event.x = event->x; 262 scroll_event.y = event->y; 263 scroll_event.state = event->state; 264 if (event->state & GDK_SHIFT_MASK) { 265 scroll_event.direction = 266 event->button == 8 ? GDK_SCROLL_LEFT : GDK_SCROLL_RIGHT; 267 } else { 268 scroll_event.direction = 269 event->button == 8 ? GDK_SCROLL_UP : GDK_SCROLL_DOWN; 270 } 271 scroll_event.device = event->device; 272 scroll_event.x_root = event->x_root; 273 scroll_event.y_root = event->y_root; 274 WebMouseWheelEvent web_event = 275 WebInputEventFactory::mouseWheelEvent(&scroll_event); 276 host_view->GetRenderWidgetHost()->ForwardWheelEvent(web_event); 277 } 278#endif 279 280 if (event->type != GDK_BUTTON_RELEASE) 281 host_view->set_last_mouse_down(event); 282 283 if (!(event->button == 1 || event->button == 2 || event->button == 3)) 284 return FALSE; // We do not forward any other buttons to the renderer. 285 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) 286 return FALSE; 287 288 // If we don't have focus already, this mouse click will focus us. 289 if (!gtk_widget_is_focus(widget)) 290 host_view->host_->OnMouseActivate(); 291 292 // Confirm existing composition text on mouse click events, to make sure 293 // the input caret won't be moved with an ongoing composition session. 294 host_view->im_context_->ConfirmComposition(); 295 296 // We want to translate the coordinates of events that do not originate 297 // from this widget to be relative to the top left of the widget. 298 GtkWidget* event_widget = gtk_get_event_widget( 299 reinterpret_cast<GdkEvent*>(event)); 300 if (event_widget != widget) { 301 int x = 0; 302 int y = 0; 303 gtk_widget_get_pointer(widget, &x, &y); 304 // If the mouse event happens outside our popup, force the popup to 305 // close. We do this so a hung renderer doesn't prevent us from 306 // releasing the x pointer grab. 307 bool click_in_popup = x >= 0 && y >= 0 && x < widget->allocation.width && 308 y < widget->allocation.height; 309 // Only Shutdown on mouse downs. Mouse ups can occur outside the render 310 // view if the user drags for DnD or while using the scrollbar on a select 311 // dropdown. Don't shutdown if we are not a popup. 312 if (event->type != GDK_BUTTON_RELEASE && host_view->IsPopup() && 313 !host_view->is_popup_first_mouse_release_ && !click_in_popup) { 314 host_view->host_->Shutdown(); 315 return FALSE; 316 } 317 event->x = x; 318 event->y = y; 319 } 320 321 // TODO(evanm): why is this necessary here but not in test shell? 322 // This logic is the same as GtkButton. 323 if (event->type == GDK_BUTTON_PRESS && !GTK_WIDGET_HAS_FOCUS(widget)) 324 gtk_widget_grab_focus(widget); 325 326 host_view->is_popup_first_mouse_release_ = false; 327 host_view->GetRenderWidgetHost()->ForwardMouseEvent( 328 WebInputEventFactory::mouseEvent(event)); 329 330 // Although we did handle the mouse event, we need to let other handlers 331 // run (in particular the one installed by TabContentsViewGtk). 332 return FALSE; 333 } 334 335 static gboolean OnMouseMoveEvent(GtkWidget* widget, 336 GdkEventMotion* event, 337 RenderWidgetHostViewGtk* host_view) { 338 // We want to translate the coordinates of events that do not originate 339 // from this widget to be relative to the top left of the widget. 340 GtkWidget* event_widget = gtk_get_event_widget( 341 reinterpret_cast<GdkEvent*>(event)); 342 if (event_widget != widget) { 343 int x = 0; 344 int y = 0; 345 gtk_widget_get_pointer(widget, &x, &y); 346 event->x = x; 347 event->y = y; 348 } 349 350 host_view->ModifyEventForEdgeDragging(widget, event); 351 host_view->GetRenderWidgetHost()->ForwardMouseEvent( 352 WebInputEventFactory::mouseEvent(event)); 353 return FALSE; 354 } 355 356 static gboolean OnCrossingEvent(GtkWidget* widget, 357 GdkEventCrossing* event, 358 RenderWidgetHostViewGtk* host_view) { 359 const int any_button_mask = 360 GDK_BUTTON1_MASK | 361 GDK_BUTTON2_MASK | 362 GDK_BUTTON3_MASK | 363 GDK_BUTTON4_MASK | 364 GDK_BUTTON5_MASK; 365 366 // Only forward crossing events if the mouse button is not down. 367 // (When the mouse button is down, the proper events are already being 368 // sent by ButtonPressReleaseEvent and MouseMoveEvent, above, and if we 369 // additionally send this crossing event with the state indicating the 370 // button is down, it causes problems with drag and drop in WebKit.) 371 if (!(event->state & any_button_mask)) { 372 host_view->GetRenderWidgetHost()->ForwardMouseEvent( 373 WebInputEventFactory::mouseEvent(event)); 374 } 375 376 return FALSE; 377 } 378 379 static gboolean OnClientEvent(GtkWidget* widget, 380 GdkEventClient* event, 381 RenderWidgetHostViewGtk* host_view) { 382 VLOG(1) << "client event type: " << event->message_type 383 << " data_format: " << event->data_format 384 << " data: " << event->data.l; 385 return TRUE; 386 } 387 388 // Allow the vertical scroll delta to be overridden from the command line. 389 // This will allow us to test more easily to discover the amount 390 // (either hard coded or computed) that's best. 391 static float GetScrollPixelsPerTick() { 392 static float scroll_pixels = -1; 393 if (scroll_pixels < 0) { 394 // TODO(brettw): Remove the command line switch (crbug.com/63525) 395 scroll_pixels = kDefaultScrollPixelsPerTick; 396 CommandLine* command_line = CommandLine::ForCurrentProcess(); 397 std::string scroll_pixels_option = 398 command_line->GetSwitchValueASCII(switches::kScrollPixels); 399 if (!scroll_pixels_option.empty()) { 400 double v; 401 if (base::StringToDouble(scroll_pixels_option, &v)) 402 scroll_pixels = static_cast<float>(v); 403 } 404 DCHECK_GT(scroll_pixels, 0); 405 } 406 return scroll_pixels; 407 } 408 409 // Return the net up / down (or left / right) distance represented by events 410 // in the events will be removed from the queue. We only look at the top of 411 // queue...any other type of event will cause us not to look farther. 412 // If there is a change to the set of modifier keys or scroll axis 413 // in the events we will stop looking as well. 414 static int GetPendingScrollDelta(bool vert, guint current_event_state) { 415 int num_clicks = 0; 416 GdkEvent* event; 417 bool event_coalesced = true; 418 while ((event = gdk_event_get()) && event_coalesced) { 419 event_coalesced = false; 420 if (event->type == GDK_SCROLL) { 421 GdkEventScroll scroll = event->scroll; 422 if (scroll.state & GDK_SHIFT_MASK) { 423 if (scroll.direction == GDK_SCROLL_UP) 424 scroll.direction = GDK_SCROLL_LEFT; 425 else if (scroll.direction == GDK_SCROLL_DOWN) 426 scroll.direction = GDK_SCROLL_RIGHT; 427 } 428 if (vert) { 429 if (scroll.direction == GDK_SCROLL_UP || 430 scroll.direction == GDK_SCROLL_DOWN) { 431 if (scroll.state == current_event_state) { 432 num_clicks += (scroll.direction == GDK_SCROLL_UP ? 1 : -1); 433 gdk_event_free(event); 434 event_coalesced = true; 435 } 436 } 437 } else { 438 if (scroll.direction == GDK_SCROLL_LEFT || 439 scroll.direction == GDK_SCROLL_RIGHT) { 440 if (scroll.state == current_event_state) { 441 num_clicks += (scroll.direction == GDK_SCROLL_LEFT ? 1 : -1); 442 gdk_event_free(event); 443 event_coalesced = true; 444 } 445 } 446 } 447 } 448 } 449 // If we have an event left we put it back on the queue. 450 if (event) { 451 gdk_event_put(event); 452 gdk_event_free(event); 453 } 454 return num_clicks * GetScrollPixelsPerTick(); 455 } 456 457 static gboolean OnMouseScrollEvent(GtkWidget* widget, 458 GdkEventScroll* event, 459 RenderWidgetHostViewGtk* host_view) { 460 // If the user is holding shift, translate it into a horizontal scroll. We 461 // don't care what other modifiers the user may be holding (zooming is 462 // handled at the TabContentsView level). 463 if (event->state & GDK_SHIFT_MASK) { 464 if (event->direction == GDK_SCROLL_UP) 465 event->direction = GDK_SCROLL_LEFT; 466 else if (event->direction == GDK_SCROLL_DOWN) 467 event->direction = GDK_SCROLL_RIGHT; 468 } 469 470 WebMouseWheelEvent web_event = WebInputEventFactory::mouseWheelEvent(event); 471 // We peek ahead at the top of the queue to look for additional pending 472 // scroll events. 473 if (event->direction == GDK_SCROLL_UP || 474 event->direction == GDK_SCROLL_DOWN) { 475 if (event->direction == GDK_SCROLL_UP) 476 web_event.deltaY = GetScrollPixelsPerTick(); 477 else 478 web_event.deltaY = -GetScrollPixelsPerTick(); 479 web_event.deltaY += GetPendingScrollDelta(true, event->state); 480 } else { 481 if (event->direction == GDK_SCROLL_LEFT) 482 web_event.deltaX = GetScrollPixelsPerTick(); 483 else 484 web_event.deltaX = -GetScrollPixelsPerTick(); 485 web_event.deltaX += GetPendingScrollDelta(false, event->state); 486 } 487 host_view->GetRenderWidgetHost()->ForwardWheelEvent(web_event); 488 return FALSE; 489 } 490 491 DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget); 492}; 493 494// static 495RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( 496 RenderWidgetHost* widget) { 497 return new RenderWidgetHostViewGtk(widget); 498} 499 500RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) 501 : host_(widget_host), 502 about_to_validate_and_paint_(false), 503 is_hidden_(false), 504 is_loading_(false), 505 is_showing_context_menu_(false), 506 overlay_color_(0), 507 overlay_animation_(this), 508 parent_(NULL), 509 is_popup_first_mouse_release_(true), 510 was_focused_before_grab_(false), 511 do_x_grab_(false), 512 is_fullscreen_(false), 513 destroy_handler_id_(0), 514 dragged_at_horizontal_edge_(0), 515 dragged_at_vertical_edge_(0), 516 compositing_surface_(gfx::kNullPluginWindow), 517 last_mouse_down_(NULL) { 518 host_->set_view(this); 519} 520 521RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() { 522 set_last_mouse_down(NULL); 523 view_.Destroy(); 524} 525 526void RenderWidgetHostViewGtk::InitAsChild() { 527 DoSharedInit(); 528 overlay_animation_.SetDuration(kFadeEffectDuration); 529 overlay_animation_.SetSlideDuration(kFadeEffectDuration); 530 gtk_widget_show(view_.get()); 531} 532 533void RenderWidgetHostViewGtk::InitAsPopup( 534 RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) { 535 // If we aren't a popup, then |window| will be leaked. 536 DCHECK(IsPopup()); 537 538 DoSharedInit(); 539 parent_ = parent_host_view->GetNativeView(); 540 GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP)); 541 gtk_container_add(GTK_CONTAINER(window), view_.get()); 542 DoPopupOrFullscreenInit(window, pos); 543 544 // The underlying X window needs to be created and mapped by the above code 545 // before we can grab the input devices. 546 if (NeedsInputGrab()) { 547 // Grab all input for the app. If a click lands outside the bounds of the 548 // popup, WebKit will notice and destroy us. Before doing this we need 549 // to ensure that the the popup is added to the browser's window group, 550 // to allow for the grabs to work correctly. 551 gtk_window_group_add_window(gtk_window_get_group( 552 GTK_WINDOW(gtk_widget_get_toplevel(parent_))), window); 553 gtk_grab_add(view_.get()); 554 555 // We need for the application to do an X grab as well. However if the app 556 // already has an X grab (as in the case of extension popup), an app grab 557 // will suffice. 558 do_x_grab_ = !gdk_pointer_is_grabbed(); 559 560 // Now grab all of X's input. 561 if (do_x_grab_) { 562 gdk_pointer_grab( 563 parent_->window, 564 TRUE, // Only events outside of the window are reported with respect 565 // to |parent_->window|. 566 static_cast<GdkEventMask>(GDK_BUTTON_PRESS_MASK | 567 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK), 568 NULL, 569 NULL, 570 GDK_CURRENT_TIME); 571 // We grab keyboard events too so things like alt+tab are eaten. 572 gdk_keyboard_grab(parent_->window, TRUE, GDK_CURRENT_TIME); 573 } 574 } 575} 576 577void RenderWidgetHostViewGtk::InitAsFullscreen() { 578 DoSharedInit(); 579 580 is_fullscreen_ = true; 581 GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); 582 gtk_window_set_decorated(window, FALSE); 583 gtk_window_fullscreen(window); 584 g_signal_connect(GTK_WIDGET(window), 585 "window-state-event", 586 G_CALLBACK(&OnWindowStateEventThunk), 587 this); 588 destroy_handler_id_ = g_signal_connect(GTK_WIDGET(window), 589 "destroy", 590 G_CALLBACK(OnDestroyThunk), 591 this); 592 gtk_container_add(GTK_CONTAINER(window), view_.get()); 593 594 // Try to move and resize the window to cover the screen in case the window 595 // manager doesn't support _NET_WM_STATE_FULLSCREEN. 596 GdkScreen* screen = gtk_window_get_screen(window); 597 gfx::Rect bounds( 598 0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen)); 599 DoPopupOrFullscreenInit(window, bounds); 600} 601 602RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const { 603 return host_; 604} 605 606void RenderWidgetHostViewGtk::DidBecomeSelected() { 607 if (!is_hidden_) 608 return; 609 610 if (tab_switch_paint_time_.is_null()) 611 tab_switch_paint_time_ = base::TimeTicks::Now(); 612 is_hidden_ = false; 613 host_->WasRestored(); 614} 615 616void RenderWidgetHostViewGtk::WasHidden() { 617 if (is_hidden_) 618 return; 619 620 // If we receive any more paint messages while we are hidden, we want to 621 // ignore them so we don't re-allocate the backing store. We will paint 622 // everything again when we become selected again. 623 is_hidden_ = true; 624 625 // If we have a renderer, then inform it that we are being hidden so it can 626 // reduce its resource utilization. 627 GetRenderWidgetHost()->WasHidden(); 628} 629 630void RenderWidgetHostViewGtk::SetSize(const gfx::Size& size) { 631 int width = std::min(size.width(), kMaxWindowWidth); 632 int height = std::min(size.height(), kMaxWindowHeight); 633 if (IsPopup()) { 634 // We're a popup, honor the size request. 635 gtk_widget_set_size_request(view_.get(), width, height); 636 } else { 637#if defined(TOOLKIT_VIEWS) 638 // TOOLKIT_VIEWS' resize logic flow matches windows. so we go ahead and 639 // size the widget. In GTK+, the size of the widget is determined by its 640 // children. 641 gtk_widget_set_size_request(view_.get(), width, height); 642#endif 643 } 644 645 // Update the size of the RWH. 646 if (requested_size_.width() != width || 647 requested_size_.height() != height) { 648 requested_size_ = gfx::Size(width, height); 649 host_->WasResized(); 650 } 651} 652 653void RenderWidgetHostViewGtk::SetBounds(const gfx::Rect& rect) { 654 // This is called when webkit has sent us a Move message. 655 if (IsPopup()) { 656 gtk_window_move(GTK_WINDOW(gtk_widget_get_toplevel(view_.get())), 657 rect.x(), rect.y()); 658 } 659 660 SetSize(rect.size()); 661} 662 663gfx::NativeView RenderWidgetHostViewGtk::GetNativeView() { 664 return view_.get(); 665} 666 667void RenderWidgetHostViewGtk::MovePluginWindows( 668 const std::vector<webkit::npapi::WebPluginGeometry>& moves) { 669 for (size_t i = 0; i < moves.size(); ++i) { 670 plugin_container_manager_.MovePluginContainer(moves[i]); 671 } 672} 673 674void RenderWidgetHostViewGtk::Focus() { 675 gtk_widget_grab_focus(view_.get()); 676} 677 678void RenderWidgetHostViewGtk::Blur() { 679 // TODO(estade): We should be clearing native focus as well, but I know of no 680 // way to do that without focusing another widget. 681 host_->Blur(); 682} 683 684bool RenderWidgetHostViewGtk::HasFocus() { 685 return gtk_widget_is_focus(view_.get()); 686} 687 688void RenderWidgetHostViewGtk::Show() { 689 gtk_widget_show(view_.get()); 690} 691 692void RenderWidgetHostViewGtk::Hide() { 693 gtk_widget_hide(view_.get()); 694} 695 696bool RenderWidgetHostViewGtk::IsShowing() { 697 // TODO(jcivelli): use gtk_widget_get_visible once we build with GTK 2.18. 698 return (GTK_WIDGET_FLAGS(view_.get()) & GTK_VISIBLE) != 0; 699} 700 701gfx::Rect RenderWidgetHostViewGtk::GetViewBounds() const { 702 GtkAllocation* alloc = &view_.get()->allocation; 703 return gfx::Rect(alloc->x, alloc->y, 704 requested_size_.width(), 705 requested_size_.height()); 706} 707 708void RenderWidgetHostViewGtk::UpdateCursor(const WebCursor& cursor) { 709 // Optimize the common case, where the cursor hasn't changed. 710 // However, we can switch between different pixmaps, so only on the 711 // non-pixmap branch. 712 if (current_cursor_.GetCursorType() != GDK_CURSOR_IS_PIXMAP && 713 current_cursor_.GetCursorType() == cursor.GetCursorType()) { 714 return; 715 } 716 717 current_cursor_ = cursor; 718 ShowCurrentCursor(); 719} 720 721void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) { 722 is_loading_ = is_loading; 723 // Only call ShowCurrentCursor() when it will actually change the cursor. 724 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) 725 ShowCurrentCursor(); 726} 727 728void RenderWidgetHostViewGtk::ImeUpdateTextInputState( 729 WebKit::WebTextInputType type, 730 const gfx::Rect& caret_rect) { 731 im_context_->UpdateInputMethodState(type, caret_rect); 732} 733 734void RenderWidgetHostViewGtk::ImeCancelComposition() { 735 im_context_->CancelComposition(); 736} 737 738void RenderWidgetHostViewGtk::DidUpdateBackingStore( 739 const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, 740 const std::vector<gfx::Rect>& copy_rects) { 741 if (is_hidden_) 742 return; 743 744 // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX. Can that 745 // be done using XCopyArea? Perhaps similar to 746 // BackingStore::ScrollBackingStore? 747 if (about_to_validate_and_paint_) 748 invalid_rect_ = invalid_rect_.Union(scroll_rect); 749 else 750 Paint(scroll_rect); 751 752 for (size_t i = 0; i < copy_rects.size(); ++i) { 753 // Avoid double painting. NOTE: This is only relevant given the call to 754 // Paint(scroll_rect) above. 755 gfx::Rect rect = copy_rects[i].Subtract(scroll_rect); 756 if (rect.IsEmpty()) 757 continue; 758 759 if (about_to_validate_and_paint_) 760 invalid_rect_ = invalid_rect_.Union(rect); 761 else 762 Paint(rect); 763 } 764} 765 766void RenderWidgetHostViewGtk::RenderViewGone(base::TerminationStatus status, 767 int error_code) { 768 Destroy(); 769 plugin_container_manager_.set_host_widget(NULL); 770} 771 772void RenderWidgetHostViewGtk::Destroy() { 773 if (compositing_surface_ != gfx::kNullPluginWindow) { 774 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); 775 manager->ReleasePermanentXID(compositing_surface_); 776 } 777 778 if (do_x_grab_) { 779 // Undo the X grab. 780 GdkDisplay* display = gtk_widget_get_display(parent_); 781 gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME); 782 gdk_display_keyboard_ungrab(display, GDK_CURRENT_TIME); 783 } 784 785 // If this is a popup or fullscreen widget, then we need to destroy the window 786 // that we created to hold it. 787 if (IsPopup() || is_fullscreen_) { 788 GtkWidget* window = gtk_widget_get_parent(view_.get()); 789 790 // Disconnect the destroy handler so that we don't try to shutdown twice. 791 if (is_fullscreen_) 792 g_signal_handler_disconnect(window, destroy_handler_id_); 793 794 gtk_widget_destroy(window); 795 } 796 797 // Remove |view_| from all containers now, so nothing else can hold a 798 // reference to |view_|'s widget except possibly a gtk signal handler if 799 // this code is currently executing within the context of a gtk signal 800 // handler. Note that |view_| is still alive after this call. It will be 801 // deallocated in the destructor. 802 // See http://www.crbug.com/11847 for details. 803 gtk_widget_destroy(view_.get()); 804 805 // The RenderWidgetHost's destruction led here, so don't call it. 806 host_ = NULL; 807 808 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 809} 810 811void RenderWidgetHostViewGtk::SetTooltipText(const std::wstring& tooltip_text) { 812 // Maximum number of characters we allow in a tooltip. 813 const int kMaxTooltipLength = 8 << 10; 814 // Clamp the tooltip length to kMaxTooltipLength so that we don't 815 // accidentally DOS the user with a mega tooltip (since GTK doesn't do 816 // this itself). 817 // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream. 818 const string16 clamped_tooltip = 819 l10n_util::TruncateString(WideToUTF16Hack(tooltip_text), 820 kMaxTooltipLength); 821 822 if (clamped_tooltip.empty()) { 823 gtk_widget_set_has_tooltip(view_.get(), FALSE); 824 } else { 825 gtk_widget_set_tooltip_text(view_.get(), 826 UTF16ToUTF8(clamped_tooltip).c_str()); 827#if defined(OS_CHROMEOS) 828 tooltip_window_->SetTooltipText(UTF16ToWideHack(clamped_tooltip)); 829#endif // defined(OS_CHROMEOS) 830 } 831} 832 833void RenderWidgetHostViewGtk::SelectionChanged(const std::string& text) { 834 if (!text.empty()) { 835 GtkClipboard* x_clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); 836 gtk_clipboard_set_text(x_clipboard, text.c_str(), text.length()); 837 } 838} 839 840void RenderWidgetHostViewGtk::ShowingContextMenu(bool showing) { 841 is_showing_context_menu_ = showing; 842} 843 844#if !defined(TOOLKIT_VIEWS) 845void RenderWidgetHostViewGtk::AppendInputMethodsContextMenu(MenuGtk* menu) { 846 im_context_->AppendInputMethodsContextMenu(menu); 847} 848#endif 849 850gboolean RenderWidgetHostViewGtk::OnWindowStateEvent( 851 GtkWidget* widget, 852 GdkEventWindowState* event) { 853 if (is_fullscreen_) { 854 // If a fullscreen widget got unfullscreened (e.g. by the window manager), 855 // close it. 856 bool unfullscreened = 857 (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) && 858 !(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN); 859 if (unfullscreened) { 860 host_->Shutdown(); 861 return TRUE; 862 } 863 } 864 865 return FALSE; 866} 867 868void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) { 869 DCHECK(is_fullscreen_); 870 host_->Shutdown(); 871} 872 873bool RenderWidgetHostViewGtk::NeedsInputGrab() { 874 return popup_type_ == WebKit::WebPopupTypeSelect; 875} 876 877bool RenderWidgetHostViewGtk::IsPopup() const { 878 return popup_type_ != WebKit::WebPopupTypeNone; 879} 880 881void RenderWidgetHostViewGtk::DoSharedInit() { 882 view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this)); 883 im_context_.reset(new GtkIMContextWrapper(this)); 884 key_bindings_handler_.reset(new GtkKeyBindingsHandler(view_.get())); 885 plugin_container_manager_.set_host_widget(view_.get()); 886#if defined(OS_CHROMEOS) 887 tooltip_window_.reset(new views::TooltipWindowGtk(view_.get())); 888#endif 889} 890 891void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow* window, 892 const gfx::Rect& bounds) { 893 requested_size_.SetSize(std::min(bounds.width(), kMaxWindowWidth), 894 std::min(bounds.height(), kMaxWindowHeight)); 895 host_->WasResized(); 896 897 gtk_widget_set_size_request( 898 view_.get(), requested_size_.width(), requested_size_.height()); 899 900 // Don't allow the window to be resized. This also forces the window to 901 // shrink down to the size of its child contents. 902 gtk_window_set_resizable(window, FALSE); 903 gtk_window_set_default_size(window, -1, -1); 904 gtk_window_move(window, bounds.x(), bounds.y()); 905 906 gtk_widget_show_all(GTK_WIDGET(window)); 907} 908 909BackingStore* RenderWidgetHostViewGtk::AllocBackingStore( 910 const gfx::Size& size) { 911 return new BackingStoreX(host_, size, 912 ui::GetVisualFromGtkWidget(view_.get()), 913 gtk_widget_get_visual(view_.get())->depth); 914} 915 916void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) { 917 RenderWidgetHostView::SetBackground(background); 918 host_->Send(new ViewMsg_SetBackground(host_->routing_id(), background)); 919} 920 921void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging( 922 GtkWidget* widget, GdkEventMotion* event) { 923 // If the widget is aligned with an edge of the monitor its on and the user 924 // attempts to drag past that edge we track the number of times it has 925 // occurred, so that we can force the widget to scroll when it otherwise 926 // would be unable to, by modifying the (x,y) position in the drag 927 // event that we forward on to webkit. If we get a move that's no longer a 928 // drag or a drag indicating the user is no longer at that edge we stop 929 // altering the drag events. 930 int new_dragged_at_horizontal_edge = 0; 931 int new_dragged_at_vertical_edge = 0; 932 // Used for checking the edges of the monitor. We cache the values to save 933 // roundtrips to the X server. 934 static gfx::Size drag_monitor_size; 935 if (event->state & GDK_BUTTON1_MASK) { 936 if (drag_monitor_size.IsEmpty()) { 937 // We can safely cache the monitor size for the duration of a drag. 938 GdkScreen* screen = gtk_widget_get_screen(widget); 939 int monitor = 940 gdk_screen_get_monitor_at_point(screen, event->x_root, event->y_root); 941 GdkRectangle geometry; 942 gdk_screen_get_monitor_geometry(screen, monitor, &geometry); 943 drag_monitor_size.SetSize(geometry.width, geometry.height); 944 } 945 946 // Check X and Y independently, as the user could be dragging into a corner. 947 if (event->x == 0 && event->x_root == 0) { 948 new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ - 1; 949 } else if (widget->allocation.width - 1 == static_cast<gint>(event->x) && 950 drag_monitor_size.width() - 1 == static_cast<gint>(event->x_root)) { 951 new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ + 1; 952 } 953 954 if (event->y == 0 && event->y_root == 0) { 955 new_dragged_at_vertical_edge = dragged_at_vertical_edge_ - 1; 956 } else if (widget->allocation.height - 1 == static_cast<gint>(event->y) && 957 drag_monitor_size.height() - 1 == static_cast<gint>(event->y_root)) { 958 new_dragged_at_vertical_edge = dragged_at_vertical_edge_ + 1; 959 } 960 961 event->x_root += new_dragged_at_horizontal_edge; 962 event->x += new_dragged_at_horizontal_edge; 963 event->y_root += new_dragged_at_vertical_edge; 964 event->y += new_dragged_at_vertical_edge; 965 } else { 966 // Clear whenever we get a non-drag mouse move. 967 drag_monitor_size.SetSize(0, 0); 968 } 969 dragged_at_horizontal_edge_ = new_dragged_at_horizontal_edge; 970 dragged_at_vertical_edge_ = new_dragged_at_vertical_edge; 971} 972 973void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) { 974 // If the GPU process is rendering directly into the View, 975 // call the compositor directly. 976 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(); 977 if (render_widget_host->is_accelerated_compositing_active()) { 978 host_->ScheduleComposite(); 979 return; 980 } 981 982 GdkWindow* window = view_.get()->window; 983 DCHECK(!about_to_validate_and_paint_); 984 985 invalid_rect_ = damage_rect; 986 about_to_validate_and_paint_ = true; 987 BackingStoreX* backing_store = static_cast<BackingStoreX*>( 988 host_->GetBackingStore(true)); 989 // Calling GetBackingStore maybe have changed |invalid_rect_|... 990 about_to_validate_and_paint_ = false; 991 992 gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight); 993 paint_rect = paint_rect.Intersect(invalid_rect_); 994 995 if (backing_store) { 996 // Only render the widget if it is attached to a window; there's a short 997 // period where this object isn't attached to a window but hasn't been 998 // Destroy()ed yet and it receives paint messages... 999 if (window) { 1000 if (SkColorGetA(overlay_color_) == 0) { 1001 // In the common case, use XCopyArea. We don't draw more than once, so 1002 // we don't need to double buffer. 1003 backing_store->XShowRect(gfx::Point(0, 0), 1004 paint_rect, ui::GetX11WindowFromGtkWidget(view_.get())); 1005 } else { 1006 // If the grey blend is showing, we make two drawing calls. Use double 1007 // buffering to prevent flicker. Use CairoShowRect because XShowRect 1008 // shortcuts GDK's double buffering. We won't be able to draw outside 1009 // of |damage_rect|, so invalidate the difference between |paint_rect| 1010 // and |damage_rect|. 1011 if (paint_rect != damage_rect) { 1012 GdkRectangle extra_gdk_rect = 1013 paint_rect.Subtract(damage_rect).ToGdkRectangle(); 1014 gdk_window_invalidate_rect(window, &extra_gdk_rect, false); 1015 } 1016 1017 GdkRectangle rect = { damage_rect.x(), damage_rect.y(), 1018 damage_rect.width(), damage_rect.height() }; 1019 gdk_window_begin_paint_rect(window, &rect); 1020 1021 backing_store->CairoShowRect(damage_rect, GDK_DRAWABLE(window)); 1022 1023 cairo_t* cr = gdk_cairo_create(window); 1024 gdk_cairo_rectangle(cr, &rect); 1025 SkColor overlay = SkColorSetA( 1026 overlay_color_, 1027 SkColorGetA(overlay_color_) * 1028 overlay_animation_.GetCurrentValue()); 1029 float r = SkColorGetR(overlay) / 255.; 1030 float g = SkColorGetG(overlay) / 255.; 1031 float b = SkColorGetB(overlay) / 255.; 1032 float a = SkColorGetA(overlay) / 255.; 1033 cairo_set_source_rgba(cr, r, g, b, a); 1034 cairo_fill(cr); 1035 cairo_destroy(cr); 1036 1037 gdk_window_end_paint(window); 1038 } 1039 } 1040 if (!whiteout_start_time_.is_null()) { 1041 base::TimeDelta whiteout_duration = base::TimeTicks::Now() - 1042 whiteout_start_time_; 1043 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration); 1044 1045 // Reset the start time to 0 so that we start recording again the next 1046 // time the backing store is NULL... 1047 whiteout_start_time_ = base::TimeTicks(); 1048 } 1049 if (!tab_switch_paint_time_.is_null()) { 1050 base::TimeDelta tab_switch_paint_duration = base::TimeTicks::Now() - 1051 tab_switch_paint_time_; 1052 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", 1053 tab_switch_paint_duration); 1054 // Reset tab_switch_paint_time_ to 0 so future tab selections are 1055 // recorded. 1056 tab_switch_paint_time_ = base::TimeTicks(); 1057 } 1058 } else { 1059 if (window) 1060 gdk_window_clear(window); 1061 if (whiteout_start_time_.is_null()) 1062 whiteout_start_time_ = base::TimeTicks::Now(); 1063 } 1064} 1065 1066void RenderWidgetHostViewGtk::ShowCurrentCursor() { 1067 // The widget may not have a window. If that's the case, abort mission. This 1068 // is the same issue as that explained above in Paint(). 1069 if (!view_.get()->window) 1070 return; 1071 1072 // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is 1073 // that calling gdk_window_set_cursor repeatedly is expensive. We should 1074 // avoid it here where possible. 1075 GdkCursor* gdk_cursor; 1076 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) { 1077 // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and 1078 // the page is loading. 1079 gdk_cursor = is_loading_ ? GetMozSpinningCursor() : NULL; 1080 } else { 1081 gdk_cursor = current_cursor_.GetNativeCursor(); 1082 } 1083 gdk_window_set_cursor(view_.get()->window, gdk_cursor); 1084} 1085 1086void RenderWidgetHostViewGtk::CreatePluginContainer( 1087 gfx::PluginWindowHandle id) { 1088 plugin_container_manager_.CreatePluginContainer(id); 1089} 1090 1091void RenderWidgetHostViewGtk::DestroyPluginContainer( 1092 gfx::PluginWindowHandle id) { 1093 plugin_container_manager_.DestroyPluginContainer(id); 1094} 1095 1096void RenderWidgetHostViewGtk::SetVisuallyDeemphasized( 1097 const SkColor* color, bool animate) { 1098 // Do nothing unless |color| has changed, meaning |animate| is only 1099 // respected for the first call. 1100 if (color && (*color == overlay_color_)) 1101 return; 1102 1103 overlay_color_ = color ? *color : 0; 1104 1105 if (animate) { 1106 overlay_animation_.Reset(); 1107 overlay_animation_.Show(); 1108 } else { 1109 overlay_animation_.Reset(1.0); 1110 gtk_widget_queue_draw(view_.get()); 1111 } 1112} 1113 1114bool RenderWidgetHostViewGtk::ContainsNativeView( 1115 gfx::NativeView native_view) const { 1116 // TODO(port) 1117 NOTREACHED() << 1118 "RenderWidgetHostViewGtk::ContainsNativeView not implemented."; 1119 return false; 1120} 1121 1122void RenderWidgetHostViewGtk::AcceleratedCompositingActivated(bool activated) { 1123 GtkPreserveWindow* widget = 1124 reinterpret_cast<GtkPreserveWindow*>(view_.get()); 1125 1126 gtk_preserve_window_delegate_resize(widget, activated); 1127} 1128 1129gfx::PluginWindowHandle RenderWidgetHostViewGtk::GetCompositingSurface() { 1130 if (compositing_surface_ == gfx::kNullPluginWindow) { 1131 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); 1132 gfx::NativeViewId view_id = gfx::IdFromNativeView(GetNativeView()); 1133 1134 if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) { 1135 DLOG(ERROR) << "Can't find XID for view id " << view_id; 1136 } 1137 } 1138 return compositing_surface_; 1139} 1140 1141void RenderWidgetHostViewGtk::ForwardKeyboardEvent( 1142 const NativeWebKeyboardEvent& event) { 1143 if (!host_) 1144 return; 1145 1146 EditCommands edit_commands; 1147 if (!event.skip_in_browser && 1148 key_bindings_handler_->Match(event, &edit_commands)) { 1149 host_->ForwardEditCommandsForNextKeyEvent(edit_commands); 1150 NativeWebKeyboardEvent copy_event(event); 1151 copy_event.match_edit_command = true; 1152 host_->ForwardKeyboardEvent(copy_event); 1153 return; 1154 } 1155 1156 host_->ForwardKeyboardEvent(event); 1157} 1158 1159void RenderWidgetHostViewGtk::AnimationEnded(const ui::Animation* animation) { 1160 gtk_widget_queue_draw(view_.get()); 1161} 1162 1163void RenderWidgetHostViewGtk::AnimationProgressed( 1164 const ui::Animation* animation) { 1165 gtk_widget_queue_draw(view_.get()); 1166} 1167 1168void RenderWidgetHostViewGtk::AnimationCanceled( 1169 const ui::Animation* animation) { 1170 gtk_widget_queue_draw(view_.get()); 1171} 1172 1173void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton* event) { 1174 GdkEventButton* temp = NULL; 1175 if (event) { 1176 temp = reinterpret_cast<GdkEventButton*>( 1177 gdk_event_copy(reinterpret_cast<GdkEvent*>(event))); 1178 } 1179 1180 if (last_mouse_down_) 1181 gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_)); 1182 1183 last_mouse_down_ = temp; 1184} 1185 1186// static 1187RenderWidgetHostView* 1188 RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView( 1189 gfx::NativeView widget) { 1190 gpointer user_data = g_object_get_data(G_OBJECT(widget), 1191 kRenderWidgetHostViewKey); 1192 return reinterpret_cast<RenderWidgetHostView*>(user_data); 1193} 1194