Home | History | Annotate | Download | only in tab_contents
      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.
      4 
      5 #include "chrome/browser/ui/gtk/tab_contents/chrome_web_contents_view_delegate_gtk.h"
      6 
      7 #include <map>
      8 
      9 #include "base/lazy_instance.h"
     10 #include "chrome/browser/browser_shutdown.h"
     11 #include "chrome/browser/ui/gtk/constrained_window_gtk.h"
     12 #include "chrome/browser/ui/gtk/tab_contents/render_view_context_menu_gtk.h"
     13 #include "chrome/browser/ui/gtk/tab_contents/web_drag_bookmark_handler_gtk.h"
     14 #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h"
     15 #include "content/public/browser/render_process_host.h"
     16 #include "content/public/browser/render_view_host.h"
     17 #include "content/public/browser/render_widget_host_view.h"
     18 #include "content/public/browser/web_contents.h"
     19 #include "content/public/browser/web_contents_view.h"
     20 #include "ui/base/gtk/focus_store_gtk.h"
     21 #include "ui/base/gtk/gtk_floating_container.h"
     22 
     23 namespace {
     24 
     25 const char kViewDelegateUserDataKey[] = "ChromeWebContentsViewDelegateGtk";
     26 
     27 class ViewDelegateUserData : public base::SupportsUserData::Data {
     28  public:
     29   explicit ViewDelegateUserData(ChromeWebContentsViewDelegateGtk* view_delegate)
     30       : view_delegate_(view_delegate) {}
     31   virtual ~ViewDelegateUserData() {}
     32   ChromeWebContentsViewDelegateGtk* view_delegate() { return view_delegate_; }
     33 
     34  private:
     35   ChromeWebContentsViewDelegateGtk* view_delegate_;  // unowned
     36 };
     37 
     38 }  // namespace
     39 
     40 ChromeWebContentsViewDelegateGtk* ChromeWebContentsViewDelegateGtk::GetFor(
     41     content::WebContents* web_contents) {
     42   ViewDelegateUserData* user_data = static_cast<ViewDelegateUserData*>(
     43       web_contents->GetUserData(&kViewDelegateUserDataKey));
     44 
     45   return user_data ? user_data->view_delegate() : NULL;
     46 }
     47 
     48 ChromeWebContentsViewDelegateGtk::ChromeWebContentsViewDelegateGtk(
     49     content::WebContents* web_contents)
     50     : floating_(gtk_floating_container_new()),
     51       web_contents_modal_dialog_(NULL),
     52       web_contents_(web_contents),
     53       expanded_container_(NULL),
     54       focus_store_(NULL) {
     55   g_object_ref_sink(floating_.get());
     56   gtk_widget_set_name(floating_.get(), "chrome-tab-contents-view");
     57   g_signal_connect(floating_.get(), "set-floating-position",
     58                    G_CALLBACK(OnSetFloatingPositionThunk), this);
     59 
     60   // Stash this in the WebContents.
     61   web_contents->SetUserData(&kViewDelegateUserDataKey,
     62                             new ViewDelegateUserData(this));
     63 }
     64 
     65 ChromeWebContentsViewDelegateGtk::~ChromeWebContentsViewDelegateGtk() {
     66 }
     67 
     68 void ChromeWebContentsViewDelegateGtk::AttachWebContentsModalDialog(
     69     GtkWidget* web_contents_modal_dialog) {
     70   DCHECK(web_contents_modal_dialog_ == NULL);
     71 
     72   web_contents_modal_dialog_ = web_contents_modal_dialog;
     73   gtk_floating_container_add_floating(GTK_FLOATING_CONTAINER(floating_.get()),
     74                                       web_contents_modal_dialog);
     75 }
     76 
     77 void ChromeWebContentsViewDelegateGtk::RemoveWebContentsModalDialog(
     78     GtkWidget* web_contents_modal_dialog) {
     79   DCHECK(web_contents_modal_dialog == web_contents_modal_dialog_);
     80 
     81   web_contents_modal_dialog_ = NULL;
     82 }
     83 
     84 void ChromeWebContentsViewDelegateGtk::Initialize(
     85     GtkWidget* expanded_container, ui::FocusStoreGtk* focus_store) {
     86   expanded_container_ = expanded_container;
     87   focus_store_ = focus_store;
     88   // We install a chrome specific handler to intercept bookmark drags for the
     89   // bookmark manager/extension API.
     90   bookmark_handler_gtk_.reset(new WebDragBookmarkHandlerGtk);
     91 
     92   gtk_container_add(GTK_CONTAINER(floating_.get()), expanded_container);
     93   gtk_widget_show(floating_.get());
     94 }
     95 
     96 gfx::NativeView ChromeWebContentsViewDelegateGtk::GetNativeView() const {
     97   return floating_.get();
     98 }
     99 
    100 void ChromeWebContentsViewDelegateGtk::Focus() {
    101   if (!web_contents_modal_dialog_) {
    102     GtkWidget* widget = web_contents_->GetView()->GetContentNativeView();
    103     if (widget)
    104       gtk_widget_grab_focus(widget);
    105   }
    106 }
    107 
    108 gboolean ChromeWebContentsViewDelegateGtk::OnNativeViewFocusEvent(
    109     GtkWidget* widget,
    110     GtkDirectionType type,
    111     gboolean* return_value) {
    112   // If we are showing a web contents modal dialog, don't allow the native view
    113   // to take focus.
    114   if (web_contents_modal_dialog_) {
    115     // If we return false, it will revert to the default handler, which will
    116     // take focus. We don't want that. But if we return true, the event will
    117     // stop being propagated, leaving focus wherever it is currently. That is
    118     // also bad. So we return false to let the default handler run, but take
    119     // focus first so as to trick it into thinking the view was already focused
    120     // and allowing the event to propagate.
    121     gtk_widget_grab_focus(widget);
    122     *return_value = FALSE;
    123     return TRUE;
    124   }
    125 
    126   // Let the default WebContentsViewGtk::OnFocus() behaviour run.
    127   return FALSE;
    128 }
    129 
    130 void ChromeWebContentsViewDelegateGtk::ShowContextMenu(
    131     const content::ContextMenuParams& params) {
    132   // Find out the RenderWidgetHostView that corresponds to the render widget on
    133   // which this context menu is showed, so that we can retrieve the last mouse
    134   // down event on the render widget and use it as the timestamp of the
    135   // activation event to show the context menu.
    136   content::RenderWidgetHostView* view = NULL;
    137   if (params.custom_context.render_widget_id !=
    138       content::CustomContextMenuContext::kCurrentRenderWidget) {
    139     content::RenderWidgetHost* host = content::RenderWidgetHost::FromID(
    140         web_contents_->GetRenderProcessHost()->GetID(),
    141         params.custom_context.render_widget_id);
    142     if (!host) {
    143       NOTREACHED();
    144       return;
    145     }
    146     view = host->GetView();
    147   } else {
    148     view = web_contents_->GetRenderWidgetHostView();
    149   }
    150 
    151   context_menu_.reset(
    152       new RenderViewContextMenuGtk(web_contents_, params, view));
    153   context_menu_->Init();
    154 
    155   // Don't show empty menus.
    156   if (context_menu_->menu_model().GetItemCount() == 0)
    157     return;
    158 
    159   gfx::Rect bounds;
    160   web_contents_->GetView()->GetContainerBounds(&bounds);
    161   gfx::Point point = bounds.origin();
    162   point.Offset(params.x, params.y);
    163   context_menu_->Popup(point);
    164 }
    165 
    166 content::WebDragDestDelegate*
    167     ChromeWebContentsViewDelegateGtk::GetDragDestDelegate() {
    168   return bookmark_handler_gtk_.get();
    169 }
    170 
    171 void ChromeWebContentsViewDelegateGtk::OnSetFloatingPosition(
    172     GtkWidget* floating_container, GtkAllocation* allocation) {
    173   if (!web_contents_modal_dialog_)
    174     return;
    175 
    176   // Place each web contents modal dialog in the center of the view.
    177   GtkWidget* widget = web_contents_modal_dialog_;
    178   DCHECK(gtk_widget_get_parent(widget) == floating_.get());
    179 
    180   GtkRequisition requisition;
    181   gtk_widget_size_request(widget, &requisition);
    182 
    183   GValue value = { 0, };
    184   g_value_init(&value, G_TYPE_INT);
    185 
    186   int child_x = std::max((allocation->width - requisition.width) / 2, 0);
    187   g_value_set_int(&value, child_x);
    188   gtk_container_child_set_property(GTK_CONTAINER(floating_container),
    189                                    widget, "x", &value);
    190 
    191   int child_y = std::max((allocation->height - requisition.height) / 2, 0);
    192   g_value_set_int(&value, child_y);
    193   gtk_container_child_set_property(GTK_CONTAINER(floating_container),
    194                                    widget, "y", &value);
    195   g_value_unset(&value);
    196 }
    197 
    198 namespace chrome {
    199 
    200 content::WebContentsViewDelegate* CreateWebContentsViewDelegate(
    201     content::WebContents* web_contents) {
    202   return new ChromeWebContentsViewDelegateGtk(web_contents);
    203 }
    204 
    205 }  // namespace chrome
    206