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