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/web_contents.h"
10 #include "content/public/browser/web_contents_view.h"
11 #include "ui/aura/env.h"
12 #include "ui/aura/root_window.h"
13 #include "ui/aura/window.h"
14 #include "ui/base/accessibility/accessibility_types.h"
15 #include "ui/base/clipboard/clipboard.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/events/event.h"
18 #include "ui/gfx/screen.h"
19 #include "ui/views/background.h"
20 #include "ui/views/controls/button/label_button.h"
21 #include "ui/views/controls/textfield/textfield.h"
22 #include "ui/views/controls/textfield/textfield_controller.h"
23 #include "ui/views/controls/webview/webview.h"
24 #include "ui/views/layout/fill_layout.h"
25 #include "ui/views/layout/grid_layout.h"
26 #include "ui/views/test/desktop_test_views_delegate.h"
27 #include "ui/views/view.h"
28 #include "ui/views/widget/desktop_aura/desktop_screen.h"
29 #include "ui/views/widget/widget.h"
30 #include "ui/views/widget/widget_delegate.h"
32 #if defined(OS_CHROMEOS)
33 #include "chromeos/dbus/dbus_thread_manager.h"
34 #include "ui/aura/test/test_screen.h"
35 #include "ui/wm/test/wm_test_helper.h"
46 // ViewDelegate implementation for aura content shell
47 class ShellViewsDelegateAura
: public views::DesktopTestViewsDelegate
{
49 ShellViewsDelegateAura() : use_transparent_windows_(false) {
52 virtual ~ShellViewsDelegateAura() {
55 void SetUseTransparentWindows(bool transparent
) {
56 use_transparent_windows_
= transparent
;
60 bool use_transparent_windows_
;
62 DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegateAura
);
65 // Maintain the UI controls and web view for content shell
66 class ShellWindowDelegateView
: public views::WidgetDelegateView
,
67 public views::TextfieldController
,
68 public views::ButtonListener
{
76 ShellWindowDelegateView(Shell
* shell
)
78 toolbar_view_(new View
),
79 contents_view_(new View
) {
81 virtual ~ShellWindowDelegateView() {}
83 // Update the state of UI controls
84 void SetAddressBarURL(const GURL
& url
) {
85 url_entry_
->SetText(base::ASCIIToUTF16(url
.spec()));
87 void SetWebContents(WebContents
* web_contents
, const gfx::Size
& size
) {
88 contents_view_
->SetLayoutManager(new views::FillLayout());
89 web_view_
= new views::WebView(web_contents
->GetBrowserContext());
90 web_view_
->SetWebContents(web_contents
);
91 web_view_
->SetPreferredSize(size
);
92 web_contents
->GetView()->Focus();
93 contents_view_
->AddChildView(web_view_
);
96 // Resize the widget, keeping the same origin.
97 gfx::Rect bounds
= GetWidget()->GetWindowBoundsInScreen();
98 bounds
.set_size(GetWidget()->GetRootView()->GetPreferredSize());
99 GetWidget()->SetBounds(bounds
);
101 // Resizing a widget on chromeos doesn't automatically resize the root, need
102 // to explicitly do that.
103 #if defined(OS_CHROMEOS)
104 GetWidget()->GetNativeWindow()->GetDispatcher()->SetHostSize(
109 void SetWindowTitle(const base::string16
& title
) { title_
= title
; }
110 void EnableUIControl(UIControl control
, bool is_enabled
) {
111 if (control
== BACK_BUTTON
) {
112 back_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
113 : views::CustomButton::STATE_DISABLED
);
114 } else if (control
== FORWARD_BUTTON
) {
115 forward_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
116 : views::CustomButton::STATE_DISABLED
);
117 } else if (control
== STOP_BUTTON
) {
118 stop_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
119 : views::CustomButton::STATE_DISABLED
);
124 // Initialize the UI control contained in shell window
125 void InitShellWindow() {
126 set_background(views::Background::CreateStandardPanelBackground());
128 views::GridLayout
* layout
= new views::GridLayout(this);
129 SetLayoutManager(layout
);
131 views::ColumnSet
* column_set
= layout
->AddColumnSet(0);
132 column_set
->AddPaddingColumn(0, 2);
133 column_set
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 1,
134 views::GridLayout::USE_PREF
, 0, 0);
135 column_set
->AddPaddingColumn(0, 2);
137 layout
->AddPaddingRow(0, 2);
139 // Add toolbar buttons and URL text field
141 layout
->StartRow(0, 0);
142 views::GridLayout
* toolbar_layout
= new views::GridLayout(toolbar_view_
);
143 toolbar_view_
->SetLayoutManager(toolbar_layout
);
145 views::ColumnSet
* toolbar_column_set
=
146 toolbar_layout
->AddColumnSet(0);
148 back_button_
= new views::LabelButton(this, base::ASCIIToUTF16("Back"));
149 back_button_
->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON
);
150 gfx::Size back_button_size
= back_button_
->GetPreferredSize();
151 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
152 views::GridLayout::CENTER
, 0,
153 views::GridLayout::FIXED
,
154 back_button_size
.width(),
155 back_button_size
.width() / 2);
158 new views::LabelButton(this, base::ASCIIToUTF16("Forward"));
159 forward_button_
->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON
);
160 gfx::Size forward_button_size
= forward_button_
->GetPreferredSize();
161 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
162 views::GridLayout::CENTER
, 0,
163 views::GridLayout::FIXED
,
164 forward_button_size
.width(),
165 forward_button_size
.width() / 2);
168 new views::LabelButton(this, base::ASCIIToUTF16("Refresh"));
169 refresh_button_
->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON
);
170 gfx::Size refresh_button_size
= refresh_button_
->GetPreferredSize();
171 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
172 views::GridLayout::CENTER
, 0,
173 views::GridLayout::FIXED
,
174 refresh_button_size
.width(),
175 refresh_button_size
.width() / 2);
177 stop_button_
= new views::LabelButton(this, base::ASCIIToUTF16("Stop"));
178 stop_button_
->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON
);
179 gfx::Size stop_button_size
= stop_button_
->GetPreferredSize();
180 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
181 views::GridLayout::CENTER
, 0,
182 views::GridLayout::FIXED
,
183 stop_button_size
.width(),
184 stop_button_size
.width() / 2);
185 toolbar_column_set
->AddPaddingColumn(0, 2);
187 url_entry_
= new views::Textfield();
188 url_entry_
->SetController(this);
189 toolbar_column_set
->AddColumn(views::GridLayout::FILL
,
190 views::GridLayout::FILL
, 1,
191 views::GridLayout::USE_PREF
, 0, 0);
193 // Fill up the first row
194 toolbar_layout
->StartRow(0, 0);
195 toolbar_layout
->AddView(back_button_
);
196 toolbar_layout
->AddView(forward_button_
);
197 toolbar_layout
->AddView(refresh_button_
);
198 toolbar_layout
->AddView(stop_button_
);
199 toolbar_layout
->AddView(url_entry_
);
201 layout
->AddView(toolbar_view_
);
204 layout
->AddPaddingRow(0, 5);
206 // Add web contents view as the second row
208 layout
->StartRow(1, 0);
209 layout
->AddView(contents_view_
);
212 layout
->AddPaddingRow(0, 5);
214 // Overridden from TextfieldController
215 virtual void ContentsChanged(views::Textfield
* sender
,
216 const base::string16
& new_contents
) OVERRIDE
{
218 virtual bool HandleKeyEvent(views::Textfield
* sender
,
219 const ui::KeyEvent
& key_event
) OVERRIDE
{
220 if (sender
== url_entry_
&& key_event
.key_code() == ui::VKEY_RETURN
) {
221 std::string text
= base::UTF16ToUTF8(url_entry_
->text());
223 if (!url
.has_scheme()) {
224 url
= GURL(std::string("http://") + std::string(text
));
225 url_entry_
->SetText(base::ASCIIToUTF16(url
.spec()));
227 shell_
->LoadURL(url
);
233 // Overridden from ButtonListener
234 virtual void ButtonPressed(views::Button
* sender
,
235 const ui::Event
& event
) OVERRIDE
{
236 if (sender
== back_button_
)
237 shell_
->GoBackOrForward(-1);
238 else if (sender
== forward_button_
)
239 shell_
->GoBackOrForward(1);
240 else if (sender
== refresh_button_
)
242 else if (sender
== stop_button_
)
246 // Overridden from WidgetDelegateView
247 virtual bool CanResize() const OVERRIDE
{ return true; }
248 virtual bool CanMaximize() const OVERRIDE
{ return true; }
249 virtual base::string16
GetWindowTitle() const OVERRIDE
{
252 virtual void WindowClosing() OVERRIDE
{
258 virtual View
* GetContentsView() OVERRIDE
{ return this; }
260 // Overridden from View
261 virtual void ViewHierarchyChanged(
262 const ViewHierarchyChangedDetails
& details
) OVERRIDE
{
263 if (details
.is_add
&& details
.child
== this) {
269 // Hold a reference of Shell for deleting it when the window is closing
273 base::string16 title_
;
275 // Toolbar view contains forward/backward/reload button and URL entry
277 views::LabelButton
* back_button_
;
278 views::LabelButton
* forward_button_
;
279 views::LabelButton
* refresh_button_
;
280 views::LabelButton
* stop_button_
;
281 views::Textfield
* url_entry_
;
283 // Contents view contains the web contents view
284 View
* contents_view_
;
285 views::WebView
* web_view_
;
287 DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView
);
292 #if defined(OS_CHROMEOS)
293 wm::WMTestHelper
* Shell::wm_test_helper_
= NULL
;
295 views::ViewsDelegate
* Shell::views_delegate_
= NULL
;
298 void Shell::PlatformInitialize(const gfx::Size
& default_window_size
) {
300 _setmode(_fileno(stdout
), _O_BINARY
);
301 _setmode(_fileno(stderr
), _O_BINARY
);
303 #if defined(OS_CHROMEOS)
304 chromeos::DBusThreadManager::Initialize();
305 gfx::Screen::SetScreenInstance(
306 gfx::SCREEN_TYPE_NATIVE
, aura::TestScreen::Create());
307 wm_test_helper_
= new wm::WMTestHelper(default_window_size
);
309 gfx::Screen::SetScreenInstance(
310 gfx::SCREEN_TYPE_NATIVE
, views::CreateDesktopScreen());
312 views_delegate_
= new ShellViewsDelegateAura();
315 void Shell::PlatformExit() {
316 #if defined(OS_CHROMEOS)
318 delete wm_test_helper_
;
321 delete views_delegate_
;
322 #if defined(OS_CHROMEOS)
323 chromeos::DBusThreadManager::Shutdown();
325 aura::Env::DeleteInstance();
328 void Shell::PlatformCleanUp() {
331 void Shell::PlatformEnableUIControl(UIControl control
, bool is_enabled
) {
332 ShellWindowDelegateView
* delegate_view
=
333 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
334 if (control
== BACK_BUTTON
) {
335 delegate_view
->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON
,
337 } else if (control
== FORWARD_BUTTON
) {
338 delegate_view
->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON
,
340 } else if (control
== STOP_BUTTON
) {
341 delegate_view
->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON
,
346 void Shell::PlatformSetAddressBarURL(const GURL
& url
) {
347 ShellWindowDelegateView
* delegate_view
=
348 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
349 delegate_view
->SetAddressBarURL(url
);
352 void Shell::PlatformSetIsLoading(bool loading
) {
355 void Shell::PlatformCreateWindow(int width
, int height
) {
356 #if defined(OS_CHROMEOS)
357 window_widget_
= views::Widget::CreateWindowWithContextAndBounds(
358 new ShellWindowDelegateView(this),
359 wm_test_helper_
->GetDefaultParent(NULL
, NULL
, gfx::Rect()),
360 gfx::Rect(0, 0, width
, height
));
362 window_widget_
= views::Widget::CreateWindowWithBounds(
363 new ShellWindowDelegateView(this), gfx::Rect(0, 0, width
, height
));
366 content_size_
= gfx::Size(width
, height
);
368 window_
= window_widget_
->GetNativeWindow();
369 // Call ShowRootWindow on RootWindow created by WMTestHelper without
370 // which XWindow owned by RootWindow doesn't get mapped.
371 window_
->GetDispatcher()->host()->Show();
372 window_widget_
->Show();
375 void Shell::PlatformSetContents() {
376 ShellWindowDelegateView
* delegate_view
=
377 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
378 delegate_view
->SetWebContents(web_contents_
.get(), content_size_
);
381 void Shell::PlatformResizeSubViews() {
384 void Shell::Close() {
385 window_widget_
->CloseNow();
388 void Shell::PlatformSetTitle(const base::string16
& title
) {
389 ShellWindowDelegateView
* delegate_view
=
390 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
391 delegate_view
->SetWindowTitle(title
);
392 window_widget_
->UpdateWindowTitle();
395 } // namespace content