Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2012 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 "chrome/browser/ui/views/constrained_window_views.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "chrome/browser/ui/browser_finder.h"
     10 #include "components/web_modal/popup_manager.h"
     11 #include "components/web_modal/web_contents_modal_dialog_host.h"
     12 #include "extensions/browser/guest_view/guest_view_base.h"
     13 #include "ui/views/border.h"
     14 #include "ui/views/widget/widget.h"
     15 #include "ui/views/widget/widget_observer.h"
     16 #include "ui/views/window/dialog_delegate.h"
     17 
     18 using web_modal::ModalDialogHost;
     19 using web_modal::ModalDialogHostObserver;
     20 
     21 namespace {
     22 // The name of a key to store on the window handle to associate
     23 // BrowserModalDialogHostObserverViews with the Widget.
     24 const char* const kBrowserModalDialogHostObserverViewsKey =
     25     "__BROWSER_MODAL_DIALOG_HOST_OBSERVER_VIEWS__";
     26 
     27 // Applies positioning changes from the ModalDialogHost to the Widget.
     28 class BrowserModalDialogHostObserverViews
     29     : public views::WidgetObserver,
     30       public ModalDialogHostObserver {
     31  public:
     32   BrowserModalDialogHostObserverViews(ModalDialogHost* host,
     33                                       views::Widget* target_widget,
     34                                       const char *const native_window_property)
     35       : host_(host),
     36         target_widget_(target_widget),
     37         native_window_property_(native_window_property) {
     38     DCHECK(host_);
     39     DCHECK(target_widget_);
     40     host_->AddObserver(this);
     41     target_widget_->AddObserver(this);
     42   }
     43 
     44   virtual ~BrowserModalDialogHostObserverViews() {
     45     if (host_)
     46       host_->RemoveObserver(this);
     47     target_widget_->RemoveObserver(this);
     48     target_widget_->SetNativeWindowProperty(native_window_property_, NULL);
     49   }
     50 
     51   // WidgetObserver overrides
     52   virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE {
     53     delete this;
     54   }
     55 
     56   // WebContentsModalDialogHostObserver overrides
     57   virtual void OnPositionRequiresUpdate() OVERRIDE {
     58     UpdateBrowserModalDialogPosition(target_widget_, host_);
     59   }
     60 
     61   virtual void OnHostDestroying() OVERRIDE {
     62     host_->RemoveObserver(this);
     63     host_ = NULL;
     64   }
     65 
     66  private:
     67   ModalDialogHost* host_;
     68   views::Widget* target_widget_;
     69   const char* const native_window_property_;
     70 
     71   DISALLOW_COPY_AND_ASSIGN(BrowserModalDialogHostObserverViews);
     72 };
     73 
     74 void UpdateModalDialogPosition(views::Widget* widget,
     75                                web_modal::ModalDialogHost* dialog_host,
     76                                const gfx::Size& size) {
     77   // Do not forcibly update the dialog widget position if it is being dragged.
     78   if (widget->HasCapture())
     79     return;
     80 
     81   gfx::Point position = dialog_host->GetDialogPosition(size);
     82   views::Border* border = widget->non_client_view()->frame_view()->border();
     83   // Border may be null during widget initialization.
     84   if (border) {
     85     // Align the first row of pixels inside the border. This is the apparent
     86     // top of the dialog.
     87     position.set_y(position.y() - border->GetInsets().top());
     88   }
     89 
     90   if (widget->is_top_level()) {
     91     position +=
     92         views::Widget::GetWidgetForNativeView(dialog_host->GetHostView())->
     93             GetClientAreaBoundsInScreen().OffsetFromOrigin();
     94   }
     95 
     96   widget->SetBounds(gfx::Rect(position, size));
     97 }
     98 
     99 }  // namespace
    100 
    101 void UpdateWebContentsModalDialogPosition(
    102     views::Widget* widget,
    103     web_modal::WebContentsModalDialogHost* dialog_host) {
    104   gfx::Size size = widget->GetRootView()->GetPreferredSize();
    105   gfx::Size max_size = dialog_host->GetMaximumDialogSize();
    106   // Enlarge the max size by the top border, as the dialog will be shifted
    107   // outside the area specified by the dialog host by this amount later.
    108   views::Border* border =
    109       widget->non_client_view()->frame_view()->border();
    110   // Border may be null during widget initialization.
    111   if (border)
    112     max_size.Enlarge(0, border->GetInsets().top());
    113   size.SetToMin(max_size);
    114   UpdateModalDialogPosition(widget, dialog_host, size);
    115 }
    116 
    117 void UpdateBrowserModalDialogPosition(views::Widget* widget,
    118                                       web_modal::ModalDialogHost* dialog_host) {
    119   UpdateModalDialogPosition(widget, dialog_host,
    120                             widget->GetRootView()->GetPreferredSize());
    121 }
    122 
    123 views::Widget* ShowWebModalDialogViews(
    124     views::WidgetDelegate* dialog,
    125     content::WebContents* initiator_web_contents) {
    126   extensions::GuestViewBase* guest_view =
    127       extensions::GuestViewBase::FromWebContents(initiator_web_contents);
    128   // For embedded WebContents, use the embedder's WebContents for constrained
    129   // window.
    130   content::WebContents* web_contents =
    131       guest_view && guest_view->embedder_web_contents() ?
    132           guest_view->embedder_web_contents() : initiator_web_contents;
    133   views::Widget* widget = CreateWebModalDialogViews(dialog, web_contents);
    134   web_modal::PopupManager* popup_manager =
    135       web_modal::PopupManager::FromWebContents(web_contents);
    136   popup_manager->ShowModalDialog(widget->GetNativeWindow(), web_contents);
    137   return widget;
    138 }
    139 
    140 views::Widget* CreateWebModalDialogViews(views::WidgetDelegate* dialog,
    141                                          content::WebContents* web_contents) {
    142   DCHECK_EQ(ui::MODAL_TYPE_CHILD, dialog->GetModalType());
    143   web_modal::PopupManager* popup_manager =
    144       web_modal::PopupManager::FromWebContents(web_contents);
    145   const gfx::NativeWindow parent = popup_manager->GetHostView();
    146   return views::DialogDelegate::CreateDialogWidget(dialog, NULL, parent);
    147 }
    148 
    149 // TODO(gbillock): Replace this with PopupManager calls.
    150 views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog,
    151                                              gfx::NativeWindow parent) {
    152   views::Widget* widget =
    153       views::DialogDelegate::CreateDialogWidget(dialog, NULL, parent);
    154   if (!dialog->UseNewStyleForThisDialog())
    155     return widget;
    156 
    157   // Get the browser dialog management and hosting components from |parent|.
    158   Browser* browser = chrome::FindBrowserWithWindow(parent);
    159   if (browser) {
    160     ChromeWebModalDialogManagerDelegate* manager = browser;
    161     ModalDialogHost* host = manager->GetWebContentsModalDialogHost();
    162     DCHECK_EQ(parent, host->GetHostView());
    163     ModalDialogHostObserver* dialog_host_observer =
    164         new BrowserModalDialogHostObserverViews(
    165             host, widget, kBrowserModalDialogHostObserverViewsKey);
    166     dialog_host_observer->OnPositionRequiresUpdate();
    167   }
    168   return widget;
    169 }
    170