extension_dialog.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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    : 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  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(min_width, min_height));
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  views::Widget* window = CreateBrowserModalDialogViews(this, 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::MaybeFocusRenderView() {
125  views::FocusManager* focus_manager = GetWidget()->GetFocusManager();
126  DCHECK(focus_manager != NULL);
127
128  // Already there's a focused view, so no need to switch the focus.
129  if (focus_manager->GetFocusedView())
130    return;
131
132  content::RenderWidgetHostView* view = host()->render_view_host()->GetView();
133  if (!view)
134    return;
135
136  // TODO(oshima): Views + aura doesn't seem to update the views focus
137  // manager when an aura window gets focus. This is a workaround for
138  // this issue. Fix it this and remove this workaround.
139  // See bug.com/127222.
140  focus_manager->SetFocusedView(GetContentsView());
141  view->Focus();
142}
143
144/////////////////////////////////////////////////////////////////////////////
145// views::DialogDelegate overrides.
146
147int ExtensionDialog::GetDialogButtons() const {
148  // The only user, SelectFileDialogExtension, provides its own buttons.
149  return ui::DIALOG_BUTTON_NONE;
150}
151
152bool ExtensionDialog::CanResize() const {
153  // Can resize only if minimum contents size set.
154  return extension_host_->view()->GetPreferredSize() != gfx::Size();
155}
156
157void ExtensionDialog::SetMinimumContentsSize(int width, int height) {
158  extension_host_->view()->SetPreferredSize(gfx::Size(width, height));
159}
160
161ui::ModalType ExtensionDialog::GetModalType() const {
162  return ui::MODAL_TYPE_WINDOW;
163}
164
165bool ExtensionDialog::ShouldShowWindowTitle() const {
166  return !window_title_.empty();
167}
168
169string16 ExtensionDialog::GetWindowTitle() const {
170  return window_title_;
171}
172
173void ExtensionDialog::WindowClosing() {
174  if (observer_)
175    observer_->ExtensionDialogClosing(this);
176}
177
178void ExtensionDialog::DeleteDelegate() {
179  // The window has finished closing.  Allow ourself to be deleted.
180  Release();
181}
182
183views::Widget* ExtensionDialog::GetWidget() {
184  return extension_host_->view()->GetWidget();
185}
186
187const views::Widget* ExtensionDialog::GetWidget() const {
188  return extension_host_->view()->GetWidget();
189}
190
191views::View* ExtensionDialog::GetContentsView() {
192  return extension_host_->view();
193}
194
195bool ExtensionDialog::UseNewStyleForThisDialog() const {
196  return false;
197}
198
199/////////////////////////////////////////////////////////////////////////////
200// content::NotificationObserver overrides.
201
202void ExtensionDialog::Observe(int type,
203                             const content::NotificationSource& source,
204                             const content::NotificationDetails& details) {
205  switch (type) {
206    case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING:
207      // Avoid potential overdraw by removing the temporary background after
208      // the extension finishes loading.
209      extension_host_->view()->set_background(NULL);
210      // The render view is created during the LoadURL(), so we should
211      // set the focus to the view if nobody else takes the focus.
212      if (content::Details<extensions::ExtensionHost>(host()) == details)
213        MaybeFocusRenderView();
214      break;
215    case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
216      // If we aren't the host of the popup, then disregard the notification.
217      if (content::Details<extensions::ExtensionHost>(host()) != details)
218        return;
219      GetWidget()->Close();
220      break;
221    case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
222      if (content::Details<extensions::ExtensionHost>(host()) != details)
223        return;
224      if (observer_)
225        observer_->ExtensionTerminated(this);
226      break;
227    default:
228      NOTREACHED() << L"Received unexpected notification";
229      break;
230  }
231}
232