Introduce the PlatformNotificationContext class.
[chromium-blink-merge.git] / content / shell / browser / shell_views.cc
blob6805aa2ad5d706beacdcd23efbc9e6f3f483d851
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 void OnWebContentsFocused(content::WebContents* web_contents) {
198 if (web_view_->GetWebContents() == web_contents)
199 web_view_->OnWebContentsFocused(web_contents);
202 private:
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);
226 // Back button
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);
235 // Forward button
236 forward_button_ =
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);
245 // Refresh button
246 refresh_button_ =
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);
255 // Stop button
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);
265 // URL entry
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);
294 InitAccelerators();
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,
304 this);
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());
314 GURL url(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);
320 return true;
322 return false;
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_)
332 shell_->Reload();
333 else if (sender == stop_button_)
334 shell_->Stop();
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 {
343 if (shell_) {
344 delete shell_;
345 shell_ = NULL;
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
353 // (preferred) size.
354 return gfx::Size();
356 void ViewHierarchyChanged(
357 const ViewHierarchyChangedDetails& details) override {
358 if (details.is_add && details.child == this) {
359 InitShellWindow();
363 // Overridden from AcceleratorTarget:
364 bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
365 switch (accelerator.key_code()) {
366 case ui::VKEY_F5:
367 shell_->Reload();
368 return true;
369 case ui::VKEY_BROWSER_BACK:
370 shell_->GoBackOrForward(-1);
371 return true;
372 case ui::VKEY_BROWSER_FORWARD:
373 shell_->GoBackOrForward(1);
374 return true;
375 default:
376 return views::WidgetDelegateView::AcceleratorPressed(accelerator);
380 private:
381 // Hold a reference of Shell for deleting it when the window is closing
382 Shell* shell_;
384 // Window title
385 base::string16 title_;
387 // Toolbar view contains forward/backward/reload button and URL entry
388 View* toolbar_view_;
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);
404 } // namespace
406 #if defined(OS_CHROMEOS)
407 wm::WMTestHelper* Shell::wm_test_helper_ = NULL;
408 gfx::Screen* Shell::test_screen_ = NULL;
409 #endif
410 views::ViewsDelegate* Shell::views_delegate_ = NULL;
412 // static
413 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
414 #if defined(OS_WIN)
415 _setmode(_fileno(stdout), _O_BINARY);
416 _setmode(_fileno(stderr), _O_BINARY);
417 #endif
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());
424 #else
425 gfx::Screen::SetScreenInstance(
426 gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen());
427 #endif
428 views_delegate_ = new ShellViewsDelegateAura();
431 void Shell::PlatformExit() {
432 #if defined(OS_CHROMEOS)
433 delete wm_test_helper_;
434 wm_test_helper_ = NULL;
436 delete test_screen_;
437 test_screen_ = NULL;
438 #endif
439 delete views_delegate_;
440 views_delegate_ = NULL;
441 delete platform_;
442 platform_ = NULL;
443 #if defined(OS_CHROMEOS)
444 device::BluetoothAdapterFactory::Shutdown();
445 chromeos::DBusThreadManager::Shutdown();
446 #endif
447 aura::Env::DeleteInstance();
450 void Shell::PlatformCleanUp() {
453 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
454 if (headless_)
455 return;
456 ShellWindowDelegateView* delegate_view =
457 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
458 if (control == BACK_BUTTON) {
459 delegate_view->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON,
460 is_enabled);
461 } else if (control == FORWARD_BUTTON) {
462 delegate_view->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON,
463 is_enabled);
464 } else if (control == STOP_BUTTON) {
465 delegate_view->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON,
466 is_enabled);
470 void Shell::PlatformSetAddressBarURL(const GURL& url) {
471 if (headless_)
472 return;
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) {
482 if (headless_) {
483 content_size_ = gfx::Size(width, height);
484 if (!platform_)
485 platform_ = new ShellPlatformDataAura(content_size_);
486 else
487 platform_->ResizeWindow(content_size_);
488 return;
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));
495 #else
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);
501 #endif
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() {
513 if (headless_) {
514 CHECK(platform_);
515 aura::Window* content = web_contents_->GetNativeView();
516 aura::Window* parent = platform_->host()->window();
517 if (!parent->Contains(content)) {
518 parent->AddChild(content);
519 content->Show();
521 content->SetBounds(gfx::Rect(content_size_));
522 RenderWidgetHostView* host_view = web_contents_->GetRenderWidgetHostView();
523 if (host_view)
524 host_view->SetSize(content_size_);
525 } else {
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() {
537 if (headless_)
538 delete this;
539 else
540 window_widget_->CloseNow();
543 void Shell::PlatformSetTitle(const base::string16& title) {
544 if (headless_)
545 return;
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) {
554 if (headless_)
555 return true;
556 ShellWindowDelegateView* delegate_view =
557 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
558 delegate_view->ShowWebViewContextMenu(params);
559 return true;
562 void Shell::PlatformWebContentsFocused(WebContents* contents) {
563 if (headless_)
564 return;
565 ShellWindowDelegateView* delegate_view =
566 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
567 delegate_view->OnWebContentsFocused(contents);
570 } // namespace content