// 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.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(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_FIRST_LOAD, content::Source(host->browser_context())); // Listen for the containing view calling window.close(); registrar_.Add(this, extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, content::Source(host->browser_context())); // Listen for a crash or other termination of the extension process. registrar_.Add(this, extensions::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, content::Source(host->browser_context())); } ExtensionDialog::~ExtensionDialog() { } // static ExtensionDialog* ExtensionDialog::Show( const GURL& url, gfx::NativeWindow 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(gfx::NativeWindow parent, int width, int height) { views::Widget* window = constrained_window::CreateBrowserModalDialogViews(this, parent); // Center the window over the browser. views::Widget* parent_widget = views::Widget::GetWidgetForNativeWindow(parent); gfx::Point center = parent_widget->GetWindowBoundsInScreen().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::GetScreen()->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()->GetWidget()->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 static_cast(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_FIRST_LOAD: // 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(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(host()) != details) return; GetWidget()->Close(); break; case extensions::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: if (content::Details(host()) != details) return; if (observer_) observer_->ExtensionTerminated(this); break; default: NOTREACHED() << L"Received unexpected notification"; break; } }