extension_dialog.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 "chrome/browser/ui/views/extensions/extension_view_views.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 "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
28namespace {
29
30ExtensionViewViews* GetExtensionView(extensions::ExtensionViewHost* host) {
31  return static_cast<ExtensionViewViews*>(host->view());
32}
33
34}  // namespace
35
36ExtensionDialog::ExtensionDialog(extensions::ExtensionViewHost* host,
37                                 ExtensionDialogObserver* observer)
38    : host_(host),
39      observer_(observer) {
40  AddRef();  // Balanced in DeleteDelegate();
41
42  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
43                 content::Source<BrowserContext>(host->browser_context()));
44  // Listen for the containing view calling window.close();
45  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
46                 content::Source<BrowserContext>(host->browser_context()));
47  // Listen for a crash or other termination of the extension process.
48  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
49                 content::Source<BrowserContext>(host->browser_context()));
50}
51
52ExtensionDialog::~ExtensionDialog() {
53}
54
55// static
56ExtensionDialog* ExtensionDialog::Show(
57    const GURL& url,
58    aura::Window* parent_window,
59    Profile* profile,
60    WebContents* web_contents,
61    int width,
62    int height,
63    int min_width,
64    int min_height,
65    const base::string16& title,
66    ExtensionDialogObserver* observer) {
67  extensions::ExtensionViewHost* host =
68      extensions::ExtensionViewHostFactory::CreateDialogHost(url, profile);
69  if (!host)
70    return NULL;
71  // Preferred size must be set before views::Widget::CreateWindowWithParent
72  // is called because CreateWindowWithParent refers the result of CanResize().
73  ExtensionViewViews* view = GetExtensionView(host);
74  view->SetPreferredSize(gfx::Size(width, height));
75  view->set_minimum_size(gfx::Size(min_width, min_height));
76  host->SetAssociatedWebContents(web_contents);
77
78  DCHECK(parent_window);
79  ExtensionDialog* dialog = new ExtensionDialog(host, observer);
80  dialog->set_title(title);
81  dialog->InitWindow(parent_window, width, height);
82
83  // Show a white background while the extension loads.  This is prettier than
84  // flashing a black unfilled window frame.
85  view->set_background(
86      views::Background::CreateSolidBackground(0xFF, 0xFF, 0xFF));
87  view->SetVisible(true);
88
89  // Ensure the DOM JavaScript can respond immediately to keyboard shortcuts.
90  host->host_contents()->Focus();
91  return dialog;
92}
93
94void ExtensionDialog::InitWindow(aura::Window* parent,
95                                 int width,
96                                 int height) {
97  views::Widget* window = CreateBrowserModalDialogViews(this, parent);
98
99  // Center the window over the browser.
100  gfx::Point center = parent->GetBoundsInScreen().CenterPoint();
101  int x = center.x() - width / 2;
102  int y = center.y() - height / 2;
103  // Ensure the top left and top right of the window are on screen, with
104  // priority given to the top left.
105  gfx::Rect screen_rect = gfx::Screen::GetScreenFor(parent)->
106      GetDisplayNearestPoint(center).bounds();
107  gfx::Rect bounds_rect = gfx::Rect(x, y, width, height);
108  bounds_rect.AdjustToFit(screen_rect);
109  window->SetBounds(bounds_rect);
110
111  window->Show();
112  // TODO(jamescook): Remove redundant call to Activate()?
113  window->Activate();
114}
115
116void ExtensionDialog::ObserverDestroyed() {
117  observer_ = NULL;
118}
119
120void ExtensionDialog::MaybeFocusRenderView() {
121  views::FocusManager* focus_manager = GetWidget()->GetFocusManager();
122  DCHECK(focus_manager != NULL);
123
124  // Already there's a focused view, so no need to switch the focus.
125  if (focus_manager->GetFocusedView())
126    return;
127
128  content::RenderWidgetHostView* view = host()->render_view_host()->GetView();
129  if (!view)
130    return;
131
132  view->Focus();
133}
134
135/////////////////////////////////////////////////////////////////////////////
136// views::DialogDelegate overrides.
137
138int ExtensionDialog::GetDialogButtons() const {
139  // The only user, SelectFileDialogExtension, provides its own buttons.
140  return ui::DIALOG_BUTTON_NONE;
141}
142
143bool ExtensionDialog::CanResize() const {
144  // Can resize only if minimum contents size set.
145  return GetExtensionView(host_.get())->GetPreferredSize() != gfx::Size();
146}
147
148void ExtensionDialog::SetMinimumContentsSize(int width, int height) {
149  GetExtensionView(host_.get())->SetPreferredSize(gfx::Size(width, height));
150}
151
152ui::ModalType ExtensionDialog::GetModalType() const {
153  return ui::MODAL_TYPE_WINDOW;
154}
155
156bool ExtensionDialog::ShouldShowWindowTitle() const {
157  return !window_title_.empty();
158}
159
160base::string16 ExtensionDialog::GetWindowTitle() const {
161  return window_title_;
162}
163
164void ExtensionDialog::WindowClosing() {
165  if (observer_)
166    observer_->ExtensionDialogClosing(this);
167}
168
169void ExtensionDialog::DeleteDelegate() {
170  // The window has finished closing.  Allow ourself to be deleted.
171  Release();
172}
173
174views::Widget* ExtensionDialog::GetWidget() {
175  return GetExtensionView(host_.get())->GetWidget();
176}
177
178const views::Widget* ExtensionDialog::GetWidget() const {
179  return GetExtensionView(host_.get())->GetWidget();
180}
181
182views::View* ExtensionDialog::GetContentsView() {
183  return GetExtensionView(host_.get());
184}
185
186bool ExtensionDialog::UseNewStyleForThisDialog() const {
187  return false;
188}
189
190/////////////////////////////////////////////////////////////////////////////
191// content::NotificationObserver overrides.
192
193void ExtensionDialog::Observe(int type,
194                             const content::NotificationSource& source,
195                             const content::NotificationDetails& details) {
196  switch (type) {
197    case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING:
198      // Avoid potential overdraw by removing the temporary background after
199      // the extension finishes loading.
200      GetExtensionView(host_.get())->set_background(NULL);
201      // The render view is created during the LoadURL(), so we should
202      // set the focus to the view if nobody else takes the focus.
203      if (content::Details<extensions::ExtensionHost>(host()) == details)
204        MaybeFocusRenderView();
205      break;
206    case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
207      // If we aren't the host of the popup, then disregard the notification.
208      if (content::Details<extensions::ExtensionHost>(host()) != details)
209        return;
210      GetWidget()->Close();
211      break;
212    case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
213      if (content::Details<extensions::ExtensionHost>(host()) != details)
214        return;
215      if (observer_)
216        observer_->ExtensionTerminated(this);
217      break;
218    default:
219      NOTREACHED() << L"Received unexpected notification";
220      break;
221  }
222}
223