extension_dialog.cc revision 010d83a9304c5a91596085d917d248abff47903a
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
24using content::BrowserContext;
25using content::WebContents;
26
27ExtensionDialog::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
43ExtensionDialog::~ExtensionDialog() {
44}
45
46// static
47ExtensionDialog* 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
84void 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
106void ExtensionDialog::ObserverDestroyed() {
107  observer_ = NULL;
108}
109
110void 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
128int ExtensionDialog::GetDialogButtons() const {
129  // The only user, SelectFileDialogExtension, provides its own buttons.
130  return ui::DIALOG_BUTTON_NONE;
131}
132
133bool ExtensionDialog::CanResize() const {
134  // Can resize only if minimum contents size set.
135  return host_->view()->GetPreferredSize() != gfx::Size();
136}
137
138void ExtensionDialog::SetMinimumContentsSize(int width, int height) {
139  host_->view()->SetPreferredSize(gfx::Size(width, height));
140}
141
142ui::ModalType ExtensionDialog::GetModalType() const {
143  return ui::MODAL_TYPE_WINDOW;
144}
145
146bool ExtensionDialog::ShouldShowWindowTitle() const {
147  return !window_title_.empty();
148}
149
150base::string16 ExtensionDialog::GetWindowTitle() const {
151  return window_title_;
152}
153
154void ExtensionDialog::WindowClosing() {
155  if (observer_)
156    observer_->ExtensionDialogClosing(this);
157}
158
159void ExtensionDialog::DeleteDelegate() {
160  // The window has finished closing.  Allow ourself to be deleted.
161  Release();
162}
163
164views::Widget* ExtensionDialog::GetWidget() {
165  return host_->view()->GetWidget();
166}
167
168const views::Widget* ExtensionDialog::GetWidget() const {
169  return host_->view()->GetWidget();
170}
171
172views::View* ExtensionDialog::GetContentsView() {
173  return host_->view();
174}
175
176bool ExtensionDialog::UseNewStyleForThisDialog() const {
177  return false;
178}
179
180/////////////////////////////////////////////////////////////////////////////
181// content::NotificationObserver overrides.
182
183void 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