Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / shell / browser / shell_views.cc
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/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"
36
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"
43 #endif
44
45 #if defined(OS_WIN)
46 #include <fcntl.h>
47 #include <io.h>
48 #endif
49
50 namespace content {
51
52 namespace {
53 // ViewDelegate implementation for aura content shell
54 class ShellViewsDelegateAura : public views::DesktopTestViewsDelegate {
55  public:
56   ShellViewsDelegateAura() : use_transparent_windows_(false) {
57   }
58
59   virtual ~ShellViewsDelegateAura() {
60   }
61
62   void SetUseTransparentWindows(bool transparent) {
63     use_transparent_windows_ = transparent;
64   }
65
66  private:
67   bool use_transparent_windows_;
68
69   DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegateAura);
70 };
71
72 // Model for the "Debug" menu
73 class ContextMenuModel : public ui::SimpleMenuModel,
74                          public ui::SimpleMenuModel::Delegate {
75  public:
76   explicit ContextMenuModel(
77       Shell* shell, const content::ContextMenuParams& params)
78     : ui::SimpleMenuModel(this),
79       shell_(shell),
80       params_(params) {
81     AddItem(COMMAND_OPEN_DEVTOOLS, base::ASCIIToUTF16("Inspect Element"));
82   }
83
84   // ui::SimpleMenuModel::Delegate:
85   virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
86     return false;
87   }
88   virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
89     return true;
90   }
91   virtual bool GetAcceleratorForCommandId(
92       int command_id,
93       ui::Accelerator* accelerator) OVERRIDE { return false; }
94   virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
95     switch (command_id) {
96       case COMMAND_OPEN_DEVTOOLS:
97         shell_->ShowDevToolsForElementAt(params_.x, params_.y);
98         break;
99     };
100   }
101
102  private:
103   enum CommandID {
104     COMMAND_OPEN_DEVTOOLS
105   };
106
107   Shell* shell_;
108   content::ContextMenuParams params_;
109
110   DISALLOW_COPY_AND_ASSIGN(ContextMenuModel);
111 };
112
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 {
117  public:
118   enum UIControl {
119     BACK_BUTTON,
120     FORWARD_BUTTON,
121     STOP_BUTTON
122   };
123
124   ShellWindowDelegateView(Shell* shell)
125     : shell_(shell),
126       toolbar_view_(new View),
127       contents_view_(new View) {
128   }
129   virtual ~ShellWindowDelegateView() {}
130
131   // Update the state of UI controls
132   void SetAddressBarURL(const GURL& url) {
133     url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
134   }
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_);
142     Layout();
143
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);
148
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);
153 #endif
154   }
155
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);
167     }
168   }
169
170   void ShowWebViewContextMenu(const content::ContextMenuParams& params) {
171     gfx::Point screen_point(params.x, params.y);
172
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,
182                 &screen_point);
183     }
184
185     context_menu_model_.reset(new ContextMenuModel(shell_, params));
186     context_menu_runner_.reset(
187         new views::MenuRunner(context_menu_model_.get()));
188
189     if (context_menu_runner_->RunMenuAt(web_view_->GetWidget(),
190                                         NULL,
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) {
196       return;
197     }
198   }
199
200   void OnWebContentsFocused(content::WebContents* web_contents) {
201     if (web_view_->GetWebContents() == web_contents)
202       web_view_->OnWebContentsFocused(web_contents);
203   }
204
205  private:
206   // Initialize the UI control contained in shell window
207   void InitShellWindow() {
208     set_background(views::Background::CreateStandardPanelBackground());
209
210     views::GridLayout* layout = new views::GridLayout(this);
211     SetLayoutManager(layout);
212
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);
218
219     layout->AddPaddingRow(0, 2);
220
221     // Add toolbar buttons and URL text field
222     {
223       layout->StartRow(0, 0);
224       views::GridLayout* toolbar_layout = new views::GridLayout(toolbar_view_);
225       toolbar_view_->SetLayoutManager(toolbar_layout);
226
227       views::ColumnSet* toolbar_column_set =
228           toolbar_layout->AddColumnSet(0);
229       // Back button
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);
238       // Forward button
239       forward_button_ =
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);
248       // Refresh button
249       refresh_button_ =
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);
258       // Stop button
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);
268       // URL entry
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);
275
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_);
283
284       layout->AddView(toolbar_view_);
285     }
286
287     layout->AddPaddingRow(0, 5);
288
289     // Add web contents view as the second row
290     {
291       layout->StartRow(1, 0);
292       layout->AddView(contents_view_);
293     }
294
295     layout->AddPaddingRow(0, 5);
296
297     InitAccelerators();
298   }
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,
307         this);
308     }
309   }
310   // Overridden from TextfieldController
311   virtual void ContentsChanged(views::Textfield* sender,
312                                const base::string16& new_contents) OVERRIDE {
313   }
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());
318      GURL url(text);
319      if (!url.has_scheme()) {
320        url = GURL(std::string("http://") + std::string(text));
321        url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
322      }
323      shell_->LoadURL(url);
324      return true;
325    }
326    return false;
327   }
328
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_)
337       shell_->Reload();
338     else if (sender == stop_button_)
339       shell_->Stop();
340   }
341
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 {
346     return title_;
347   }
348   virtual void WindowClosing() OVERRIDE {
349     if (shell_) {
350       delete shell_;
351       shell_ = NULL;
352     }
353   }
354   virtual View* GetContentsView() OVERRIDE { return this; }
355
356   // Overridden from View
357   virtual gfx::Size GetMinimumSize() OVERRIDE {
358     // We want to be able to make the window smaller than its initial
359     // (preferred) size.
360     return gfx::Size();
361   }
362   virtual void ViewHierarchyChanged(
363       const ViewHierarchyChangedDetails& details) OVERRIDE {
364     if (details.is_add && details.child == this) {
365       InitShellWindow();
366     }
367   }
368
369   // Overridden from AcceleratorTarget:
370   virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE {
371     switch (accelerator.key_code()) {
372     case ui::VKEY_F5:
373       shell_->Reload();
374       return true;
375     case ui::VKEY_BROWSER_BACK:
376       shell_->GoBackOrForward(-1);
377       return true;
378     case ui::VKEY_BROWSER_FORWARD:
379       shell_->GoBackOrForward(1);
380       return true;
381     default:
382       return views::WidgetDelegateView::AcceleratorPressed(accelerator);
383     }
384   }
385
386  private:
387   // Hold a reference of Shell for deleting it when the window is closing
388   Shell* shell_;
389
390   // Window title
391   base::string16 title_;
392
393   // Toolbar view contains forward/backward/reload button and URL entry
394   View* toolbar_view_;
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_;
402
403   // Contents view contains the web contents view
404   View* contents_view_;
405   views::WebView* web_view_;
406
407   DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView);
408 };
409
410 }  // namespace
411
412 #if defined(OS_CHROMEOS)
413 wm::WMTestHelper* Shell::wm_test_helper_ = NULL;
414 #endif
415 views::ViewsDelegate* Shell::views_delegate_ = NULL;
416
417 // static
418 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
419 #if defined(OS_WIN)
420   _setmode(_fileno(stdout), _O_BINARY);
421   _setmode(_fileno(stderr), _O_BINARY);
422 #endif
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);
428 #else
429   gfx::Screen::SetScreenInstance(
430       gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen());
431 #endif
432   views_delegate_ = new ShellViewsDelegateAura();
433 }
434
435 void Shell::PlatformExit() {
436 #if defined(OS_CHROMEOS)
437   delete wm_test_helper_;
438 #endif
439   delete views_delegate_;
440   views_delegate_ = NULL;
441   delete platform_;
442   platform_ = NULL;
443 #if defined(OS_CHROMEOS)
444   chromeos::DBusThreadManager::Shutdown();
445 #endif
446   aura::Env::DeleteInstance();
447 }
448
449 void Shell::PlatformCleanUp() {
450 }
451
452 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
453   if (headless_)
454     return;
455   ShellWindowDelegateView* delegate_view =
456     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
457   if (control == BACK_BUTTON) {
458     delegate_view->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON,
459         is_enabled);
460   } else if (control == FORWARD_BUTTON) {
461     delegate_view->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON,
462         is_enabled);
463   } else if (control == STOP_BUTTON) {
464     delegate_view->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON,
465         is_enabled);
466   }
467 }
468
469 void Shell::PlatformSetAddressBarURL(const GURL& url) {
470   if (headless_)
471     return;
472   ShellWindowDelegateView* delegate_view =
473     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
474   delegate_view->SetAddressBarURL(url);
475 }
476
477 void Shell::PlatformSetIsLoading(bool loading) {
478 }
479
480 void Shell::PlatformCreateWindow(int width, int height) {
481   if (headless_) {
482     content_size_ = gfx::Size(width, height);
483     if (!platform_)
484       platform_ = new ShellPlatformDataAura(content_size_);
485     else
486       platform_->ResizeWindow(content_size_);
487     return;
488   }
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));
494 #else
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);
502 #endif
503
504   content_size_ = gfx::Size(width, height);
505
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();
511 }
512
513 void Shell::PlatformSetContents() {
514   if (headless_) {
515     CHECK(platform_);
516     aura::Window* content = web_contents_->GetNativeView();
517     aura::Window* parent = platform_->host()->window();
518     if (!parent->Contains(content)) {
519       parent->AddChild(content);
520       content->Show();
521     }
522     content->SetBounds(gfx::Rect(content_size_));
523     RenderWidgetHostView* host_view = web_contents_->GetRenderWidgetHostView();
524     if (host_view)
525       host_view->SetSize(content_size_);
526   } else {
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_);
531   }
532 }
533
534 void Shell::PlatformResizeSubViews() {
535 }
536
537 void Shell::Close() {
538   if (headless_)
539     delete this;
540   else
541     window_widget_->CloseNow();
542 }
543
544 void Shell::PlatformSetTitle(const base::string16& title) {
545   if (headless_)
546     return;
547   ShellWindowDelegateView* delegate_view =
548     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
549   delegate_view->SetWindowTitle(title);
550   window_widget_->UpdateWindowTitle();
551 }
552
553 bool Shell::PlatformHandleContextMenu(
554     const content::ContextMenuParams& params) {
555   if (headless_)
556     return true;
557   ShellWindowDelegateView* delegate_view =
558     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
559   delegate_view->ShowWebViewContextMenu(params);
560   return true;
561 }
562
563 void Shell::PlatformWebContentsFocused(WebContents* contents) {
564   if (headless_)
565     return;
566   ShellWindowDelegateView* delegate_view =
567     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
568   delegate_view->OnWebContentsFocused(contents);
569 }
570
571 }  // namespace content