// 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 #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); }