Upstreaming browser/ui/uikit_ui_util from iOS.
[chromium-blink-merge.git] / content / shell / browser / shell_views.cc
blob4ce0fa6806ffb5a87c9390f0a4659a804a9e6db1
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"
45 #endif
47 #if defined(OS_WIN)
48 #include <fcntl.h>
49 #include <io.h>
50 #endif
52 namespace content {
54 namespace {
55 // ViewDelegate implementation for aura content shell
56 class ShellViewsDelegateAura : public views::DesktopTestViewsDelegate {
57 public:
58 ShellViewsDelegateAura() : use_transparent_windows_(false) {
61 ~ShellViewsDelegateAura() override {}
63 void SetUseTransparentWindows(bool transparent) {
64 use_transparent_windows_ = transparent;
67 private:
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 {
76 public:
77 explicit ContextMenuModel(
78 Shell* shell, const content::ContextMenuParams& params)
79 : ui::SimpleMenuModel(this),
80 shell_(shell),
81 params_(params) {
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 {
90 return false;
92 void ExecuteCommand(int command_id, int event_flags) override {
93 switch (command_id) {
94 case COMMAND_OPEN_DEVTOOLS:
95 shell_->ShowDevToolsForElementAt(params_.x, params_.y);
96 break;
100 private:
101 enum CommandID {
102 COMMAND_OPEN_DEVTOOLS
105 Shell* shell_;
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 {
115 public:
116 enum UIControl {
117 BACK_BUTTON,
118 FORWARD_BUTTON,
119 STOP_BUTTON
122 ShellWindowDelegateView(Shell* shell)
123 : 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_);
140 Layout();
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);
151 #endif
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,
180 &screen_point);
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(),
188 NULL,
189 gfx::Rect(screen_point, gfx::Size()),
190 views::MENU_ANCHOR_TOPRIGHT,
191 ui::MENU_SOURCE_NONE) ==
192 views::MenuRunner::MENU_DELETED) {
193 return;
197 private:
198 // Initialize the UI control contained in shell window
199 void InitShellWindow() {
200 set_background(views::Background::CreateStandardPanelBackground());
202 views::GridLayout* layout = new views::GridLayout(this);
203 SetLayoutManager(layout);
205 views::ColumnSet* column_set = layout->AddColumnSet(0);
206 column_set->AddPaddingColumn(0, 2);
207 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
208 views::GridLayout::USE_PREF, 0, 0);
209 column_set->AddPaddingColumn(0, 2);
211 layout->AddPaddingRow(0, 2);
213 // Add toolbar buttons and URL text field
215 layout->StartRow(0, 0);
216 views::GridLayout* toolbar_layout = new views::GridLayout(toolbar_view_);
217 toolbar_view_->SetLayoutManager(toolbar_layout);
219 views::ColumnSet* toolbar_column_set =
220 toolbar_layout->AddColumnSet(0);
221 // Back button
222 back_button_ = new views::LabelButton(this, base::ASCIIToUTF16("Back"));
223 back_button_->SetStyle(views::Button::STYLE_BUTTON);
224 gfx::Size back_button_size = back_button_->GetPreferredSize();
225 toolbar_column_set->AddColumn(views::GridLayout::CENTER,
226 views::GridLayout::CENTER, 0,
227 views::GridLayout::FIXED,
228 back_button_size.width(),
229 back_button_size.width() / 2);
230 // Forward button
231 forward_button_ =
232 new views::LabelButton(this, base::ASCIIToUTF16("Forward"));
233 forward_button_->SetStyle(views::Button::STYLE_BUTTON);
234 gfx::Size forward_button_size = forward_button_->GetPreferredSize();
235 toolbar_column_set->AddColumn(views::GridLayout::CENTER,
236 views::GridLayout::CENTER, 0,
237 views::GridLayout::FIXED,
238 forward_button_size.width(),
239 forward_button_size.width() / 2);
240 // Refresh button
241 refresh_button_ =
242 new views::LabelButton(this, base::ASCIIToUTF16("Refresh"));
243 refresh_button_->SetStyle(views::Button::STYLE_BUTTON);
244 gfx::Size refresh_button_size = refresh_button_->GetPreferredSize();
245 toolbar_column_set->AddColumn(views::GridLayout::CENTER,
246 views::GridLayout::CENTER, 0,
247 views::GridLayout::FIXED,
248 refresh_button_size.width(),
249 refresh_button_size.width() / 2);
250 // Stop button
251 stop_button_ = new views::LabelButton(this, base::ASCIIToUTF16("Stop"));
252 stop_button_->SetStyle(views::Button::STYLE_BUTTON);
253 gfx::Size stop_button_size = stop_button_->GetPreferredSize();
254 toolbar_column_set->AddColumn(views::GridLayout::CENTER,
255 views::GridLayout::CENTER, 0,
256 views::GridLayout::FIXED,
257 stop_button_size.width(),
258 stop_button_size.width() / 2);
259 toolbar_column_set->AddPaddingColumn(0, 2);
260 // URL entry
261 url_entry_ = new views::Textfield();
262 url_entry_->set_controller(this);
263 toolbar_column_set->AddColumn(views::GridLayout::FILL,
264 views::GridLayout::FILL, 1,
265 views::GridLayout::USE_PREF, 0, 0);
266 toolbar_column_set->AddPaddingColumn(0, 2);
268 // Fill up the first row
269 toolbar_layout->StartRow(0, 0);
270 toolbar_layout->AddView(back_button_);
271 toolbar_layout->AddView(forward_button_);
272 toolbar_layout->AddView(refresh_button_);
273 toolbar_layout->AddView(stop_button_);
274 toolbar_layout->AddView(url_entry_);
276 layout->AddView(toolbar_view_);
279 layout->AddPaddingRow(0, 5);
281 // Add web contents view as the second row
283 layout->StartRow(1, 0);
284 layout->AddView(contents_view_);
287 layout->AddPaddingRow(0, 5);
289 InitAccelerators();
291 void InitAccelerators() {
292 static const ui::KeyboardCode keys[] = { ui::VKEY_F5,
293 ui::VKEY_BROWSER_BACK,
294 ui::VKEY_BROWSER_FORWARD };
295 for (size_t i = 0; i < arraysize(keys); ++i) {
296 GetFocusManager()->RegisterAccelerator(
297 ui::Accelerator(keys[i], ui::EF_NONE),
298 ui::AcceleratorManager::kNormalPriority,
299 this);
302 // Overridden from TextfieldController
303 void ContentsChanged(views::Textfield* sender,
304 const base::string16& new_contents) override {}
305 bool HandleKeyEvent(views::Textfield* sender,
306 const ui::KeyEvent& key_event) override {
307 if (sender == url_entry_ && key_event.key_code() == ui::VKEY_RETURN) {
308 std::string text = base::UTF16ToUTF8(url_entry_->text());
309 GURL url(text);
310 if (!url.has_scheme()) {
311 url = GURL(std::string("http://") + std::string(text));
312 url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
314 shell_->LoadURL(url);
315 return true;
317 return false;
320 // Overridden from ButtonListener
321 void ButtonPressed(views::Button* sender, const ui::Event& event) override {
322 if (sender == back_button_)
323 shell_->GoBackOrForward(-1);
324 else if (sender == forward_button_)
325 shell_->GoBackOrForward(1);
326 else if (sender == refresh_button_)
327 shell_->Reload();
328 else if (sender == stop_button_)
329 shell_->Stop();
332 // Overridden from WidgetDelegateView
333 bool CanResize() const override { return true; }
334 bool CanMaximize() const override { return true; }
335 bool CanMinimize() const override { return true; }
336 base::string16 GetWindowTitle() const override { return title_; }
337 void WindowClosing() override {
338 if (shell_) {
339 delete shell_;
340 shell_ = NULL;
343 View* GetContentsView() override { return this; }
345 // Overridden from View
346 gfx::Size GetMinimumSize() const override {
347 // We want to be able to make the window smaller than its initial
348 // (preferred) size.
349 return gfx::Size();
351 void ViewHierarchyChanged(
352 const ViewHierarchyChangedDetails& details) override {
353 if (details.is_add && details.child == this) {
354 InitShellWindow();
358 // Overridden from AcceleratorTarget:
359 bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
360 switch (accelerator.key_code()) {
361 case ui::VKEY_F5:
362 shell_->Reload();
363 return true;
364 case ui::VKEY_BROWSER_BACK:
365 shell_->GoBackOrForward(-1);
366 return true;
367 case ui::VKEY_BROWSER_FORWARD:
368 shell_->GoBackOrForward(1);
369 return true;
370 default:
371 return views::WidgetDelegateView::AcceleratorPressed(accelerator);
375 private:
376 // Hold a reference of Shell for deleting it when the window is closing
377 Shell* shell_;
379 // Window title
380 base::string16 title_;
382 // Toolbar view contains forward/backward/reload button and URL entry
383 View* toolbar_view_;
384 views::LabelButton* back_button_;
385 views::LabelButton* forward_button_;
386 views::LabelButton* refresh_button_;
387 views::LabelButton* stop_button_;
388 views::Textfield* url_entry_;
389 scoped_ptr<ContextMenuModel> context_menu_model_;
390 scoped_ptr<views::MenuRunner> context_menu_runner_;
392 // Contents view contains the web contents view
393 View* contents_view_;
394 views::WebView* web_view_;
396 DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView);
399 } // namespace
401 #if defined(OS_CHROMEOS)
402 wm::WMTestHelper* Shell::wm_test_helper_ = NULL;
403 gfx::Screen* Shell::test_screen_ = NULL;
404 #endif
405 views::ViewsDelegate* Shell::views_delegate_ = NULL;
407 // static
408 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
409 #if defined(OS_WIN)
410 _setmode(_fileno(stdout), _O_BINARY);
411 _setmode(_fileno(stderr), _O_BINARY);
412 #endif
413 #if defined(OS_CHROMEOS)
414 chromeos::DBusThreadManager::Initialize();
415 test_screen_ = aura::TestScreen::Create(gfx::Size());
416 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen_);
417 wm_test_helper_ = new wm::WMTestHelper(default_window_size,
418 GetContextFactory());
419 #else
420 gfx::Screen::SetScreenInstance(
421 gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen());
422 #endif
423 views_delegate_ = new ShellViewsDelegateAura();
426 void Shell::PlatformExit() {
427 #if defined(OS_CHROMEOS)
428 delete wm_test_helper_;
429 wm_test_helper_ = NULL;
431 delete test_screen_;
432 test_screen_ = NULL;
433 #endif
434 delete views_delegate_;
435 views_delegate_ = NULL;
436 delete platform_;
437 platform_ = NULL;
438 #if defined(OS_CHROMEOS)
439 device::BluetoothAdapterFactory::Shutdown();
440 chromeos::DBusThreadManager::Shutdown();
441 #endif
442 aura::Env::DeleteInstance();
445 void Shell::PlatformCleanUp() {
448 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
449 if (headless_)
450 return;
451 ShellWindowDelegateView* delegate_view =
452 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
453 if (control == BACK_BUTTON) {
454 delegate_view->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON,
455 is_enabled);
456 } else if (control == FORWARD_BUTTON) {
457 delegate_view->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON,
458 is_enabled);
459 } else if (control == STOP_BUTTON) {
460 delegate_view->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON,
461 is_enabled);
465 void Shell::PlatformSetAddressBarURL(const GURL& url) {
466 if (headless_)
467 return;
468 ShellWindowDelegateView* delegate_view =
469 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
470 delegate_view->SetAddressBarURL(url);
473 void Shell::PlatformSetIsLoading(bool loading) {
476 void Shell::PlatformCreateWindow(int width, int height) {
477 if (headless_) {
478 content_size_ = gfx::Size(width, height);
479 if (!platform_)
480 platform_ = new ShellPlatformDataAura(content_size_);
481 else
482 platform_->ResizeWindow(content_size_);
483 return;
485 #if defined(OS_CHROMEOS)
486 window_widget_ = views::Widget::CreateWindowWithContextAndBounds(
487 new ShellWindowDelegateView(this),
488 wm_test_helper_->GetDefaultParent(NULL, NULL, gfx::Rect()),
489 gfx::Rect(0, 0, width, height));
490 #else
491 window_widget_ = new views::Widget;
492 views::Widget::InitParams params;
493 params.bounds = gfx::Rect(0, 0, width, height);
494 params.delegate = new ShellWindowDelegateView(this);
495 window_widget_->Init(params);
496 #endif
498 content_size_ = gfx::Size(width, height);
500 window_ = window_widget_->GetNativeWindow();
501 // Call ShowRootWindow on RootWindow created by WMTestHelper without
502 // which XWindow owned by RootWindow doesn't get mapped.
503 window_->GetHost()->Show();
504 window_widget_->Show();
507 void Shell::PlatformSetContents() {
508 if (headless_) {
509 CHECK(platform_);
510 aura::Window* content = web_contents_->GetNativeView();
511 aura::Window* parent = platform_->host()->window();
512 if (!parent->Contains(content)) {
513 parent->AddChild(content);
514 content->Show();
516 content->SetBounds(gfx::Rect(content_size_));
517 RenderWidgetHostView* host_view = web_contents_->GetRenderWidgetHostView();
518 if (host_view)
519 host_view->SetSize(content_size_);
520 } else {
521 views::WidgetDelegate* widget_delegate = window_widget_->widget_delegate();
522 ShellWindowDelegateView* delegate_view =
523 static_cast<ShellWindowDelegateView*>(widget_delegate);
524 delegate_view->SetWebContents(web_contents_.get(), content_size_);
528 void Shell::PlatformResizeSubViews() {
531 void Shell::Close() {
532 if (headless_)
533 delete this;
534 else
535 window_widget_->CloseNow();
538 void Shell::PlatformSetTitle(const base::string16& title) {
539 if (headless_)
540 return;
541 ShellWindowDelegateView* delegate_view =
542 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
543 delegate_view->SetWindowTitle(title);
544 window_widget_->UpdateWindowTitle();
547 bool Shell::PlatformHandleContextMenu(
548 const content::ContextMenuParams& params) {
549 if (headless_)
550 return true;
551 ShellWindowDelegateView* delegate_view =
552 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
553 delegate_view->ShowWebViewContextMenu(params);
554 return true;
557 } // namespace content