• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ShowWebInspectorActivated(GtkWidget * widget,Shell * shell)27 gboolean ShowWebInspectorActivated(GtkWidget* widget, Shell* shell) {
28   shell->ShowDevTools();
29   return FALSE;  // Don't stop this message.
30 }
31 
AddMenuEntry(GtkWidget * menu_widget,const char * text,GCallback callback,Shell * shell)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 
CreateMenu(GtkWidget * menu_bar,const char * text)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 
CreateMenuBar(Shell * shell)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 
PlatformInitialize(const gfx::Size & default_window_size)58 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
59 }
60 
PlatformExit()61 void Shell::PlatformExit() {
62 }
63 
PlatformCleanUp()64 void Shell::PlatformCleanUp() {
65   // Nothing to clean up; GTK will clean up the widgets shortly after.
66 }
67 
PlatformEnableUIControl(UIControl control,bool is_enabled)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 
PlatformSetAddressBarURL(const GURL & url)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 
PlatformSetIsLoading(bool loading)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 
PlatformCreateWindow(int width,int height)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 
PlatformSetContents()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 
SizeTo(const gfx::Size & content_size)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 
PlatformResizeSubViews()256 void Shell::PlatformResizeSubViews() {
257   // Not needed; the subviews are bound.
258 }
259 
Close()260 void Shell::Close() {
261   if (headless_) {
262     delete this;
263     return;
264   }
265 
266   gtk_widget_destroy(GTK_WIDGET(window_));
267 }
268 
OnBackButtonClicked(GtkWidget * widget)269 void Shell::OnBackButtonClicked(GtkWidget* widget) {
270   GoBackOrForward(-1);
271 }
272 
OnForwardButtonClicked(GtkWidget * widget)273 void Shell::OnForwardButtonClicked(GtkWidget* widget) {
274   GoBackOrForward(1);
275 }
276 
OnReloadButtonClicked(GtkWidget * widget)277 void Shell::OnReloadButtonClicked(GtkWidget* widget) {
278   Reload();
279 }
280 
OnStopButtonClicked(GtkWidget * widget)281 void Shell::OnStopButtonClicked(GtkWidget* widget) {
282   Stop();
283 }
284 
OnURLEntryActivate(GtkWidget * entry)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.
OnWindowDestroyed(GtkWidget * window)295 gboolean Shell::OnWindowDestroyed(GtkWidget* window) {
296   delete this;
297   return FALSE;  // Don't stop this message.
298 }
299 
OnCloseWindowKeyPressed(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier)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 
OnNewWindowKeyPressed(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier)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 
OnHighlightURLView(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier)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 
OnReloadKeyPressed(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier)330 gboolean Shell::OnReloadKeyPressed(GtkAccelGroup* accel_group,
331                                    GObject* acceleratable,
332                                    guint keyval,
333                                    GdkModifierType modifier) {
334   Reload();
335   return TRUE;
336 }
337 
PlatformSetTitle(const base::string16 & title)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