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