extension_dialog.cc revision f2477e01787aa58f445919b809d89e252beef54f
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
25using content::BrowserContext;
26using content::WebContents;
27
28ExtensionDialog::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
44ExtensionDialog::~ExtensionDialog() {
45}
46
47// static
48ExtensionDialog* 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 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(min_width, min_height));
66  host->SetAssociatedWebContents(web_contents);
67
68  CHECK(base_window);
69  ExtensionDialog* dialog = new ExtensionDialog(host, observer);
70  dialog->set_title(title);
71  dialog->InitWindow(base_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()->GetView()->Focus();
81  return dialog;
82}
83
84void ExtensionDialog::InitWindow(ui::BaseWindow* base_window,
85                                 int width,
86                                 int height) {
87  gfx::NativeWindow parent = base_window->GetNativeWindow();
88  views::Widget* window = CreateBrowserModalDialogViews(this, parent);
89
90  // Center the window over the browser.
91  gfx::Point center = base_window->GetBounds().CenterPoint();
92  int x = center.x() - width / 2;
93  int y = center.y() - height / 2;
94  // Ensure the top left and top right of the window are on screen, with
95  // priority given to the top left.
96  gfx::Rect screen_rect = gfx::Screen::GetScreenFor(parent)->
97      GetDisplayNearestPoint(center).bounds();
98  gfx::Rect bounds_rect = gfx::Rect(x, y, width, height);
99  bounds_rect.AdjustToFit(screen_rect);
100  window->SetBounds(bounds_rect);
101
102  window->Show();
103  // TODO(jamescook): Remove redundant call to Activate()?
104  window->Activate();
105}
106
107void ExtensionDialog::ObserverDestroyed() {
108  observer_ = NULL;
109}
110
111void ExtensionDialog::MaybeFocusRenderView() {
112  views::FocusManager* focus_manager = GetWidget()->GetFocusManager();
113  DCHECK(focus_manager != NULL);
114
115  // Already there's a focused view, so no need to switch the focus.
116  if (focus_manager->GetFocusedView())
117    return;
118
119  content::RenderWidgetHostView* view = host()->render_view_host()->GetView();
120  if (!view)
121    return;
122
123  view->Focus();
124}
125
126/////////////////////////////////////////////////////////////////////////////
127// views::DialogDelegate overrides.
128
129int ExtensionDialog::GetDialogButtons() const {
130  // The only user, SelectFileDialogExtension, provides its own buttons.
131  return ui::DIALOG_BUTTON_NONE;
132}
133
134bool ExtensionDialog::CanResize() const {
135  // Can resize only if minimum contents size set.
136  return host_->view()->GetPreferredSize() != gfx::Size();
137}
138
139void ExtensionDialog::SetMinimumContentsSize(int width, int height) {
140  host_->view()->SetPreferredSize(gfx::Size(width, height));
141}
142
143ui::ModalType ExtensionDialog::GetModalType() const {
144  return ui::MODAL_TYPE_WINDOW;
145}
146
147bool ExtensionDialog::ShouldShowWindowTitle() const {
148  return !window_title_.empty();
149}
150
151string16 ExtensionDialog::GetWindowTitle() const {
152  return window_title_;
153}
154
155void ExtensionDialog::WindowClosing() {
156  if (observer_)
157    observer_->ExtensionDialogClosing(this);
158}
159
160void ExtensionDialog::DeleteDelegate() {
161  // The window has finished closing.  Allow ourself to be deleted.
162  Release();
163}
164
165views::Widget* ExtensionDialog::GetWidget() {
166  return host_->view()->GetWidget();
167}
168
169const views::Widget* ExtensionDialog::GetWidget() const {
170  return host_->view()->GetWidget();
171}
172
173views::View* ExtensionDialog::GetContentsView() {
174  return host_->view();
175}
176
177bool ExtensionDialog::UseNewStyleForThisDialog() const {
178  return false;
179}
180
181/////////////////////////////////////////////////////////////////////////////
182// content::NotificationObserver overrides.
183
184void ExtensionDialog::Observe(int type,
185                             const content::NotificationSource& source,
186                             const content::NotificationDetails& details) {
187  switch (type) {
188    case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING:
189      // Avoid potential overdraw by removing the temporary background after
190      // the extension finishes loading.
191      host_->view()->set_background(NULL);
192      // The render view is created during the LoadURL(), so we should
193      // set the focus to the view if nobody else takes the focus.
194      if (content::Details<extensions::ExtensionHost>(host()) == details)
195        MaybeFocusRenderView();
196      break;
197    case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
198      // If we aren't the host of the popup, then disregard the notification.
199      if (content::Details<extensions::ExtensionHost>(host()) != details)
200        return;
201      GetWidget()->Close();
202      break;
203    case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
204      if (content::Details<extensions::ExtensionHost>(host()) != details)
205        return;
206      if (observer_)
207        observer_->ExtensionTerminated(this);
208      break;
209    default:
210      NOTREACHED() << L"Received unexpected notification";
211      break;
212  }
213}
214