Home | History | Annotate | Download | only in browser
      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:
     57   ShellViewsDelegateAura() : use_transparent_windows_(false) {
     58   }
     59 
     60   virtual ~ShellViewsDelegateAura() {
     61   }
     62 
     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:
     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:
     86   virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
     87     return false;
     88   }
     89   virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
     90     return true;
     91   }
     92   virtual bool GetAcceleratorForCommandId(
     93       int command_id,
     94       ui::Accelerator* accelerator) OVERRIDE { return false; }
     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 
    125   ShellWindowDelegateView(Shell* shell)
    126     : shell_(shell),
    127       toolbar_view_(new View),
    128       contents_view_(new View) {
    129   }
    130   virtual ~ShellWindowDelegateView() {}
    131 
    132   // Update the state of UI controls
    133   void SetAddressBarURL(const GURL& url) {
    134     url_entry_->SetText(base::ASCIIToUTF16(url.spec()));
    135   }
    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 
    157   void SetWindowTitle(const base::string16& title) { title_ = title; }
    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 
    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(new views::MenuRunner(
    188         context_menu_model_.get(), views::MenuRunner::CONTEXT_MENU));
    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::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 bool CanMinimize() const OVERRIDE { return true; }
    346   virtual base::string16 GetWindowTitle() const OVERRIDE {
    347     return title_;
    348   }
    349   virtual void WindowClosing() OVERRIDE {
    350     if (shell_) {
    351       delete shell_;
    352       shell_ = NULL;
    353     }
    354   }
    355   virtual View* GetContentsView() OVERRIDE { return this; }
    356 
    357   // Overridden from View
    358   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   }
    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:
    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 gfx::Screen* Shell::test_screen_ = NULL;
    416 #endif
    417 views::ViewsDelegate* Shell::views_delegate_ = NULL;
    418 
    419 // static
    420 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
    421 #if defined(OS_WIN)
    422   _setmode(_fileno(stdout), _O_BINARY);
    423   _setmode(_fileno(stderr), _O_BINARY);
    424 #endif
    425 #if defined(OS_CHROMEOS)
    426   chromeos::DBusThreadManager::Initialize();
    427   test_screen_ = aura::TestScreen::Create(gfx::Size());
    428   gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen_);
    429   wm_test_helper_ = new wm::WMTestHelper(default_window_size,
    430                                          GetContextFactory());
    431 #else
    432   gfx::Screen::SetScreenInstance(
    433       gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen());
    434 #endif
    435   views_delegate_ = new ShellViewsDelegateAura();
    436 }
    437 
    438 void Shell::PlatformExit() {
    439 #if defined(OS_CHROMEOS)
    440   delete wm_test_helper_;
    441   wm_test_helper_ = NULL;
    442 
    443   delete test_screen_;
    444   test_screen_ = NULL;
    445 #endif
    446   delete views_delegate_;
    447   views_delegate_ = NULL;
    448   delete platform_;
    449   platform_ = NULL;
    450 #if defined(OS_CHROMEOS)
    451   chromeos::DBusThreadManager::Shutdown();
    452 #endif
    453   aura::Env::DeleteInstance();
    454 }
    455 
    456 void Shell::PlatformCleanUp() {
    457 }
    458 
    459 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
    460   if (headless_)
    461     return;
    462   ShellWindowDelegateView* delegate_view =
    463     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
    464   if (control == BACK_BUTTON) {
    465     delegate_view->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON,
    466         is_enabled);
    467   } else if (control == FORWARD_BUTTON) {
    468     delegate_view->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON,
    469         is_enabled);
    470   } else if (control == STOP_BUTTON) {
    471     delegate_view->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON,
    472         is_enabled);
    473   }
    474 }
    475 
    476 void Shell::PlatformSetAddressBarURL(const GURL& url) {
    477   if (headless_)
    478     return;
    479   ShellWindowDelegateView* delegate_view =
    480     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
    481   delegate_view->SetAddressBarURL(url);
    482 }
    483 
    484 void Shell::PlatformSetIsLoading(bool loading) {
    485 }
    486 
    487 void Shell::PlatformCreateWindow(int width, int height) {
    488   if (headless_) {
    489     content_size_ = gfx::Size(width, height);
    490     if (!platform_)
    491       platform_ = new ShellPlatformDataAura(content_size_);
    492     else
    493       platform_->ResizeWindow(content_size_);
    494     return;
    495   }
    496 #if defined(OS_CHROMEOS)
    497   window_widget_ = views::Widget::CreateWindowWithContextAndBounds(
    498       new ShellWindowDelegateView(this),
    499       wm_test_helper_->GetDefaultParent(NULL, NULL, gfx::Rect()),
    500       gfx::Rect(0, 0, width, height));
    501 #else
    502   window_widget_ = new views::Widget;
    503   views::Widget::InitParams params;
    504   params.bounds = gfx::Rect(0, 0, width, height);
    505   params.delegate = new ShellWindowDelegateView(this);
    506   window_widget_->Init(params);
    507 #endif
    508 
    509   content_size_ = gfx::Size(width, height);
    510 
    511   window_ = window_widget_->GetNativeWindow();
    512   // Call ShowRootWindow on RootWindow created by WMTestHelper without
    513   // which XWindow owned by RootWindow doesn't get mapped.
    514   window_->GetHost()->Show();
    515   window_widget_->Show();
    516 }
    517 
    518 void Shell::PlatformSetContents() {
    519   if (headless_) {
    520     CHECK(platform_);
    521     aura::Window* content = web_contents_->GetNativeView();
    522     aura::Window* parent = platform_->host()->window();
    523     if (!parent->Contains(content)) {
    524       parent->AddChild(content);
    525       content->Show();
    526     }
    527     content->SetBounds(gfx::Rect(content_size_));
    528     RenderWidgetHostView* host_view = web_contents_->GetRenderWidgetHostView();
    529     if (host_view)
    530       host_view->SetSize(content_size_);
    531   } else {
    532     views::WidgetDelegate* widget_delegate = window_widget_->widget_delegate();
    533     ShellWindowDelegateView* delegate_view =
    534         static_cast<ShellWindowDelegateView*>(widget_delegate);
    535     delegate_view->SetWebContents(web_contents_.get(), content_size_);
    536   }
    537 }
    538 
    539 void Shell::PlatformResizeSubViews() {
    540 }
    541 
    542 void Shell::Close() {
    543   if (headless_)
    544     delete this;
    545   else
    546     window_widget_->CloseNow();
    547 }
    548 
    549 void Shell::PlatformSetTitle(const base::string16& title) {
    550   if (headless_)
    551     return;
    552   ShellWindowDelegateView* delegate_view =
    553     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
    554   delegate_view->SetWindowTitle(title);
    555   window_widget_->UpdateWindowTitle();
    556 }
    557 
    558 bool Shell::PlatformHandleContextMenu(
    559     const content::ContextMenuParams& params) {
    560   if (headless_)
    561     return true;
    562   ShellWindowDelegateView* delegate_view =
    563     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
    564   delegate_view->ShowWebViewContextMenu(params);
    565   return true;
    566 }
    567 
    568 void Shell::PlatformWebContentsFocused(WebContents* contents) {
    569   if (headless_)
    570     return;
    571   ShellWindowDelegateView* delegate_view =
    572     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
    573   delegate_view->OnWebContentsFocused(contents);
    574 }
    575 
    576 }  // namespace content
    577