Home | History | Annotate | Download | only in shell
      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