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