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