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/common/context_menu_params.h"
12 #include "content/shell/browser/shell_platform_data_aura.h"
13 #include "ui/aura/client/screen_position_client.h"
14 #include "ui/aura/env.h"
15 #include "ui/aura/window.h"
16 #include "ui/aura/window_event_dispatcher.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_runner.h"
27 #include "ui/views/controls/textfield/textfield.h"
28 #include "ui/views/controls/textfield/textfield_controller.h"
29 #include "ui/views/controls/webview/webview.h"
30 #include "ui/views/layout/fill_layout.h"
31 #include "ui/views/layout/grid_layout.h"
32 #include "ui/views/test/desktop_test_views_delegate.h"
33 #include "ui/views/view.h"
34 #include "ui/views/widget/widget.h"
35 #include "ui/views/widget/widget_delegate.h"
37 #if defined(OS_CHROMEOS)
38 #include "chromeos/dbus/dbus_thread_manager.h"
39 #include "ui/aura/test/test_screen.h"
40 #include "ui/wm/test/wm_test_helper.h"
41 #else // !defined(OS_CHROMEOS)
42 #include "ui/views/widget/desktop_aura/desktop_screen.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 ContextMenuModel : public ui::SimpleMenuModel,
74 public ui::SimpleMenuModel::Delegate {
76 explicit ContextMenuModel(
77 Shell* shell, const content::ContextMenuParams& params)
78 : ui::SimpleMenuModel(this),
81 AddItem(COMMAND_OPEN_DEVTOOLS, base::ASCIIToUTF16("Inspect Element"));
84 // ui::SimpleMenuModel::Delegate:
85 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
88 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
91 virtual bool GetAcceleratorForCommandId(
93 ui::Accelerator* accelerator) OVERRIDE { return false; }
94 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
96 case COMMAND_OPEN_DEVTOOLS:
97 shell_->ShowDevToolsForElementAt(params_.x, params_.y);
104 COMMAND_OPEN_DEVTOOLS
108 content::ContextMenuParams params_;
110 DISALLOW_COPY_AND_ASSIGN(ContextMenuModel);
113 // Maintain the UI controls and web view for content shell
114 class ShellWindowDelegateView : public views::WidgetDelegateView,
115 public views::TextfieldController,
116 public views::ButtonListener {
124 ShellWindowDelegateView(Shell* shell)
126 toolbar_view_(new View),
127 contents_view_(new View) {
129 virtual ~ShellWindowDelegateView() {}
131 // Update the state of UI controls
132 void SetAddressBarURL(const GURL& url) {
133 url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
135 void SetWebContents(WebContents* web_contents, const gfx::Size& size) {
136 contents_view_->SetLayoutManager(new views::FillLayout());
137 web_view_ = new views::WebView(web_contents->GetBrowserContext());
138 web_view_->SetWebContents(web_contents);
139 web_view_->SetPreferredSize(size);
140 web_contents->Focus();
141 contents_view_->AddChildView(web_view_);
144 // Resize the widget, keeping the same origin.
145 gfx::Rect bounds = GetWidget()->GetWindowBoundsInScreen();
146 bounds.set_size(GetWidget()->GetRootView()->GetPreferredSize());
147 GetWidget()->SetBounds(bounds);
149 // Resizing a widget on chromeos doesn't automatically resize the root, need
150 // to explicitly do that.
151 #if defined(OS_CHROMEOS)
152 GetWidget()->GetNativeWindow()->GetHost()->SetBounds(bounds);
156 void SetWindowTitle(const base::string16& title) { title_ = title; }
157 void EnableUIControl(UIControl control, bool is_enabled) {
158 if (control == BACK_BUTTON) {
159 back_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL
160 : views::CustomButton::STATE_DISABLED);
161 } else if (control == FORWARD_BUTTON) {
162 forward_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL
163 : views::CustomButton::STATE_DISABLED);
164 } else if (control == STOP_BUTTON) {
165 stop_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL
166 : views::CustomButton::STATE_DISABLED);
170 void ShowWebViewContextMenu(const content::ContextMenuParams& params) {
171 gfx::Point screen_point(params.x, params.y);
173 // Convert from content coordinates to window coordinates.
174 // This code copied from chrome_web_contents_view_delegate_views.cc
175 aura::Window* web_contents_window =
176 shell_->web_contents()->GetNativeView();
177 aura::Window* root_window = web_contents_window->GetRootWindow();
178 aura::client::ScreenPositionClient* screen_position_client =
179 aura::client::GetScreenPositionClient(root_window);
180 if (screen_position_client) {
181 screen_position_client->ConvertPointToScreen(web_contents_window,
185 context_menu_model_.reset(new ContextMenuModel(shell_, params));
186 context_menu_runner_.reset(
187 new views::MenuRunner(context_menu_model_.get()));
189 if (context_menu_runner_->RunMenuAt(web_view_->GetWidget(),
191 gfx::Rect(screen_point, gfx::Size()),
192 views::MENU_ANCHOR_TOPRIGHT,
193 ui::MENU_SOURCE_NONE,
194 views::MenuRunner::CONTEXT_MENU) ==
195 views::MenuRunner::MENU_DELETED) {
200 void OnWebContentsFocused(content::WebContents* web_contents) {
201 if (web_view_->GetWebContents() == web_contents)
202 web_view_->OnWebContentsFocused(web_contents);
206 // Initialize the UI control contained in shell window
207 void InitShellWindow() {
208 set_background(views::Background::CreateStandardPanelBackground());
210 views::GridLayout* layout = new views::GridLayout(this);
211 SetLayoutManager(layout);
213 views::ColumnSet* column_set = layout->AddColumnSet(0);
214 column_set->AddPaddingColumn(0, 2);
215 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
216 views::GridLayout::USE_PREF, 0, 0);
217 column_set->AddPaddingColumn(0, 2);
219 layout->AddPaddingRow(0, 2);
221 // Add toolbar buttons and URL text field
223 layout->StartRow(0, 0);
224 views::GridLayout* toolbar_layout = new views::GridLayout(toolbar_view_);
225 toolbar_view_->SetLayoutManager(toolbar_layout);
227 views::ColumnSet* toolbar_column_set =
228 toolbar_layout->AddColumnSet(0);
230 back_button_ = new views::LabelButton(this, base::ASCIIToUTF16("Back"));
231 back_button_->SetStyle(views::Button::STYLE_BUTTON);
232 gfx::Size back_button_size = back_button_->GetPreferredSize();
233 toolbar_column_set->AddColumn(views::GridLayout::CENTER,
234 views::GridLayout::CENTER, 0,
235 views::GridLayout::FIXED,
236 back_button_size.width(),
237 back_button_size.width() / 2);
240 new views::LabelButton(this, base::ASCIIToUTF16("Forward"));
241 forward_button_->SetStyle(views::Button::STYLE_BUTTON);
242 gfx::Size forward_button_size = forward_button_->GetPreferredSize();
243 toolbar_column_set->AddColumn(views::GridLayout::CENTER,
244 views::GridLayout::CENTER, 0,
245 views::GridLayout::FIXED,
246 forward_button_size.width(),
247 forward_button_size.width() / 2);
250 new views::LabelButton(this, base::ASCIIToUTF16("Refresh"));
251 refresh_button_->SetStyle(views::Button::STYLE_BUTTON);
252 gfx::Size refresh_button_size = refresh_button_->GetPreferredSize();
253 toolbar_column_set->AddColumn(views::GridLayout::CENTER,
254 views::GridLayout::CENTER, 0,
255 views::GridLayout::FIXED,
256 refresh_button_size.width(),
257 refresh_button_size.width() / 2);
259 stop_button_ = new views::LabelButton(this, base::ASCIIToUTF16("Stop"));
260 stop_button_->SetStyle(views::Button::STYLE_BUTTON);
261 gfx::Size stop_button_size = stop_button_->GetPreferredSize();
262 toolbar_column_set->AddColumn(views::GridLayout::CENTER,
263 views::GridLayout::CENTER, 0,
264 views::GridLayout::FIXED,
265 stop_button_size.width(),
266 stop_button_size.width() / 2);
267 toolbar_column_set->AddPaddingColumn(0, 2);
269 url_entry_ = new views::Textfield();
270 url_entry_->set_controller(this);
271 toolbar_column_set->AddColumn(views::GridLayout::FILL,
272 views::GridLayout::FILL, 1,
273 views::GridLayout::USE_PREF, 0, 0);
274 toolbar_column_set->AddPaddingColumn(0, 2);
276 // Fill up the first row
277 toolbar_layout->StartRow(0, 0);
278 toolbar_layout->AddView(back_button_);
279 toolbar_layout->AddView(forward_button_);
280 toolbar_layout->AddView(refresh_button_);
281 toolbar_layout->AddView(stop_button_);
282 toolbar_layout->AddView(url_entry_);
284 layout->AddView(toolbar_view_);
287 layout->AddPaddingRow(0, 5);
289 // Add web contents view as the second row
291 layout->StartRow(1, 0);
292 layout->AddView(contents_view_);
295 layout->AddPaddingRow(0, 5);
299 void InitAccelerators() {
300 static const ui::KeyboardCode keys[] = { ui::VKEY_F5,
301 ui::VKEY_BROWSER_BACK,
302 ui::VKEY_BROWSER_FORWARD };
303 for (size_t i = 0; i < arraysize(keys); ++i) {
304 GetFocusManager()->RegisterAccelerator(
305 ui::Accelerator(keys[i], ui::EF_NONE),
306 ui::AcceleratorManager::kNormalPriority,
310 // Overridden from TextfieldController
311 virtual void ContentsChanged(views::Textfield* sender,
312 const base::string16& new_contents) OVERRIDE {
314 virtual bool HandleKeyEvent(views::Textfield* sender,
315 const ui::KeyEvent& key_event) OVERRIDE {
316 if (sender == url_entry_ && key_event.key_code() == ui::VKEY_RETURN) {
317 std::string text = base::UTF16ToUTF8(url_entry_->text());
319 if (!url.has_scheme()) {
320 url = GURL(std::string("http://") + std::string(text));
321 url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
323 shell_->LoadURL(url);
329 // Overridden from ButtonListener
330 virtual void ButtonPressed(views::Button* sender,
331 const ui::Event& event) OVERRIDE {
332 if (sender == back_button_)
333 shell_->GoBackOrForward(-1);
334 else if (sender == forward_button_)
335 shell_->GoBackOrForward(1);
336 else if (sender == refresh_button_)
338 else if (sender == stop_button_)
342 // Overridden from WidgetDelegateView
343 virtual bool CanResize() const OVERRIDE { return true; }
344 virtual bool CanMaximize() const OVERRIDE { return true; }
345 virtual base::string16 GetWindowTitle() const OVERRIDE {
348 virtual void WindowClosing() OVERRIDE {
354 virtual View* GetContentsView() OVERRIDE { return this; }
356 // Overridden from View
357 virtual gfx::Size GetMinimumSize() OVERRIDE {
358 // We want to be able to make the window smaller than its initial
362 virtual void ViewHierarchyChanged(
363 const ViewHierarchyChangedDetails& details) OVERRIDE {
364 if (details.is_add && details.child == this) {
369 // Overridden from AcceleratorTarget:
370 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE {
371 switch (accelerator.key_code()) {
375 case ui::VKEY_BROWSER_BACK:
376 shell_->GoBackOrForward(-1);
378 case ui::VKEY_BROWSER_FORWARD:
379 shell_->GoBackOrForward(1);
382 return views::WidgetDelegateView::AcceleratorPressed(accelerator);
387 // Hold a reference of Shell for deleting it when the window is closing
391 base::string16 title_;
393 // Toolbar view contains forward/backward/reload button and URL entry
395 views::LabelButton* back_button_;
396 views::LabelButton* forward_button_;
397 views::LabelButton* refresh_button_;
398 views::LabelButton* stop_button_;
399 views::Textfield* url_entry_;
400 scoped_ptr<ContextMenuModel> context_menu_model_;
401 scoped_ptr<views::MenuRunner> context_menu_runner_;
403 // Contents view contains the web contents view
404 View* contents_view_;
405 views::WebView* web_view_;
407 DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView);
412 #if defined(OS_CHROMEOS)
413 wm::WMTestHelper* Shell::wm_test_helper_ = NULL;
415 views::ViewsDelegate* Shell::views_delegate_ = NULL;
418 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
420 _setmode(_fileno(stdout), _O_BINARY);
421 _setmode(_fileno(stderr), _O_BINARY);
423 #if defined(OS_CHROMEOS)
424 chromeos::DBusThreadManager::Initialize();
425 gfx::Screen::SetScreenInstance(
426 gfx::SCREEN_TYPE_NATIVE, aura::TestScreen::Create());
427 wm_test_helper_ = new wm::WMTestHelper(default_window_size);
429 gfx::Screen::SetScreenInstance(
430 gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen());
432 views_delegate_ = new ShellViewsDelegateAura();
435 void Shell::PlatformExit() {
436 #if defined(OS_CHROMEOS)
437 delete wm_test_helper_;
439 delete views_delegate_;
440 views_delegate_ = NULL;
443 #if defined(OS_CHROMEOS)
444 chromeos::DBusThreadManager::Shutdown();
446 aura::Env::DeleteInstance();
449 void Shell::PlatformCleanUp() {
452 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
455 ShellWindowDelegateView* delegate_view =
456 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
457 if (control == BACK_BUTTON) {
458 delegate_view->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON,
460 } else if (control == FORWARD_BUTTON) {
461 delegate_view->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON,
463 } else if (control == STOP_BUTTON) {
464 delegate_view->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON,
469 void Shell::PlatformSetAddressBarURL(const GURL& url) {
472 ShellWindowDelegateView* delegate_view =
473 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
474 delegate_view->SetAddressBarURL(url);
477 void Shell::PlatformSetIsLoading(bool loading) {
480 void Shell::PlatformCreateWindow(int width, int height) {
482 content_size_ = gfx::Size(width, height);
484 platform_ = new ShellPlatformDataAura(content_size_);
486 platform_->ResizeWindow(content_size_);
489 #if defined(OS_CHROMEOS)
490 window_widget_ = views::Widget::CreateWindowWithContextAndBounds(
491 new ShellWindowDelegateView(this),
492 wm_test_helper_->GetDefaultParent(NULL, NULL, gfx::Rect()),
493 gfx::Rect(0, 0, width, height));
495 window_widget_ = new views::Widget;
496 views::Widget::InitParams params;
497 params.bounds = gfx::Rect(0, 0, width, height);
498 params.delegate = new ShellWindowDelegateView(this);
499 params.top_level = true;
500 params.remove_standard_frame = true;
501 window_widget_->Init(params);
504 content_size_ = gfx::Size(width, height);
506 window_ = window_widget_->GetNativeWindow();
507 // Call ShowRootWindow on RootWindow created by WMTestHelper without
508 // which XWindow owned by RootWindow doesn't get mapped.
509 window_->GetHost()->Show();
510 window_widget_->Show();
513 void Shell::PlatformSetContents() {
516 aura::Window* content = web_contents_->GetNativeView();
517 aura::Window* parent = platform_->host()->window();
518 if (!parent->Contains(content)) {
519 parent->AddChild(content);
522 content->SetBounds(gfx::Rect(content_size_));
523 RenderWidgetHostView* host_view = web_contents_->GetRenderWidgetHostView();
525 host_view->SetSize(content_size_);
527 views::WidgetDelegate* widget_delegate = window_widget_->widget_delegate();
528 ShellWindowDelegateView* delegate_view =
529 static_cast<ShellWindowDelegateView*>(widget_delegate);
530 delegate_view->SetWebContents(web_contents_.get(), content_size_);
534 void Shell::PlatformResizeSubViews() {
537 void Shell::Close() {
541 window_widget_->CloseNow();
544 void Shell::PlatformSetTitle(const base::string16& title) {
547 ShellWindowDelegateView* delegate_view =
548 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
549 delegate_view->SetWindowTitle(title);
550 window_widget_->UpdateWindowTitle();
553 bool Shell::PlatformHandleContextMenu(
554 const content::ContextMenuParams& params) {
557 ShellWindowDelegateView* delegate_view =
558 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
559 delegate_view->ShowWebViewContextMenu(params);
563 void Shell::PlatformWebContentsFocused(WebContents* contents) {
566 ShellWindowDelegateView* delegate_view =
567 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
568 delegate_view->OnWebContentsFocused(contents);
571 } // namespace content