Remove PlatformFile from profile_browsertest
[chromium-blink-merge.git] / content / browser / web_contents / web_contents_view_gtk.cc
blob1e77e9f62eb87a16388ad19130f869c290bf65b5
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/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "build/build_config.h"
16 #include "content/browser/frame_host/interstitial_page_impl.h"
17 #include "content/browser/renderer_host/render_view_host_factory.h"
18 #include "content/browser/renderer_host/render_view_host_impl.h"
19 #include "content/browser/renderer_host/render_widget_host_view_gtk.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 "content/public/common/drop_data.h"
26 #include "ui/base/gtk/gtk_expanded_container.h"
27 #include "ui/gfx/image/image_skia.h"
28 #include "ui/gfx/point.h"
29 #include "ui/gfx/rect.h"
30 #include "ui/gfx/size.h"
32 using blink::WebDragOperation;
33 using blink::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 WebContentsViewPort* 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_)
104 delegate_->Initialize(expanded_.get(), &focus_store_);
107 WebContentsViewGtk::~WebContentsViewGtk() {
108 expanded_.Destroy();
111 gfx::NativeView WebContentsViewGtk::GetNativeView() const {
112 if (delegate_)
113 return delegate_->GetNativeView();
115 return expanded_.get();
118 gfx::NativeView WebContentsViewGtk::GetContentNativeView() const {
119 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
120 if (!rwhv)
121 return NULL;
122 return rwhv->GetNativeView();
125 gfx::NativeWindow WebContentsViewGtk::GetTopLevelNativeWindow() const {
126 GtkWidget* window = gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW);
127 return window ? GTK_WINDOW(window) : NULL;
130 void WebContentsViewGtk::GetContainerBounds(gfx::Rect* out) const {
131 // This is used for positioning the download shelf arrow animation,
132 // as well as sizing some other widgets in Windows. In GTK the size is
133 // managed for us, so it appears to be only used for the download shelf
134 // animation.
135 int x = 0;
136 int y = 0;
137 GdkWindow* expanded_window = gtk_widget_get_window(expanded_.get());
138 if (expanded_window)
139 gdk_window_get_origin(expanded_window, &x, &y);
141 GtkAllocation allocation;
142 gtk_widget_get_allocation(expanded_.get(), &allocation);
143 out->SetRect(x + allocation.x, y + allocation.y,
144 requested_size_.width(), requested_size_.height());
147 void WebContentsViewGtk::OnTabCrashed(base::TerminationStatus status,
148 int error_code) {
151 void WebContentsViewGtk::Focus() {
152 if (web_contents_->ShowingInterstitialPage()) {
153 web_contents_->GetInterstitialPage()->Focus();
154 } else if (delegate_) {
155 delegate_->Focus();
159 void WebContentsViewGtk::SetInitialFocus() {
160 if (web_contents_->FocusLocationBarByDefault())
161 web_contents_->SetFocusToLocationBar(false);
162 else
163 Focus();
166 void WebContentsViewGtk::StoreFocus() {
167 focus_store_.Store(GetNativeView());
170 void WebContentsViewGtk::RestoreFocus() {
171 if (focus_store_.widget())
172 gtk_widget_grab_focus(focus_store_.widget());
173 else
174 SetInitialFocus();
177 DropData* WebContentsViewGtk::GetDropData() const {
178 if (!drag_dest_)
179 return NULL;
180 return drag_dest_->current_drop_data();
183 gfx::Rect WebContentsViewGtk::GetViewBounds() const {
184 gfx::Rect rect;
185 GdkWindow* window = gtk_widget_get_window(GetNativeView());
186 if (!window) {
187 rect.SetRect(0, 0, requested_size_.width(), requested_size_.height());
188 return rect;
190 int x = 0, y = 0, w, h;
191 gdk_window_get_geometry(window, &x, &y, &w, &h, NULL);
192 rect.SetRect(x, y, w, h);
193 return rect;
196 void WebContentsViewGtk::CreateView(
197 const gfx::Size& initial_size, gfx::NativeView context) {
198 requested_size_ = initial_size;
201 RenderWidgetHostView* WebContentsViewGtk::CreateViewForWidget(
202 RenderWidgetHost* render_widget_host) {
203 if (render_widget_host->GetView()) {
204 // During testing, the view will already be set up in most cases to the
205 // test view, so we don't want to clobber it with a real one. To verify that
206 // this actually is happening (and somebody isn't accidentally creating the
207 // view twice), we check for the RVH Factory, which will be set when we're
208 // making special ones (which go along with the special views).
209 DCHECK(RenderViewHostFactory::has_factory());
210 return render_widget_host->GetView();
213 RenderWidgetHostView* view =
214 RenderWidgetHostView::CreateViewForWidget(render_widget_host);
215 view->InitAsChild(NULL);
216 gfx::NativeView content_view = view->GetNativeView();
217 g_signal_connect(content_view, "focus", G_CALLBACK(OnFocusThunk), this);
218 g_signal_connect(content_view, "leave-notify-event",
219 G_CALLBACK(OnLeaveNotify), web_contents_);
220 g_signal_connect(content_view, "motion-notify-event",
221 G_CALLBACK(OnMouseMove), web_contents_);
222 g_signal_connect(content_view, "scroll-event",
223 G_CALLBACK(OnMouseScroll), web_contents_);
224 gtk_widget_add_events(content_view, GDK_LEAVE_NOTIFY_MASK |
225 GDK_POINTER_MOTION_MASK);
226 InsertIntoContentArea(content_view);
228 if (render_widget_host->IsRenderView()) {
229 RenderViewHost* rvh = RenderViewHost::From(render_widget_host);
230 // If |rvh| is already the current render view host for the web contents, we
231 // need to initialize |drag_dest_| for drags to be properly handled.
232 // Otherwise, |drag_dest_| will be updated in RenderViewSwappedIn. The
233 // reason we can't simply check that this isn't a swapped-out view is
234 // because there are navigations that create non-swapped-out views that may
235 // never be displayed, e.g. a navigation that becomes a download.
236 if (rvh == web_contents_->GetRenderViewHost()) {
237 UpdateDragDest(rvh);
241 return view;
244 RenderWidgetHostView* WebContentsViewGtk::CreateViewForPopupWidget(
245 RenderWidgetHost* render_widget_host) {
246 return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
249 void WebContentsViewGtk::SetPageTitle(const base::string16& title) {
250 // Set the window name to include the page title so it's easier to spot
251 // when debugging (e.g. via xwininfo -tree).
252 gfx::NativeView content_view = GetContentNativeView();
253 if (content_view) {
254 GdkWindow* content_window = gtk_widget_get_window(content_view);
255 if (content_window) {
256 gdk_window_set_title(content_window, base::UTF16ToUTF8(title).c_str());
261 void WebContentsViewGtk::SizeContents(const gfx::Size& size) {
262 // We don't need to manually set the size of of widgets in GTK+, but we do
263 // need to pass the sizing information on to the RWHV which will pass the
264 // sizing information on to the renderer.
265 requested_size_ = size;
266 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
267 if (rwhv)
268 rwhv->SetSize(size);
271 void WebContentsViewGtk::RenderViewCreated(RenderViewHost* host) {
274 void WebContentsViewGtk::RenderViewSwappedIn(RenderViewHost* host) {
275 UpdateDragDest(host);
278 void WebContentsViewGtk::SetOverscrollControllerEnabled(bool enabled) {
281 WebContents* WebContentsViewGtk::web_contents() {
282 return web_contents_;
285 void WebContentsViewGtk::UpdateDragCursor(WebDragOperation operation) {
286 if (!drag_dest_)
287 return;
288 drag_dest_->UpdateDragStatus(operation);
291 void WebContentsViewGtk::GotFocus() {
292 // This is only used in the views FocusManager stuff but it bleeds through
293 // all subclasses. http://crbug.com/21875
296 // This is called when the renderer asks us to take focus back (i.e., it has
297 // iterated past the last focusable element on the page).
298 void WebContentsViewGtk::TakeFocus(bool reverse) {
299 if (!web_contents_->GetDelegate())
300 return;
301 if (!web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse) &&
302 GetTopLevelNativeWindow()) {
303 gtk_widget_child_focus(GTK_WIDGET(GetTopLevelNativeWindow()),
304 reverse ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
308 void WebContentsViewGtk::InsertIntoContentArea(GtkWidget* widget) {
309 gtk_container_add(GTK_CONTAINER(expanded_.get()), widget);
312 void WebContentsViewGtk::UpdateDragDest(RenderViewHost* host) {
313 // Drag-and-drop is entirely managed by BrowserPluginGuest for guest
314 // processes in a largely platform independent way. WebDragDestGtk
315 // will result in spurious messages being sent to the guest process which
316 // will violate assumptions.
317 if (host->GetProcess() && host->GetProcess()->IsGuest()) {
318 DCHECK(!drag_dest_);
319 return;
322 gfx::NativeView content_view = host->GetView()->GetNativeView();
324 // If the host is already used by the drag_dest_, there's no point in deleting
325 // the old one to create an identical copy.
326 if (drag_dest_.get() && drag_dest_->widget() == content_view)
327 return;
329 // Clear the currently connected drag drop signals by deleting the old
330 // drag_dest_ before creating the new one.
331 drag_dest_.reset();
332 // Create the new drag_dest_.
333 drag_dest_.reset(new WebDragDestGtk(web_contents_, content_view));
335 if (delegate_)
336 drag_dest_->set_delegate(delegate_->GetDragDestDelegate());
339 // Called when the content view gtk widget is tabbed to, or after the call to
340 // gtk_widget_child_focus() in TakeFocus(). We return true
341 // and grab focus if we don't have it. The call to
342 // FocusThroughTabTraversal(bool) forwards the "move focus forward" effect to
343 // webkit.
344 gboolean WebContentsViewGtk::OnFocus(GtkWidget* widget,
345 GtkDirectionType focus) {
346 // Give our view wrapper first chance at this event.
347 if (delegate_) {
348 gboolean return_value = FALSE;
349 if (delegate_->OnNativeViewFocusEvent(widget, focus, &return_value))
350 return return_value;
353 // If we already have focus, let the next widget have a shot at it. We will
354 // reach this situation after the call to gtk_widget_child_focus() in
355 // TakeFocus().
356 if (gtk_widget_is_focus(widget))
357 return FALSE;
359 gtk_widget_grab_focus(widget);
360 bool reverse = focus == GTK_DIR_TAB_BACKWARD;
361 web_contents_->FocusThroughTabTraversal(reverse);
362 return TRUE;
365 void WebContentsViewGtk::ShowContextMenu(RenderFrameHost* render_frame_host,
366 const ContextMenuParams& params) {
367 if (delegate_)
368 delegate_->ShowContextMenu(render_frame_host, params);
369 else
370 DLOG(ERROR) << "Cannot show context menus without a delegate.";
373 // Render view DnD -------------------------------------------------------------
375 void WebContentsViewGtk::StartDragging(const DropData& drop_data,
376 WebDragOperationsMask ops,
377 const gfx::ImageSkia& image,
378 const gfx::Vector2d& image_offset,
379 const DragEventSourceInfo& event_info) {
380 DCHECK(GetContentNativeView());
382 RenderWidgetHostViewGtk* view_gtk = static_cast<RenderWidgetHostViewGtk*>(
383 web_contents_->GetRenderWidgetHostView());
384 if (!view_gtk || !view_gtk->GetLastMouseDown() ||
385 !drag_source_->StartDragging(drop_data, ops, view_gtk->GetLastMouseDown(),
386 *image.bitmap(), image_offset)) {
387 web_contents_->SystemDragEnded();
391 // -----------------------------------------------------------------------------
393 void WebContentsViewGtk::OnChildSizeRequest(GtkWidget* widget,
394 GtkWidget* child,
395 GtkRequisition* requisition) {
396 if (web_contents_->GetDelegate()) {
397 requisition->height +=
398 web_contents_->GetDelegate()->GetExtraRenderViewHeight();
402 void WebContentsViewGtk::OnSizeAllocate(GtkWidget* widget,
403 GtkAllocation* allocation) {
404 int width = allocation->width;
405 int height = allocation->height;
406 // |delegate()| can be NULL here during browser teardown.
407 if (web_contents_->GetDelegate())
408 height += web_contents_->GetDelegate()->GetExtraRenderViewHeight();
409 gfx::Size size(width, height);
410 requested_size_ = size;
412 // We manually tell our RWHV to resize the renderer content. This avoids
413 // spurious resizes from GTK+.
414 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
415 if (rwhv)
416 rwhv->SetSize(size);
417 if (web_contents_->GetInterstitialPage())
418 web_contents_->GetInterstitialPage()->SetSize(size);
421 } // namespace content