blob: 64f635a9e9764fb84b7fafa32be71619017985e1 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/gtk/tab_contents_container_gtk.h"
#include <algorithm>
#include "base/i18n/rtl.h"
#include "chrome/browser/ui/gtk/status_bubble_gtk.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "ui/base/gtk/gtk_expanded_container.h"
#include "ui/base/gtk/gtk_floating_container.h"
#include "ui/gfx/native_widget_types.h"
namespace {
void HideWidget(GtkWidget* widget, gpointer ignored) {
gtk_widget_hide(widget);
}
} // namespace
TabContentsContainerGtk::TabContentsContainerGtk(StatusBubbleGtk* status_bubble,
bool embed_fullscreen_widget)
: status_bubble_(status_bubble),
should_embed_fullscreen_widgets_(embed_fullscreen_widget),
is_embedding_fullscreen_widget_(false) {
// A high level overview of the TabContentsContainer:
//
// +- GtkFloatingContainer |floating_| -------------------------------+
// |+- GtkExpandedContainer |expanded_| -----------------------------+|
// || ||
// || ||
// || ||
// || ||
// |+- (StatusBubble) ------+ ||
// |+ + ||
// |+-----------------------+----------------------------------------+|
// +------------------------------------------------------------------+
floating_.Own(gtk_floating_container_new());
gtk_widget_set_name(floating_.get(), "chrome-tab-contents-container");
expanded_ = gtk_expanded_container_new();
gtk_container_add(GTK_CONTAINER(floating_.get()), expanded_);
if (status_bubble_) {
gtk_floating_container_add_floating(GTK_FLOATING_CONTAINER(floating_.get()),
status_bubble_->widget());
g_signal_connect(floating_.get(), "set-floating-position",
G_CALLBACK(OnSetFloatingPosition), this);
}
gtk_widget_show(expanded_);
gtk_widget_show(floating_.get());
ViewIDUtil::SetDelegateForWidget(widget(), this);
}
TabContentsContainerGtk::~TabContentsContainerGtk() {
floating_.Destroy();
}
void TabContentsContainerGtk::SetTab(content::WebContents* tab) {
if (tab == web_contents())
return;
HideTab();
WebContentsObserver::Observe(tab);
is_embedding_fullscreen_widget_ =
should_embed_fullscreen_widgets_ &&
tab && tab->GetFullscreenRenderWidgetHostView();
PackTab();
}
void TabContentsContainerGtk::PackTab() {
content::WebContents* const tab = web_contents();
if (!tab)
return;
const gfx::NativeView widget = is_embedding_fullscreen_widget_ ?
tab->GetFullscreenRenderWidgetHostView()->GetNativeView() :
tab->GetView()->GetNativeView();
if (widget) {
if (gtk_widget_get_parent(widget) != expanded_)
gtk_container_add(GTK_CONTAINER(expanded_), widget);
gtk_widget_show(widget);
}
if (is_embedding_fullscreen_widget_)
return;
tab->WasShown();
// Make sure that the tab is below the find bar. Sometimes the content
// native view will be null.
GtkWidget* const content_widget = tab->GetView()->GetContentNativeView();
if (content_widget) {
GdkWindow* const content_gdk_window = gtk_widget_get_window(content_widget);
if (content_gdk_window)
gdk_window_lower(content_gdk_window);
}
}
void TabContentsContainerGtk::HideTab() {
content::WebContents* const tab = web_contents();
if (!tab)
return;
gtk_container_foreach(GTK_CONTAINER(expanded_), &HideWidget, NULL);
if (is_embedding_fullscreen_widget_)
return;
tab->WasHidden();
}
void TabContentsContainerGtk::DetachTab(content::WebContents* tab) {
if (!tab)
return;
if (tab == web_contents()) {
HideTab();
WebContentsObserver::Observe(NULL);
}
const gfx::NativeView widget = tab->GetView()->GetNativeView();
const gfx::NativeView fs_widget =
(should_embed_fullscreen_widgets_ &&
tab->GetFullscreenRenderWidgetHostView()) ?
tab->GetFullscreenRenderWidgetHostView()->GetNativeView() : NULL;
// It is possible to detach an unrealized, unparented WebContents if you
// slow things down enough in valgrind. Might happen in the real world, too.
if (widget) {
GtkWidget* const parent = gtk_widget_get_parent(widget);
if (parent) {
DCHECK_EQ(parent, expanded_);
gtk_container_remove(GTK_CONTAINER(expanded_), widget);
}
}
if (fs_widget) {
GtkWidget* const parent = gtk_widget_get_parent(fs_widget);
if (parent) {
DCHECK_EQ(parent, expanded_);
gtk_container_remove(GTK_CONTAINER(expanded_), fs_widget);
}
}
}
void TabContentsContainerGtk::WebContentsDestroyed(
content::WebContents* contents) {
// Sometimes, a WebContents is destroyed before we know about it. This allows
// us to clean up our state in case this happens.
DetachTab(contents);
}
// -----------------------------------------------------------------------------
// ViewIDUtil::Delegate implementation
GtkWidget* TabContentsContainerGtk::GetWidgetForViewID(ViewID view_id) {
if (view_id == VIEW_ID_TAB_CONTAINER)
return widget();
return NULL;
}
// -----------------------------------------------------------------------------
void TabContentsContainerGtk::DidShowFullscreenWidget(int routing_id) {
if (!should_embed_fullscreen_widgets_)
return;
HideTab();
is_embedding_fullscreen_widget_ =
web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
PackTab();
}
void TabContentsContainerGtk::DidDestroyFullscreenWidget(int routing_id) {
if (!should_embed_fullscreen_widgets_)
return;
HideTab();
is_embedding_fullscreen_widget_ = false;
PackTab();
}
// static
void TabContentsContainerGtk::OnSetFloatingPosition(
GtkFloatingContainer* floating_container, GtkAllocation* allocation,
TabContentsContainerGtk* tab_contents_container) {
StatusBubbleGtk* status = tab_contents_container->status_bubble_;
// Look at the size request of the status bubble and tell the
// GtkFloatingContainer where we want it positioned.
GtkRequisition requisition;
gtk_widget_size_request(status->widget(), &requisition);
bool ltr = !base::i18n::IsRTL();
GValue value = { 0, };
g_value_init(&value, G_TYPE_INT);
if (ltr ^ status->flip_horizontally()) // Is it on the left?
g_value_set_int(&value, 0);
else
g_value_set_int(&value, allocation->width - requisition.width);
gtk_container_child_set_property(GTK_CONTAINER(floating_container),
status->widget(), "x", &value);
int child_y = std::max(allocation->height - requisition.height, 0);
g_value_set_int(&value, child_y + status->y_offset());
gtk_container_child_set_property(GTK_CONTAINER(floating_container),
status->widget(), "y", &value);
g_value_unset(&value);
}