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