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