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
43 namespace content {
44
45 namespace {
46 // ViewDelegate implementation for aura content shell
47 class ShellViewsDelegateAura : public views::DesktopTestViewsDelegate {
48 public:
ShellViewsDelegateAura()49 ShellViewsDelegateAura() : use_transparent_windows_(false) {
50 }
51
~ShellViewsDelegateAura()52 virtual ~ShellViewsDelegateAura() {
53 }
54
SetUseTransparentWindows(bool transparent)55 void SetUseTransparentWindows(bool transparent) {
56 use_transparent_windows_ = transparent;
57 }
58
59 // Overridden from views::TestViewsDelegate:
UseTransparentWindows() const60 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
71 class 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
ShellWindowDelegateView(Shell * shell)81 ShellWindowDelegateView(Shell* shell)
82 : shell_(shell),
83 toolbar_view_(new View),
84 contents_view_(new View) {
85 }
~ShellWindowDelegateView()86 virtual ~ShellWindowDelegateView() {}
87
88 // Update the state of UI controls
SetAddressBarURL(const GURL & url)89 void SetAddressBarURL(const GURL& url) {
90 url_entry_->SetText(ASCIIToUTF16(url.spec()));
91 }
SetWebContents(WebContents * web_contents,const gfx::Size & size)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
SetWindowTitle(const base::string16 & title)114 void SetWindowTitle(const base::string16& title) { title_ = title; }
EnableUIControl(UIControl control,bool is_enabled)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
InitShellWindow()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
ContentsChanged(views::Textfield * sender,const base::string16 & new_contents)218 virtual void ContentsChanged(views::Textfield* sender,
219 const base::string16& new_contents) OVERRIDE {
220 }
HandleKeyEvent(views::Textfield * sender,const ui::KeyEvent & key_event)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
ButtonPressed(views::Button * sender,const ui::Event & event)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
CanResize() const250 virtual bool CanResize() const OVERRIDE { return true; }
CanMaximize() const251 virtual bool CanMaximize() const OVERRIDE { return true; }
GetWindowTitle() const252 virtual base::string16 GetWindowTitle() const OVERRIDE {
253 return title_;
254 }
WindowClosing()255 virtual void WindowClosing() OVERRIDE {
256 if (shell_) {
257 delete shell_;
258 shell_ = NULL;
259 }
260 }
GetContentsView()261 virtual View* GetContentsView() OVERRIDE { return this; }
262
263 // Overridden from View
ViewHierarchyChanged(const ViewHierarchyChangedDetails & details)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)
296 wm::WMTestHelper* Shell::wm_test_helper_ = NULL;
297 #endif
298 views::ViewsDelegate* Shell::views_delegate_ = NULL;
299
300 // static
PlatformInitialize(const gfx::Size & default_window_size)301 void 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
PlatformExit()318 void 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
PlatformCleanUp()331 void Shell::PlatformCleanUp() {
332 }
333
PlatformEnableUIControl(UIControl control,bool is_enabled)334 void 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
PlatformSetAddressBarURL(const GURL & url)349 void Shell::PlatformSetAddressBarURL(const GURL& url) {
350 ShellWindowDelegateView* delegate_view =
351 static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
352 delegate_view->SetAddressBarURL(url);
353 }
354
PlatformSetIsLoading(bool loading)355 void Shell::PlatformSetIsLoading(bool loading) {
356 }
357
PlatformCreateWindow(int width,int height)358 void 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
PlatformSetContents()378 void 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
PlatformResizeSubViews()384 void Shell::PlatformResizeSubViews() {
385 }
386
Close()387 void Shell::Close() {
388 window_widget_->CloseNow();
389 }
390
PlatformSetTitle(const base::string16 & title)391 void 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