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