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 "base/command_line.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "content/public/browser/render_widget_host_view.h"
10 #include "content/public/browser/web_contents.h"
11 #include "content/public/browser/web_contents_view.h"
12 #include "content/shell/browser/shell_platform_data_aura.h"
13 #include "ui/aura/env.h"
14 #include "ui/aura/root_window.h"
15 #include "ui/aura/window.h"
16 #include "ui/base/accessibility/accessibility_types.h"
17 #include "ui/base/clipboard/clipboard.h"
18 #include "ui/base/models/simple_menu_model.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/events/event.h"
21 #include "ui/gfx/screen.h"
22 #include "ui/views/background.h"
23 #include "ui/views/controls/button/label_button.h"
24 #include "ui/views/controls/button/menu_button.h"
25 #include "ui/views/controls/button/menu_button_listener.h"
26 #include "ui/views/controls/menu/menu_item_view.h"
27 #include "ui/views/controls/menu/menu_runner.h"
28 #include "ui/views/controls/textfield/textfield.h"
29 #include "ui/views/controls/textfield/textfield_controller.h"
30 #include "ui/views/controls/webview/webview.h"
31 #include "ui/views/layout/fill_layout.h"
32 #include "ui/views/layout/grid_layout.h"
33 #include "ui/views/test/desktop_test_views_delegate.h"
34 #include "ui/views/view.h"
35 #include "ui/views/widget/desktop_aura/desktop_screen.h"
36 #include "ui/views/widget/widget.h"
37 #include "ui/views/widget/widget_delegate.h"
39 #if defined(OS_CHROMEOS)
40 #include "chromeos/dbus/dbus_thread_manager.h"
41 #include "ui/aura/test/test_screen.h"
42 #include "ui/wm/test/wm_test_helper.h"
53 // ViewDelegate implementation for aura content shell
54 class ShellViewsDelegateAura
: public views::DesktopTestViewsDelegate
{
56 ShellViewsDelegateAura() : use_transparent_windows_(false) {
59 virtual ~ShellViewsDelegateAura() {
62 void SetUseTransparentWindows(bool transparent
) {
63 use_transparent_windows_
= transparent
;
67 bool use_transparent_windows_
;
69 DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegateAura
);
72 // Model for the "Debug" menu
73 class DevToolsMenuModel
: public ui::SimpleMenuModel
,
74 public ui::SimpleMenuModel::Delegate
{
76 explicit DevToolsMenuModel(Shell
* shell
)
77 : ui::SimpleMenuModel(this),
79 AddItem(COMMAND_OPEN_DEVTOOLS
, base::ASCIIToUTF16("Developer Tools"));
82 // ui::SimpleMenuModel::Delegate:
83 virtual bool IsCommandIdChecked(int command_id
) const OVERRIDE
{
86 virtual bool IsCommandIdEnabled(int command_id
) const OVERRIDE
{
89 virtual bool GetAcceleratorForCommandId(
91 ui::Accelerator
* accelerator
) OVERRIDE
{ return false; }
92 virtual void ExecuteCommand(int command_id
, int event_flags
) OVERRIDE
{
94 case COMMAND_OPEN_DEVTOOLS
:
95 shell_
->ShowDevTools();
102 COMMAND_OPEN_DEVTOOLS
107 DISALLOW_COPY_AND_ASSIGN(DevToolsMenuModel
);
110 class DebugMenuButton
: public views::MenuButton
,
111 public views::MenuButtonListener
{
113 explicit DebugMenuButton(Shell
* shell
)
114 : MenuButton(NULL
, base::ASCIIToUTF16("Debug"), this, true),
119 // MenuButtonListener:
120 virtual void OnMenuButtonClicked(View
* source
,
121 const gfx::Point
& point
) OVERRIDE
{
122 menu_runner_
.reset(new views::MenuRunner(&menu_model_
));
124 if (menu_runner_
->RunMenuAt(source
->GetWidget()->GetTopLevelWidget(),
125 this, gfx::Rect(point
, gfx::Size()),
126 views::MenuItemView::TOPRIGHT
, ui::MENU_SOURCE_NONE
,
127 views::MenuRunner::HAS_MNEMONICS
) ==
128 views::MenuRunner::MENU_DELETED
)
132 DevToolsMenuModel menu_model_
;
133 scoped_ptr
<views::MenuRunner
> menu_runner_
;
135 DISALLOW_COPY_AND_ASSIGN(DebugMenuButton
);
138 // Maintain the UI controls and web view for content shell
139 class ShellWindowDelegateView
: public views::WidgetDelegateView
,
140 public views::TextfieldController
,
141 public views::ButtonListener
{
149 ShellWindowDelegateView(Shell
* shell
)
151 toolbar_view_(new View
),
152 contents_view_(new View
) {
154 virtual ~ShellWindowDelegateView() {}
156 // Update the state of UI controls
157 void SetAddressBarURL(const GURL
& url
) {
158 url_entry_
->SetText(base::ASCIIToUTF16(url
.spec()));
160 void SetWebContents(WebContents
* web_contents
, const gfx::Size
& size
) {
161 contents_view_
->SetLayoutManager(new views::FillLayout());
162 web_view_
= new views::WebView(web_contents
->GetBrowserContext());
163 web_view_
->SetWebContents(web_contents
);
164 web_view_
->SetPreferredSize(size
);
165 web_contents
->GetView()->Focus();
166 contents_view_
->AddChildView(web_view_
);
169 // Resize the widget, keeping the same origin.
170 gfx::Rect bounds
= GetWidget()->GetWindowBoundsInScreen();
171 bounds
.set_size(GetWidget()->GetRootView()->GetPreferredSize());
172 GetWidget()->SetBounds(bounds
);
174 // Resizing a widget on chromeos doesn't automatically resize the root, need
175 // to explicitly do that.
176 #if defined(OS_CHROMEOS)
177 GetWidget()->GetNativeWindow()->GetDispatcher()->host()->SetBounds(bounds
);
181 void SetWindowTitle(const base::string16
& title
) { title_
= title
; }
182 void EnableUIControl(UIControl control
, bool is_enabled
) {
183 if (control
== BACK_BUTTON
) {
184 back_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
185 : views::CustomButton::STATE_DISABLED
);
186 } else if (control
== FORWARD_BUTTON
) {
187 forward_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
188 : views::CustomButton::STATE_DISABLED
);
189 } else if (control
== STOP_BUTTON
) {
190 stop_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
191 : views::CustomButton::STATE_DISABLED
);
196 // Initialize the UI control contained in shell window
197 void InitShellWindow() {
198 set_background(views::Background::CreateStandardPanelBackground());
200 views::GridLayout
* layout
= new views::GridLayout(this);
201 SetLayoutManager(layout
);
203 views::ColumnSet
* column_set
= layout
->AddColumnSet(0);
204 column_set
->AddPaddingColumn(0, 2);
205 column_set
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 1,
206 views::GridLayout::USE_PREF
, 0, 0);
207 column_set
->AddPaddingColumn(0, 2);
209 layout
->AddPaddingRow(0, 2);
211 // Add toolbar buttons and URL text field
213 layout
->StartRow(0, 0);
214 views::GridLayout
* toolbar_layout
= new views::GridLayout(toolbar_view_
);
215 toolbar_view_
->SetLayoutManager(toolbar_layout
);
217 views::ColumnSet
* toolbar_column_set
=
218 toolbar_layout
->AddColumnSet(0);
220 back_button_
= new views::LabelButton(this, base::ASCIIToUTF16("Back"));
221 back_button_
->SetStyle(views::Button::STYLE_BUTTON
);
222 gfx::Size back_button_size
= back_button_
->GetPreferredSize();
223 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
224 views::GridLayout::CENTER
, 0,
225 views::GridLayout::FIXED
,
226 back_button_size
.width(),
227 back_button_size
.width() / 2);
230 new views::LabelButton(this, base::ASCIIToUTF16("Forward"));
231 forward_button_
->SetStyle(views::Button::STYLE_BUTTON
);
232 gfx::Size forward_button_size
= forward_button_
->GetPreferredSize();
233 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
234 views::GridLayout::CENTER
, 0,
235 views::GridLayout::FIXED
,
236 forward_button_size
.width(),
237 forward_button_size
.width() / 2);
240 new views::LabelButton(this, base::ASCIIToUTF16("Refresh"));
241 refresh_button_
->SetStyle(views::Button::STYLE_BUTTON
);
242 gfx::Size refresh_button_size
= refresh_button_
->GetPreferredSize();
243 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
244 views::GridLayout::CENTER
, 0,
245 views::GridLayout::FIXED
,
246 refresh_button_size
.width(),
247 refresh_button_size
.width() / 2);
249 stop_button_
= new views::LabelButton(this, base::ASCIIToUTF16("Stop"));
250 stop_button_
->SetStyle(views::Button::STYLE_BUTTON
);
251 gfx::Size stop_button_size
= stop_button_
->GetPreferredSize();
252 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
253 views::GridLayout::CENTER
, 0,
254 views::GridLayout::FIXED
,
255 stop_button_size
.width(),
256 stop_button_size
.width() / 2);
257 toolbar_column_set
->AddPaddingColumn(0, 2);
259 url_entry_
= new views::Textfield();
260 url_entry_
->set_controller(this);
261 toolbar_column_set
->AddColumn(views::GridLayout::FILL
,
262 views::GridLayout::FILL
, 1,
263 views::GridLayout::USE_PREF
, 0, 0);
264 toolbar_column_set
->AddPaddingColumn(0, 2);
266 debug_button_
= new DebugMenuButton(shell_
);
267 gfx::Size debug_button_size
= debug_button_
->GetPreferredSize();
268 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
269 views::GridLayout::CENTER
, 0,
270 views::GridLayout::FIXED
,
271 debug_button_size
.width(),
272 debug_button_size
.width() / 2);
274 // Fill up the first row
275 toolbar_layout
->StartRow(0, 0);
276 toolbar_layout
->AddView(back_button_
);
277 toolbar_layout
->AddView(forward_button_
);
278 toolbar_layout
->AddView(refresh_button_
);
279 toolbar_layout
->AddView(stop_button_
);
280 toolbar_layout
->AddView(url_entry_
);
281 toolbar_layout
->AddView(debug_button_
);
283 layout
->AddView(toolbar_view_
);
286 layout
->AddPaddingRow(0, 5);
288 // Add web contents view as the second row
290 layout
->StartRow(1, 0);
291 layout
->AddView(contents_view_
);
294 layout
->AddPaddingRow(0, 5);
298 void InitAccelerators() {
299 static const ui::KeyboardCode keys
[] = { ui::VKEY_F5
,
300 ui::VKEY_BROWSER_BACK
,
301 ui::VKEY_BROWSER_FORWARD
};
302 for (size_t i
= 0; i
< arraysize(keys
); ++i
) {
303 GetFocusManager()->RegisterAccelerator(
304 ui::Accelerator(keys
[i
], ui::EF_NONE
),
305 ui::AcceleratorManager::kNormalPriority
,
309 // Overridden from TextfieldController
310 virtual void ContentsChanged(views::Textfield
* sender
,
311 const base::string16
& new_contents
) OVERRIDE
{
313 virtual bool HandleKeyEvent(views::Textfield
* sender
,
314 const ui::KeyEvent
& key_event
) OVERRIDE
{
315 if (sender
== url_entry_
&& key_event
.key_code() == ui::VKEY_RETURN
) {
316 std::string text
= base::UTF16ToUTF8(url_entry_
->text());
318 if (!url
.has_scheme()) {
319 url
= GURL(std::string("http://") + std::string(text
));
320 url_entry_
->SetText(base::ASCIIToUTF16(url
.spec()));
322 shell_
->LoadURL(url
);
328 // Overridden from ButtonListener
329 virtual void ButtonPressed(views::Button
* sender
,
330 const ui::Event
& event
) OVERRIDE
{
331 if (sender
== back_button_
)
332 shell_
->GoBackOrForward(-1);
333 else if (sender
== forward_button_
)
334 shell_
->GoBackOrForward(1);
335 else if (sender
== refresh_button_
)
337 else if (sender
== stop_button_
)
341 // Overridden from WidgetDelegateView
342 virtual bool CanResize() const OVERRIDE
{ return true; }
343 virtual bool CanMaximize() const OVERRIDE
{ return true; }
344 virtual base::string16
GetWindowTitle() const OVERRIDE
{
347 virtual void WindowClosing() OVERRIDE
{
353 virtual View
* GetContentsView() OVERRIDE
{ return this; }
355 // Overridden from View
356 virtual gfx::Size
GetMinimumSize() OVERRIDE
{
357 // We want to be able to make the window smaller than its initial
361 virtual void ViewHierarchyChanged(
362 const ViewHierarchyChangedDetails
& details
) OVERRIDE
{
363 if (details
.is_add
&& details
.child
== this) {
368 // Overridden from AcceleratorTarget:
369 virtual bool AcceleratorPressed(const ui::Accelerator
& accelerator
) OVERRIDE
{
370 switch (accelerator
.key_code()) {
374 case ui::VKEY_BROWSER_BACK
:
375 shell_
->GoBackOrForward(-1);
377 case ui::VKEY_BROWSER_FORWARD
:
378 shell_
->GoBackOrForward(1);
381 return views::WidgetDelegateView::AcceleratorPressed(accelerator
);
386 // Hold a reference of Shell for deleting it when the window is closing
390 base::string16 title_
;
392 // Toolbar view contains forward/backward/reload button and URL entry
394 views::LabelButton
* back_button_
;
395 views::LabelButton
* forward_button_
;
396 views::LabelButton
* refresh_button_
;
397 views::LabelButton
* stop_button_
;
398 views::Textfield
* url_entry_
;
399 DebugMenuButton
* debug_button_
;
401 // Contents view contains the web contents view
402 View
* contents_view_
;
403 views::WebView
* web_view_
;
405 DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView
);
410 #if defined(OS_CHROMEOS)
411 wm::WMTestHelper
* Shell::wm_test_helper_
= NULL
;
413 views::ViewsDelegate
* Shell::views_delegate_
= NULL
;
416 void Shell::PlatformInitialize(const gfx::Size
& default_window_size
) {
418 _setmode(_fileno(stdout
), _O_BINARY
);
419 _setmode(_fileno(stderr
), _O_BINARY
);
421 #if defined(OS_CHROMEOS)
422 chromeos::DBusThreadManager::Initialize();
423 gfx::Screen::SetScreenInstance(
424 gfx::SCREEN_TYPE_NATIVE
, aura::TestScreen::Create());
425 wm_test_helper_
= new wm::WMTestHelper(default_window_size
);
427 gfx::Screen::SetScreenInstance(
428 gfx::SCREEN_TYPE_NATIVE
, views::CreateDesktopScreen());
430 views_delegate_
= new ShellViewsDelegateAura();
433 void Shell::PlatformExit() {
434 #if defined(OS_CHROMEOS)
435 delete wm_test_helper_
;
437 delete views_delegate_
;
438 views_delegate_
= NULL
;
441 #if defined(OS_CHROMEOS)
442 chromeos::DBusThreadManager::Shutdown();
444 aura::Env::DeleteInstance();
447 void Shell::PlatformCleanUp() {
450 void Shell::PlatformEnableUIControl(UIControl control
, bool is_enabled
) {
453 ShellWindowDelegateView
* delegate_view
=
454 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
455 if (control
== BACK_BUTTON
) {
456 delegate_view
->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON
,
458 } else if (control
== FORWARD_BUTTON
) {
459 delegate_view
->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON
,
461 } else if (control
== STOP_BUTTON
) {
462 delegate_view
->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON
,
467 void Shell::PlatformSetAddressBarURL(const GURL
& url
) {
470 ShellWindowDelegateView
* delegate_view
=
471 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
472 delegate_view
->SetAddressBarURL(url
);
475 void Shell::PlatformSetIsLoading(bool loading
) {
478 void Shell::PlatformCreateWindow(int width
, int height
) {
480 content_size_
= gfx::Size(width
, height
);
482 platform_
= new ShellPlatformDataAura(content_size_
);
484 platform_
->ResizeWindow(content_size_
);
487 #if defined(OS_CHROMEOS)
488 window_widget_
= views::Widget::CreateWindowWithContextAndBounds(
489 new ShellWindowDelegateView(this),
490 wm_test_helper_
->GetDefaultParent(NULL
, NULL
, gfx::Rect()),
491 gfx::Rect(0, 0, width
, height
));
493 window_widget_
= views::Widget::CreateWindowWithBounds(
494 new ShellWindowDelegateView(this), gfx::Rect(0, 0, width
, height
));
497 content_size_
= gfx::Size(width
, height
);
499 window_
= window_widget_
->GetNativeWindow();
500 // Call ShowRootWindow on RootWindow created by WMTestHelper without
501 // which XWindow owned by RootWindow doesn't get mapped.
502 window_
->GetDispatcher()->host()->Show();
503 window_widget_
->Show();
506 void Shell::PlatformSetContents() {
509 aura::Window
* content
= web_contents_
->GetView()->GetNativeView();
510 aura::Window
* parent
= platform_
->window()->window();
511 if (!parent
->Contains(content
)) {
512 parent
->AddChild(content
);
515 content
->SetBounds(gfx::Rect(content_size_
));
516 RenderWidgetHostView
* host_view
= web_contents_
->GetRenderWidgetHostView();
518 host_view
->SetSize(content_size_
);
520 views::WidgetDelegate
* widget_delegate
= window_widget_
->widget_delegate();
521 ShellWindowDelegateView
* delegate_view
=
522 static_cast<ShellWindowDelegateView
*>(widget_delegate
);
523 delegate_view
->SetWebContents(web_contents_
.get(), content_size_
);
527 void Shell::PlatformResizeSubViews() {
530 void Shell::Close() {
534 window_widget_
->CloseNow();
537 void Shell::PlatformSetTitle(const base::string16
& title
) {
540 ShellWindowDelegateView
* delegate_view
=
541 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
542 delegate_view
->SetWindowTitle(title
);
543 window_widget_
->UpdateWindowTitle();
546 } // namespace content