Broke ContentSettingBubbleModelTest.Plugins on Android.
[chromium-blink-merge.git] / content / browser / web_contents / web_contents_view_gtk.cc
blob8f64ef04175b463e027559e10f41676c08bc9757
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"
7 #include <gdk/gdk.h>
8 #include <gdk/gdkkeysyms.h>
9 #include <gtk/gtk.h>
11 #include <algorithm>
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;
35 namespace content {
36 namespace {
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);
44 return 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);
53 return FALSE;
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()) !=
60 GDK_CONTROL_MASK) {
61 return FALSE;
64 WebContentsDelegate* delegate = web_contents->GetDelegate();
65 if (!delegate)
66 return FALSE;
68 if (!(event->direction == GDK_SCROLL_DOWN ||
69 event->direction == GDK_SCROLL_UP)) {
70 return FALSE;
73 delegate->ContentsZoomChange(event->direction == GDK_SCROLL_UP);
74 return TRUE;
77 } // namespace
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;
85 return rv;
88 WebContentsViewGtk::WebContentsViewGtk(
89 WebContentsImpl* web_contents,
90 WebContentsViewDelegate* delegate)
91 : web_contents_(web_contents),
92 expanded_(gtk_expanded_container_new()),
93 delegate_(delegate) {
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));
103 if (delegate_.get())
104 delegate_->Initialize(expanded_.get(), &focus_store_);
107 WebContentsViewGtk::~WebContentsViewGtk() {
108 expanded_.Destroy();
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));
146 if (delegate_.get())
147 drag_dest_->set_delegate(delegate_->GetDragDestDelegate());
149 return view;
152 RenderWidgetHostView* WebContentsViewGtk::CreateViewForPopupWidget(
153 RenderWidgetHost* render_widget_host) {
154 return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
157 gfx::NativeView WebContentsViewGtk::GetNativeView() const {
158 if (delegate_.get())
159 return delegate_->GetNativeView();
161 return expanded_.get();
164 gfx::NativeView WebContentsViewGtk::GetContentNativeView() const {
165 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
166 if (!rwhv)
167 return NULL;
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
180 // animation.
181 int x = 0;
182 int y = 0;
183 GdkWindow* expanded_window = gtk_widget_get_window(expanded_.get());
184 if (expanded_window)
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();
197 if (content_view) {
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,
206 int error_code) {
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();
215 if (rwhv)
216 rwhv->SetSize(size);
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()) {
226 delegate_->Focus();
230 void WebContentsViewGtk::SetInitialFocus() {
231 if (web_contents_->FocusLocationBarByDefault())
232 web_contents_->SetFocusToLocationBar(false);
233 else
234 Focus();
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());
244 else
245 SetInitialFocus();
248 WebDropData* WebContentsViewGtk::GetDropData() const {
249 return drag_dest_->current_drop_data();
252 bool WebContentsViewGtk::IsEventTracking() const {
253 return false;
256 void WebContentsViewGtk::CloseTabAfterEventTracking() {
259 gfx::Rect WebContentsViewGtk::GetViewBounds() const {
260 gfx::Rect rect;
261 GdkWindow* window = gtk_widget_get_window(GetNativeView());
262 if (!window) {
263 rect.SetRect(0, 0, requested_size_.width(), requested_size_.height());
264 return rect;
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);
269 return rect;
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())
289 return;
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
305 // webkit.
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))
312 return 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
317 // TakeFocus().
318 if (gtk_widget_is_focus(widget))
319 return FALSE;
321 gtk_widget_grab_focus(widget);
322 bool reverse = focus == GTK_DIR_TAB_BACKWARD;
323 web_contents_->FocusThroughTabTraversal(reverse);
324 return TRUE;
327 void WebContentsViewGtk::ShowContextMenu(
328 const ContextMenuParams& params,
329 ContextMenuSourceType type) {
330 if (delegate_.get())
331 delegate_->ShowContextMenu(params, type);
332 else
333 DLOG(ERROR) << "Cannot show context menus without a delegate.";
336 void WebContentsViewGtk::ShowPopupMenu(const gfx::Rect& bounds,
337 int item_height,
338 double item_font_size,
339 int selected_item,
340 const std::vector<WebMenuItem>& items,
341 bool right_aligned,
342 bool allow_multiple_selection) {
343 // External popup menus are only used on Mac and Android.
344 NOTIMPLEMENTED();
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())
359 return;
361 drag_source_->StartDragging(drop_data, ops, view_gtk->GetLastMouseDown(),
362 *image.bitmap(), image_offset);
365 // -----------------------------------------------------------------------------
367 void WebContentsViewGtk::OnChildSizeRequest(GtkWidget* widget,
368 GtkWidget* child,
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();
389 if (rwhv)
390 rwhv->SetSize(size);
391 if (web_contents_->GetInterstitialPage())
392 web_contents_->GetInterstitialPage()->SetSize(size);
395 } // namespace content