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.
5 #include "content/browser/web_contents/web_contents_view_gtk.h"
8 #include <gdk/gdkkeysyms.h>
13 #include "base/string_util.h"
14 #include "base/utf_string_conversions.h"
15 #include "build/build_config.h"
16 #include "content/browser/renderer_host/render_view_host_factory.h"
17 #include "content/browser/renderer_host/render_view_host_impl.h"
18 #include "content/browser/renderer_host/render_widget_host_view_gtk.h"
19 #include "content/browser/web_contents/interstitial_page_impl.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/browser/web_contents/web_drag_dest_gtk.h"
22 #include "content/browser/web_contents/web_drag_source_gtk.h"
23 #include "content/public/browser/web_contents_delegate.h"
24 #include "content/public/browser/web_contents_view_delegate.h"
25 #include "ui/base/gtk/gtk_expanded_container.h"
26 #include "ui/gfx/image/image_skia.h"
27 #include "ui/gfx/point.h"
28 #include "ui/gfx/rect.h"
29 #include "ui/gfx/size.h"
30 #include "webkit/glue/webdropdata.h"
32 using WebKit::WebDragOperation
;
33 using WebKit::WebDragOperationsMask
;
38 // Called when the mouse leaves the widget. We notify our delegate.
39 gboolean
OnLeaveNotify(GtkWidget
* widget
, GdkEventCrossing
* event
,
40 WebContentsImpl
* web_contents
) {
41 if (web_contents
->GetDelegate())
42 web_contents
->GetDelegate()->ContentsMouseEvent(
43 web_contents
, gfx::Point(event
->x_root
, event
->y_root
), false);
47 // Called when the mouse moves within the widget. We notify our delegate.
48 gboolean
OnMouseMove(GtkWidget
* widget
, GdkEventMotion
* event
,
49 WebContentsImpl
* web_contents
) {
50 if (web_contents
->GetDelegate())
51 web_contents
->GetDelegate()->ContentsMouseEvent(
52 web_contents
, gfx::Point(event
->x_root
, event
->y_root
), true);
56 // See tab_contents_view_views.cc for discussion of mouse scroll zooming.
57 gboolean
OnMouseScroll(GtkWidget
* widget
, GdkEventScroll
* event
,
58 WebContentsImpl
* web_contents
) {
59 if ((event
->state
& gtk_accelerator_get_default_mod_mask()) !=
64 WebContentsDelegate
* delegate
= web_contents
->GetDelegate();
68 if (!(event
->direction
== GDK_SCROLL_DOWN
||
69 event
->direction
== GDK_SCROLL_UP
)) {
73 delegate
->ContentsZoomChange(event
->direction
== GDK_SCROLL_UP
);
79 WebContentsView
* CreateWebContentsView(
80 WebContentsImpl
* web_contents
,
81 WebContentsViewDelegate
* delegate
,
82 RenderViewHostDelegateView
** render_view_host_delegate_view
) {
83 WebContentsViewGtk
* rv
= new WebContentsViewGtk(web_contents
, delegate
);
84 *render_view_host_delegate_view
= rv
;
88 WebContentsViewGtk::WebContentsViewGtk(
89 WebContentsImpl
* web_contents
,
90 WebContentsViewDelegate
* delegate
)
91 : web_contents_(web_contents
),
92 expanded_(gtk_expanded_container_new()),
94 gtk_widget_set_name(expanded_
.get(), "chrome-web-contents-view");
95 g_signal_connect(expanded_
.get(), "size-allocate",
96 G_CALLBACK(OnSizeAllocateThunk
), this);
97 g_signal_connect(expanded_
.get(), "child-size-request",
98 G_CALLBACK(OnChildSizeRequestThunk
), this);
100 gtk_widget_show(expanded_
.get());
101 drag_source_
.reset(new WebDragSourceGtk(web_contents
));
104 delegate_
->Initialize(expanded_
.get(), &focus_store_
);
107 WebContentsViewGtk::~WebContentsViewGtk() {
111 void WebContentsViewGtk::CreateView(
112 const gfx::Size
& initial_size
, gfx::NativeView context
) {
113 requested_size_
= initial_size
;
116 RenderWidgetHostView
* WebContentsViewGtk::CreateViewForWidget(
117 RenderWidgetHost
* render_widget_host
) {
118 if (render_widget_host
->GetView()) {
119 // During testing, the view will already be set up in most cases to the
120 // test view, so we don't want to clobber it with a real one. To verify that
121 // this actually is happening (and somebody isn't accidentally creating the
122 // view twice), we check for the RVH Factory, which will be set when we're
123 // making special ones (which go along with the special views).
124 DCHECK(RenderViewHostFactory::has_factory());
125 return render_widget_host
->GetView();
128 RenderWidgetHostView
* view
=
129 RenderWidgetHostView::CreateViewForWidget(render_widget_host
);
130 view
->InitAsChild(NULL
);
131 gfx::NativeView content_view
= view
->GetNativeView();
132 g_signal_connect(content_view
, "focus", G_CALLBACK(OnFocusThunk
), this);
133 g_signal_connect(content_view
, "leave-notify-event",
134 G_CALLBACK(OnLeaveNotify
), web_contents_
);
135 g_signal_connect(content_view
, "motion-notify-event",
136 G_CALLBACK(OnMouseMove
), web_contents_
);
137 g_signal_connect(content_view
, "scroll-event",
138 G_CALLBACK(OnMouseScroll
), web_contents_
);
139 gtk_widget_add_events(content_view
, GDK_LEAVE_NOTIFY_MASK
|
140 GDK_POINTER_MOTION_MASK
);
141 InsertIntoContentArea(content_view
);
143 // Renderer target DnD.
144 drag_dest_
.reset(new WebDragDestGtk(web_contents_
, content_view
));
147 drag_dest_
->set_delegate(delegate_
->GetDragDestDelegate());
152 RenderWidgetHostView
* WebContentsViewGtk::CreateViewForPopupWidget(
153 RenderWidgetHost
* render_widget_host
) {
154 return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host
);
157 gfx::NativeView
WebContentsViewGtk::GetNativeView() const {
159 return delegate_
->GetNativeView();
161 return expanded_
.get();
164 gfx::NativeView
WebContentsViewGtk::GetContentNativeView() const {
165 RenderWidgetHostView
* rwhv
= web_contents_
->GetRenderWidgetHostView();
168 return rwhv
->GetNativeView();
171 gfx::NativeWindow
WebContentsViewGtk::GetTopLevelNativeWindow() const {
172 GtkWidget
* window
= gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW
);
173 return window
? GTK_WINDOW(window
) : NULL
;
176 void WebContentsViewGtk::GetContainerBounds(gfx::Rect
* out
) const {
177 // This is used for positioning the download shelf arrow animation,
178 // as well as sizing some other widgets in Windows. In GTK the size is
179 // managed for us, so it appears to be only used for the download shelf
183 GdkWindow
* expanded_window
= gtk_widget_get_window(expanded_
.get());
185 gdk_window_get_origin(expanded_window
, &x
, &y
);
187 GtkAllocation allocation
;
188 gtk_widget_get_allocation(expanded_
.get(), &allocation
);
189 out
->SetRect(x
+ allocation
.x
, y
+ allocation
.y
,
190 requested_size_
.width(), requested_size_
.height());
193 void WebContentsViewGtk::SetPageTitle(const string16
& title
) {
194 // Set the window name to include the page title so it's easier to spot
195 // when debugging (e.g. via xwininfo -tree).
196 gfx::NativeView content_view
= GetContentNativeView();
198 GdkWindow
* content_window
= gtk_widget_get_window(content_view
);
199 if (content_window
) {
200 gdk_window_set_title(content_window
, UTF16ToUTF8(title
).c_str());
205 void WebContentsViewGtk::OnTabCrashed(base::TerminationStatus status
,
209 void WebContentsViewGtk::SizeContents(const gfx::Size
& size
) {
210 // We don't need to manually set the size of of widgets in GTK+, but we do
211 // need to pass the sizing information on to the RWHV which will pass the
212 // sizing information on to the renderer.
213 requested_size_
= size
;
214 RenderWidgetHostView
* rwhv
= web_contents_
->GetRenderWidgetHostView();
219 void WebContentsViewGtk::RenderViewCreated(RenderViewHost
* host
) {
222 void WebContentsViewGtk::Focus() {
223 if (web_contents_
->ShowingInterstitialPage()) {
224 web_contents_
->GetInterstitialPage()->Focus();
225 } else if (delegate_
.get()) {
230 void WebContentsViewGtk::SetInitialFocus() {
231 if (web_contents_
->FocusLocationBarByDefault())
232 web_contents_
->SetFocusToLocationBar(false);
237 void WebContentsViewGtk::StoreFocus() {
238 focus_store_
.Store(GetNativeView());
241 void WebContentsViewGtk::RestoreFocus() {
242 if (focus_store_
.widget())
243 gtk_widget_grab_focus(focus_store_
.widget());
248 WebDropData
* WebContentsViewGtk::GetDropData() const {
249 return drag_dest_
->current_drop_data();
252 bool WebContentsViewGtk::IsEventTracking() const {
256 void WebContentsViewGtk::CloseTabAfterEventTracking() {
259 gfx::Rect
WebContentsViewGtk::GetViewBounds() const {
261 GdkWindow
* window
= gtk_widget_get_window(GetNativeView());
263 rect
.SetRect(0, 0, requested_size_
.width(), requested_size_
.height());
266 int x
= 0, y
= 0, w
, h
;
267 gdk_window_get_geometry(window
, &x
, &y
, &w
, &h
, NULL
);
268 rect
.SetRect(x
, y
, w
, h
);
272 WebContents
* WebContentsViewGtk::web_contents() {
273 return web_contents_
;
276 void WebContentsViewGtk::UpdateDragCursor(WebDragOperation operation
) {
277 drag_dest_
->UpdateDragStatus(operation
);
280 void WebContentsViewGtk::GotFocus() {
281 // This is only used in the views FocusManager stuff but it bleeds through
282 // all subclasses. http://crbug.com/21875
285 // This is called when the renderer asks us to take focus back (i.e., it has
286 // iterated past the last focusable element on the page).
287 void WebContentsViewGtk::TakeFocus(bool reverse
) {
288 if (!web_contents_
->GetDelegate())
290 if (!web_contents_
->GetDelegate()->TakeFocus(web_contents_
, reverse
) &&
291 GetTopLevelNativeWindow()) {
292 gtk_widget_child_focus(GTK_WIDGET(GetTopLevelNativeWindow()),
293 reverse
? GTK_DIR_TAB_BACKWARD
: GTK_DIR_TAB_FORWARD
);
297 void WebContentsViewGtk::InsertIntoContentArea(GtkWidget
* widget
) {
298 gtk_container_add(GTK_CONTAINER(expanded_
.get()), widget
);
301 // Called when the content view gtk widget is tabbed to, or after the call to
302 // gtk_widget_child_focus() in TakeFocus(). We return true
303 // and grab focus if we don't have it. The call to
304 // FocusThroughTabTraversal(bool) forwards the "move focus forward" effect to
306 gboolean
WebContentsViewGtk::OnFocus(GtkWidget
* widget
,
307 GtkDirectionType focus
) {
308 // Give our view wrapper first chance at this event.
309 if (delegate_
.get()) {
310 gboolean return_value
= FALSE
;
311 if (delegate_
->OnNativeViewFocusEvent(widget
, focus
, &return_value
))
315 // If we already have focus, let the next widget have a shot at it. We will
316 // reach this situation after the call to gtk_widget_child_focus() in
318 if (gtk_widget_is_focus(widget
))
321 gtk_widget_grab_focus(widget
);
322 bool reverse
= focus
== GTK_DIR_TAB_BACKWARD
;
323 web_contents_
->FocusThroughTabTraversal(reverse
);
327 void WebContentsViewGtk::ShowContextMenu(
328 const ContextMenuParams
& params
,
329 ContextMenuSourceType type
) {
331 delegate_
->ShowContextMenu(params
, type
);
333 DLOG(ERROR
) << "Cannot show context menus without a delegate.";
336 void WebContentsViewGtk::ShowPopupMenu(const gfx::Rect
& bounds
,
338 double item_font_size
,
340 const std::vector
<WebMenuItem
>& items
,
342 bool allow_multiple_selection
) {
343 // External popup menus are only used on Mac and Android.
347 // Render view DnD -------------------------------------------------------------
349 void WebContentsViewGtk::StartDragging(const WebDropData
& drop_data
,
350 WebDragOperationsMask ops
,
351 const gfx::ImageSkia
& image
,
352 const gfx::Vector2d
& image_offset
,
353 const DragEventSourceInfo
& event_info
) {
354 DCHECK(GetContentNativeView());
356 RenderWidgetHostViewGtk
* view_gtk
= static_cast<RenderWidgetHostViewGtk
*>(
357 web_contents_
->GetRenderWidgetHostView());
358 if (!view_gtk
|| !view_gtk
->GetLastMouseDown())
361 drag_source_
->StartDragging(drop_data
, ops
, view_gtk
->GetLastMouseDown(),
362 *image
.bitmap(), image_offset
);
365 // -----------------------------------------------------------------------------
367 void WebContentsViewGtk::OnChildSizeRequest(GtkWidget
* widget
,
369 GtkRequisition
* requisition
) {
370 if (web_contents_
->GetDelegate()) {
371 requisition
->height
+=
372 web_contents_
->GetDelegate()->GetExtraRenderViewHeight();
376 void WebContentsViewGtk::OnSizeAllocate(GtkWidget
* widget
,
377 GtkAllocation
* allocation
) {
378 int width
= allocation
->width
;
379 int height
= allocation
->height
;
380 // |delegate()| can be NULL here during browser teardown.
381 if (web_contents_
->GetDelegate())
382 height
+= web_contents_
->GetDelegate()->GetExtraRenderViewHeight();
383 gfx::Size
size(width
, height
);
384 requested_size_
= size
;
386 // We manually tell our RWHV to resize the renderer content. This avoids
387 // spurious resizes from GTK+.
388 RenderWidgetHostView
* rwhv
= web_contents_
->GetRenderWidgetHostView();
391 if (web_contents_
->GetInterstitialPage())
392 web_contents_
->GetInterstitialPage()->SetSize(size
);
395 } // namespace content