Home | History | Annotate | Download | only in extensions
      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/extensions/extension_dialog.h"
      6 
      7 #include "chrome/browser/chrome_notification_types.h"
      8 #include "chrome/browser/extensions/extension_view_host.h"
      9 #include "chrome/browser/extensions/extension_view_host_factory.h"
     10 #include "chrome/browser/profiles/profile.h"
     11 #include "chrome/browser/ui/views/constrained_window_views.h"
     12 #include "chrome/browser/ui/views/extensions/extension_dialog_observer.h"
     13 #include "content/public/browser/notification_details.h"
     14 #include "content/public/browser/notification_source.h"
     15 #include "content/public/browser/render_view_host.h"
     16 #include "content/public/browser/render_widget_host_view.h"
     17 #include "content/public/browser/web_contents.h"
     18 #include "ui/base/base_window.h"
     19 #include "ui/gfx/screen.h"
     20 #include "ui/views/background.h"
     21 #include "ui/views/widget/widget.h"
     22 #include "url/gurl.h"
     23 
     24 using content::BrowserContext;
     25 using content::WebContents;
     26 
     27 ExtensionDialog::ExtensionDialog(extensions::ExtensionViewHost* host,
     28                                  ExtensionDialogObserver* observer)
     29     : host_(host),
     30       observer_(observer) {
     31   AddRef();  // Balanced in DeleteDelegate();
     32 
     33   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
     34                  content::Source<BrowserContext>(host->browser_context()));
     35   // Listen for the containing view calling window.close();
     36   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
     37                  content::Source<BrowserContext>(host->browser_context()));
     38   // Listen for a crash or other termination of the extension process.
     39   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
     40                  content::Source<BrowserContext>(host->browser_context()));
     41 }
     42 
     43 ExtensionDialog::~ExtensionDialog() {
     44 }
     45 
     46 // static
     47 ExtensionDialog* ExtensionDialog::Show(
     48     const GURL& url,
     49     aura::Window* parent_window,
     50     Profile* profile,
     51     WebContents* web_contents,
     52     int width,
     53     int height,
     54     int min_width,
     55     int min_height,
     56     const base::string16& title,
     57     ExtensionDialogObserver* observer) {
     58   extensions::ExtensionViewHost* host =
     59       extensions::ExtensionViewHostFactory::CreateDialogHost(url, profile);
     60   if (!host)
     61     return NULL;
     62   // Preferred size must be set before views::Widget::CreateWindowWithParent
     63   // is called because CreateWindowWithParent refers the result of CanResize().
     64   host->view()->SetPreferredSize(gfx::Size(width, height));
     65   host->view()->set_minimum_size(gfx::Size(min_width, min_height));
     66   host->SetAssociatedWebContents(web_contents);
     67 
     68   DCHECK(parent_window);
     69   ExtensionDialog* dialog = new ExtensionDialog(host, observer);
     70   dialog->set_title(title);
     71   dialog->InitWindow(parent_window, width, height);
     72 
     73   // Show a white background while the extension loads.  This is prettier than
     74   // flashing a black unfilled window frame.
     75   host->view()->set_background(
     76       views::Background::CreateSolidBackground(0xFF, 0xFF, 0xFF));
     77   host->view()->SetVisible(true);
     78 
     79   // Ensure the DOM JavaScript can respond immediately to keyboard shortcuts.
     80   host->host_contents()->Focus();
     81   return dialog;
     82 }
     83 
     84 void ExtensionDialog::InitWindow(aura::Window* parent,
     85                                  int width,
     86                                  int height) {
     87   views::Widget* window = CreateBrowserModalDialogViews(this, parent);
     88 
     89   // Center the window over the browser.
     90   gfx::Point center = parent->GetBoundsInScreen().CenterPoint();
     91   int x = center.x() - width / 2;
     92   int y = center.y() - height / 2;
     93   // Ensure the top left and top right of the window are on screen, with
     94   // priority given to the top left.
     95   gfx::Rect screen_rect = gfx::Screen::GetScreenFor(parent)->
     96       GetDisplayNearestPoint(center).bounds();
     97   gfx::Rect bounds_rect = gfx::Rect(x, y, width, height);
     98   bounds_rect.AdjustToFit(screen_rect);
     99   window->SetBounds(bounds_rect);
    100 
    101   window->Show();
    102   // TODO(jamescook): Remove redundant call to Activate()?
    103   window->Activate();
    104 }
    105 
    106 void ExtensionDialog::ObserverDestroyed() {
    107   observer_ = NULL;
    108 }
    109 
    110 void ExtensionDialog::MaybeFocusRenderView() {
    111   views::FocusManager* focus_manager = GetWidget()->GetFocusManager();
    112   DCHECK(focus_manager != NULL);
    113 
    114   // Already there's a focused view, so no need to switch the focus.
    115   if (focus_manager->GetFocusedView())
    116     return;
    117 
    118   content::RenderWidgetHostView* view = host()->render_view_host()->GetView();
    119   if (!view)
    120     return;
    121 
    122   view->Focus();
    123 }
    124 
    125 /////////////////////////////////////////////////////////////////////////////
    126 // views::DialogDelegate overrides.
    127 
    128 int ExtensionDialog::GetDialogButtons() const {
    129   // The only user, SelectFileDialogExtension, provides its own buttons.
    130   return ui::DIALOG_BUTTON_NONE;
    131 }
    132 
    133 bool ExtensionDialog::CanResize() const {
    134   // Can resize only if minimum contents size set.
    135   return host_->view()->GetPreferredSize() != gfx::Size();
    136 }
    137 
    138 void ExtensionDialog::SetMinimumContentsSize(int width, int height) {
    139   host_->view()->SetPreferredSize(gfx::Size(width, height));
    140 }
    141 
    142 ui::ModalType ExtensionDialog::GetModalType() const {
    143   return ui::MODAL_TYPE_WINDOW;
    144 }
    145 
    146 bool ExtensionDialog::ShouldShowWindowTitle() const {
    147   return !window_title_.empty();
    148 }
    149 
    150 base::string16 ExtensionDialog::GetWindowTitle() const {
    151   return window_title_;
    152 }
    153 
    154 void ExtensionDialog::WindowClosing() {
    155   if (observer_)
    156     observer_->ExtensionDialogClosing(this);
    157 }
    158 
    159 void ExtensionDialog::DeleteDelegate() {
    160   // The window has finished closing.  Allow ourself to be deleted.
    161   Release();
    162 }
    163 
    164 views::Widget* ExtensionDialog::GetWidget() {
    165   return host_->view()->GetWidget();
    166 }
    167 
    168 const views::Widget* ExtensionDialog::GetWidget() const {
    169   return host_->view()->GetWidget();
    170 }
    171 
    172 views::View* ExtensionDialog::GetContentsView() {
    173   return host_->view();
    174 }
    175 
    176 bool ExtensionDialog::UseNewStyleForThisDialog() const {
    177   return false;
    178 }
    179 
    180 /////////////////////////////////////////////////////////////////////////////
    181 // content::NotificationObserver overrides.
    182 
    183 void ExtensionDialog::Observe(int type,
    184                              const content::NotificationSource& source,
    185                              const content::NotificationDetails& details) {
    186   switch (type) {
    187     case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING:
    188       // Avoid potential overdraw by removing the temporary background after
    189       // the extension finishes loading.
    190       host_->view()->set_background(NULL);
    191       // The render view is created during the LoadURL(), so we should
    192       // set the focus to the view if nobody else takes the focus.
    193       if (content::Details<extensions::ExtensionHost>(host()) == details)
    194         MaybeFocusRenderView();
    195       break;
    196     case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
    197       // If we aren't the host of the popup, then disregard the notification.
    198       if (content::Details<extensions::ExtensionHost>(host()) != details)
    199         return;
    200       GetWidget()->Close();
    201       break;
    202     case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
    203       if (content::Details<extensions::ExtensionHost>(host()) != details)
    204         return;
    205       if (observer_)
    206         observer_->ExtensionTerminated(this);
    207       break;
    208     default:
    209       NOTREACHED() << L"Received unexpected notification";
    210       break;
    211   }
    212 }
    213