| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/views/extensions/extension_dialog.h" |
| |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/extensions/extension_view_host.h" |
| #include "chrome/browser/extensions/extension_view_host_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/views/extensions/extension_dialog_observer.h" |
| #include "chrome/browser/ui/views/extensions/extension_view_views.h" |
| #include "components/constrained_window/constrained_window_views.h" |
| #include "content/public/browser/notification_details.h" |
| #include "content/public/browser/notification_source.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "content/public/browser/web_contents.h" |
| #include "ui/base/base_window.h" |
| #include "ui/gfx/screen.h" |
| #include "ui/views/background.h" |
| #include "ui/views/widget/widget.h" |
| #include "url/gurl.h" |
| |
| using content::BrowserContext; |
| using content::WebContents; |
| |
| namespace { |
| |
| ExtensionViewViews* GetExtensionView(extensions::ExtensionViewHost* host) { |
| return static_cast<ExtensionViewViews*>(host->view()); |
| } |
| |
| } // namespace |
| |
| ExtensionDialog::ExtensionDialog(extensions::ExtensionViewHost* host, |
| ExtensionDialogObserver* observer) |
| : host_(host), |
| observer_(observer) { |
| AddRef(); // Balanced in DeleteDelegate(); |
| |
| registrar_.Add(this, |
| extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, |
| content::Source<BrowserContext>(host->browser_context())); |
| // Listen for the containing view calling window.close(); |
| registrar_.Add(this, |
| extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, |
| content::Source<BrowserContext>(host->browser_context())); |
| // Listen for a crash or other termination of the extension process. |
| registrar_.Add(this, |
| extensions::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, |
| content::Source<BrowserContext>(host->browser_context())); |
| } |
| |
| ExtensionDialog::~ExtensionDialog() { |
| } |
| |
| // static |
| ExtensionDialog* ExtensionDialog::Show( |
| const GURL& url, |
| aura::Window* parent_window, |
| Profile* profile, |
| WebContents* web_contents, |
| int width, |
| int height, |
| int min_width, |
| int min_height, |
| const base::string16& title, |
| ExtensionDialogObserver* observer) { |
| extensions::ExtensionViewHost* host = |
| extensions::ExtensionViewHostFactory::CreateDialogHost(url, profile); |
| if (!host) |
| return NULL; |
| // Preferred size must be set before views::Widget::CreateWindowWithParent |
| // is called because CreateWindowWithParent refers the result of CanResize(). |
| ExtensionViewViews* view = GetExtensionView(host); |
| view->SetPreferredSize(gfx::Size(width, height)); |
| view->set_minimum_size(gfx::Size(min_width, min_height)); |
| host->SetAssociatedWebContents(web_contents); |
| |
| DCHECK(parent_window); |
| ExtensionDialog* dialog = new ExtensionDialog(host, observer); |
| dialog->set_title(title); |
| dialog->InitWindow(parent_window, width, height); |
| |
| // Show a white background while the extension loads. This is prettier than |
| // flashing a black unfilled window frame. |
| view->set_background( |
| views::Background::CreateSolidBackground(0xFF, 0xFF, 0xFF)); |
| view->SetVisible(true); |
| |
| // Ensure the DOM JavaScript can respond immediately to keyboard shortcuts. |
| host->host_contents()->Focus(); |
| return dialog; |
| } |
| |
| void ExtensionDialog::InitWindow(aura::Window* parent, |
| int width, |
| int height) { |
| views::Widget* window = CreateBrowserModalDialogViews(this, parent); |
| |
| // Center the window over the browser. |
| gfx::Point center = parent->GetBoundsInScreen().CenterPoint(); |
| int x = center.x() - width / 2; |
| int y = center.y() - height / 2; |
| // Ensure the top left and top right of the window are on screen, with |
| // priority given to the top left. |
| gfx::Rect screen_rect = gfx::Screen::GetScreenFor(parent)-> |
| GetDisplayNearestPoint(center).bounds(); |
| gfx::Rect bounds_rect = gfx::Rect(x, y, width, height); |
| bounds_rect.AdjustToFit(screen_rect); |
| window->SetBounds(bounds_rect); |
| |
| window->Show(); |
| // TODO(jamescook): Remove redundant call to Activate()? |
| window->Activate(); |
| } |
| |
| void ExtensionDialog::ObserverDestroyed() { |
| observer_ = NULL; |
| } |
| |
| void ExtensionDialog::MaybeFocusRenderView() { |
| views::FocusManager* focus_manager = GetWidget()->GetFocusManager(); |
| DCHECK(focus_manager != NULL); |
| |
| // Already there's a focused view, so no need to switch the focus. |
| if (focus_manager->GetFocusedView()) |
| return; |
| |
| content::RenderWidgetHostView* view = host()->render_view_host()->GetView(); |
| if (!view) |
| return; |
| |
| view->Focus(); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // views::DialogDelegate overrides. |
| |
| int ExtensionDialog::GetDialogButtons() const { |
| // The only user, SelectFileDialogExtension, provides its own buttons. |
| return ui::DIALOG_BUTTON_NONE; |
| } |
| |
| bool ExtensionDialog::CanResize() const { |
| // Can resize only if minimum contents size set. |
| return GetExtensionView(host_.get())->GetPreferredSize() != gfx::Size(); |
| } |
| |
| void ExtensionDialog::SetMinimumContentsSize(int width, int height) { |
| GetExtensionView(host_.get())->SetPreferredSize(gfx::Size(width, height)); |
| } |
| |
| ui::ModalType ExtensionDialog::GetModalType() const { |
| return ui::MODAL_TYPE_WINDOW; |
| } |
| |
| bool ExtensionDialog::ShouldShowWindowTitle() const { |
| return !window_title_.empty(); |
| } |
| |
| base::string16 ExtensionDialog::GetWindowTitle() const { |
| return window_title_; |
| } |
| |
| void ExtensionDialog::WindowClosing() { |
| if (observer_) |
| observer_->ExtensionDialogClosing(this); |
| } |
| |
| void ExtensionDialog::DeleteDelegate() { |
| // The window has finished closing. Allow ourself to be deleted. |
| Release(); |
| } |
| |
| views::Widget* ExtensionDialog::GetWidget() { |
| return GetExtensionView(host_.get())->GetWidget(); |
| } |
| |
| const views::Widget* ExtensionDialog::GetWidget() const { |
| return GetExtensionView(host_.get())->GetWidget(); |
| } |
| |
| views::View* ExtensionDialog::GetContentsView() { |
| return GetExtensionView(host_.get()); |
| } |
| |
| bool ExtensionDialog::UseNewStyleForThisDialog() const { |
| return false; |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // content::NotificationObserver overrides. |
| |
| void ExtensionDialog::Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| switch (type) { |
| case extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: |
| // Avoid potential overdraw by removing the temporary background after |
| // the extension finishes loading. |
| GetExtensionView(host_.get())->set_background(NULL); |
| // The render view is created during the LoadURL(), so we should |
| // set the focus to the view if nobody else takes the focus. |
| if (content::Details<extensions::ExtensionHost>(host()) == details) |
| MaybeFocusRenderView(); |
| break; |
| case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: |
| // If we aren't the host of the popup, then disregard the notification. |
| if (content::Details<extensions::ExtensionHost>(host()) != details) |
| return; |
| GetWidget()->Close(); |
| break; |
| case extensions::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: |
| if (content::Details<extensions::ExtensionHost>(host()) != details) |
| return; |
| if (observer_) |
| observer_->ExtensionTerminated(this); |
| break; |
| default: |
| NOTREACHED() << L"Received unexpected notification"; |
| break; |
| } |
| } |