// Copyright 2013 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 "content/shell/browser/shell.h" #include <gdk/gdkkeysyms.h> #include <gtk/gtk.h> #include "base/logging.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/native_web_keyboard_event.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 "content/public/common/renderer_preferences.h" #include "content/shell/browser/shell_browser_context.h" #include "content/shell/browser/shell_content_browser_client.h" namespace content { namespace { // Callback for Debug > Show web inspector... menu item. gboolean ShowWebInspectorActivated(GtkWidget* widget, Shell* shell) { shell->ShowDevTools(); return FALSE; // Don't stop this message. } GtkWidget* AddMenuEntry(GtkWidget* menu_widget, const char* text, GCallback callback, Shell* shell) { GtkWidget* entry = gtk_menu_item_new_with_label(text); g_signal_connect(entry, "activate", callback, shell); gtk_menu_shell_append(GTK_MENU_SHELL(menu_widget), entry); return entry; } GtkWidget* CreateMenu(GtkWidget* menu_bar, const char* text) { GtkWidget* menu_widget = gtk_menu_new(); GtkWidget* menu_header = gtk_menu_item_new_with_label(text); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_header), menu_widget); gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_header); return menu_widget; } GtkWidget* CreateMenuBar(Shell* shell) { GtkWidget* menu_bar = gtk_menu_bar_new(); GtkWidget* debug_menu = CreateMenu(menu_bar, "Debug"); AddMenuEntry(debug_menu, "Show web inspector...", G_CALLBACK(ShowWebInspectorActivated), shell); return menu_bar; } } // namespace void Shell::PlatformInitialize(const gfx::Size& default_window_size) { } void Shell::PlatformExit() { } void Shell::PlatformCleanUp() { // Nothing to clean up; GTK will clean up the widgets shortly after. } void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) { if (headless_) return; GtkToolItem* item = NULL; switch (control) { case BACK_BUTTON: item = back_button_; break; case FORWARD_BUTTON: item = forward_button_; break; case STOP_BUTTON: item = stop_button_; break; default: NOTREACHED() << "Unknown UI control"; return; } gtk_widget_set_sensitive(GTK_WIDGET(item), is_enabled); } void Shell::PlatformSetAddressBarURL(const GURL& url) { if (headless_) return; gtk_entry_set_text(GTK_ENTRY(url_edit_view_), url.spec().c_str()); } void Shell::PlatformSetIsLoading(bool loading) { if (headless_) return; if (loading) gtk_spinner_start(GTK_SPINNER(spinner_)); else gtk_spinner_stop(GTK_SPINNER(spinner_)); } void Shell::PlatformCreateWindow(int width, int height) { ui_elements_height_ = 0; if (headless_) { content_size_ = gfx::Size(width, height); return; } window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_window_set_title(window_, "Content Shell"); g_signal_connect(G_OBJECT(window_), "destroy", G_CALLBACK(OnWindowDestroyedThunk), this); vbox_ = gtk_vbox_new(FALSE, 0); // Create the menu bar. GtkWidget* menu_bar = CreateMenuBar(this); gtk_box_pack_start(GTK_BOX(vbox_), menu_bar, FALSE, FALSE, 0); // Create the object that mediates accelerators. GtkAccelGroup* accel_group = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(window_), accel_group); // Set global window handling accelerators: gtk_accel_group_connect( accel_group, GDK_w, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, g_cclosure_new(G_CALLBACK(OnCloseWindowKeyPressedThunk), this, NULL)); gtk_accel_group_connect( accel_group, GDK_n, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, g_cclosure_new(G_CALLBACK(OnNewWindowKeyPressedThunk), this, NULL)); gtk_accel_group_connect( accel_group, GDK_F5, (GdkModifierType)0, GTK_ACCEL_VISIBLE, g_cclosure_new(G_CALLBACK(OnReloadKeyPressedThunk), this, NULL)); GtkWidget* toolbar = gtk_toolbar_new(); // Turn off the labels on the toolbar buttons. gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); back_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK); g_signal_connect(back_button_, "clicked", G_CALLBACK(&OnBackButtonClickedThunk), this); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back_button_, -1 /* append */); gtk_widget_add_accelerator(GTK_WIDGET(back_button_), "clicked", accel_group, GDK_Left, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); forward_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD); g_signal_connect(forward_button_, "clicked", G_CALLBACK(&OnForwardButtonClickedThunk), this); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward_button_, -1 /* append */); gtk_widget_add_accelerator(GTK_WIDGET(forward_button_), "clicked", accel_group, GDK_Right, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); reload_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH); g_signal_connect(reload_button_, "clicked", G_CALLBACK(&OnReloadButtonClickedThunk), this); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload_button_, -1 /* append */); gtk_widget_add_accelerator(GTK_WIDGET(reload_button_), "clicked", accel_group, GDK_r, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); stop_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_STOP); g_signal_connect(stop_button_, "clicked", G_CALLBACK(&OnStopButtonClickedThunk), this); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop_button_, -1 /* append */); url_edit_view_ = gtk_entry_new(); g_signal_connect(G_OBJECT(url_edit_view_), "activate", G_CALLBACK(&OnURLEntryActivateThunk), this); gtk_accel_group_connect( accel_group, GDK_l, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, g_cclosure_new(G_CALLBACK(OnHighlightURLViewThunk), this, NULL)); GtkToolItem* tool_item = gtk_tool_item_new(); gtk_container_add(GTK_CONTAINER(tool_item), url_edit_view_); gtk_tool_item_set_expand(tool_item, TRUE); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1 /* append */); // Center a 20x20 spinner in a 26x24 area. GtkWidget* spinner_alignment = gtk_alignment_new(0.5, 0.5, 0, 0); gtk_alignment_set_padding(GTK_ALIGNMENT(spinner_alignment), 2, 2, 4, 4); spinner_ = gtk_spinner_new(); gtk_widget_set_size_request(spinner_, 20, 20); gtk_container_add(GTK_CONTAINER(spinner_alignment), spinner_); spinner_item_ = gtk_tool_item_new(); gtk_container_add(GTK_CONTAINER(spinner_item_), spinner_alignment); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), spinner_item_, -1 /* append */); gtk_box_pack_start(GTK_BOX(vbox_), toolbar, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(window_), vbox_); // Trigger layout of the UI elements, so that we can measure their // heights. The width and height passed to this method are meant for the web // contents view, not the top-level window. Since Gtk only seems to provide a // suitable resizing function for top-level windows, we need to know how to // convert from web contents view size to top-level window size. gtk_widget_show_all(GTK_WIDGET(vbox_)); // Measure the heights of the UI elements, now that they have been laid out. GtkRequisition elm_size; gtk_widget_size_request(menu_bar, &elm_size); ui_elements_height_ += elm_size.height; gtk_widget_size_request(toolbar, &elm_size); ui_elements_height_ += elm_size.height; // We're ready to set an initial window size. SizeTo(gfx::Size(width, height)); // Finally, show the window. gtk_widget_show_all(GTK_WIDGET(window_)); } void Shell::PlatformSetContents() { if (headless_) { SizeTo(content_size_); return; } WebContentsView* content_view = web_contents_->GetView(); gtk_container_add(GTK_CONTAINER(vbox_), content_view->GetNativeView()); } void Shell::SizeTo(const gfx::Size& content_size) { content_size_ = content_size; if (window_) { gtk_window_resize(window_, content_size.width(), content_size.height() + ui_elements_height_); } else if (web_contents_) { RenderWidgetHostView* render_widget_host_view = web_contents_->GetRenderWidgetHostView(); if (render_widget_host_view) render_widget_host_view->SetSize(content_size); } } void Shell::PlatformResizeSubViews() { // Not needed; the subviews are bound. } void Shell::Close() { if (headless_) { delete this; return; } gtk_widget_destroy(GTK_WIDGET(window_)); } void Shell::OnBackButtonClicked(GtkWidget* widget) { GoBackOrForward(-1); } void Shell::OnForwardButtonClicked(GtkWidget* widget) { GoBackOrForward(1); } void Shell::OnReloadButtonClicked(GtkWidget* widget) { Reload(); } void Shell::OnStopButtonClicked(GtkWidget* widget) { Stop(); } void Shell::OnURLEntryActivate(GtkWidget* entry) { const gchar* str = gtk_entry_get_text(GTK_ENTRY(entry)); GURL url(str); if (!url.has_scheme()) url = GURL(std::string("http://") + std::string(str)); if (url.is_valid()) LoadURL(url); } // Callback for when the main window is destroyed. gboolean Shell::OnWindowDestroyed(GtkWidget* window) { delete this; return FALSE; // Don't stop this message. } gboolean Shell::OnCloseWindowKeyPressed(GtkAccelGroup* accel_group, GObject* acceleratable, guint keyval, GdkModifierType modifier) { gtk_widget_destroy(GTK_WIDGET(window_)); return TRUE; } gboolean Shell::OnNewWindowKeyPressed(GtkAccelGroup* accel_group, GObject* acceleratable, guint keyval, GdkModifierType modifier) { ShellBrowserContext* browser_context = ShellContentBrowserClient::Get()->browser_context(); Shell::CreateNewWindow(browser_context, GURL(), NULL, MSG_ROUTING_NONE, gfx::Size()); return TRUE; } gboolean Shell::OnHighlightURLView(GtkAccelGroup* accel_group, GObject* acceleratable, guint keyval, GdkModifierType modifier) { gtk_widget_grab_focus(GTK_WIDGET(url_edit_view_)); return TRUE; } gboolean Shell::OnReloadKeyPressed(GtkAccelGroup* accel_group, GObject* acceleratable, guint keyval, GdkModifierType modifier) { Reload(); return TRUE; } void Shell::PlatformSetTitle(const base::string16& title) { if (headless_) return; std::string title_utf8 = UTF16ToUTF8(title); gtk_window_set_title(GTK_WINDOW(window_), title_utf8.c_str()); } } // namespace content