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