extension_dialog.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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_host.h"
9#include "chrome/browser/extensions/extension_process_manager.h"
10#include "chrome/browser/extensions/extension_system.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/ui/views/constrained_window_views.h"
13#include "chrome/browser/ui/views/extensions/extension_dialog_observer.h"
14#include "content/public/browser/notification_details.h"
15#include "content/public/browser/notification_source.h"
16#include "content/public/browser/render_view_host.h"
17#include "content/public/browser/render_widget_host_view.h"
18#include "content/public/browser/web_contents.h"
19#include "content/public/browser/web_contents_view.h"
20#include "ui/base/base_window.h"
21#include "ui/gfx/screen.h"
22#include "ui/views/background.h"
23#include "ui/views/widget/widget.h"
24#include "url/gurl.h"
25
26using content::WebContents;
27
28ExtensionDialog::ExtensionDialog(extensions::ExtensionHost* host,
29                                 ExtensionDialogObserver* observer)
30    : window_(NULL),
31      extension_host_(host),
32      observer_(observer) {
33  AddRef();  // Balanced in DeleteDelegate();
34
35  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
36                 content::Source<Profile>(host->profile()));
37  // Listen for the containing view calling window.close();
38  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
39                 content::Source<Profile>(host->profile()));
40  // Listen for a crash or other termination of the extension process.
41  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
42                 content::Source<Profile>(host->profile()));
43}
44
45ExtensionDialog::~ExtensionDialog() {
46}
47
48// static
49ExtensionDialog* ExtensionDialog::Show(
50    const GURL& url,
51    ui::BaseWindow* base_window,
52    Profile* profile,
53    WebContents* web_contents,
54    int width,
55    int height,
56    int min_width,
57    int min_height,
58    const string16& title,
59    ExtensionDialogObserver* observer) {
60  extensions::ExtensionHost* host = CreateExtensionHost(url, profile);
61  // Preferred size must be set before views::Widget::CreateWindowWithParent
62  // is called because CreateWindowWithParent refers the result of CanResize().
63  host->view()->SetPreferredSize(gfx::Size(min_width, min_height));
64  if (!host)
65    return NULL;
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
84// static
85extensions::ExtensionHost* ExtensionDialog::CreateExtensionHost(
86    const GURL& url,
87    Profile* profile) {
88  DCHECK(profile);
89  ExtensionProcessManager* manager =
90      extensions::ExtensionSystem::Get(profile)->process_manager();
91
92  DCHECK(manager);
93  if (!manager)
94    return NULL;
95  return manager->CreateDialogHost(url);
96}
97
98void ExtensionDialog::InitWindow(ui::BaseWindow* base_window,
99                                 int width,
100                                 int height) {
101  gfx::NativeWindow parent = base_window->GetNativeWindow();
102  window_ = CreateBrowserModalDialogViews(this, parent);
103
104  // Center the window over the browser.
105  gfx::Point center = base_window->GetBounds().CenterPoint();
106  int x = center.x() - width / 2;
107  int y = center.y() - height / 2;
108  // Ensure the top left and top right of the window are on screen, with
109  // priority given to the top left.
110  gfx::Rect screen_rect = gfx::Screen::GetScreenFor(parent)->
111      GetDisplayNearestPoint(center).bounds();
112  gfx::Rect bounds_rect = gfx::Rect(x, y, width, height);
113  bounds_rect.AdjustToFit(screen_rect);
114  window_->SetBounds(bounds_rect);
115
116  window_->Show();
117  // TODO(jamescook): Remove redundant call to Activate()?
118  window_->Activate();
119}
120
121void ExtensionDialog::ObserverDestroyed() {
122  observer_ = NULL;
123}
124
125void ExtensionDialog::Close() {
126  if (!window_)
127    return;
128
129  window_->Close();
130  window_ = NULL;
131}
132
133void ExtensionDialog::MaybeFocusRenderView() {
134  views::FocusManager* focus_manager = GetWidget()->GetFocusManager();
135  DCHECK(focus_manager != NULL);
136
137  // Already there's a focused view, so no need to switch the focus.
138  if (focus_manager->GetFocusedView())
139    return;
140
141  content::RenderWidgetHostView* view = host()->render_view_host()->GetView();
142  if (!view)
143    return;
144
145  // TODO(oshima): Views + aura doesn't seem to update the views focus
146  // manager when an aura window gets focus. This is a workaround for
147  // this issue. Fix it this and remove this workaround.
148  // See bug.com/127222.
149  focus_manager->SetFocusedView(GetContentsView());
150  view->Focus();
151}
152
153/////////////////////////////////////////////////////////////////////////////
154// views::DialogDelegate overrides.
155
156int ExtensionDialog::GetDialogButtons() const {
157  // The only user, SelectFileDialogExtension, provides its own buttons.
158  return ui::DIALOG_BUTTON_NONE;
159}
160
161bool ExtensionDialog::CanResize() const {
162  // Can resize only if minimum contents size set.
163  return extension_host_->view()->GetPreferredSize() != gfx::Size();
164}
165
166void ExtensionDialog::SetMinimumContentsSize(int width, int height) {
167  extension_host_->view()->SetPreferredSize(gfx::Size(width, height));
168}
169
170ui::ModalType ExtensionDialog::GetModalType() const {
171  return ui::MODAL_TYPE_WINDOW;
172}
173
174bool ExtensionDialog::ShouldShowWindowTitle() const {
175  return !window_title_.empty();
176}
177
178string16 ExtensionDialog::GetWindowTitle() const {
179  return window_title_;
180}
181
182void ExtensionDialog::WindowClosing() {
183  if (observer_)
184    observer_->ExtensionDialogClosing(this);
185}
186
187void ExtensionDialog::DeleteDelegate() {
188  // The window has finished closing.  Allow ourself to be deleted.
189  Release();
190}
191
192views::Widget* ExtensionDialog::GetWidget() {
193  return extension_host_->view()->GetWidget();
194}
195
196const views::Widget* ExtensionDialog::GetWidget() const {
197  return extension_host_->view()->GetWidget();
198}
199
200views::View* ExtensionDialog::GetContentsView() {
201  return extension_host_->view();
202}
203
204bool ExtensionDialog::UseNewStyleForThisDialog() const {
205  return false;
206}
207
208/////////////////////////////////////////////////////////////////////////////
209// content::NotificationObserver overrides.
210
211void ExtensionDialog::Observe(int type,
212                             const content::NotificationSource& source,
213                             const content::NotificationDetails& details) {
214  switch (type) {
215    case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING:
216      // Avoid potential overdraw by removing the temporary background after
217      // the extension finishes loading.
218      extension_host_->view()->set_background(NULL);
219      // The render view is created during the LoadURL(), so we should
220      // set the focus to the view if nobody else takes the focus.
221      if (content::Details<extensions::ExtensionHost>(host()) == details)
222        MaybeFocusRenderView();
223      break;
224    case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
225      // If we aren't the host of the popup, then disregard the notification.
226      if (content::Details<extensions::ExtensionHost>(host()) != details)
227        return;
228      Close();
229      break;
230    case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
231      if (content::Details<extensions::ExtensionHost>(host()) != details)
232        return;
233      if (observer_)
234        observer_->ExtensionTerminated(this);
235      break;
236    default:
237      NOTREACHED() << L"Received unexpected notification";
238      break;
239  }
240}
241