• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 "ui/aura/client/screen_position_client.h"
15 #include "ui/aura/env.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_event_dispatcher.h"
18 #include "ui/base/clipboard/clipboard.h"
19 #include "ui/base/models/simple_menu_model.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/events/event.h"
22 #include "ui/gfx/screen.h"
23 #include "ui/views/background.h"
24 #include "ui/views/controls/button/label_button.h"
25 #include "ui/views/controls/button/menu_button.h"
26 #include "ui/views/controls/button/menu_button_listener.h"
27 #include "ui/views/controls/menu/menu_runner.h"
28 #include "ui/views/controls/textfield/textfield.h"
29 #include "ui/views/controls/textfield/textfield_controller.h"
30 #include "ui/views/controls/webview/webview.h"
31 #include "ui/views/layout/fill_layout.h"
32 #include "ui/views/layout/grid_layout.h"
33 #include "ui/views/test/desktop_test_views_delegate.h"
34 #include "ui/views/view.h"
35 #include "ui/views/widget/widget.h"
36 #include "ui/views/widget/widget_delegate.h"
37 
38 #if defined(OS_CHROMEOS)
39 #include "chromeos/dbus/dbus_thread_manager.h"
40 #include "ui/aura/test/test_screen.h"
41 #include "ui/wm/test/wm_test_helper.h"
42 #else  // !defined(OS_CHROMEOS)
43 #include "ui/views/widget/desktop_aura/desktop_screen.h"
44 #endif
45 
46 #if defined(OS_WIN)
47 #include <fcntl.h>
48 #include <io.h>
49 #endif
50 
51 namespace content {
52 
53 namespace {
54 // ViewDelegate implementation for aura content shell
55 class ShellViewsDelegateAura : public views::DesktopTestViewsDelegate {
56  public:
ShellViewsDelegateAura()57   ShellViewsDelegateAura() : use_transparent_windows_(false) {
58   }
59 
~ShellViewsDelegateAura()60   virtual ~ShellViewsDelegateAura() {
61   }
62 
SetUseTransparentWindows(bool transparent)63   void SetUseTransparentWindows(bool transparent) {
64     use_transparent_windows_ = transparent;
65   }
66 
67  private:
68   bool use_transparent_windows_;
69 
70   DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegateAura);
71 };
72 
73 // Model for the "Debug" menu
74 class ContextMenuModel : public ui::SimpleMenuModel,
75                          public ui::SimpleMenuModel::Delegate {
76  public:
ContextMenuModel(Shell * shell,const content::ContextMenuParams & params)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"));
83   }
84 
85   // ui::SimpleMenuModel::Delegate:
IsCommandIdChecked(int command_id) const86   virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
87     return false;
88   }
IsCommandIdEnabled(int command_id) const89   virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
90     return true;
91   }
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)92   virtual bool GetAcceleratorForCommandId(
93       int command_id,
94       ui::Accelerator* accelerator) OVERRIDE { return false; }
ExecuteCommand(int command_id,int event_flags)95   virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE {
96     switch (command_id) {
97       case COMMAND_OPEN_DEVTOOLS:
98         shell_->ShowDevToolsForElementAt(params_.x, params_.y);
99         break;
100     };
101   }
102 
103  private:
104   enum CommandID {
105     COMMAND_OPEN_DEVTOOLS
106   };
107 
108   Shell* shell_;
109   content::ContextMenuParams params_;
110 
111   DISALLOW_COPY_AND_ASSIGN(ContextMenuModel);
112 };
113 
114 // Maintain the UI controls and web view for content shell
115 class ShellWindowDelegateView : public views::WidgetDelegateView,
116                                 public views::TextfieldController,
117                                 public views::ButtonListener {
118  public:
119   enum UIControl {
120     BACK_BUTTON,
121     FORWARD_BUTTON,
122     STOP_BUTTON
123   };
124 
ShellWindowDelegateView(Shell * shell)125   ShellWindowDelegateView(Shell* shell)
126     : shell_(shell),
127       toolbar_view_(new View),
128       contents_view_(new View) {
129   }
~ShellWindowDelegateView()130   virtual ~ShellWindowDelegateView() {}
131 
132   // Update the state of UI controls
SetAddressBarURL(const GURL & url)133   void SetAddressBarURL(const GURL& url) {
134     url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
135   }
SetWebContents(WebContents * web_contents,const gfx::Size & size)136   void SetWebContents(WebContents* web_contents, const gfx::Size& size) {
137     contents_view_->SetLayoutManager(new views::FillLayout());
138     web_view_ = new views::WebView(web_contents->GetBrowserContext());
139     web_view_->SetWebContents(web_contents);
140     web_view_->SetPreferredSize(size);
141     web_contents->Focus();
142     contents_view_->AddChildView(web_view_);
143     Layout();
144 
145     // Resize the widget, keeping the same origin.
146     gfx::Rect bounds = GetWidget()->GetWindowBoundsInScreen();
147     bounds.set_size(GetWidget()->GetRootView()->GetPreferredSize());
148     GetWidget()->SetBounds(bounds);
149 
150     // Resizing a widget on chromeos doesn't automatically resize the root, need
151     // to explicitly do that.
152 #if defined(OS_CHROMEOS)
153     GetWidget()->GetNativeWindow()->GetHost()->SetBounds(bounds);
154 #endif
155   }
156 
SetWindowTitle(const base::string16 & title)157   void SetWindowTitle(const base::string16& title) { title_ = title; }
EnableUIControl(UIControl control,bool is_enabled)158   void EnableUIControl(UIControl control, bool is_enabled) {
159     if (control == BACK_BUTTON) {
160       back_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL
161           : views::CustomButton::STATE_DISABLED);
162     } else if (control == FORWARD_BUTTON) {
163       forward_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL
164           : views::CustomButton::STATE_DISABLED);
165     } else if (control == STOP_BUTTON) {
166       stop_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL
167           : views::CustomButton::STATE_DISABLED);
168     }
169   }
170 
ShowWebViewContextMenu(const content::ContextMenuParams & params)171   void ShowWebViewContextMenu(const content::ContextMenuParams& params) {
172     gfx::Point screen_point(params.x, params.y);
173 
174     // Convert from content coordinates to window coordinates.
175     // This code copied from chrome_web_contents_view_delegate_views.cc
176     aura::Window* web_contents_window =
177         shell_->web_contents()->GetNativeView();
178     aura::Window* root_window = web_contents_window->GetRootWindow();
179     aura::client::ScreenPositionClient* screen_position_client =
180         aura::client::GetScreenPositionClient(root_window);
181     if (screen_position_client) {
182         screen_position_client->ConvertPointToScreen(web_contents_window,
183                 &screen_point);
184     }
185 
186     context_menu_model_.reset(new ContextMenuModel(shell_, params));
187     context_menu_runner_.reset(
188         new views::MenuRunner(context_menu_model_.get()));
189 
190     if (context_menu_runner_->RunMenuAt(web_view_->GetWidget(),
191                                         NULL,
192                                         gfx::Rect(screen_point, gfx::Size()),
193                                         views::MENU_ANCHOR_TOPRIGHT,
194                                         ui::MENU_SOURCE_NONE,
195                                         views::MenuRunner::CONTEXT_MENU) ==
196         views::MenuRunner::MENU_DELETED) {
197       return;
198     }
199   }
200 
OnWebContentsFocused(content::WebContents * web_contents)201   void OnWebContentsFocused(content::WebContents* web_contents) {
202     if (web_view_->GetWebContents() == web_contents)
203       web_view_->OnWebContentsFocused(web_contents);
204   }
205 
206  private:
207   // Initialize the UI control contained in shell window
InitShellWindow()208   void InitShellWindow() {
209     set_background(views::Background::CreateStandardPanelBackground());
210 
211     views::GridLayout* layout = new views::GridLayout(this);
212     SetLayoutManager(layout);
213 
214     views::ColumnSet* column_set = layout->AddColumnSet(0);
215     column_set->AddPaddingColumn(0, 2);
216     column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
217                           views::GridLayout::USE_PREF, 0, 0);
218     column_set->AddPaddingColumn(0, 2);
219 
220     layout->AddPaddingRow(0, 2);
221 
222     // Add toolbar buttons and URL text field
223     {
224       layout->StartRow(0, 0);
225       views::GridLayout* toolbar_layout = new views::GridLayout(toolbar_view_);
226       toolbar_view_->SetLayoutManager(toolbar_layout);
227 
228       views::ColumnSet* toolbar_column_set =
229           toolbar_layout->AddColumnSet(0);
230       // Back button
231       back_button_ = new views::LabelButton(this, base::ASCIIToUTF16("Back"));
232       back_button_->SetStyle(views::Button::STYLE_BUTTON);
233       gfx::Size back_button_size = back_button_->GetPreferredSize();
234       toolbar_column_set->AddColumn(views::GridLayout::CENTER,
235                                     views::GridLayout::CENTER, 0,
236                                     views::GridLayout::FIXED,
237                                     back_button_size.width(),
238                                     back_button_size.width() / 2);
239       // Forward button
240       forward_button_ =
241           new views::LabelButton(this, base::ASCIIToUTF16("Forward"));
242       forward_button_->SetStyle(views::Button::STYLE_BUTTON);
243       gfx::Size forward_button_size = forward_button_->GetPreferredSize();
244       toolbar_column_set->AddColumn(views::GridLayout::CENTER,
245                                     views::GridLayout::CENTER, 0,
246                                     views::GridLayout::FIXED,
247                                     forward_button_size.width(),
248                                     forward_button_size.width() / 2);
249       // Refresh button
250       refresh_button_ =
251           new views::LabelButton(this, base::ASCIIToUTF16("Refresh"));
252       refresh_button_->SetStyle(views::Button::STYLE_BUTTON);
253       gfx::Size refresh_button_size = refresh_button_->GetPreferredSize();
254       toolbar_column_set->AddColumn(views::GridLayout::CENTER,
255                                     views::GridLayout::CENTER, 0,
256                                     views::GridLayout::FIXED,
257                                     refresh_button_size.width(),
258                                     refresh_button_size.width() / 2);
259       // Stop button
260       stop_button_ = new views::LabelButton(this, base::ASCIIToUTF16("Stop"));
261       stop_button_->SetStyle(views::Button::STYLE_BUTTON);
262       gfx::Size stop_button_size = stop_button_->GetPreferredSize();
263       toolbar_column_set->AddColumn(views::GridLayout::CENTER,
264                                     views::GridLayout::CENTER, 0,
265                                     views::GridLayout::FIXED,
266                                     stop_button_size.width(),
267                                     stop_button_size.width() / 2);
268       toolbar_column_set->AddPaddingColumn(0, 2);
269       // URL entry
270       url_entry_ = new views::Textfield();
271       url_entry_->set_controller(this);
272       toolbar_column_set->AddColumn(views::GridLayout::FILL,
273                                     views::GridLayout::FILL, 1,
274                                     views::GridLayout::USE_PREF, 0, 0);
275       toolbar_column_set->AddPaddingColumn(0, 2);
276 
277       // Fill up the first row
278       toolbar_layout->StartRow(0, 0);
279       toolbar_layout->AddView(back_button_);
280       toolbar_layout->AddView(forward_button_);
281       toolbar_layout->AddView(refresh_button_);
282       toolbar_layout->AddView(stop_button_);
283       toolbar_layout->AddView(url_entry_);
284 
285       layout->AddView(toolbar_view_);
286     }
287 
288     layout->AddPaddingRow(0, 5);
289 
290     // Add web contents view as the second row
291     {
292       layout->StartRow(1, 0);
293       layout->AddView(contents_view_);
294     }
295 
296     layout->AddPaddingRow(0, 5);
297 
298     InitAccelerators();
299   }
InitAccelerators()300   void InitAccelerators() {
301     static const ui::KeyboardCode keys[] = { ui::VKEY_F5,
302                                              ui::VKEY_BROWSER_BACK,
303                                              ui::VKEY_BROWSER_FORWARD };
304     for (size_t i = 0; i < arraysize(keys); ++i) {
305       GetFocusManager()->RegisterAccelerator(
306         ui::Accelerator(keys[i], ui::EF_NONE),
307         ui::AcceleratorManager::kNormalPriority,
308         this);
309     }
310   }
311   // Overridden from TextfieldController
ContentsChanged(views::Textfield * sender,const base::string16 & new_contents)312   virtual void ContentsChanged(views::Textfield* sender,
313                                const base::string16& new_contents) OVERRIDE {
314   }
HandleKeyEvent(views::Textfield * sender,const ui::KeyEvent & key_event)315   virtual bool HandleKeyEvent(views::Textfield* sender,
316                               const ui::KeyEvent& key_event) OVERRIDE {
317    if (sender == url_entry_ && key_event.key_code() == ui::VKEY_RETURN) {
318      std::string text = base::UTF16ToUTF8(url_entry_->text());
319      GURL url(text);
320      if (!url.has_scheme()) {
321        url = GURL(std::string("http://") + std::string(text));
322        url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
323      }
324      shell_->LoadURL(url);
325      return true;
326    }
327    return false;
328   }
329 
330   // Overridden from ButtonListener
ButtonPressed(views::Button * sender,const ui::Event & event)331   virtual void ButtonPressed(views::Button* sender,
332                              const ui::Event& event) OVERRIDE {
333     if (sender == back_button_)
334       shell_->GoBackOrForward(-1);
335     else if (sender == forward_button_)
336       shell_->GoBackOrForward(1);
337     else if (sender == refresh_button_)
338       shell_->Reload();
339     else if (sender == stop_button_)
340       shell_->Stop();
341   }
342 
343   // Overridden from WidgetDelegateView
CanResize() const344   virtual bool CanResize() const OVERRIDE { return true; }
CanMaximize() const345   virtual bool CanMaximize() const OVERRIDE { return true; }
GetWindowTitle() const346   virtual base::string16 GetWindowTitle() const OVERRIDE {
347     return title_;
348   }
WindowClosing()349   virtual void WindowClosing() OVERRIDE {
350     if (shell_) {
351       delete shell_;
352       shell_ = NULL;
353     }
354   }
GetContentsView()355   virtual View* GetContentsView() OVERRIDE { return this; }
356 
357   // Overridden from View
GetMinimumSize() const358   virtual gfx::Size GetMinimumSize() const OVERRIDE {
359     // We want to be able to make the window smaller than its initial
360     // (preferred) size.
361     return gfx::Size();
362   }
ViewHierarchyChanged(const ViewHierarchyChangedDetails & details)363   virtual void ViewHierarchyChanged(
364       const ViewHierarchyChangedDetails& details) OVERRIDE {
365     if (details.is_add && details.child == this) {
366       InitShellWindow();
367     }
368   }
369 
370   // Overridden from AcceleratorTarget:
AcceleratorPressed(const ui::Accelerator & accelerator)371   virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE {
372     switch (accelerator.key_code()) {
373     case ui::VKEY_F5:
374       shell_->Reload();
375       return true;
376     case ui::VKEY_BROWSER_BACK:
377       shell_->GoBackOrForward(-1);
378       return true;
379     case ui::VKEY_BROWSER_FORWARD:
380       shell_->GoBackOrForward(1);
381       return true;
382     default:
383       return views::WidgetDelegateView::AcceleratorPressed(accelerator);
384     }
385   }
386 
387  private:
388   // Hold a reference of Shell for deleting it when the window is closing
389   Shell* shell_;
390 
391   // Window title
392   base::string16 title_;
393 
394   // Toolbar view contains forward/backward/reload button and URL entry
395   View* toolbar_view_;
396   views::LabelButton* back_button_;
397   views::LabelButton* forward_button_;
398   views::LabelButton* refresh_button_;
399   views::LabelButton* stop_button_;
400   views::Textfield* url_entry_;
401   scoped_ptr<ContextMenuModel> context_menu_model_;
402   scoped_ptr<views::MenuRunner> context_menu_runner_;
403 
404   // Contents view contains the web contents view
405   View* contents_view_;
406   views::WebView* web_view_;
407 
408   DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView);
409 };
410 
411 }  // namespace
412 
413 #if defined(OS_CHROMEOS)
414 wm::WMTestHelper* Shell::wm_test_helper_ = NULL;
415 #endif
416 views::ViewsDelegate* Shell::views_delegate_ = NULL;
417 
418 // static
PlatformInitialize(const gfx::Size & default_window_size)419 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
420 #if defined(OS_WIN)
421   _setmode(_fileno(stdout), _O_BINARY);
422   _setmode(_fileno(stderr), _O_BINARY);
423 #endif
424 #if defined(OS_CHROMEOS)
425   chromeos::DBusThreadManager::Initialize();
426   gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE,
427                                  aura::TestScreen::Create(gfx::Size()));
428   wm_test_helper_ = new wm::WMTestHelper(default_window_size,
429                                          GetContextFactory());
430 #else
431   gfx::Screen::SetScreenInstance(
432       gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen());
433 #endif
434   views_delegate_ = new ShellViewsDelegateAura();
435 }
436 
PlatformExit()437 void Shell::PlatformExit() {
438 #if defined(OS_CHROMEOS)
439   delete wm_test_helper_;
440   wm_test_helper_ = NULL;
441 #endif
442   delete views_delegate_;
443   views_delegate_ = NULL;
444   delete platform_;
445   platform_ = NULL;
446 #if defined(OS_CHROMEOS)
447   chromeos::DBusThreadManager::Shutdown();
448 #endif
449   aura::Env::DeleteInstance();
450 }
451 
PlatformCleanUp()452 void Shell::PlatformCleanUp() {
453 }
454 
PlatformEnableUIControl(UIControl control,bool is_enabled)455 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
456   if (headless_)
457     return;
458   ShellWindowDelegateView* delegate_view =
459     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
460   if (control == BACK_BUTTON) {
461     delegate_view->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON,
462         is_enabled);
463   } else if (control == FORWARD_BUTTON) {
464     delegate_view->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON,
465         is_enabled);
466   } else if (control == STOP_BUTTON) {
467     delegate_view->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON,
468         is_enabled);
469   }
470 }
471 
PlatformSetAddressBarURL(const GURL & url)472 void Shell::PlatformSetAddressBarURL(const GURL& url) {
473   if (headless_)
474     return;
475   ShellWindowDelegateView* delegate_view =
476     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
477   delegate_view->SetAddressBarURL(url);
478 }
479 
PlatformSetIsLoading(bool loading)480 void Shell::PlatformSetIsLoading(bool loading) {
481 }
482 
PlatformCreateWindow(int width,int height)483 void Shell::PlatformCreateWindow(int width, int height) {
484   if (headless_) {
485     content_size_ = gfx::Size(width, height);
486     if (!platform_)
487       platform_ = new ShellPlatformDataAura(content_size_);
488     else
489       platform_->ResizeWindow(content_size_);
490     return;
491   }
492 #if defined(OS_CHROMEOS)
493   window_widget_ = views::Widget::CreateWindowWithContextAndBounds(
494       new ShellWindowDelegateView(this),
495       wm_test_helper_->GetDefaultParent(NULL, NULL, gfx::Rect()),
496       gfx::Rect(0, 0, width, height));
497 #else
498   window_widget_ = new views::Widget;
499   views::Widget::InitParams params;
500   params.bounds = gfx::Rect(0, 0, width, height);
501   params.delegate = new ShellWindowDelegateView(this);
502   params.remove_standard_frame = true;
503   window_widget_->Init(params);
504 #endif
505 
506   content_size_ = gfx::Size(width, height);
507 
508   window_ = window_widget_->GetNativeWindow();
509   // Call ShowRootWindow on RootWindow created by WMTestHelper without
510   // which XWindow owned by RootWindow doesn't get mapped.
511   window_->GetHost()->Show();
512   window_widget_->Show();
513 }
514 
PlatformSetContents()515 void Shell::PlatformSetContents() {
516   if (headless_) {
517     CHECK(platform_);
518     aura::Window* content = web_contents_->GetNativeView();
519     aura::Window* parent = platform_->host()->window();
520     if (!parent->Contains(content)) {
521       parent->AddChild(content);
522       content->Show();
523     }
524     content->SetBounds(gfx::Rect(content_size_));
525     RenderWidgetHostView* host_view = web_contents_->GetRenderWidgetHostView();
526     if (host_view)
527       host_view->SetSize(content_size_);
528   } else {
529     views::WidgetDelegate* widget_delegate = window_widget_->widget_delegate();
530     ShellWindowDelegateView* delegate_view =
531         static_cast<ShellWindowDelegateView*>(widget_delegate);
532     delegate_view->SetWebContents(web_contents_.get(), content_size_);
533   }
534 }
535 
PlatformResizeSubViews()536 void Shell::PlatformResizeSubViews() {
537 }
538 
Close()539 void Shell::Close() {
540   if (headless_)
541     delete this;
542   else
543     window_widget_->CloseNow();
544 }
545 
PlatformSetTitle(const base::string16 & title)546 void Shell::PlatformSetTitle(const base::string16& title) {
547   if (headless_)
548     return;
549   ShellWindowDelegateView* delegate_view =
550     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
551   delegate_view->SetWindowTitle(title);
552   window_widget_->UpdateWindowTitle();
553 }
554 
PlatformHandleContextMenu(const content::ContextMenuParams & params)555 bool Shell::PlatformHandleContextMenu(
556     const content::ContextMenuParams& params) {
557   if (headless_)
558     return true;
559   ShellWindowDelegateView* delegate_view =
560     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
561   delegate_view->ShowWebViewContextMenu(params);
562   return true;
563 }
564 
PlatformWebContentsFocused(WebContents * contents)565 void Shell::PlatformWebContentsFocused(WebContents* contents) {
566   if (headless_)
567     return;
568   ShellWindowDelegateView* delegate_view =
569     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
570   delegate_view->OnWebContentsFocused(contents);
571 }
572 
573 }  // namespace content
574