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/context_factory.h"
10 #include "content/public/browser/render_widget_host_view.h"
11 #include "content/public/browser/web_contents.h"
12 #include "content/public/common/context_menu_params.h"
13 #include "content/shell/browser/shell_platform_data_aura.h"
14 #include "device/bluetooth/bluetooth_adapter_factory.h"
15 #include "ui/aura/client/screen_position_client.h"
16 #include "ui/aura/env.h"
17 #include "ui/aura/window.h"
18 #include "ui/aura/window_event_dispatcher.h"
19 #include "ui/base/clipboard/clipboard.h"
20 #include "ui/base/models/simple_menu_model.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/events/event.h"
23 #include "ui/gfx/screen.h"
24 #include "ui/views/background.h"
25 #include "ui/views/controls/button/label_button.h"
26 #include "ui/views/controls/button/menu_button.h"
27 #include "ui/views/controls/button/menu_button_listener.h"
28 #include "ui/views/controls/menu/menu_runner.h"
29 #include "ui/views/controls/textfield/textfield.h"
30 #include "ui/views/controls/textfield/textfield_controller.h"
31 #include "ui/views/controls/webview/webview.h"
32 #include "ui/views/layout/fill_layout.h"
33 #include "ui/views/layout/grid_layout.h"
34 #include "ui/views/test/desktop_test_views_delegate.h"
35 #include "ui/views/view.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"
43 #else // !defined(OS_CHROMEOS)
44 #include "ui/views/widget/desktop_aura/desktop_screen.h"
55 // ViewDelegate implementation for aura content shell
56 class ShellViewsDelegateAura
: public views::DesktopTestViewsDelegate
{
58 ShellViewsDelegateAura() : use_transparent_windows_(false) {
61 ~ShellViewsDelegateAura() override
{}
63 void SetUseTransparentWindows(bool transparent
) {
64 use_transparent_windows_
= transparent
;
68 bool use_transparent_windows_
;
70 DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegateAura
);
73 // Model for the "Debug" menu
74 class ContextMenuModel
: public ui::SimpleMenuModel
,
75 public ui::SimpleMenuModel::Delegate
{
77 explicit ContextMenuModel(
78 Shell
* shell
, const content::ContextMenuParams
& params
)
79 : ui::SimpleMenuModel(this),
82 AddItem(COMMAND_OPEN_DEVTOOLS
, base::ASCIIToUTF16("Inspect Element"));
85 // ui::SimpleMenuModel::Delegate:
86 bool IsCommandIdChecked(int command_id
) const override
{ return false; }
87 bool IsCommandIdEnabled(int command_id
) const override
{ return true; }
88 bool GetAcceleratorForCommandId(int command_id
,
89 ui::Accelerator
* accelerator
) override
{
92 void ExecuteCommand(int command_id
, int event_flags
) override
{
94 case COMMAND_OPEN_DEVTOOLS
:
95 shell_
->ShowDevToolsForElementAt(params_
.x
, params_
.y
);
102 COMMAND_OPEN_DEVTOOLS
106 content::ContextMenuParams params_
;
108 DISALLOW_COPY_AND_ASSIGN(ContextMenuModel
);
111 // Maintain the UI controls and web view for content shell
112 class ShellWindowDelegateView
: public views::WidgetDelegateView
,
113 public views::TextfieldController
,
114 public views::ButtonListener
{
122 ShellWindowDelegateView(Shell
* shell
)
124 toolbar_view_(new View
),
125 contents_view_(new View
) {
127 ~ShellWindowDelegateView() override
{}
129 // Update the state of UI controls
130 void SetAddressBarURL(const GURL
& url
) {
131 url_entry_
->SetText(base::ASCIIToUTF16(url
.spec()));
133 void SetWebContents(WebContents
* web_contents
, const gfx::Size
& size
) {
134 contents_view_
->SetLayoutManager(new views::FillLayout());
135 web_view_
= new views::WebView(web_contents
->GetBrowserContext());
136 web_view_
->SetWebContents(web_contents
);
137 web_view_
->SetPreferredSize(size
);
138 web_contents
->Focus();
139 contents_view_
->AddChildView(web_view_
);
142 // Resize the widget, keeping the same origin.
143 gfx::Rect bounds
= GetWidget()->GetWindowBoundsInScreen();
144 bounds
.set_size(GetWidget()->GetRootView()->GetPreferredSize());
145 GetWidget()->SetBounds(bounds
);
147 // Resizing a widget on chromeos doesn't automatically resize the root, need
148 // to explicitly do that.
149 #if defined(OS_CHROMEOS)
150 GetWidget()->GetNativeWindow()->GetHost()->SetBounds(bounds
);
154 void SetWindowTitle(const base::string16
& title
) { title_
= title
; }
155 void EnableUIControl(UIControl control
, bool is_enabled
) {
156 if (control
== BACK_BUTTON
) {
157 back_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
158 : views::CustomButton::STATE_DISABLED
);
159 } else if (control
== FORWARD_BUTTON
) {
160 forward_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
161 : views::CustomButton::STATE_DISABLED
);
162 } else if (control
== STOP_BUTTON
) {
163 stop_button_
->SetState(is_enabled
? views::CustomButton::STATE_NORMAL
164 : views::CustomButton::STATE_DISABLED
);
168 void ShowWebViewContextMenu(const content::ContextMenuParams
& params
) {
169 gfx::Point
screen_point(params
.x
, params
.y
);
171 // Convert from content coordinates to window coordinates.
172 // This code copied from chrome_web_contents_view_delegate_views.cc
173 aura::Window
* web_contents_window
=
174 shell_
->web_contents()->GetNativeView();
175 aura::Window
* root_window
= web_contents_window
->GetRootWindow();
176 aura::client::ScreenPositionClient
* screen_position_client
=
177 aura::client::GetScreenPositionClient(root_window
);
178 if (screen_position_client
) {
179 screen_position_client
->ConvertPointToScreen(web_contents_window
,
183 context_menu_model_
.reset(new ContextMenuModel(shell_
, params
));
184 context_menu_runner_
.reset(new views::MenuRunner(
185 context_menu_model_
.get(), views::MenuRunner::CONTEXT_MENU
));
187 if (context_menu_runner_
->RunMenuAt(web_view_
->GetWidget(),
189 gfx::Rect(screen_point
, gfx::Size()),
190 views::MENU_ANCHOR_TOPRIGHT
,
191 ui::MENU_SOURCE_NONE
) ==
192 views::MenuRunner::MENU_DELETED
) {
197 void OnWebContentsFocused(content::WebContents
* web_contents
) {
198 if (web_view_
->GetWebContents() == web_contents
)
199 web_view_
->OnWebContentsFocused(web_contents
);
203 // Initialize the UI control contained in shell window
204 void InitShellWindow() {
205 set_background(views::Background::CreateStandardPanelBackground());
207 views::GridLayout
* layout
= new views::GridLayout(this);
208 SetLayoutManager(layout
);
210 views::ColumnSet
* column_set
= layout
->AddColumnSet(0);
211 column_set
->AddPaddingColumn(0, 2);
212 column_set
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
, 1,
213 views::GridLayout::USE_PREF
, 0, 0);
214 column_set
->AddPaddingColumn(0, 2);
216 layout
->AddPaddingRow(0, 2);
218 // Add toolbar buttons and URL text field
220 layout
->StartRow(0, 0);
221 views::GridLayout
* toolbar_layout
= new views::GridLayout(toolbar_view_
);
222 toolbar_view_
->SetLayoutManager(toolbar_layout
);
224 views::ColumnSet
* toolbar_column_set
=
225 toolbar_layout
->AddColumnSet(0);
227 back_button_
= new views::LabelButton(this, base::ASCIIToUTF16("Back"));
228 back_button_
->SetStyle(views::Button::STYLE_BUTTON
);
229 gfx::Size back_button_size
= back_button_
->GetPreferredSize();
230 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
231 views::GridLayout::CENTER
, 0,
232 views::GridLayout::FIXED
,
233 back_button_size
.width(),
234 back_button_size
.width() / 2);
237 new views::LabelButton(this, base::ASCIIToUTF16("Forward"));
238 forward_button_
->SetStyle(views::Button::STYLE_BUTTON
);
239 gfx::Size forward_button_size
= forward_button_
->GetPreferredSize();
240 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
241 views::GridLayout::CENTER
, 0,
242 views::GridLayout::FIXED
,
243 forward_button_size
.width(),
244 forward_button_size
.width() / 2);
247 new views::LabelButton(this, base::ASCIIToUTF16("Refresh"));
248 refresh_button_
->SetStyle(views::Button::STYLE_BUTTON
);
249 gfx::Size refresh_button_size
= refresh_button_
->GetPreferredSize();
250 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
251 views::GridLayout::CENTER
, 0,
252 views::GridLayout::FIXED
,
253 refresh_button_size
.width(),
254 refresh_button_size
.width() / 2);
256 stop_button_
= new views::LabelButton(this, base::ASCIIToUTF16("Stop"));
257 stop_button_
->SetStyle(views::Button::STYLE_BUTTON
);
258 gfx::Size stop_button_size
= stop_button_
->GetPreferredSize();
259 toolbar_column_set
->AddColumn(views::GridLayout::CENTER
,
260 views::GridLayout::CENTER
, 0,
261 views::GridLayout::FIXED
,
262 stop_button_size
.width(),
263 stop_button_size
.width() / 2);
264 toolbar_column_set
->AddPaddingColumn(0, 2);
266 url_entry_
= new views::Textfield();
267 url_entry_
->set_controller(this);
268 toolbar_column_set
->AddColumn(views::GridLayout::FILL
,
269 views::GridLayout::FILL
, 1,
270 views::GridLayout::USE_PREF
, 0, 0);
271 toolbar_column_set
->AddPaddingColumn(0, 2);
273 // Fill up the first row
274 toolbar_layout
->StartRow(0, 0);
275 toolbar_layout
->AddView(back_button_
);
276 toolbar_layout
->AddView(forward_button_
);
277 toolbar_layout
->AddView(refresh_button_
);
278 toolbar_layout
->AddView(stop_button_
);
279 toolbar_layout
->AddView(url_entry_
);
281 layout
->AddView(toolbar_view_
);
284 layout
->AddPaddingRow(0, 5);
286 // Add web contents view as the second row
288 layout
->StartRow(1, 0);
289 layout
->AddView(contents_view_
);
292 layout
->AddPaddingRow(0, 5);
296 void InitAccelerators() {
297 static const ui::KeyboardCode keys
[] = { ui::VKEY_F5
,
298 ui::VKEY_BROWSER_BACK
,
299 ui::VKEY_BROWSER_FORWARD
};
300 for (size_t i
= 0; i
< arraysize(keys
); ++i
) {
301 GetFocusManager()->RegisterAccelerator(
302 ui::Accelerator(keys
[i
], ui::EF_NONE
),
303 ui::AcceleratorManager::kNormalPriority
,
307 // Overridden from TextfieldController
308 void ContentsChanged(views::Textfield
* sender
,
309 const base::string16
& new_contents
) override
{}
310 bool HandleKeyEvent(views::Textfield
* sender
,
311 const ui::KeyEvent
& key_event
) override
{
312 if (sender
== url_entry_
&& key_event
.key_code() == ui::VKEY_RETURN
) {
313 std::string text
= base::UTF16ToUTF8(url_entry_
->text());
315 if (!url
.has_scheme()) {
316 url
= GURL(std::string("http://") + std::string(text
));
317 url_entry_
->SetText(base::ASCIIToUTF16(url
.spec()));
319 shell_
->LoadURL(url
);
325 // Overridden from ButtonListener
326 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
{
327 if (sender
== back_button_
)
328 shell_
->GoBackOrForward(-1);
329 else if (sender
== forward_button_
)
330 shell_
->GoBackOrForward(1);
331 else if (sender
== refresh_button_
)
333 else if (sender
== stop_button_
)
337 // Overridden from WidgetDelegateView
338 bool CanResize() const override
{ return true; }
339 bool CanMaximize() const override
{ return true; }
340 bool CanMinimize() const override
{ return true; }
341 base::string16
GetWindowTitle() const override
{ return title_
; }
342 void WindowClosing() override
{
348 View
* GetContentsView() override
{ return this; }
350 // Overridden from View
351 gfx::Size
GetMinimumSize() const override
{
352 // We want to be able to make the window smaller than its initial
356 void ViewHierarchyChanged(
357 const ViewHierarchyChangedDetails
& details
) override
{
358 if (details
.is_add
&& details
.child
== this) {
363 // Overridden from AcceleratorTarget:
364 bool AcceleratorPressed(const ui::Accelerator
& accelerator
) override
{
365 switch (accelerator
.key_code()) {
369 case ui::VKEY_BROWSER_BACK
:
370 shell_
->GoBackOrForward(-1);
372 case ui::VKEY_BROWSER_FORWARD
:
373 shell_
->GoBackOrForward(1);
376 return views::WidgetDelegateView::AcceleratorPressed(accelerator
);
381 // Hold a reference of Shell for deleting it when the window is closing
385 base::string16 title_
;
387 // Toolbar view contains forward/backward/reload button and URL entry
389 views::LabelButton
* back_button_
;
390 views::LabelButton
* forward_button_
;
391 views::LabelButton
* refresh_button_
;
392 views::LabelButton
* stop_button_
;
393 views::Textfield
* url_entry_
;
394 scoped_ptr
<ContextMenuModel
> context_menu_model_
;
395 scoped_ptr
<views::MenuRunner
> context_menu_runner_
;
397 // Contents view contains the web contents view
398 View
* contents_view_
;
399 views::WebView
* web_view_
;
401 DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView
);
406 #if defined(OS_CHROMEOS)
407 wm::WMTestHelper
* Shell::wm_test_helper_
= NULL
;
408 gfx::Screen
* Shell::test_screen_
= NULL
;
410 views::ViewsDelegate
* Shell::views_delegate_
= NULL
;
413 void Shell::PlatformInitialize(const gfx::Size
& default_window_size
) {
415 _setmode(_fileno(stdout
), _O_BINARY
);
416 _setmode(_fileno(stderr
), _O_BINARY
);
418 #if defined(OS_CHROMEOS)
419 chromeos::DBusThreadManager::Initialize();
420 test_screen_
= aura::TestScreen::Create(gfx::Size());
421 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE
, test_screen_
);
422 wm_test_helper_
= new wm::WMTestHelper(default_window_size
,
423 GetContextFactory());
425 gfx::Screen::SetScreenInstance(
426 gfx::SCREEN_TYPE_NATIVE
, views::CreateDesktopScreen());
428 views_delegate_
= new ShellViewsDelegateAura();
431 void Shell::PlatformExit() {
432 #if defined(OS_CHROMEOS)
433 delete wm_test_helper_
;
434 wm_test_helper_
= NULL
;
439 delete views_delegate_
;
440 views_delegate_
= NULL
;
443 #if defined(OS_CHROMEOS)
444 device::BluetoothAdapterFactory::Shutdown();
445 chromeos::DBusThreadManager::Shutdown();
447 aura::Env::DeleteInstance();
450 void Shell::PlatformCleanUp() {
453 void Shell::PlatformEnableUIControl(UIControl control
, bool is_enabled
) {
456 ShellWindowDelegateView
* delegate_view
=
457 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
458 if (control
== BACK_BUTTON
) {
459 delegate_view
->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON
,
461 } else if (control
== FORWARD_BUTTON
) {
462 delegate_view
->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON
,
464 } else if (control
== STOP_BUTTON
) {
465 delegate_view
->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON
,
470 void Shell::PlatformSetAddressBarURL(const GURL
& url
) {
473 ShellWindowDelegateView
* delegate_view
=
474 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
475 delegate_view
->SetAddressBarURL(url
);
478 void Shell::PlatformSetIsLoading(bool loading
) {
481 void Shell::PlatformCreateWindow(int width
, int height
) {
483 content_size_
= gfx::Size(width
, height
);
485 platform_
= new ShellPlatformDataAura(content_size_
);
487 platform_
->ResizeWindow(content_size_
);
490 #if defined(OS_CHROMEOS)
491 window_widget_
= views::Widget::CreateWindowWithContextAndBounds(
492 new ShellWindowDelegateView(this),
493 wm_test_helper_
->GetDefaultParent(NULL
, NULL
, gfx::Rect()),
494 gfx::Rect(0, 0, width
, height
));
496 window_widget_
= new views::Widget
;
497 views::Widget::InitParams params
;
498 params
.bounds
= gfx::Rect(0, 0, width
, height
);
499 params
.delegate
= new ShellWindowDelegateView(this);
500 window_widget_
->Init(params
);
503 content_size_
= gfx::Size(width
, height
);
505 window_
= window_widget_
->GetNativeWindow();
506 // Call ShowRootWindow on RootWindow created by WMTestHelper without
507 // which XWindow owned by RootWindow doesn't get mapped.
508 window_
->GetHost()->Show();
509 window_widget_
->Show();
512 void Shell::PlatformSetContents() {
515 aura::Window
* content
= web_contents_
->GetNativeView();
516 aura::Window
* parent
= platform_
->host()->window();
517 if (!parent
->Contains(content
)) {
518 parent
->AddChild(content
);
521 content
->SetBounds(gfx::Rect(content_size_
));
522 RenderWidgetHostView
* host_view
= web_contents_
->GetRenderWidgetHostView();
524 host_view
->SetSize(content_size_
);
526 views::WidgetDelegate
* widget_delegate
= window_widget_
->widget_delegate();
527 ShellWindowDelegateView
* delegate_view
=
528 static_cast<ShellWindowDelegateView
*>(widget_delegate
);
529 delegate_view
->SetWebContents(web_contents_
.get(), content_size_
);
533 void Shell::PlatformResizeSubViews() {
536 void Shell::Close() {
540 window_widget_
->CloseNow();
543 void Shell::PlatformSetTitle(const base::string16
& title
) {
546 ShellWindowDelegateView
* delegate_view
=
547 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
548 delegate_view
->SetWindowTitle(title
);
549 window_widget_
->UpdateWindowTitle();
552 bool Shell::PlatformHandleContextMenu(
553 const content::ContextMenuParams
& params
) {
556 ShellWindowDelegateView
* delegate_view
=
557 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
558 delegate_view
->ShowWebViewContextMenu(params
);
562 void Shell::PlatformWebContentsFocused(WebContents
* contents
) {
565 ShellWindowDelegateView
* delegate_view
=
566 static_cast<ShellWindowDelegateView
*>(window_widget_
->widget_delegate());
567 delegate_view
->OnWebContentsFocused(contents
);
570 } // namespace content