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