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.
5 #include "content/shell/browser/shell.h"
7 #include <gdk/gdkkeysyms.h>
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"
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.
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
);
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
);
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
);
58 void Shell::PlatformInitialize(const gfx::Size
& default_window_size
) {
61 void Shell::PlatformExit() {
64 void Shell::PlatformCleanUp() {
65 // Nothing to clean up; GTK will clean up the widgets shortly after.
68 void Shell::PlatformEnableUIControl(UIControl control
, bool is_enabled
) {
72 GtkToolItem
* item
= NULL
;
78 item
= forward_button_
;
84 NOTREACHED() << "Unknown UI control";
87 gtk_widget_set_sensitive(GTK_WIDGET(item
), is_enabled
);
90 void Shell::PlatformSetAddressBarURL(const GURL
& url
) {
94 gtk_entry_set_text(GTK_ENTRY(url_edit_view_
), url
.spec().c_str());
97 void Shell::PlatformSetIsLoading(bool loading
) {
102 gtk_spinner_start(GTK_SPINNER(spinner_
));
104 gtk_spinner_stop(GTK_SPINNER(spinner_
));
107 void Shell::PlatformCreateWindow(int width
, int height
) {
108 ui_elements_height_
= 0;
110 content_size_
= gfx::Size(width
, height
);
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);
119 vbox_
= gtk_vbox_new(FALSE
, 0);
121 // Create the menu bar.
122 GtkWidget
* menu_bar
= CreateMenuBar(this);
123 gtk_box_pack_start(GTK_BOX(vbox_
), menu_bar
, FALSE
, FALSE
, 0);
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
);
129 // Set global window handling accelerators:
130 gtk_accel_group_connect(
131 accel_group
, GDK_w
, GDK_CONTROL_MASK
,
133 g_cclosure_new(G_CALLBACK(OnCloseWindowKeyPressedThunk
),
136 gtk_accel_group_connect(
137 accel_group
, GDK_n
, GDK_CONTROL_MASK
,
139 g_cclosure_new(G_CALLBACK(OnNewWindowKeyPressedThunk
),
142 gtk_accel_group_connect(
143 accel_group
, GDK_F5
, (GdkModifierType
)0,
145 g_cclosure_new(G_CALLBACK(OnReloadKeyPressedThunk
),
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
);
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
);
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",
165 GDK_Right
, GDK_MOD1_MASK
, GTK_ACCEL_VISIBLE
);
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",
173 GDK_r
, GDK_CONTROL_MASK
, GTK_ACCEL_VISIBLE
);
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 */);
180 url_edit_view_
= gtk_entry_new();
181 g_signal_connect(G_OBJECT(url_edit_view_
), "activate",
182 G_CALLBACK(&OnURLEntryActivateThunk
), this);
184 gtk_accel_group_connect(
185 accel_group
, GDK_l
, GDK_CONTROL_MASK
,
187 g_cclosure_new(G_CALLBACK(OnHighlightURLViewThunk
),
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 */);
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_
);
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 */);
206 gtk_box_pack_start(GTK_BOX(vbox_
), toolbar
, FALSE
, FALSE
, 0);
208 gtk_container_add(GTK_CONTAINER(window_
), vbox_
);
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_
));
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
;
224 // We're ready to set an initial window size.
225 SizeTo(gfx::Size(width
, height
));
227 // Finally, show the window.
228 gtk_widget_show_all(GTK_WIDGET(window_
));
231 void Shell::PlatformSetContents() {
233 SizeTo(content_size_
);
237 WebContentsView
* content_view
= web_contents_
->GetView();
238 gtk_container_add(GTK_CONTAINER(vbox_
), content_view
->GetNativeView());
241 void Shell::SizeTo(const gfx::Size
& content_size
) {
242 content_size_
= content_size
;
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
);
256 void Shell::PlatformResizeSubViews() {
257 // Not needed; the subviews are bound.
260 void Shell::Close() {
266 gtk_widget_destroy(GTK_WIDGET(window_
));
269 void Shell::OnBackButtonClicked(GtkWidget
* widget
) {
273 void Shell::OnForwardButtonClicked(GtkWidget
* widget
) {
277 void Shell::OnReloadButtonClicked(GtkWidget
* widget
) {
281 void Shell::OnStopButtonClicked(GtkWidget
* widget
) {
285 void Shell::OnURLEntryActivate(GtkWidget
* entry
) {
286 const gchar
* str
= gtk_entry_get_text(GTK_ENTRY(entry
));
288 if (!url
.has_scheme())
289 url
= GURL(std::string("http://") + std::string(str
));
294 // Callback for when the main window is destroyed.
295 gboolean
Shell::OnWindowDestroyed(GtkWidget
* window
) {
297 return FALSE
; // Don't stop this message.
300 gboolean
Shell::OnCloseWindowKeyPressed(GtkAccelGroup
* accel_group
,
301 GObject
* acceleratable
,
303 GdkModifierType modifier
) {
304 gtk_widget_destroy(GTK_WIDGET(window_
));
308 gboolean
Shell::OnNewWindowKeyPressed(GtkAccelGroup
* accel_group
,
309 GObject
* acceleratable
,
311 GdkModifierType modifier
) {
312 ShellBrowserContext
* browser_context
=
313 ShellContentBrowserClient::Get()->browser_context();
314 Shell::CreateNewWindow(browser_context
,
322 gboolean
Shell::OnHighlightURLView(GtkAccelGroup
* accel_group
,
323 GObject
* acceleratable
,
325 GdkModifierType modifier
) {
326 gtk_widget_grab_focus(GTK_WIDGET(url_edit_view_
));
330 gboolean
Shell::OnReloadKeyPressed(GtkAccelGroup
* accel_group
,
331 GObject
* acceleratable
,
333 GdkModifierType modifier
) {
338 void Shell::PlatformSetTitle(const base::string16
& title
) {
342 std::string title_utf8
= base::UTF16ToUTF8(title
);
343 gtk_window_set_title(GTK_WINDOW(window_
), title_utf8
.c_str());
346 } // namespace content