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. 4 5#include "content/shell/browser/shell.h" 6 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" 31 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" 36#endif 37 38#if defined(OS_WIN) 39#include <fcntl.h> 40#include <io.h> 41#endif 42 43namespace content { 44 45namespace { 46// ViewDelegate implementation for aura content shell 47class ShellViewsDelegateAura : public views::DesktopTestViewsDelegate { 48 public: 49 ShellViewsDelegateAura() : use_transparent_windows_(false) { 50 } 51 52 virtual ~ShellViewsDelegateAura() { 53 } 54 55 void SetUseTransparentWindows(bool transparent) { 56 use_transparent_windows_ = transparent; 57 } 58 59 // Overridden from views::TestViewsDelegate: 60 virtual bool UseTransparentWindows() const OVERRIDE { 61 return use_transparent_windows_; 62 } 63 64 private: 65 bool use_transparent_windows_; 66 67 DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegateAura); 68}; 69 70// Maintain the UI controls and web view for content shell 71class ShellWindowDelegateView : public views::WidgetDelegateView, 72 public views::TextfieldController, 73 public views::ButtonListener { 74 public: 75 enum UIControl { 76 BACK_BUTTON, 77 FORWARD_BUTTON, 78 STOP_BUTTON 79 }; 80 81 ShellWindowDelegateView(Shell* shell) 82 : shell_(shell), 83 toolbar_view_(new View), 84 contents_view_(new View) { 85 } 86 virtual ~ShellWindowDelegateView() {} 87 88 // Update the state of UI controls 89 void SetAddressBarURL(const GURL& url) { 90 url_entry_->SetText(ASCIIToUTF16(url.spec())); 91 } 92 void SetWebContents(WebContents* web_contents, const gfx::Size& size) { 93 contents_view_->SetLayoutManager(new views::FillLayout()); 94 web_view_ = new views::WebView(web_contents->GetBrowserContext()); 95 web_view_->SetWebContents(web_contents); 96 web_view_->SetPreferredSize(size); 97 web_contents->GetView()->Focus(); 98 contents_view_->AddChildView(web_view_); 99 Layout(); 100 101 // Resize the widget, keeping the same origin. 102 gfx::Rect bounds = GetWidget()->GetWindowBoundsInScreen(); 103 bounds.set_size(GetWidget()->GetRootView()->GetPreferredSize()); 104 GetWidget()->SetBounds(bounds); 105 106 // Resizing a widget on chromeos doesn't automatically resize the root, need 107 // to explicitly do that. 108#if defined(OS_CHROMEOS) 109 GetWidget()->GetNativeWindow()->GetDispatcher()->SetHostSize( 110 bounds.size()); 111#endif 112 } 113 114 void SetWindowTitle(const base::string16& title) { title_ = title; } 115 void EnableUIControl(UIControl control, bool is_enabled) { 116 if (control == BACK_BUTTON) { 117 back_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL 118 : views::CustomButton::STATE_DISABLED); 119 } else if (control == FORWARD_BUTTON) { 120 forward_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL 121 : views::CustomButton::STATE_DISABLED); 122 } else if (control == STOP_BUTTON) { 123 stop_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL 124 : views::CustomButton::STATE_DISABLED); 125 } 126 } 127 128 private: 129 // Initialize the UI control contained in shell window 130 void InitShellWindow() { 131 set_background(views::Background::CreateStandardPanelBackground()); 132 133 views::GridLayout* layout = new views::GridLayout(this); 134 SetLayoutManager(layout); 135 136 views::ColumnSet* column_set = layout->AddColumnSet(0); 137 column_set->AddPaddingColumn(0, 2); 138 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, 139 views::GridLayout::USE_PREF, 0, 0); 140 column_set->AddPaddingColumn(0, 2); 141 142 layout->AddPaddingRow(0, 2); 143 144 // Add toolbar buttons and URL text field 145 { 146 layout->StartRow(0, 0); 147 views::GridLayout* toolbar_layout = new views::GridLayout(toolbar_view_); 148 toolbar_view_->SetLayoutManager(toolbar_layout); 149 150 views::ColumnSet* toolbar_column_set = 151 toolbar_layout->AddColumnSet(0); 152 // Back button 153 back_button_ = new views::LabelButton(this, ASCIIToUTF16("Back")); 154 back_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); 155 gfx::Size back_button_size = back_button_->GetPreferredSize(); 156 toolbar_column_set->AddColumn(views::GridLayout::CENTER, 157 views::GridLayout::CENTER, 0, 158 views::GridLayout::FIXED, 159 back_button_size.width(), 160 back_button_size.width() / 2); 161 // Forward button 162 forward_button_ = new views::LabelButton(this, ASCIIToUTF16("Forward")); 163 forward_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); 164 gfx::Size forward_button_size = forward_button_->GetPreferredSize(); 165 toolbar_column_set->AddColumn(views::GridLayout::CENTER, 166 views::GridLayout::CENTER, 0, 167 views::GridLayout::FIXED, 168 forward_button_size.width(), 169 forward_button_size.width() / 2); 170 // Refresh button 171 refresh_button_ = new views::LabelButton(this, ASCIIToUTF16("Refresh")); 172 refresh_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); 173 gfx::Size refresh_button_size = refresh_button_->GetPreferredSize(); 174 toolbar_column_set->AddColumn(views::GridLayout::CENTER, 175 views::GridLayout::CENTER, 0, 176 views::GridLayout::FIXED, 177 refresh_button_size.width(), 178 refresh_button_size.width() / 2); 179 // Stop button 180 stop_button_ = new views::LabelButton(this, ASCIIToUTF16("Stop")); 181 stop_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); 182 gfx::Size stop_button_size = stop_button_->GetPreferredSize(); 183 toolbar_column_set->AddColumn(views::GridLayout::CENTER, 184 views::GridLayout::CENTER, 0, 185 views::GridLayout::FIXED, 186 stop_button_size.width(), 187 stop_button_size.width() / 2); 188 toolbar_column_set->AddPaddingColumn(0, 2); 189 // URL entry 190 url_entry_ = new views::Textfield(); 191 url_entry_->SetController(this); 192 toolbar_column_set->AddColumn(views::GridLayout::FILL, 193 views::GridLayout::FILL, 1, 194 views::GridLayout::USE_PREF, 0, 0); 195 196 // Fill up the first row 197 toolbar_layout->StartRow(0, 0); 198 toolbar_layout->AddView(back_button_); 199 toolbar_layout->AddView(forward_button_); 200 toolbar_layout->AddView(refresh_button_); 201 toolbar_layout->AddView(stop_button_); 202 toolbar_layout->AddView(url_entry_); 203 204 layout->AddView(toolbar_view_); 205 } 206 207 layout->AddPaddingRow(0, 5); 208 209 // Add web contents view as the second row 210 { 211 layout->StartRow(1, 0); 212 layout->AddView(contents_view_); 213 } 214 215 layout->AddPaddingRow(0, 5); 216 } 217 // Overridden from TextfieldController 218 virtual void ContentsChanged(views::Textfield* sender, 219 const base::string16& new_contents) OVERRIDE { 220 } 221 virtual bool HandleKeyEvent(views::Textfield* sender, 222 const ui::KeyEvent& key_event) OVERRIDE { 223 if (sender == url_entry_ && key_event.key_code() == ui::VKEY_RETURN) { 224 std::string text = UTF16ToUTF8(url_entry_->text()); 225 GURL url(text); 226 if (!url.has_scheme()) { 227 url = GURL(std::string("http://") + std::string(text)); 228 url_entry_->SetText(ASCIIToUTF16(url.spec())); 229 } 230 shell_->LoadURL(url); 231 return true; 232 } 233 return false; 234 } 235 236 // Overridden from ButtonListener 237 virtual void ButtonPressed(views::Button* sender, 238 const ui::Event& event) OVERRIDE { 239 if (sender == back_button_) 240 shell_->GoBackOrForward(-1); 241 else if (sender == forward_button_) 242 shell_->GoBackOrForward(1); 243 else if (sender == refresh_button_) 244 shell_->Reload(); 245 else if (sender == stop_button_) 246 shell_->Stop(); 247 } 248 249 // Overridden from WidgetDelegateView 250 virtual bool CanResize() const OVERRIDE { return true; } 251 virtual bool CanMaximize() const OVERRIDE { return true; } 252 virtual base::string16 GetWindowTitle() const OVERRIDE { 253 return title_; 254 } 255 virtual void WindowClosing() OVERRIDE { 256 if (shell_) { 257 delete shell_; 258 shell_ = NULL; 259 } 260 } 261 virtual View* GetContentsView() OVERRIDE { return this; } 262 263 // Overridden from View 264 virtual void ViewHierarchyChanged( 265 const ViewHierarchyChangedDetails& details) OVERRIDE { 266 if (details.is_add && details.child == this) { 267 InitShellWindow(); 268 } 269 } 270 271 private: 272 // Hold a reference of Shell for deleting it when the window is closing 273 Shell* shell_; 274 275 // Window title 276 base::string16 title_; 277 278 // Toolbar view contains forward/backward/reload button and URL entry 279 View* toolbar_view_; 280 views::LabelButton* back_button_; 281 views::LabelButton* forward_button_; 282 views::LabelButton* refresh_button_; 283 views::LabelButton* stop_button_; 284 views::Textfield* url_entry_; 285 286 // Contents view contains the web contents view 287 View* contents_view_; 288 views::WebView* web_view_; 289 290 DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView); 291}; 292 293} // namespace 294 295#if defined(OS_CHROMEOS) 296wm::WMTestHelper* Shell::wm_test_helper_ = NULL; 297#endif 298views::ViewsDelegate* Shell::views_delegate_ = NULL; 299 300// static 301void Shell::PlatformInitialize(const gfx::Size& default_window_size) { 302#if defined(OS_WIN) 303 _setmode(_fileno(stdout), _O_BINARY); 304 _setmode(_fileno(stderr), _O_BINARY); 305#endif 306#if defined(OS_CHROMEOS) 307 chromeos::DBusThreadManager::Initialize(); 308 gfx::Screen::SetScreenInstance( 309 gfx::SCREEN_TYPE_NATIVE, aura::TestScreen::Create()); 310 wm_test_helper_ = new wm::WMTestHelper(default_window_size); 311#else 312 gfx::Screen::SetScreenInstance( 313 gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen()); 314#endif 315 views_delegate_ = new ShellViewsDelegateAura(); 316} 317 318void Shell::PlatformExit() { 319#if defined(OS_CHROMEOS) 320 if (wm_test_helper_) 321 delete wm_test_helper_; 322#endif 323 if (views_delegate_) 324 delete views_delegate_; 325#if defined(OS_CHROMEOS) 326 chromeos::DBusThreadManager::Shutdown(); 327#endif 328 aura::Env::DeleteInstance(); 329} 330 331void Shell::PlatformCleanUp() { 332} 333 334void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) { 335 ShellWindowDelegateView* delegate_view = 336 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate()); 337 if (control == BACK_BUTTON) { 338 delegate_view->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON, 339 is_enabled); 340 } else if (control == FORWARD_BUTTON) { 341 delegate_view->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON, 342 is_enabled); 343 } else if (control == STOP_BUTTON) { 344 delegate_view->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON, 345 is_enabled); 346 } 347} 348 349void Shell::PlatformSetAddressBarURL(const GURL& url) { 350 ShellWindowDelegateView* delegate_view = 351 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate()); 352 delegate_view->SetAddressBarURL(url); 353} 354 355void Shell::PlatformSetIsLoading(bool loading) { 356} 357 358void Shell::PlatformCreateWindow(int width, int height) { 359#if defined(OS_CHROMEOS) 360 window_widget_ = views::Widget::CreateWindowWithContextAndBounds( 361 new ShellWindowDelegateView(this), 362 wm_test_helper_->GetDefaultParent(NULL, NULL, gfx::Rect()), 363 gfx::Rect(0, 0, width, height)); 364#else 365 window_widget_ = views::Widget::CreateWindowWithBounds( 366 new ShellWindowDelegateView(this), gfx::Rect(0, 0, width, height)); 367#endif 368 369 content_size_ = gfx::Size(width, height); 370 371 window_ = window_widget_->GetNativeWindow(); 372 // Call ShowRootWindow on RootWindow created by WMTestHelper without 373 // which XWindow owned by RootWindow doesn't get mapped. 374 window_->GetDispatcher()->host()->Show(); 375 window_widget_->Show(); 376} 377 378void Shell::PlatformSetContents() { 379 ShellWindowDelegateView* delegate_view = 380 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate()); 381 delegate_view->SetWebContents(web_contents_.get(), content_size_); 382} 383 384void Shell::PlatformResizeSubViews() { 385} 386 387void Shell::Close() { 388 window_widget_->CloseNow(); 389} 390 391void Shell::PlatformSetTitle(const base::string16& title) { 392 ShellWindowDelegateView* delegate_view = 393 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate()); 394 delegate_view->SetWindowTitle(title); 395 window_widget_->UpdateWindowTitle(); 396} 397 398} // namespace content 399