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

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

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

Powered by Google App Engine
This is Rietveld 408576698