OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/renderer_host/render_widget_host_view_gtk.h" | |
6 | |
7 #include <cairo/cairo.h> | |
8 #include <gdk/gdk.h> | |
9 #include <gdk/gdkkeysyms.h> | |
10 #include <gdk/gdkx.h> | |
11 #include <gtk/gtk.h> | |
12 | |
13 #include <algorithm> | |
14 #include <string> | |
15 | |
16 #include "base/bind_helpers.h" | |
17 #include "base/command_line.h" | |
18 #include "base/debug/trace_event.h" | |
19 #include "base/logging.h" | |
20 #include "base/message_loop/message_loop.h" | |
21 #include "base/metrics/histogram.h" | |
22 #include "base/strings/string_number_conversions.h" | |
23 #include "base/strings/utf_offset_string_conversions.h" | |
24 #include "base/strings/utf_string_conversions.h" | |
25 #include "base/time/time.h" | |
26 #include "content/browser/accessibility/browser_accessibility_gtk.h" | |
27 #include "content/browser/accessibility/browser_accessibility_manager_gtk.h" | |
28 #include "content/browser/renderer_host/backing_store_gtk.h" | |
29 #include "content/browser/renderer_host/gtk_im_context_wrapper.h" | |
30 #include "content/browser/renderer_host/gtk_key_bindings_handler.h" | |
31 #include "content/browser/renderer_host/gtk_window_utils.h" | |
32 #include "content/browser/renderer_host/input/web_input_event_builders_gtk.h" | |
33 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
34 #include "content/browser/renderer_host/render_view_host_impl.h" | |
35 #include "content/common/cursors/webcursor_gtk_data.h" | |
36 #include "content/common/gpu/gpu_messages.h" | |
37 #include "content/common/input_messages.h" | |
38 #include "content/common/view_messages.h" | |
39 #include "content/common/webplugin_geometry.h" | |
40 #include "content/public/browser/native_web_keyboard_event.h" | |
41 #include "content/public/common/content_switches.h" | |
42 #include "skia/ext/platform_canvas.h" | |
43 #include "third_party/WebKit/public/platform/WebScreenInfo.h" | |
44 #include "third_party/WebKit/public/web/WebInputEvent.h" | |
45 #include "ui/base/clipboard/scoped_clipboard_writer.h" | |
46 #include "ui/base/x/active_window_watcher_x.h" | |
47 #include "ui/base/x/x11_util.h" | |
48 #include "ui/gfx/gtk_compat.h" | |
49 #include "ui/gfx/gtk_native_view_id_manager.h" | |
50 #include "ui/gfx/gtk_preserve_window.h" | |
51 #include "ui/gfx/text_elider.h" | |
52 | |
53 using blink::WebMouseWheelEvent; | |
54 using blink::WebScreenInfo; | |
55 | |
56 #error "The GTK+ port will be deleted later this week. If you are seeing this, y
ou are trying to compile it. Please check your gyp flags for 'use_aura=0' and re
move them." | |
57 | |
58 namespace content { | |
59 namespace { | |
60 | |
61 // Paint rects on Linux are bounded by the maximum size of a shared memory | |
62 // region. By default that's 32MB, but many distros increase it significantly | |
63 // (i.e. to 256MB). | |
64 // | |
65 // We fetch the maximum value from /proc/sys/kernel/shmmax at runtime and, if | |
66 // we exceed that, then we limit the height of the paint rect in the renderer. | |
67 // | |
68 // These constants are here to ensure that, in the event that we exceed it, we | |
69 // end up with something a little more square. Previously we had 4000x4000, but | |
70 // people's monitor setups are actually exceeding that these days. | |
71 const int kMaxWindowWidth = 10000; | |
72 const int kMaxWindowHeight = 10000; | |
73 | |
74 const GdkColor kBGColor = | |
75 #if defined(NDEBUG) | |
76 { 0, 0xff * 257, 0xff * 257, 0xff * 257 }; | |
77 #else | |
78 { 0, 0x00 * 257, 0xff * 257, 0x00 * 257 }; | |
79 #endif | |
80 | |
81 // Returns the spinning cursor used for loading state. | |
82 GdkCursor* GetMozSpinningCursor() { | |
83 static GdkCursor* moz_spinning_cursor = NULL; | |
84 if (!moz_spinning_cursor) { | |
85 const GdkColor fg = { 0, 0, 0, 0 }; | |
86 const GdkColor bg = { 65535, 65535, 65535, 65535 }; | |
87 GdkPixmap* source = gdk_bitmap_create_from_data( | |
88 NULL, reinterpret_cast<const gchar*>(moz_spinning_bits), 32, 32); | |
89 GdkPixmap* mask = gdk_bitmap_create_from_data( | |
90 NULL, reinterpret_cast<const gchar*>(moz_spinning_mask_bits), 32, 32); | |
91 moz_spinning_cursor = | |
92 gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 2, 2); | |
93 g_object_unref(source); | |
94 g_object_unref(mask); | |
95 } | |
96 return moz_spinning_cursor; | |
97 } | |
98 | |
99 bool MovedToPoint(const blink::WebMouseEvent& mouse_event, | |
100 const gfx::Point& center) { | |
101 return mouse_event.globalX == center.x() && | |
102 mouse_event.globalY == center.y(); | |
103 } | |
104 | |
105 } // namespace | |
106 | |
107 // This class is a simple convenience wrapper for Gtk functions. It has only | |
108 // static methods. | |
109 class RenderWidgetHostViewGtkWidget { | |
110 public: | |
111 static AtkObject* GetAccessible(void* userdata) { | |
112 return (static_cast<RenderWidgetHostViewGtk*>(userdata))-> | |
113 GetAccessible(); | |
114 } | |
115 | |
116 static GtkWidget* CreateNewWidget(RenderWidgetHostViewGtk* host_view) { | |
117 GtkWidget* widget = gtk_preserve_window_new(); | |
118 gtk_widget_set_name(widget, "chrome-render-widget-host-view"); | |
119 // We manually double-buffer in Paint() because Paint() may or may not be | |
120 // called in repsonse to an "expose-event" signal. | |
121 gtk_widget_set_double_buffered(widget, FALSE); | |
122 gtk_widget_set_redraw_on_allocate(widget, FALSE); | |
123 gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &kBGColor); | |
124 // Allow the browser window to be resized freely. | |
125 gtk_widget_set_size_request(widget, 0, 0); | |
126 | |
127 gtk_widget_add_events(widget, GDK_EXPOSURE_MASK | | |
128 GDK_STRUCTURE_MASK | | |
129 GDK_POINTER_MOTION_MASK | | |
130 GDK_BUTTON_PRESS_MASK | | |
131 GDK_BUTTON_RELEASE_MASK | | |
132 GDK_KEY_PRESS_MASK | | |
133 GDK_KEY_RELEASE_MASK | | |
134 GDK_FOCUS_CHANGE_MASK | | |
135 GDK_ENTER_NOTIFY_MASK | | |
136 GDK_LEAVE_NOTIFY_MASK); | |
137 gtk_widget_set_can_focus(widget, TRUE); | |
138 | |
139 g_signal_connect(widget, "expose-event", | |
140 G_CALLBACK(OnExposeEvent), host_view); | |
141 g_signal_connect(widget, "realize", | |
142 G_CALLBACK(OnRealize), host_view); | |
143 g_signal_connect(widget, "configure-event", | |
144 G_CALLBACK(OnConfigureEvent), host_view); | |
145 g_signal_connect(widget, "size-allocate", | |
146 G_CALLBACK(OnSizeAllocate), host_view); | |
147 g_signal_connect(widget, "key-press-event", | |
148 G_CALLBACK(OnKeyPressReleaseEvent), host_view); | |
149 g_signal_connect(widget, "key-release-event", | |
150 G_CALLBACK(OnKeyPressReleaseEvent), host_view); | |
151 g_signal_connect(widget, "focus-in-event", | |
152 G_CALLBACK(OnFocusIn), host_view); | |
153 g_signal_connect(widget, "focus-out-event", | |
154 G_CALLBACK(OnFocusOut), host_view); | |
155 g_signal_connect(widget, "grab-notify", | |
156 G_CALLBACK(OnGrabNotify), host_view); | |
157 g_signal_connect(widget, "button-press-event", | |
158 G_CALLBACK(OnButtonPressReleaseEvent), host_view); | |
159 g_signal_connect(widget, "button-release-event", | |
160 G_CALLBACK(OnButtonPressReleaseEvent), host_view); | |
161 g_signal_connect(widget, "motion-notify-event", | |
162 G_CALLBACK(OnMouseMoveEvent), host_view); | |
163 g_signal_connect(widget, "enter-notify-event", | |
164 G_CALLBACK(OnCrossingEvent), host_view); | |
165 g_signal_connect(widget, "leave-notify-event", | |
166 G_CALLBACK(OnCrossingEvent), host_view); | |
167 g_signal_connect(widget, "client-event", | |
168 G_CALLBACK(OnClientEvent), host_view); | |
169 | |
170 | |
171 // Connect after so that we are called after the handler installed by the | |
172 // WebContentsView which handles zoom events. | |
173 g_signal_connect_after(widget, "scroll-event", | |
174 G_CALLBACK(OnMouseScrollEvent), host_view); | |
175 | |
176 // Route calls to get_accessible to the view. | |
177 gtk_preserve_window_set_accessible_factory( | |
178 GTK_PRESERVE_WINDOW(widget), GetAccessible, host_view); | |
179 | |
180 return widget; | |
181 } | |
182 | |
183 private: | |
184 static gboolean OnExposeEvent(GtkWidget* widget, | |
185 GdkEventExpose* expose, | |
186 RenderWidgetHostViewGtk* host_view) { | |
187 if (host_view->host_->is_hidden()) | |
188 return FALSE; | |
189 const gfx::Rect damage_rect(expose->area); | |
190 host_view->Paint(damage_rect); | |
191 return FALSE; | |
192 } | |
193 | |
194 static gboolean OnRealize(GtkWidget* widget, | |
195 RenderWidgetHostViewGtk* host_view) { | |
196 // Use GtkSignalRegistrar to register events on a widget we don't | |
197 // control the lifetime of, auto disconnecting at our end of our life. | |
198 host_view->signals_.Connect(gtk_widget_get_toplevel(widget), | |
199 "configure-event", | |
200 G_CALLBACK(OnConfigureEvent), host_view); | |
201 return FALSE; | |
202 } | |
203 | |
204 static gboolean OnConfigureEvent(GtkWidget* widget, | |
205 GdkEventConfigure* event, | |
206 RenderWidgetHostViewGtk* host_view) { | |
207 host_view->MarkCachedWidgetCenterStale(); | |
208 host_view->UpdateScreenInfo(host_view->GetNativeView()); | |
209 return FALSE; | |
210 } | |
211 | |
212 static gboolean OnSizeAllocate(GtkWidget* widget, | |
213 GdkRectangle* allocation, | |
214 RenderWidgetHostViewGtk* host_view) { | |
215 if (!host_view->IsPopup() && !host_view->is_fullscreen_) | |
216 host_view->SetSize(gfx::Size(allocation->width, allocation->height)); | |
217 return FALSE; | |
218 } | |
219 | |
220 static gboolean OnKeyPressReleaseEvent(GtkWidget* widget, | |
221 GdkEventKey* event, | |
222 RenderWidgetHostViewGtk* host_view) { | |
223 TRACE_EVENT0("browser", | |
224 "RenderWidgetHostViewGtkWidget::OnKeyPressReleaseEvent"); | |
225 // Force popups or fullscreen windows to close on Escape so they won't keep | |
226 // the keyboard grabbed or be stuck onscreen if the renderer is hanging. | |
227 bool should_close_on_escape = | |
228 (host_view->IsPopup() && host_view->NeedsInputGrab()) || | |
229 host_view->is_fullscreen_; | |
230 if (should_close_on_escape && GDK_Escape == event->keyval) { | |
231 host_view->host_->Shutdown(); | |
232 } else { | |
233 // Send key event to input method. | |
234 host_view->im_context_->ProcessKeyEvent(event); | |
235 } | |
236 | |
237 // We return TRUE because we did handle the event. If it turns out webkit | |
238 // can't handle the event, we'll deal with it in | |
239 // RenderView::UnhandledKeyboardEvent(). | |
240 return TRUE; | |
241 } | |
242 | |
243 static gboolean OnFocusIn(GtkWidget* widget, | |
244 GdkEventFocus* focus, | |
245 RenderWidgetHostViewGtk* host_view) { | |
246 host_view->ShowCurrentCursor(); | |
247 RenderWidgetHostImpl* host = | |
248 RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); | |
249 host->GotFocus(); | |
250 host->SetActive(true); | |
251 | |
252 // The only way to enable a GtkIMContext object is to call its focus in | |
253 // handler. | |
254 host_view->im_context_->OnFocusIn(); | |
255 | |
256 return TRUE; | |
257 } | |
258 | |
259 static gboolean OnFocusOut(GtkWidget* widget, | |
260 GdkEventFocus* focus, | |
261 RenderWidgetHostViewGtk* host_view) { | |
262 // Whenever we lose focus, set the cursor back to that of our parent window, | |
263 // which should be the default arrow. | |
264 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL); | |
265 // If we are showing a context menu, maintain the illusion that webkit has | |
266 // focus. | |
267 if (!host_view->IsShowingContextMenu()) { | |
268 RenderWidgetHostImpl* host = | |
269 RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); | |
270 host->SetActive(false); | |
271 host->Blur(); | |
272 } | |
273 | |
274 // Prevents us from stealing input context focus in OnGrabNotify() handler. | |
275 host_view->was_imcontext_focused_before_grab_ = false; | |
276 | |
277 // Disable the GtkIMContext object. | |
278 host_view->im_context_->OnFocusOut(); | |
279 | |
280 host_view->set_last_mouse_down(NULL); | |
281 | |
282 return TRUE; | |
283 } | |
284 | |
285 // Called when we are shadowed or unshadowed by a keyboard grab (which will | |
286 // occur for activatable popups, such as dropdown menus). Popup windows do not | |
287 // take focus, so we never get a focus out or focus in event when they are | |
288 // shown, and must rely on this signal instead. | |
289 static void OnGrabNotify(GtkWidget* widget, gboolean was_grabbed, | |
290 RenderWidgetHostViewGtk* host_view) { | |
291 if (was_grabbed) { | |
292 if (host_view->was_imcontext_focused_before_grab_) | |
293 host_view->im_context_->OnFocusIn(); | |
294 } else { | |
295 host_view->was_imcontext_focused_before_grab_ = | |
296 host_view->im_context_->is_focused(); | |
297 if (host_view->was_imcontext_focused_before_grab_) { | |
298 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL); | |
299 host_view->im_context_->OnFocusOut(); | |
300 } | |
301 } | |
302 } | |
303 | |
304 static gboolean OnButtonPressReleaseEvent( | |
305 GtkWidget* widget, | |
306 GdkEventButton* event, | |
307 RenderWidgetHostViewGtk* host_view) { | |
308 TRACE_EVENT0("browser", | |
309 "RenderWidgetHostViewGtkWidget::OnButtonPressReleaseEvent"); | |
310 | |
311 if (event->type != GDK_BUTTON_RELEASE) | |
312 host_view->set_last_mouse_down(event); | |
313 | |
314 if (!(event->button == 1 || event->button == 2 || event->button == 3)) | |
315 return FALSE; // We do not forward any other buttons to the renderer. | |
316 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) | |
317 return FALSE; | |
318 | |
319 // If we don't have focus already, this mouse click will focus us. | |
320 if (!gtk_widget_is_focus(widget)) | |
321 host_view->host_->OnPointerEventActivate(); | |
322 | |
323 // Confirm existing composition text on mouse click events, to make sure | |
324 // the input caret won't be moved with an ongoing composition session. | |
325 if (event->type != GDK_BUTTON_RELEASE) | |
326 host_view->im_context_->ConfirmComposition(); | |
327 | |
328 // We want to translate the coordinates of events that do not originate | |
329 // from this widget to be relative to the top left of the widget. | |
330 GtkWidget* event_widget = gtk_get_event_widget( | |
331 reinterpret_cast<GdkEvent*>(event)); | |
332 if (event_widget != widget) { | |
333 int x = 0; | |
334 int y = 0; | |
335 gtk_widget_get_pointer(widget, &x, &y); | |
336 // If the mouse event happens outside our popup, force the popup to | |
337 // close. We do this so a hung renderer doesn't prevent us from | |
338 // releasing the x pointer grab. | |
339 GtkAllocation allocation; | |
340 gtk_widget_get_allocation(widget, &allocation); | |
341 bool click_in_popup = x >= 0 && y >= 0 && x < allocation.width && | |
342 y < allocation.height; | |
343 // Only Shutdown on mouse downs. Mouse ups can occur outside the render | |
344 // view if the user drags for DnD or while using the scrollbar on a select | |
345 // dropdown. Don't shutdown if we are not a popup. | |
346 if (event->type != GDK_BUTTON_RELEASE && host_view->IsPopup() && | |
347 !host_view->is_popup_first_mouse_release_ && !click_in_popup) { | |
348 host_view->host_->Shutdown(); | |
349 return FALSE; | |
350 } | |
351 event->x = x; | |
352 event->y = y; | |
353 } | |
354 | |
355 // TODO(evanm): why is this necessary here but not in test shell? | |
356 // This logic is the same as GtkButton. | |
357 if (event->type == GDK_BUTTON_PRESS && !gtk_widget_has_focus(widget)) | |
358 gtk_widget_grab_focus(widget); | |
359 | |
360 host_view->is_popup_first_mouse_release_ = false; | |
361 RenderWidgetHostImpl* widget_host = | |
362 RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); | |
363 if (widget_host) | |
364 widget_host->ForwardMouseEvent(WebMouseEventBuilder::Build(event)); | |
365 | |
366 // Although we did handle the mouse event, we need to let other handlers | |
367 // run (in particular the one installed by WebContentsViewGtk). | |
368 return FALSE; | |
369 } | |
370 | |
371 static gboolean OnMouseMoveEvent(GtkWidget* widget, | |
372 GdkEventMotion* event, | |
373 RenderWidgetHostViewGtk* host_view) { | |
374 TRACE_EVENT0("browser", | |
375 "RenderWidgetHostViewGtkWidget::OnMouseMoveEvent"); | |
376 // We want to translate the coordinates of events that do not originate | |
377 // from this widget to be relative to the top left of the widget. | |
378 GtkWidget* event_widget = gtk_get_event_widget( | |
379 reinterpret_cast<GdkEvent*>(event)); | |
380 if (event_widget != widget) { | |
381 int x = 0; | |
382 int y = 0; | |
383 gtk_widget_get_pointer(widget, &x, &y); | |
384 event->x = x; | |
385 event->y = y; | |
386 } | |
387 | |
388 host_view->ModifyEventForEdgeDragging(widget, event); | |
389 | |
390 blink::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event); | |
391 | |
392 if (host_view->mouse_locked_) { | |
393 gfx::Point center = host_view->GetWidgetCenter(); | |
394 | |
395 bool moved_to_center = MovedToPoint(mouse_event, center); | |
396 if (moved_to_center) | |
397 host_view->mouse_has_been_warped_to_new_center_ = true; | |
398 | |
399 host_view->ModifyEventMovementAndCoords(&mouse_event); | |
400 | |
401 if (!moved_to_center && | |
402 (mouse_event.movementX || mouse_event.movementY)) { | |
403 GdkDisplay* display = gtk_widget_get_display(widget); | |
404 GdkScreen* screen = gtk_widget_get_screen(widget); | |
405 gdk_display_warp_pointer(display, screen, center.x(), center.y()); | |
406 if (host_view->mouse_has_been_warped_to_new_center_) | |
407 RenderWidgetHostImpl::From( | |
408 host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event); | |
409 } | |
410 } else { // Mouse is not locked. | |
411 host_view->ModifyEventMovementAndCoords(&mouse_event); | |
412 // Do not send mouse events while the mouse cursor is being warped back | |
413 // to the unlocked location. | |
414 if (!host_view->mouse_is_being_warped_to_unlocked_position_) { | |
415 RenderWidgetHostImpl::From( | |
416 host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event); | |
417 } | |
418 } | |
419 return FALSE; | |
420 } | |
421 | |
422 static gboolean OnCrossingEvent(GtkWidget* widget, | |
423 GdkEventCrossing* event, | |
424 RenderWidgetHostViewGtk* host_view) { | |
425 TRACE_EVENT0("browser", | |
426 "RenderWidgetHostViewGtkWidget::OnCrossingEvent"); | |
427 const int any_button_mask = | |
428 GDK_BUTTON1_MASK | | |
429 GDK_BUTTON2_MASK | | |
430 GDK_BUTTON3_MASK | | |
431 GDK_BUTTON4_MASK | | |
432 GDK_BUTTON5_MASK; | |
433 | |
434 // Only forward crossing events if the mouse button is not down. | |
435 // (When the mouse button is down, the proper events are already being | |
436 // sent by ButtonPressReleaseEvent and MouseMoveEvent, above, and if we | |
437 // additionally send this crossing event with the state indicating the | |
438 // button is down, it causes problems with drag and drop in WebKit.) | |
439 if (!(event->state & any_button_mask)) { | |
440 blink::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event); | |
441 host_view->ModifyEventMovementAndCoords(&mouse_event); | |
442 // When crossing out and back into a render view the movement values | |
443 // must represent the instantaneous movement of the mouse, not the jump | |
444 // from the exit to re-entry point. | |
445 mouse_event.movementX = 0; | |
446 mouse_event.movementY = 0; | |
447 RenderWidgetHostImpl::From( | |
448 host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event); | |
449 } | |
450 | |
451 return FALSE; | |
452 } | |
453 | |
454 static gboolean OnClientEvent(GtkWidget* widget, | |
455 GdkEventClient* event, | |
456 RenderWidgetHostViewGtk* host_view) { | |
457 VLOG(1) << "client event type: " << event->message_type | |
458 << " data_format: " << event->data_format | |
459 << " data: " << event->data.l; | |
460 return TRUE; | |
461 } | |
462 | |
463 // Return the net up / down (or left / right) distance represented by events | |
464 // in the events will be removed from the queue. We only look at the top of | |
465 // queue...any other type of event will cause us not to look farther. | |
466 // If there is a change to the set of modifier keys or scroll axis | |
467 // in the events we will stop looking as well. | |
468 static int GetPendingScrollDelta(bool vert, guint current_event_state) { | |
469 int num_clicks = 0; | |
470 GdkEvent* event; | |
471 bool event_coalesced = true; | |
472 while ((event = gdk_event_get()) && event_coalesced) { | |
473 event_coalesced = false; | |
474 if (event->type == GDK_SCROLL) { | |
475 GdkEventScroll scroll = event->scroll; | |
476 if (scroll.state & GDK_SHIFT_MASK) { | |
477 if (scroll.direction == GDK_SCROLL_UP) | |
478 scroll.direction = GDK_SCROLL_LEFT; | |
479 else if (scroll.direction == GDK_SCROLL_DOWN) | |
480 scroll.direction = GDK_SCROLL_RIGHT; | |
481 } | |
482 if (vert) { | |
483 if (scroll.direction == GDK_SCROLL_UP || | |
484 scroll.direction == GDK_SCROLL_DOWN) { | |
485 if (scroll.state == current_event_state) { | |
486 num_clicks += (scroll.direction == GDK_SCROLL_UP ? 1 : -1); | |
487 gdk_event_free(event); | |
488 event_coalesced = true; | |
489 } | |
490 } | |
491 } else { | |
492 if (scroll.direction == GDK_SCROLL_LEFT || | |
493 scroll.direction == GDK_SCROLL_RIGHT) { | |
494 if (scroll.state == current_event_state) { | |
495 num_clicks += (scroll.direction == GDK_SCROLL_LEFT ? 1 : -1); | |
496 gdk_event_free(event); | |
497 event_coalesced = true; | |
498 } | |
499 } | |
500 } | |
501 } | |
502 } | |
503 // If we have an event left we put it back on the queue. | |
504 if (event) { | |
505 gdk_event_put(event); | |
506 gdk_event_free(event); | |
507 } | |
508 return num_clicks * WebMouseWheelEventBuilder::ScrollbarPixelsPerTick(); | |
509 } | |
510 | |
511 static gboolean OnMouseScrollEvent(GtkWidget* widget, | |
512 GdkEventScroll* event, | |
513 RenderWidgetHostViewGtk* host_view) { | |
514 TRACE_EVENT0("browser", | |
515 "RenderWidgetHostViewGtkWidget::OnMouseScrollEvent"); | |
516 // If the user is holding shift, translate it into a horizontal scroll. We | |
517 // don't care what other modifiers the user may be holding (zooming is | |
518 // handled at the WebContentsView level). | |
519 if (event->state & GDK_SHIFT_MASK) { | |
520 if (event->direction == GDK_SCROLL_UP) | |
521 event->direction = GDK_SCROLL_LEFT; | |
522 else if (event->direction == GDK_SCROLL_DOWN) | |
523 event->direction = GDK_SCROLL_RIGHT; | |
524 } | |
525 | |
526 WebMouseWheelEvent web_event = WebMouseWheelEventBuilder::Build(event); | |
527 const float pixelsPerTick = | |
528 WebMouseWheelEventBuilder::ScrollbarPixelsPerTick(); | |
529 // We peek ahead at the top of the queue to look for additional pending | |
530 // scroll events. | |
531 if (event->direction == GDK_SCROLL_UP || | |
532 event->direction == GDK_SCROLL_DOWN) { | |
533 if (event->direction == GDK_SCROLL_UP) | |
534 web_event.deltaY = pixelsPerTick; | |
535 else | |
536 web_event.deltaY = -pixelsPerTick; | |
537 web_event.deltaY += GetPendingScrollDelta(true, event->state); | |
538 } else { | |
539 if (event->direction == GDK_SCROLL_LEFT) | |
540 web_event.deltaX = pixelsPerTick; | |
541 else | |
542 web_event.deltaX = -pixelsPerTick; | |
543 web_event.deltaX += GetPendingScrollDelta(false, event->state); | |
544 } | |
545 RenderWidgetHostImpl::From( | |
546 host_view->GetRenderWidgetHost())->ForwardWheelEvent(web_event); | |
547 return FALSE; | |
548 } | |
549 | |
550 DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget); | |
551 }; | |
552 | |
553 RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) | |
554 : host_(RenderWidgetHostImpl::From(widget_host)), | |
555 about_to_validate_and_paint_(false), | |
556 is_loading_(false), | |
557 parent_(NULL), | |
558 is_popup_first_mouse_release_(true), | |
559 was_imcontext_focused_before_grab_(false), | |
560 do_x_grab_(false), | |
561 is_fullscreen_(false), | |
562 made_active_(false), | |
563 mouse_is_being_warped_to_unlocked_position_(false), | |
564 destroy_handler_id_(0), | |
565 dragged_at_horizontal_edge_(0), | |
566 dragged_at_vertical_edge_(0), | |
567 compositing_surface_(gfx::kNullPluginWindow), | |
568 last_mouse_down_(NULL) { | |
569 host_->SetView(this); | |
570 } | |
571 | |
572 RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() { | |
573 UnlockMouse(); | |
574 set_last_mouse_down(NULL); | |
575 view_.Destroy(); | |
576 } | |
577 | |
578 bool RenderWidgetHostViewGtk::OnMessageReceived(const IPC::Message& message) { | |
579 bool handled = true; | |
580 IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewGtk, message) | |
581 IPC_MESSAGE_HANDLER(ViewHostMsg_CreatePluginContainer, | |
582 OnCreatePluginContainer) | |
583 IPC_MESSAGE_HANDLER(ViewHostMsg_DestroyPluginContainer, | |
584 OnDestroyPluginContainer) | |
585 IPC_MESSAGE_UNHANDLED(handled = false) | |
586 IPC_END_MESSAGE_MAP() | |
587 return handled; | |
588 } | |
589 | |
590 void RenderWidgetHostViewGtk::InitAsChild( | |
591 gfx::NativeView parent_view) { | |
592 DoSharedInit(); | |
593 gtk_widget_show(view_.get()); | |
594 } | |
595 | |
596 void RenderWidgetHostViewGtk::InitAsPopup( | |
597 RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) { | |
598 // If we aren't a popup, then |window| will be leaked. | |
599 DCHECK(IsPopup()); | |
600 | |
601 DoSharedInit(); | |
602 parent_ = parent_host_view->GetNativeView(); | |
603 GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP)); | |
604 gtk_container_add(GTK_CONTAINER(window), view_.get()); | |
605 DoPopupOrFullscreenInit(window, pos); | |
606 | |
607 // Grab all input for the app. If a click lands outside the bounds of the | |
608 // popup, WebKit will notice and destroy us. The underlying X window needs to | |
609 // be created and mapped by the above code before we can grab the input | |
610 // devices. | |
611 if (NeedsInputGrab()) { | |
612 // If our parent is in a widget hierarchy that ends with a window, add | |
613 // ourselves to the same window group to make sure that our GTK grab | |
614 // covers it. | |
615 GtkWidget* toplevel = gtk_widget_get_toplevel(parent_); | |
616 if (toplevel && | |
617 GTK_WIDGET_TOPLEVEL(toplevel) && | |
618 GTK_IS_WINDOW(toplevel)) { | |
619 gtk_window_group_add_window( | |
620 gtk_window_get_group(GTK_WINDOW(toplevel)), window); | |
621 } | |
622 | |
623 // Install an application-level GTK grab to make sure that we receive all of | |
624 // the app's input. | |
625 gtk_grab_add(view_.get()); | |
626 | |
627 // We need to install an X grab as well. However if the app already has an X | |
628 // grab (as in the case of extension popup), an app grab will suffice. | |
629 do_x_grab_ = !gdk_pointer_is_grabbed(); | |
630 if (do_x_grab_) { | |
631 // Install the grab on behalf our parent window if it and all of its | |
632 // ancestors are mapped; otherwise, just use ourselves (maybe we're being | |
633 // shown on behalf of an inactive tab). | |
634 GdkWindow* grab_window = gtk_widget_get_window(parent_); | |
635 if (!grab_window || !gdk_window_is_viewable(grab_window)) | |
636 grab_window = gtk_widget_get_window(view_.get()); | |
637 | |
638 gdk_pointer_grab( | |
639 grab_window, | |
640 TRUE, // Only events outside of the window are reported with | |
641 // respect to |parent_->window|. | |
642 static_cast<GdkEventMask>(GDK_BUTTON_PRESS_MASK | | |
643 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK), | |
644 NULL, | |
645 NULL, | |
646 GDK_CURRENT_TIME); | |
647 // We grab keyboard events too so things like alt+tab are eaten. | |
648 gdk_keyboard_grab(grab_window, TRUE, GDK_CURRENT_TIME); | |
649 } | |
650 } | |
651 } | |
652 | |
653 void RenderWidgetHostViewGtk::InitAsFullscreen( | |
654 RenderWidgetHostView* reference_host_view) { | |
655 DCHECK(reference_host_view); | |
656 DoSharedInit(); | |
657 | |
658 is_fullscreen_ = true; | |
659 GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); | |
660 gtk_window_set_decorated(window, FALSE); | |
661 destroy_handler_id_ = g_signal_connect(GTK_WIDGET(window), | |
662 "destroy", | |
663 G_CALLBACK(OnDestroyThunk), | |
664 this); | |
665 gtk_container_add(GTK_CONTAINER(window), view_.get()); | |
666 | |
667 // Try to move and resize the window to cover the screen in case the window | |
668 // manager doesn't support _NET_WM_STATE_FULLSCREEN. | |
669 GdkScreen* screen = gtk_window_get_screen(window); | |
670 GdkWindow* ref_gdk_window = gtk_widget_get_window( | |
671 reference_host_view->GetNativeView()); | |
672 | |
673 gfx::Rect bounds; | |
674 if (ref_gdk_window) { | |
675 const int monitor_id = gdk_screen_get_monitor_at_window(screen, | |
676 ref_gdk_window); | |
677 GdkRectangle monitor_rect; | |
678 gdk_screen_get_monitor_geometry(screen, monitor_id, &monitor_rect); | |
679 bounds = gfx::Rect(monitor_rect); | |
680 } else { | |
681 bounds = gfx::Rect( | |
682 0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen)); | |
683 } | |
684 gtk_window_move(window, bounds.x(), bounds.y()); | |
685 gtk_window_resize(window, bounds.width(), bounds.height()); | |
686 gtk_window_fullscreen(window); | |
687 DoPopupOrFullscreenInit(window, bounds); | |
688 } | |
689 | |
690 RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const { | |
691 return host_; | |
692 } | |
693 | |
694 void RenderWidgetHostViewGtk::WasShown() { | |
695 if (!host_ || !host_->is_hidden()) | |
696 return; | |
697 | |
698 if (web_contents_switch_paint_time_.is_null()) | |
699 web_contents_switch_paint_time_ = base::TimeTicks::Now(); | |
700 | |
701 host_->WasShown(); | |
702 } | |
703 | |
704 void RenderWidgetHostViewGtk::WasHidden() { | |
705 if (!host_ || host_->is_hidden()) | |
706 return; | |
707 | |
708 // If we have a renderer, then inform it that we are being hidden so it can | |
709 // reduce its resource utilization. | |
710 host_->WasHidden(); | |
711 | |
712 web_contents_switch_paint_time_ = base::TimeTicks(); | |
713 } | |
714 | |
715 void RenderWidgetHostViewGtk::SetSize(const gfx::Size& size) { | |
716 int width = std::min(size.width(), kMaxWindowWidth); | |
717 int height = std::min(size.height(), kMaxWindowHeight); | |
718 if (IsPopup()) { | |
719 // We're a popup, honor the size request. | |
720 gtk_widget_set_size_request(view_.get(), width, height); | |
721 } | |
722 | |
723 // Update the size of the RWH. | |
724 if (requested_size_.width() != width || | |
725 requested_size_.height() != height) { | |
726 requested_size_ = gfx::Size(width, height); | |
727 host_->SendScreenRects(); | |
728 host_->WasResized(); | |
729 } | |
730 } | |
731 | |
732 void RenderWidgetHostViewGtk::SetBounds(const gfx::Rect& rect) { | |
733 // This is called when webkit has sent us a Move message. | |
734 if (IsPopup()) { | |
735 gtk_window_move(GTK_WINDOW(gtk_widget_get_toplevel(view_.get())), | |
736 rect.x(), rect.y()); | |
737 } | |
738 | |
739 SetSize(rect.size()); | |
740 } | |
741 | |
742 gfx::NativeView RenderWidgetHostViewGtk::GetNativeView() const { | |
743 return view_.get(); | |
744 } | |
745 | |
746 gfx::NativeViewId RenderWidgetHostViewGtk::GetNativeViewId() const { | |
747 return GtkNativeViewManager::GetInstance()->GetIdForWidget(view_.get()); | |
748 } | |
749 | |
750 gfx::NativeViewAccessible RenderWidgetHostViewGtk::GetNativeViewAccessible() { | |
751 NOTIMPLEMENTED(); | |
752 return NULL; | |
753 } | |
754 | |
755 void RenderWidgetHostViewGtk::MovePluginWindows( | |
756 const gfx::Vector2d& scroll_offset, | |
757 const std::vector<WebPluginGeometry>& moves) { | |
758 for (size_t i = 0; i < moves.size(); ++i) { | |
759 plugin_container_manager_.MovePluginContainer(moves[i]); | |
760 } | |
761 } | |
762 | |
763 void RenderWidgetHostViewGtk::Focus() { | |
764 gtk_widget_grab_focus(view_.get()); | |
765 } | |
766 | |
767 void RenderWidgetHostViewGtk::Blur() { | |
768 // TODO(estade): We should be clearing native focus as well, but I know of no | |
769 // way to do that without focusing another widget. | |
770 host_->Blur(); | |
771 } | |
772 | |
773 bool RenderWidgetHostViewGtk::HasFocus() const { | |
774 return gtk_widget_has_focus(view_.get()); | |
775 } | |
776 | |
777 void RenderWidgetHostViewGtk::ActiveWindowChanged(GdkWindow* window) { | |
778 GdkWindow* our_window = gtk_widget_get_parent_window(view_.get()); | |
779 | |
780 if (our_window == window) | |
781 made_active_ = true; | |
782 | |
783 // If the window was previously active, but isn't active anymore, shut it | |
784 // down. | |
785 if (is_fullscreen_ && our_window != window && made_active_) | |
786 host_->Shutdown(); | |
787 } | |
788 | |
789 bool RenderWidgetHostViewGtk::Send(IPC::Message* message) { | |
790 return host_->Send(message); | |
791 } | |
792 | |
793 bool RenderWidgetHostViewGtk::IsSurfaceAvailableForCopy() const { | |
794 return true; | |
795 } | |
796 | |
797 void RenderWidgetHostViewGtk::Show() { | |
798 gtk_widget_show(view_.get()); | |
799 WasShown(); | |
800 } | |
801 | |
802 void RenderWidgetHostViewGtk::Hide() { | |
803 gtk_widget_hide(view_.get()); | |
804 WasHidden(); | |
805 } | |
806 | |
807 bool RenderWidgetHostViewGtk::IsShowing() { | |
808 return gtk_widget_get_visible(view_.get()); | |
809 } | |
810 | |
811 gfx::Rect RenderWidgetHostViewGtk::GetViewBounds() const { | |
812 GdkWindow* gdk_window = gtk_widget_get_window(view_.get()); | |
813 if (!gdk_window) | |
814 return gfx::Rect(requested_size_); | |
815 GdkRectangle window_rect; | |
816 gdk_window_get_origin(gdk_window, &window_rect.x, &window_rect.y); | |
817 return gfx::Rect(window_rect.x, window_rect.y, | |
818 requested_size_.width(), requested_size_.height()); | |
819 } | |
820 | |
821 void RenderWidgetHostViewGtk::UpdateCursor(const WebCursor& cursor) { | |
822 // Optimize the common case, where the cursor hasn't changed. | |
823 // However, we can switch between different pixmaps, so only on the | |
824 // non-pixmap branch. | |
825 if (current_cursor_.GetCursorType() != GDK_CURSOR_IS_PIXMAP && | |
826 current_cursor_.GetCursorType() == cursor.GetCursorType()) { | |
827 return; | |
828 } | |
829 | |
830 current_cursor_ = cursor; | |
831 ShowCurrentCursor(); | |
832 } | |
833 | |
834 void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) { | |
835 is_loading_ = is_loading; | |
836 // Only call ShowCurrentCursor() when it will actually change the cursor. | |
837 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) | |
838 ShowCurrentCursor(); | |
839 } | |
840 | |
841 void RenderWidgetHostViewGtk::TextInputTypeChanged( | |
842 ui::TextInputType type, | |
843 ui::TextInputMode input_mode, | |
844 bool can_compose_inline) { | |
845 im_context_->UpdateInputMethodState(type, can_compose_inline); | |
846 } | |
847 | |
848 void RenderWidgetHostViewGtk::ImeCancelComposition() { | |
849 im_context_->CancelComposition(); | |
850 } | |
851 | |
852 void RenderWidgetHostViewGtk::DidUpdateBackingStore( | |
853 const gfx::Rect& scroll_rect, | |
854 const gfx::Vector2d& scroll_delta, | |
855 const std::vector<gfx::Rect>& copy_rects, | |
856 const std::vector<ui::LatencyInfo>& latency_info) { | |
857 TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::DidUpdateBackingStore"); | |
858 for (size_t i = 0; i < latency_info.size(); i++) | |
859 software_latency_info_.push_back(latency_info[i]); | |
860 | |
861 if (host_->is_hidden()) | |
862 return; | |
863 | |
864 // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX. Can that | |
865 // be done using XCopyArea? Perhaps similar to | |
866 // BackingStore::ScrollBackingStore? | |
867 if (about_to_validate_and_paint_) | |
868 invalid_rect_.Union(scroll_rect); | |
869 else | |
870 Paint(scroll_rect); | |
871 | |
872 for (size_t i = 0; i < copy_rects.size(); ++i) { | |
873 // Avoid double painting. NOTE: This is only relevant given the call to | |
874 // Paint(scroll_rect) above. | |
875 gfx::Rect rect = gfx::SubtractRects(copy_rects[i], scroll_rect); | |
876 if (rect.IsEmpty()) | |
877 continue; | |
878 | |
879 if (about_to_validate_and_paint_) | |
880 invalid_rect_.Union(rect); | |
881 else | |
882 Paint(rect); | |
883 } | |
884 } | |
885 | |
886 void RenderWidgetHostViewGtk::RenderProcessGone(base::TerminationStatus status, | |
887 int error_code) { | |
888 Destroy(); | |
889 plugin_container_manager_.set_host_widget(NULL); | |
890 } | |
891 | |
892 void RenderWidgetHostViewGtk::Destroy() { | |
893 if (compositing_surface_ != gfx::kNullPluginWindow) { | |
894 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); | |
895 manager->ReleasePermanentXID(compositing_surface_); | |
896 } | |
897 | |
898 if (do_x_grab_) { | |
899 // Undo the X grab. | |
900 GdkDisplay* display = gtk_widget_get_display(parent_); | |
901 gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME); | |
902 gdk_display_keyboard_ungrab(display, GDK_CURRENT_TIME); | |
903 } | |
904 | |
905 if (view_.get()) { | |
906 // If this is a popup or fullscreen widget, then we need to destroy the | |
907 // window that we created to hold it. | |
908 if (IsPopup() || is_fullscreen_) { | |
909 GtkWidget* window = gtk_widget_get_parent(view_.get()); | |
910 | |
911 ui::ActiveWindowWatcherX::RemoveObserver(this); | |
912 | |
913 // Disconnect the destroy handler so that we don't try to shutdown twice. | |
914 if (is_fullscreen_) | |
915 g_signal_handler_disconnect(window, destroy_handler_id_); | |
916 | |
917 gtk_widget_destroy(window); | |
918 } | |
919 | |
920 // Remove |view_| from all containers now, so nothing else can hold a | |
921 // reference to |view_|'s widget except possibly a gtk signal handler if | |
922 // this code is currently executing within the context of a gtk signal | |
923 // handler. Note that |view_| is still alive after this call. It will be | |
924 // deallocated in the destructor. | |
925 // See http://crbug.com/11847 for details. | |
926 gtk_widget_destroy(view_.get()); | |
927 } | |
928 | |
929 // The RenderWidgetHost's destruction led here, so don't call it. | |
930 host_ = NULL; | |
931 | |
932 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); | |
933 } | |
934 | |
935 void RenderWidgetHostViewGtk::SetTooltipText( | |
936 const base::string16& tooltip_text) { | |
937 // Maximum number of characters we allow in a tooltip. | |
938 const int kMaxTooltipLength = 8 << 10; | |
939 // Clamp the tooltip length to kMaxTooltipLength so that we don't | |
940 // accidentally DOS the user with a mega tooltip (since GTK doesn't do | |
941 // this itself). | |
942 // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream. | |
943 const base::string16 clamped_tooltip = | |
944 gfx::TruncateString(tooltip_text, kMaxTooltipLength); | |
945 | |
946 if (clamped_tooltip.empty()) { | |
947 gtk_widget_set_has_tooltip(view_.get(), FALSE); | |
948 } else { | |
949 gtk_widget_set_tooltip_text(view_.get(), | |
950 base::UTF16ToUTF8(clamped_tooltip).c_str()); | |
951 } | |
952 } | |
953 | |
954 void RenderWidgetHostViewGtk::SelectionChanged(const base::string16& text, | |
955 size_t offset, | |
956 const gfx::Range& range) { | |
957 RenderWidgetHostViewBase::SelectionChanged(text, offset, range); | |
958 | |
959 if (text.empty() || range.is_empty()) | |
960 return; | |
961 size_t pos = range.GetMin() - offset; | |
962 size_t n = range.length(); | |
963 | |
964 DCHECK(pos + n <= text.length()) << "The text can not fully cover range."; | |
965 if (pos >= text.length()) { | |
966 NOTREACHED() << "The text can not cover range."; | |
967 return; | |
968 } | |
969 | |
970 // Set the CLIPBOARD_TYPE SELECTION to the ui::Clipboard. | |
971 ui::ScopedClipboardWriter clipboard_writer( | |
972 ui::Clipboard::GetForCurrentThread(), | |
973 ui::CLIPBOARD_TYPE_SELECTION); | |
974 clipboard_writer.WriteText(text.substr(pos, n)); | |
975 } | |
976 | |
977 void RenderWidgetHostViewGtk::SelectionBoundsChanged( | |
978 const ViewHostMsg_SelectionBounds_Params& params) { | |
979 im_context_->UpdateCaretBounds( | |
980 gfx::UnionRects(params.anchor_rect, params.focus_rect)); | |
981 } | |
982 | |
983 void RenderWidgetHostViewGtk::ScrollOffsetChanged() { | |
984 } | |
985 | |
986 GdkEventButton* RenderWidgetHostViewGtk::GetLastMouseDown() { | |
987 return last_mouse_down_; | |
988 } | |
989 | |
990 gfx::NativeView RenderWidgetHostViewGtk::BuildInputMethodsGtkMenu() { | |
991 return im_context_->BuildInputMethodsGtkMenu(); | |
992 } | |
993 | |
994 void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) { | |
995 DCHECK(is_fullscreen_); | |
996 host_->Shutdown(); | |
997 } | |
998 | |
999 bool RenderWidgetHostViewGtk::NeedsInputGrab() { | |
1000 return popup_type_ == blink::WebPopupTypeSelect; | |
1001 } | |
1002 | |
1003 bool RenderWidgetHostViewGtk::IsPopup() const { | |
1004 return popup_type_ != blink::WebPopupTypeNone; | |
1005 } | |
1006 | |
1007 void RenderWidgetHostViewGtk::DoSharedInit() { | |
1008 view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this)); | |
1009 im_context_.reset(new GtkIMContextWrapper(this)); | |
1010 plugin_container_manager_.set_host_widget(view_.get()); | |
1011 key_bindings_handler_.reset(new GtkKeyBindingsHandler(view_.get())); | |
1012 } | |
1013 | |
1014 void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow* window, | |
1015 const gfx::Rect& bounds) { | |
1016 requested_size_.SetSize(std::min(bounds.width(), kMaxWindowWidth), | |
1017 std::min(bounds.height(), kMaxWindowHeight)); | |
1018 host_->WasResized(); | |
1019 | |
1020 ui::ActiveWindowWatcherX::AddObserver(this); | |
1021 | |
1022 // Don't set the size when we're going fullscreen. This can confuse the | |
1023 // window manager into thinking we're resizing a fullscreen window and | |
1024 // therefore not fullscreen anymore. | |
1025 if (!is_fullscreen_) { | |
1026 gtk_widget_set_size_request( | |
1027 view_.get(), requested_size_.width(), requested_size_.height()); | |
1028 | |
1029 // Don't allow the window to be resized. This also forces the window to | |
1030 // shrink down to the size of its child contents. | |
1031 gtk_window_set_resizable(window, FALSE); | |
1032 gtk_window_set_default_size(window, -1, -1); | |
1033 gtk_window_move(window, bounds.x(), bounds.y()); | |
1034 } | |
1035 | |
1036 gtk_widget_show_all(GTK_WIDGET(window)); | |
1037 } | |
1038 | |
1039 BackingStore* RenderWidgetHostViewGtk::AllocBackingStore( | |
1040 const gfx::Size& size) { | |
1041 gint depth = gdk_visual_get_depth(gtk_widget_get_visual(view_.get())); | |
1042 return new BackingStoreGtk(host_, size, | |
1043 ui::GetVisualFromGtkWidget(view_.get()), | |
1044 depth); | |
1045 } | |
1046 | |
1047 // NOTE: |output| is initialized with the size of |src_subrect|, and |dst_size| | |
1048 // is ignored on GTK. | |
1049 void RenderWidgetHostViewGtk::CopyFromCompositingSurface( | |
1050 const gfx::Rect& src_subrect, | |
1051 const gfx::Size& /* dst_size */, | |
1052 const base::Callback<void(bool, const SkBitmap&)>& callback, | |
1053 SkBitmap::Config config) { | |
1054 if (config != SkBitmap::kARGB_8888_Config) { | |
1055 NOTIMPLEMENTED(); | |
1056 callback.Run(false, SkBitmap()); | |
1057 } | |
1058 // Grab the snapshot from the renderer as that's the only reliable way to | |
1059 // readback from the GPU for this platform right now. | |
1060 GetRenderWidgetHost()->GetSnapshotFromRenderer(src_subrect, callback); | |
1061 } | |
1062 | |
1063 void RenderWidgetHostViewGtk::CopyFromCompositingSurfaceToVideoFrame( | |
1064 const gfx::Rect& src_subrect, | |
1065 const scoped_refptr<media::VideoFrame>& target, | |
1066 const base::Callback<void(bool)>& callback) { | |
1067 NOTIMPLEMENTED(); | |
1068 callback.Run(false); | |
1069 } | |
1070 | |
1071 bool RenderWidgetHostViewGtk::CanCopyToVideoFrame() const { | |
1072 return false; | |
1073 } | |
1074 | |
1075 void RenderWidgetHostViewGtk::AcceleratedSurfaceInitialized(int host_id, | |
1076 int route_id) { | |
1077 } | |
1078 | |
1079 void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped( | |
1080 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, | |
1081 int gpu_host_id) { | |
1082 AcceleratedSurfaceMsg_BufferPresented_Params ack_params; | |
1083 ack_params.sync_point = 0; | |
1084 RenderWidgetHostImpl::AcknowledgeBufferPresent( | |
1085 params.route_id, gpu_host_id, ack_params); | |
1086 RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info); | |
1087 } | |
1088 | |
1089 void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer( | |
1090 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, | |
1091 int gpu_host_id) { | |
1092 AcceleratedSurfaceMsg_BufferPresented_Params ack_params; | |
1093 ack_params.sync_point = 0; | |
1094 RenderWidgetHostImpl::AcknowledgeBufferPresent( | |
1095 params.route_id, gpu_host_id, ack_params); | |
1096 RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info); | |
1097 } | |
1098 | |
1099 void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() { | |
1100 } | |
1101 | |
1102 void RenderWidgetHostViewGtk::AcceleratedSurfaceRelease() { | |
1103 } | |
1104 | |
1105 bool RenderWidgetHostViewGtk::HasAcceleratedSurface( | |
1106 const gfx::Size& desired_size) { | |
1107 // TODO(jbates) Implement this so this view can use GetBackingStore for both | |
1108 // software and GPU frames. Defaulting to false just makes GetBackingStore | |
1109 // only useable for software frames. | |
1110 return false; | |
1111 } | |
1112 | |
1113 void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) { | |
1114 RenderWidgetHostViewBase::SetBackground(background); | |
1115 Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background)); | |
1116 } | |
1117 | |
1118 void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging( | |
1119 GtkWidget* widget, GdkEventMotion* event) { | |
1120 // If the widget is aligned with an edge of the monitor its on and the user | |
1121 // attempts to drag past that edge we track the number of times it has | |
1122 // occurred, so that we can force the widget to scroll when it otherwise | |
1123 // would be unable to, by modifying the (x,y) position in the drag | |
1124 // event that we forward on to webkit. If we get a move that's no longer a | |
1125 // drag or a drag indicating the user is no longer at that edge we stop | |
1126 // altering the drag events. | |
1127 int new_dragged_at_horizontal_edge = 0; | |
1128 int new_dragged_at_vertical_edge = 0; | |
1129 // Used for checking the edges of the monitor. We cache the values to save | |
1130 // roundtrips to the X server. | |
1131 CR_DEFINE_STATIC_LOCAL(gfx::Size, drag_monitor_size, ()); | |
1132 if (event->state & GDK_BUTTON1_MASK) { | |
1133 if (drag_monitor_size.IsEmpty()) { | |
1134 // We can safely cache the monitor size for the duration of a drag. | |
1135 GdkScreen* screen = gtk_widget_get_screen(widget); | |
1136 int monitor = | |
1137 gdk_screen_get_monitor_at_point(screen, event->x_root, event->y_root); | |
1138 GdkRectangle geometry; | |
1139 gdk_screen_get_monitor_geometry(screen, monitor, &geometry); | |
1140 drag_monitor_size.SetSize(geometry.width, geometry.height); | |
1141 } | |
1142 GtkAllocation allocation; | |
1143 gtk_widget_get_allocation(widget, &allocation); | |
1144 // Check X and Y independently, as the user could be dragging into a corner. | |
1145 if (event->x == 0 && event->x_root == 0) { | |
1146 new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ - 1; | |
1147 } else if (allocation.width - 1 == static_cast<gint>(event->x) && | |
1148 drag_monitor_size.width() - 1 == static_cast<gint>(event->x_root)) { | |
1149 new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ + 1; | |
1150 } | |
1151 | |
1152 if (event->y == 0 && event->y_root == 0) { | |
1153 new_dragged_at_vertical_edge = dragged_at_vertical_edge_ - 1; | |
1154 } else if (allocation.height - 1 == static_cast<gint>(event->y) && | |
1155 drag_monitor_size.height() - 1 == static_cast<gint>(event->y_root)) { | |
1156 new_dragged_at_vertical_edge = dragged_at_vertical_edge_ + 1; | |
1157 } | |
1158 | |
1159 event->x_root += new_dragged_at_horizontal_edge; | |
1160 event->x += new_dragged_at_horizontal_edge; | |
1161 event->y_root += new_dragged_at_vertical_edge; | |
1162 event->y += new_dragged_at_vertical_edge; | |
1163 } else { | |
1164 // Clear whenever we get a non-drag mouse move. | |
1165 drag_monitor_size.SetSize(0, 0); | |
1166 } | |
1167 dragged_at_horizontal_edge_ = new_dragged_at_horizontal_edge; | |
1168 dragged_at_vertical_edge_ = new_dragged_at_vertical_edge; | |
1169 } | |
1170 | |
1171 void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) { | |
1172 TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::Paint"); | |
1173 | |
1174 // If the GPU process is rendering directly into the View, | |
1175 // call the compositor directly. | |
1176 RenderWidgetHostImpl* render_widget_host = | |
1177 RenderWidgetHostImpl::From(GetRenderWidgetHost()); | |
1178 if (render_widget_host->is_accelerated_compositing_active()) { | |
1179 host_->ScheduleComposite(); | |
1180 return; | |
1181 } | |
1182 | |
1183 GdkWindow* window = gtk_widget_get_window(view_.get()); | |
1184 DCHECK(!about_to_validate_and_paint_); | |
1185 | |
1186 invalid_rect_ = damage_rect; | |
1187 about_to_validate_and_paint_ = true; | |
1188 | |
1189 // If the size of our canvas is (0,0), then we don't want to block here. We | |
1190 // are doing one of our first paints and probably have animations going on. | |
1191 bool force_create = !host_->empty(); | |
1192 BackingStoreGtk* backing_store = static_cast<BackingStoreGtk*>( | |
1193 host_->GetBackingStore(force_create)); | |
1194 // Calling GetBackingStore maybe have changed |invalid_rect_|... | |
1195 about_to_validate_and_paint_ = false; | |
1196 | |
1197 gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight); | |
1198 paint_rect.Intersect(invalid_rect_); | |
1199 | |
1200 if (backing_store) { | |
1201 // Only render the widget if it is attached to a window; there's a short | |
1202 // period where this object isn't attached to a window but hasn't been | |
1203 // Destroy()ed yet and it receives paint messages... | |
1204 if (window) { | |
1205 backing_store->XShowRect(gfx::Point(0, 0), | |
1206 paint_rect, ui::GetX11WindowFromGtkWidget(view_.get())); | |
1207 } | |
1208 if (!whiteout_start_time_.is_null()) { | |
1209 base::TimeDelta whiteout_duration = base::TimeTicks::Now() - | |
1210 whiteout_start_time_; | |
1211 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration); | |
1212 | |
1213 // Reset the start time to 0 so that we start recording again the next | |
1214 // time the backing store is NULL... | |
1215 whiteout_start_time_ = base::TimeTicks(); | |
1216 } | |
1217 if (!web_contents_switch_paint_time_.is_null()) { | |
1218 base::TimeDelta web_contents_switch_paint_duration = | |
1219 base::TimeTicks::Now() - web_contents_switch_paint_time_; | |
1220 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", | |
1221 web_contents_switch_paint_duration); | |
1222 // Reset web_contents_switch_paint_time_ to 0 so future tab selections are | |
1223 // recorded. | |
1224 web_contents_switch_paint_time_ = base::TimeTicks(); | |
1225 } | |
1226 | |
1227 for (size_t i = 0; i < software_latency_info_.size(); i++) { | |
1228 software_latency_info_[i].AddLatencyNumber( | |
1229 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0); | |
1230 render_widget_host->FrameSwapped(software_latency_info_[i]); | |
1231 } | |
1232 software_latency_info_.clear(); | |
1233 } else { | |
1234 if (window) | |
1235 gdk_window_clear(window); | |
1236 if (whiteout_start_time_.is_null()) | |
1237 whiteout_start_time_ = base::TimeTicks::Now(); | |
1238 } | |
1239 } | |
1240 | |
1241 void RenderWidgetHostViewGtk::ShowCurrentCursor() { | |
1242 // The widget may not have a window. If that's the case, abort mission. This | |
1243 // is the same issue as that explained above in Paint(). | |
1244 if (!gtk_widget_get_window(view_.get())) | |
1245 return; | |
1246 | |
1247 // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is | |
1248 // that calling gdk_window_set_cursor repeatedly is expensive. We should | |
1249 // avoid it here where possible. | |
1250 GdkCursor* gdk_cursor; | |
1251 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) { | |
1252 // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and | |
1253 // the page is loading. | |
1254 gdk_cursor = is_loading_ ? GetMozSpinningCursor() : NULL; | |
1255 } else { | |
1256 gdk_cursor = current_cursor_.GetNativeCursor(); | |
1257 } | |
1258 gdk_window_set_cursor(gtk_widget_get_window(view_.get()), gdk_cursor); | |
1259 } | |
1260 | |
1261 void RenderWidgetHostViewGtk::SetHasHorizontalScrollbar( | |
1262 bool has_horizontal_scrollbar) { | |
1263 } | |
1264 | |
1265 void RenderWidgetHostViewGtk::SetScrollOffsetPinning( | |
1266 bool is_pinned_to_left, bool is_pinned_to_right) { | |
1267 } | |
1268 | |
1269 | |
1270 void RenderWidgetHostViewGtk::OnAcceleratedCompositingStateChange() { | |
1271 bool activated = host_->is_accelerated_compositing_active(); | |
1272 GtkPreserveWindow* widget = reinterpret_cast<GtkPreserveWindow*>(view_.get()); | |
1273 | |
1274 gtk_preserve_window_delegate_resize(widget, activated); | |
1275 } | |
1276 | |
1277 void RenderWidgetHostViewGtk::GetScreenInfo(WebScreenInfo* results) { | |
1278 GdkWindow* gdk_window = gtk_widget_get_window(view_.get()); | |
1279 if (!gdk_window) { | |
1280 GdkDisplay* display = gdk_display_get_default(); | |
1281 gdk_window = gdk_display_get_default_group(display); | |
1282 } | |
1283 if (!gdk_window) | |
1284 return; | |
1285 GetScreenInfoFromNativeWindow(gdk_window, results); | |
1286 } | |
1287 | |
1288 gfx::Rect RenderWidgetHostViewGtk::GetBoundsInRootWindow() { | |
1289 GtkWidget* toplevel = gtk_widget_get_toplevel(view_.get()); | |
1290 if (!toplevel) | |
1291 return GetViewBounds(); | |
1292 | |
1293 GdkRectangle frame_extents; | |
1294 GdkWindow* gdk_window = gtk_widget_get_window(toplevel); | |
1295 if (!gdk_window) | |
1296 return GetViewBounds(); | |
1297 | |
1298 gdk_window_get_frame_extents(gdk_window, &frame_extents); | |
1299 return gfx::Rect(frame_extents.x, frame_extents.y, | |
1300 frame_extents.width, frame_extents.height); | |
1301 } | |
1302 | |
1303 gfx::GLSurfaceHandle RenderWidgetHostViewGtk::GetCompositingSurface() { | |
1304 if (compositing_surface_ == gfx::kNullPluginWindow) { | |
1305 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); | |
1306 gfx::NativeViewId view_id = GetNativeViewId(); | |
1307 | |
1308 if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) { | |
1309 DLOG(ERROR) << "Can't find XID for view id " << view_id; | |
1310 } | |
1311 } | |
1312 return gfx::GLSurfaceHandle(compositing_surface_, gfx::NATIVE_TRANSPORT); | |
1313 } | |
1314 | |
1315 void RenderWidgetHostViewGtk::ResizeCompositingSurface(const gfx::Size& size) { | |
1316 GtkWidget* widget = view_.get(); | |
1317 GdkWindow* window = gtk_widget_get_window(widget); | |
1318 if (window) { | |
1319 Display* display = GDK_WINDOW_XDISPLAY(window); | |
1320 gdk_window_resize(window, size.width(), size.height()); | |
1321 XSync(display, False); | |
1322 } | |
1323 } | |
1324 | |
1325 bool RenderWidgetHostViewGtk::LockMouse() { | |
1326 if (mouse_locked_) | |
1327 return true; | |
1328 | |
1329 mouse_locked_ = true; | |
1330 | |
1331 // Release any current grab. | |
1332 GtkWidget* current_grab_window = gtk_grab_get_current(); | |
1333 if (current_grab_window) { | |
1334 gtk_grab_remove(current_grab_window); | |
1335 LOG(WARNING) << "Locking Mouse with gdk_pointer_grab, " | |
1336 << "but had to steal grab from another window"; | |
1337 } | |
1338 | |
1339 GtkWidget* widget = view_.get(); | |
1340 GdkWindow* window = gtk_widget_get_window(widget); | |
1341 GdkCursor* cursor = gdk_cursor_new(GDK_BLANK_CURSOR); | |
1342 | |
1343 GdkGrabStatus grab_status = | |
1344 gdk_pointer_grab(window, | |
1345 FALSE, // owner_events | |
1346 static_cast<GdkEventMask>( | |
1347 GDK_POINTER_MOTION_MASK | | |
1348 GDK_BUTTON_PRESS_MASK | | |
1349 GDK_BUTTON_RELEASE_MASK), | |
1350 window, // confine_to | |
1351 cursor, | |
1352 GDK_CURRENT_TIME); | |
1353 | |
1354 if (grab_status != GDK_GRAB_SUCCESS) { | |
1355 LOG(WARNING) << "Failed to grab pointer for LockMouse. " | |
1356 << "gdk_pointer_grab returned: " << grab_status; | |
1357 mouse_locked_ = false; | |
1358 return false; | |
1359 } | |
1360 | |
1361 // Clear the tooltip window. | |
1362 SetTooltipText(base::string16()); | |
1363 | |
1364 // Ensure that the widget center location will be relevant for this mouse | |
1365 // lock session. It is updated whenever the window geometry moves | |
1366 // but may be out of date due to switching tabs. | |
1367 MarkCachedWidgetCenterStale(); | |
1368 | |
1369 // Ensure that if we were previously warping the cursor to a specific point | |
1370 // that we no longer track doing so when entering lock. It should be cleared | |
1371 // by the cursor moving to the warp point, and this shouldn't be necessary. | |
1372 // But, this is a small effort to ensure robustness in the event a warp isn't | |
1373 // completed. | |
1374 mouse_is_being_warped_to_unlocked_position_ = false; | |
1375 | |
1376 return true; | |
1377 } | |
1378 | |
1379 void RenderWidgetHostViewGtk::UnlockMouse() { | |
1380 if (!mouse_locked_) | |
1381 return; | |
1382 | |
1383 mouse_locked_ = false; | |
1384 | |
1385 GtkWidget* widget = view_.get(); | |
1386 GdkDisplay* display = gtk_widget_get_display(widget); | |
1387 GdkScreen* screen = gtk_widget_get_screen(widget); | |
1388 gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME); | |
1389 gdk_display_warp_pointer(display, screen, | |
1390 unlocked_global_mouse_position_.x(), | |
1391 unlocked_global_mouse_position_.y()); | |
1392 mouse_is_being_warped_to_unlocked_position_ = true; | |
1393 | |
1394 if (host_) | |
1395 host_->LostMouseLock(); | |
1396 } | |
1397 | |
1398 void RenderWidgetHostViewGtk::ForwardKeyboardEvent( | |
1399 const NativeWebKeyboardEvent& event) { | |
1400 if (!host_) | |
1401 return; | |
1402 | |
1403 EditCommands edit_commands; | |
1404 if (!event.skip_in_browser && | |
1405 key_bindings_handler_->Match(event, &edit_commands)) { | |
1406 Send(new InputMsg_SetEditCommandsForNextKeyEvent( | |
1407 host_->GetRoutingID(), edit_commands)); | |
1408 NativeWebKeyboardEvent copy_event(event); | |
1409 copy_event.match_edit_command = true; | |
1410 host_->ForwardKeyboardEvent(copy_event); | |
1411 return; | |
1412 } | |
1413 | |
1414 host_->ForwardKeyboardEvent(event); | |
1415 } | |
1416 | |
1417 bool RenderWidgetHostViewGtk::RetrieveSurrounding(std::string* text, | |
1418 size_t* cursor_index) { | |
1419 if (!selection_range_.IsValid()) | |
1420 return false; | |
1421 | |
1422 size_t offset = selection_range_.GetMin() - selection_text_offset_; | |
1423 DCHECK(offset <= selection_text_.length()); | |
1424 | |
1425 if (offset == selection_text_.length()) { | |
1426 *text = base::UTF16ToUTF8(selection_text_); | |
1427 *cursor_index = text->length(); | |
1428 return true; | |
1429 } | |
1430 | |
1431 *text = base::UTF16ToUTF8AndAdjustOffset( | |
1432 base::StringPiece16(selection_text_), &offset); | |
1433 if (offset == base::string16::npos) { | |
1434 NOTREACHED() << "Invalid offset in UTF16 string."; | |
1435 return false; | |
1436 } | |
1437 *cursor_index = offset; | |
1438 return true; | |
1439 } | |
1440 | |
1441 void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton* event) { | |
1442 GdkEventButton* temp = NULL; | |
1443 if (event) { | |
1444 temp = reinterpret_cast<GdkEventButton*>( | |
1445 gdk_event_copy(reinterpret_cast<GdkEvent*>(event))); | |
1446 } | |
1447 | |
1448 if (last_mouse_down_) | |
1449 gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_)); | |
1450 | |
1451 last_mouse_down_ = temp; | |
1452 } | |
1453 | |
1454 void RenderWidgetHostViewGtk::MarkCachedWidgetCenterStale() { | |
1455 widget_center_valid_ = false; | |
1456 mouse_has_been_warped_to_new_center_ = false; | |
1457 } | |
1458 | |
1459 gfx::Point RenderWidgetHostViewGtk::GetWidgetCenter() { | |
1460 if (widget_center_valid_) | |
1461 return widget_center_; | |
1462 | |
1463 GdkWindow* window = gtk_widget_get_window(view_.get()); | |
1464 gint window_x = 0; | |
1465 gint window_y = 0; | |
1466 gdk_window_get_origin(window, &window_x, &window_y); | |
1467 gint window_w = gdk_window_get_width(window); | |
1468 gint window_h = gdk_window_get_height(window); | |
1469 widget_center_.SetPoint(window_x + window_w / 2, | |
1470 window_y + window_h / 2); | |
1471 widget_center_valid_ = true; | |
1472 return widget_center_; | |
1473 } | |
1474 | |
1475 void RenderWidgetHostViewGtk::ModifyEventMovementAndCoords( | |
1476 blink::WebMouseEvent* event) { | |
1477 // Movement is computed by taking the difference of the new cursor position | |
1478 // and the previous. Under mouse lock the cursor will be warped back to the | |
1479 // center so that we are not limited by clipping boundaries. | |
1480 // We do not measure movement as the delta from cursor to center because | |
1481 // we may receive more mouse movement events before our warp has taken | |
1482 // effect. | |
1483 event->movementX = event->globalX - global_mouse_position_.x(); | |
1484 event->movementY = event->globalY - global_mouse_position_.y(); | |
1485 | |
1486 // While the cursor is being warped back to the unlocked position, suppress | |
1487 // the movement member data. | |
1488 if (mouse_is_being_warped_to_unlocked_position_) { | |
1489 event->movementX = 0; | |
1490 event->movementY = 0; | |
1491 if (MovedToPoint(*event, unlocked_global_mouse_position_)) | |
1492 mouse_is_being_warped_to_unlocked_position_ = false; | |
1493 } | |
1494 | |
1495 global_mouse_position_.SetPoint(event->globalX, event->globalY); | |
1496 | |
1497 // Under mouse lock, coordinates of mouse are locked to what they were when | |
1498 // mouse lock was entered. | |
1499 if (mouse_locked_) { | |
1500 event->x = unlocked_mouse_position_.x(); | |
1501 event->y = unlocked_mouse_position_.y(); | |
1502 event->windowX = unlocked_mouse_position_.x(); | |
1503 event->windowY = unlocked_mouse_position_.y(); | |
1504 event->globalX = unlocked_global_mouse_position_.x(); | |
1505 event->globalY = unlocked_global_mouse_position_.y(); | |
1506 } else if (!mouse_is_being_warped_to_unlocked_position_) { | |
1507 unlocked_mouse_position_.SetPoint(event->windowX, event->windowY); | |
1508 unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY); | |
1509 } | |
1510 } | |
1511 | |
1512 //////////////////////////////////////////////////////////////////////////////// | |
1513 // RenderWidgetHostView, public: | |
1514 | |
1515 // static | |
1516 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( | |
1517 RenderWidgetHost* widget) { | |
1518 return new RenderWidgetHostViewGtk(widget); | |
1519 } | |
1520 | |
1521 // static | |
1522 void RenderWidgetHostViewPort::GetDefaultScreenInfo(WebScreenInfo* results) { | |
1523 GdkWindow* gdk_window = | |
1524 gdk_display_get_default_group(gdk_display_get_default()); | |
1525 GetScreenInfoFromNativeWindow(gdk_window, results); | |
1526 } | |
1527 | |
1528 void RenderWidgetHostViewGtk::SetAccessibilityFocus(int acc_obj_id) { | |
1529 if (!host_) | |
1530 return; | |
1531 | |
1532 host_->AccessibilitySetFocus(acc_obj_id); | |
1533 } | |
1534 | |
1535 void RenderWidgetHostViewGtk::AccessibilityDoDefaultAction(int acc_obj_id) { | |
1536 if (!host_) | |
1537 return; | |
1538 | |
1539 host_->AccessibilityDoDefaultAction(acc_obj_id); | |
1540 } | |
1541 | |
1542 void RenderWidgetHostViewGtk::AccessibilityScrollToMakeVisible( | |
1543 int acc_obj_id, gfx::Rect subfocus) { | |
1544 if (!host_) | |
1545 return; | |
1546 | |
1547 host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus); | |
1548 } | |
1549 | |
1550 void RenderWidgetHostViewGtk::AccessibilityScrollToPoint( | |
1551 int acc_obj_id, gfx::Point point) { | |
1552 if (!host_) | |
1553 return; | |
1554 | |
1555 host_->AccessibilityScrollToPoint(acc_obj_id, point); | |
1556 } | |
1557 | |
1558 void RenderWidgetHostViewGtk::AccessibilitySetTextSelection( | |
1559 int acc_obj_id, int start_offset, int end_offset) { | |
1560 if (!host_) | |
1561 return; | |
1562 | |
1563 host_->AccessibilitySetTextSelection(acc_obj_id, start_offset, end_offset); | |
1564 } | |
1565 | |
1566 gfx::Point RenderWidgetHostViewGtk::GetLastTouchEventLocation() const { | |
1567 // Not needed on Linux. | |
1568 return gfx::Point(); | |
1569 } | |
1570 | |
1571 void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() { | |
1572 if (host_) { | |
1573 host_->FatalAccessibilityTreeError(); | |
1574 SetBrowserAccessibilityManager(NULL); | |
1575 } else { | |
1576 CHECK(FALSE); | |
1577 } | |
1578 } | |
1579 | |
1580 void RenderWidgetHostViewGtk::CreateBrowserAccessibilityManagerIfNeeded() { | |
1581 if (!GetBrowserAccessibilityManager()) { | |
1582 GtkWidget* parent = gtk_widget_get_parent(view_.get()); | |
1583 SetBrowserAccessibilityManager( | |
1584 new BrowserAccessibilityManagerGtk( | |
1585 parent, | |
1586 BrowserAccessibilityManagerGtk::GetEmptyDocument(), | |
1587 this)); | |
1588 } | |
1589 } | |
1590 | |
1591 AtkObject* RenderWidgetHostViewGtk::GetAccessible() { | |
1592 if (!GetBrowserAccessibilityManager()) { | |
1593 GtkWidget* parent = gtk_widget_get_parent(view_.get()); | |
1594 SetBrowserAccessibilityManager( | |
1595 new BrowserAccessibilityManagerGtk( | |
1596 parent, | |
1597 BrowserAccessibilityManagerGtk::GetEmptyDocument(), | |
1598 this)); | |
1599 } | |
1600 BrowserAccessibilityGtk* root = | |
1601 GetBrowserAccessibilityManager()->GetRoot()->ToBrowserAccessibilityGtk(); | |
1602 | |
1603 atk_object_set_role(root->GetAtkObject(), ATK_ROLE_HTML_CONTAINER); | |
1604 return root->GetAtkObject(); | |
1605 } | |
1606 | |
1607 void RenderWidgetHostViewGtk::OnCreatePluginContainer( | |
1608 gfx::PluginWindowHandle id) { | |
1609 plugin_container_manager_.CreatePluginContainer(id); | |
1610 } | |
1611 | |
1612 void RenderWidgetHostViewGtk::OnDestroyPluginContainer( | |
1613 gfx::PluginWindowHandle id) { | |
1614 plugin_container_manager_.DestroyPluginContainer(id); | |
1615 } | |
1616 | |
1617 SkBitmap::Config RenderWidgetHostViewGtk::PreferredReadbackFormat() { | |
1618 return SkBitmap::kARGB_8888_Config; | |
1619 } | |
1620 | |
1621 } // namespace content | |
OLD | NEW |