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