1 // Copyright 2013 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 "content/shell/browser/shell.h" 6 7 #include <gdk/gdkkeysyms.h> 8 #include <gtk/gtk.h> 9 10 #include "base/logging.h" 11 #include "base/strings/string_piece.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "content/public/browser/browser_context.h" 14 #include "content/public/browser/native_web_keyboard_event.h" 15 #include "content/public/browser/render_widget_host_view.h" 16 #include "content/public/browser/web_contents.h" 17 #include "content/public/browser/web_contents_view.h" 18 #include "content/public/common/renderer_preferences.h" 19 #include "content/shell/browser/shell_browser_context.h" 20 #include "content/shell/browser/shell_content_browser_client.h" 21 22 namespace content { 23 24 namespace { 25 26 // Callback for Debug > Show web inspector... menu item. 27 gboolean ShowWebInspectorActivated(GtkWidget* widget, Shell* shell) { 28 shell->ShowDevTools(); 29 return FALSE; // Don't stop this message. 30 } 31 32 GtkWidget* AddMenuEntry(GtkWidget* menu_widget, const char* text, 33 GCallback callback, Shell* shell) { 34 GtkWidget* entry = gtk_menu_item_new_with_label(text); 35 g_signal_connect(entry, "activate", callback, shell); 36 gtk_menu_shell_append(GTK_MENU_SHELL(menu_widget), entry); 37 return entry; 38 } 39 40 GtkWidget* CreateMenu(GtkWidget* menu_bar, const char* text) { 41 GtkWidget* menu_widget = gtk_menu_new(); 42 GtkWidget* menu_header = gtk_menu_item_new_with_label(text); 43 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_header), menu_widget); 44 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_header); 45 return menu_widget; 46 } 47 48 GtkWidget* CreateMenuBar(Shell* shell) { 49 GtkWidget* menu_bar = gtk_menu_bar_new(); 50 GtkWidget* debug_menu = CreateMenu(menu_bar, "Debug"); 51 AddMenuEntry(debug_menu, "Show web inspector...", 52 G_CALLBACK(ShowWebInspectorActivated), shell); 53 return menu_bar; 54 } 55 56 } // namespace 57 58 void Shell::PlatformInitialize(const gfx::Size& default_window_size) { 59 } 60 61 void Shell::PlatformExit() { 62 } 63 64 void Shell::PlatformCleanUp() { 65 // Nothing to clean up; GTK will clean up the widgets shortly after. 66 } 67 68 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) { 69 if (headless_) 70 return; 71 72 GtkToolItem* item = NULL; 73 switch (control) { 74 case BACK_BUTTON: 75 item = back_button_; 76 break; 77 case FORWARD_BUTTON: 78 item = forward_button_; 79 break; 80 case STOP_BUTTON: 81 item = stop_button_; 82 break; 83 default: 84 NOTREACHED() << "Unknown UI control"; 85 return; 86 } 87 gtk_widget_set_sensitive(GTK_WIDGET(item), is_enabled); 88 } 89 90 void Shell::PlatformSetAddressBarURL(const GURL& url) { 91 if (headless_) 92 return; 93 94 gtk_entry_set_text(GTK_ENTRY(url_edit_view_), url.spec().c_str()); 95 } 96 97 void Shell::PlatformSetIsLoading(bool loading) { 98 if (headless_) 99 return; 100 101 if (loading) 102 gtk_spinner_start(GTK_SPINNER(spinner_)); 103 else 104 gtk_spinner_stop(GTK_SPINNER(spinner_)); 105 } 106 107 void Shell::PlatformCreateWindow(int width, int height) { 108 ui_elements_height_ = 0; 109 if (headless_) { 110 content_size_ = gfx::Size(width, height); 111 return; 112 } 113 114 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); 115 gtk_window_set_title(window_, "Content Shell"); 116 g_signal_connect(G_OBJECT(window_), "destroy", 117 G_CALLBACK(OnWindowDestroyedThunk), this); 118 119 vbox_ = gtk_vbox_new(FALSE, 0); 120 121 // Create the menu bar. 122 GtkWidget* menu_bar = CreateMenuBar(this); 123 gtk_box_pack_start(GTK_BOX(vbox_), menu_bar, FALSE, FALSE, 0); 124 125 // Create the object that mediates accelerators. 126 GtkAccelGroup* accel_group = gtk_accel_group_new(); 127 gtk_window_add_accel_group(GTK_WINDOW(window_), accel_group); 128 129 // Set global window handling accelerators: 130 gtk_accel_group_connect( 131 accel_group, GDK_w, GDK_CONTROL_MASK, 132 GTK_ACCEL_VISIBLE, 133 g_cclosure_new(G_CALLBACK(OnCloseWindowKeyPressedThunk), 134 this, NULL)); 135 136 gtk_accel_group_connect( 137 accel_group, GDK_n, GDK_CONTROL_MASK, 138 GTK_ACCEL_VISIBLE, 139 g_cclosure_new(G_CALLBACK(OnNewWindowKeyPressedThunk), 140 this, NULL)); 141 142 gtk_accel_group_connect( 143 accel_group, GDK_F5, (GdkModifierType)0, 144 GTK_ACCEL_VISIBLE, 145 g_cclosure_new(G_CALLBACK(OnReloadKeyPressedThunk), 146 this, NULL)); 147 148 GtkWidget* toolbar = gtk_toolbar_new(); 149 // Turn off the labels on the toolbar buttons. 150 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); 151 152 back_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK); 153 g_signal_connect(back_button_, "clicked", 154 G_CALLBACK(&OnBackButtonClickedThunk), this); 155 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back_button_, -1 /* append */); 156 gtk_widget_add_accelerator(GTK_WIDGET(back_button_), "clicked", accel_group, 157 GDK_Left, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); 158 159 forward_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD); 160 g_signal_connect(forward_button_, "clicked", 161 G_CALLBACK(&OnForwardButtonClickedThunk), this); 162 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward_button_, -1 /* append */); 163 gtk_widget_add_accelerator(GTK_WIDGET(forward_button_), "clicked", 164 accel_group, 165 GDK_Right, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); 166 167 reload_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH); 168 g_signal_connect(reload_button_, "clicked", 169 G_CALLBACK(&OnReloadButtonClickedThunk), this); 170 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload_button_, -1 /* append */); 171 gtk_widget_add_accelerator(GTK_WIDGET(reload_button_), "clicked", 172 accel_group, 173 GDK_r, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 174 175 stop_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_STOP); 176 g_signal_connect(stop_button_, "clicked", 177 G_CALLBACK(&OnStopButtonClickedThunk), this); 178 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop_button_, -1 /* append */); 179 180 url_edit_view_ = gtk_entry_new(); 181 g_signal_connect(G_OBJECT(url_edit_view_), "activate", 182 G_CALLBACK(&OnURLEntryActivateThunk), this); 183 184 gtk_accel_group_connect( 185 accel_group, GDK_l, GDK_CONTROL_MASK, 186 GTK_ACCEL_VISIBLE, 187 g_cclosure_new(G_CALLBACK(OnHighlightURLViewThunk), 188 this, NULL)); 189 190 GtkToolItem* tool_item = gtk_tool_item_new(); 191 gtk_container_add(GTK_CONTAINER(tool_item), url_edit_view_); 192 gtk_tool_item_set_expand(tool_item, TRUE); 193 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1 /* append */); 194 195 // Center a 20x20 spinner in a 26x24 area. 196 GtkWidget* spinner_alignment = gtk_alignment_new(0.5, 0.5, 0, 0); 197 gtk_alignment_set_padding(GTK_ALIGNMENT(spinner_alignment), 2, 2, 4, 4); 198 spinner_ = gtk_spinner_new(); 199 gtk_widget_set_size_request(spinner_, 20, 20); 200 gtk_container_add(GTK_CONTAINER(spinner_alignment), spinner_); 201 202 spinner_item_ = gtk_tool_item_new(); 203 gtk_container_add(GTK_CONTAINER(spinner_item_), spinner_alignment); 204 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), spinner_item_, -1 /* append */); 205 206 gtk_box_pack_start(GTK_BOX(vbox_), toolbar, FALSE, FALSE, 0); 207 208 gtk_container_add(GTK_CONTAINER(window_), vbox_); 209 210 // Trigger layout of the UI elements, so that we can measure their 211 // heights. The width and height passed to this method are meant for the web 212 // contents view, not the top-level window. Since Gtk only seems to provide a 213 // suitable resizing function for top-level windows, we need to know how to 214 // convert from web contents view size to top-level window size. 215 gtk_widget_show_all(GTK_WIDGET(vbox_)); 216 217 // Measure the heights of the UI elements, now that they have been laid out. 218 GtkRequisition elm_size; 219 gtk_widget_size_request(menu_bar, &elm_size); 220 ui_elements_height_ += elm_size.height; 221 gtk_widget_size_request(toolbar, &elm_size); 222 ui_elements_height_ += elm_size.height; 223 224 // We're ready to set an initial window size. 225 SizeTo(gfx::Size(width, height)); 226 227 // Finally, show the window. 228 gtk_widget_show_all(GTK_WIDGET(window_)); 229 } 230 231 void Shell::PlatformSetContents() { 232 if (headless_) { 233 SizeTo(content_size_); 234 return; 235 } 236 237 WebContentsView* content_view = web_contents_->GetView(); 238 gtk_container_add(GTK_CONTAINER(vbox_), content_view->GetNativeView()); 239 } 240 241 void Shell::SizeTo(const gfx::Size& content_size) { 242 content_size_ = content_size; 243 244 if (window_) { 245 gtk_window_resize(window_, 246 content_size.width(), 247 content_size.height() + ui_elements_height_); 248 } else if (web_contents_) { 249 RenderWidgetHostView* render_widget_host_view = 250 web_contents_->GetRenderWidgetHostView(); 251 if (render_widget_host_view) 252 render_widget_host_view->SetSize(content_size); 253 } 254 } 255 256 void Shell::PlatformResizeSubViews() { 257 // Not needed; the subviews are bound. 258 } 259 260 void Shell::Close() { 261 if (headless_) { 262 delete this; 263 return; 264 } 265 266 gtk_widget_destroy(GTK_WIDGET(window_)); 267 } 268 269 void Shell::OnBackButtonClicked(GtkWidget* widget) { 270 GoBackOrForward(-1); 271 } 272 273 void Shell::OnForwardButtonClicked(GtkWidget* widget) { 274 GoBackOrForward(1); 275 } 276 277 void Shell::OnReloadButtonClicked(GtkWidget* widget) { 278 Reload(); 279 } 280 281 void Shell::OnStopButtonClicked(GtkWidget* widget) { 282 Stop(); 283 } 284 285 void Shell::OnURLEntryActivate(GtkWidget* entry) { 286 const gchar* str = gtk_entry_get_text(GTK_ENTRY(entry)); 287 GURL url(str); 288 if (!url.has_scheme()) 289 url = GURL(std::string("http://") + std::string(str)); 290 if (url.is_valid()) 291 LoadURL(url); 292 } 293 294 // Callback for when the main window is destroyed. 295 gboolean Shell::OnWindowDestroyed(GtkWidget* window) { 296 delete this; 297 return FALSE; // Don't stop this message. 298 } 299 300 gboolean Shell::OnCloseWindowKeyPressed(GtkAccelGroup* accel_group, 301 GObject* acceleratable, 302 guint keyval, 303 GdkModifierType modifier) { 304 gtk_widget_destroy(GTK_WIDGET(window_)); 305 return TRUE; 306 } 307 308 gboolean Shell::OnNewWindowKeyPressed(GtkAccelGroup* accel_group, 309 GObject* acceleratable, 310 guint keyval, 311 GdkModifierType modifier) { 312 ShellBrowserContext* browser_context = 313 ShellContentBrowserClient::Get()->browser_context(); 314 Shell::CreateNewWindow(browser_context, 315 GURL(), 316 NULL, 317 MSG_ROUTING_NONE, 318 gfx::Size()); 319 return TRUE; 320 } 321 322 gboolean Shell::OnHighlightURLView(GtkAccelGroup* accel_group, 323 GObject* acceleratable, 324 guint keyval, 325 GdkModifierType modifier) { 326 gtk_widget_grab_focus(GTK_WIDGET(url_edit_view_)); 327 return TRUE; 328 } 329 330 gboolean Shell::OnReloadKeyPressed(GtkAccelGroup* accel_group, 331 GObject* acceleratable, 332 guint keyval, 333 GdkModifierType modifier) { 334 Reload(); 335 return TRUE; 336 } 337 338 void Shell::PlatformSetTitle(const base::string16& title) { 339 if (headless_) 340 return; 341 342 std::string title_utf8 = UTF16ToUTF8(title); 343 gtk_window_set_title(GTK_WINDOW(window_), title_utf8.c_str()); 344 } 345 346 } // namespace content 347