blob: 57fefa85d47a93229320c9ecb890f33807bad225 [file] [log] [blame]
// 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/webui/constrained_web_dialog_delegate_base.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
#include "ui/web_dialogs/web_dialog_ui.h"
namespace {
// WebContentsObserver that tracks the lifetime of the WebContents to avoid
// potential use after destruction.
class InitiatorWebContentsObserver
: public content::WebContentsObserver {
public:
explicit InitiatorWebContentsObserver(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {
}
private:
DISALLOW_COPY_AND_ASSIGN(InitiatorWebContentsObserver);
};
class WebDialogWebContentsDelegateViews
: public ui::WebDialogWebContentsDelegate {
public:
WebDialogWebContentsDelegateViews(content::BrowserContext* browser_context,
InitiatorWebContentsObserver* observer,
views::WebView* web_view)
: ui::WebDialogWebContentsDelegate(browser_context,
new ChromeWebContentsHandler()),
initiator_observer_(observer),
web_view_(web_view) {
}
~WebDialogWebContentsDelegateViews() override {}
// ui::WebDialogWebContentsDelegate:
void HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override {
// Forward shortcut keys in dialog to our initiator's delegate.
// http://crbug.com/104586
// Disabled on Mac due to http://crbug.com/112173
#if !defined(OS_MACOSX)
if (!initiator_observer_->web_contents())
return;
auto* delegate = initiator_observer_->web_contents()->GetDelegate();
if (!delegate)
return;
delegate->HandleKeyboardEvent(initiator_observer_->web_contents(), event);
#endif
}
void ResizeDueToAutoResize(content::WebContents* source,
const gfx::Size& preferred_size) override {
if (source != web_view_->GetWebContents())
return;
if (!initiator_observer_->web_contents())
return;
// Sets WebView's preferred size based on auto-resized contents.
web_view_->SetPreferredSize(preferred_size);
content::WebContents* top_level_web_contents =
constrained_window::GetTopLevelWebContents(
initiator_observer_->web_contents());
if (top_level_web_contents) {
constrained_window::UpdateWebContentsModalDialogPosition(
web_view_->GetWidget(),
web_modal::WebContentsModalDialogManager::FromWebContents(
top_level_web_contents)
->delegate()
->GetWebContentsModalDialogHost());
}
}
private:
InitiatorWebContentsObserver* const initiator_observer_;
views::WebView* web_view_;
DISALLOW_COPY_AND_ASSIGN(WebDialogWebContentsDelegateViews);
};
class ConstrainedWebDialogDelegateViews
: public ConstrainedWebDialogDelegateBase {
public:
ConstrainedWebDialogDelegateViews(content::BrowserContext* context,
ui::WebDialogDelegate* delegate,
InitiatorWebContentsObserver* observer,
views::WebView* view)
: ConstrainedWebDialogDelegateBase(context, delegate,
new WebDialogWebContentsDelegateViews(context, observer, view)),
view_(view) {}
~ConstrainedWebDialogDelegateViews() override {}
// ui::WebDialogWebContentsDelegate:
void CloseContents(content::WebContents* source) override {
view_->GetWidget()->Close();
}
// contents::WebContentsDelegate:
void HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override {
unhandled_keyboard_event_handler_.HandleKeyboardEvent(
event, view_->GetFocusManager());
}
// ConstrainedWebDialogDelegate:
gfx::NativeWindow GetNativeDialog() override {
return view_->GetWidget()->GetNativeWindow();
}
private:
// Converts keyboard events on the WebContents to accelerators.
views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
views::WebView* view_;
DISALLOW_COPY_AND_ASSIGN(ConstrainedWebDialogDelegateViews);
};
class ConstrainedWebDialogDelegateViewViews
: public views::WebView,
public ConstrainedWebDialogDelegate,
public views::WidgetDelegate {
public:
ConstrainedWebDialogDelegateViewViews(
content::BrowserContext* browser_context,
ui::WebDialogDelegate* delegate,
content::WebContents* web_contents,
const gfx::Size& min_size,
const gfx::Size& max_size)
: views::WebView(browser_context),
initiator_observer_(web_contents),
impl_(new ConstrainedWebDialogDelegateViews(browser_context, delegate,
&initiator_observer_,
this)),
min_size_(min_size),
max_size_(max_size) {
SetWebContents(GetWebContents());
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
if (!max_size_.IsEmpty())
EnableAutoResize();
}
~ConstrainedWebDialogDelegateViewViews() override {}
// ConstrainedWebDialogDelegate:
const ui::WebDialogDelegate* GetWebDialogDelegate() const override {
return impl_->GetWebDialogDelegate();
}
ui::WebDialogDelegate* GetWebDialogDelegate() override {
return impl_->GetWebDialogDelegate();
}
void OnDialogCloseFromWebUI() override {
return impl_->OnDialogCloseFromWebUI();
}
void ReleaseWebContentsOnDialogClose() override {
return impl_->ReleaseWebContentsOnDialogClose();
}
gfx::NativeWindow GetNativeDialog() override {
return impl_->GetNativeDialog();
}
content::WebContents* GetWebContents() override {
return impl_->GetWebContents();
}
// views::WidgetDelegate:
views::View* GetInitiallyFocusedView() override { return this; }
void WindowClosing() override {
if (!impl_->closed_via_webui())
GetWebDialogDelegate()->OnDialogClosed(std::string());
}
views::Widget* GetWidget() override { return View::GetWidget(); }
const views::Widget* GetWidget() const override { return View::GetWidget(); }
base::string16 GetWindowTitle() const override {
return impl_->closed_via_webui() ? base::string16() :
GetWebDialogDelegate()->GetDialogTitle();
}
views::View* GetContentsView() override { return this; }
views::NonClientFrameView* CreateNonClientFrameView(
views::Widget* widget) override {
return views::DialogDelegate::CreateDialogFrameView(widget, gfx::Insets());
}
bool ShouldShowCloseButton() const override {
// No close button if the dialog doesn't want a title bar.
return impl_->GetWebDialogDelegate()->ShouldShowDialogTitle();
}
ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_CHILD; }
// views::WebView:
bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
// Pressing ESC closes the dialog.
DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code());
GetWidget()->Close();
return true;
}
gfx::Size GetPreferredSize() const override {
gfx::Size size;
if (!impl_->closed_via_webui()) {
// If auto-resizing is enabled and the dialog has been auto-resized,
// GetPreferredSize() will return the appropriate current size. In this
// case, GetDialogSize() should leave its argument untouched. In all
// other cases, GetDialogSize() will overwrite the passed-in size.
size = WebView::GetPreferredSize();
GetWebDialogDelegate()->GetDialogSize(&size);
}
return size;
}
gfx::Size GetMinimumSize() const override {
return min_size_;
}
gfx::Size GetMaximumSize() const override {
return !max_size_.IsEmpty() ? max_size_ : WebView::GetMaximumSize();
}
void RenderViewCreated(content::RenderViewHost* render_view_host) override {
if (!max_size_.IsEmpty())
EnableAutoResize();
}
void RenderViewHostChanged(content::RenderViewHost* old_host,
content::RenderViewHost* new_host) override {
if (!max_size_.IsEmpty())
EnableAutoResize();
}
void DocumentOnLoadCompletedInMainFrame() override {
if (!max_size_.IsEmpty() && initiator_observer_.web_contents()) {
content::WebContents* top_level_web_contents =
constrained_window::GetTopLevelWebContents(
initiator_observer_.web_contents());
if (top_level_web_contents) {
constrained_window::ShowModalDialog(GetWidget()->GetNativeWindow(),
top_level_web_contents);
}
}
}
private:
void EnableAutoResize() {
content::RenderViewHost* render_view_host =
GetWebContents()->GetRenderViewHost();
render_view_host->EnableAutoResize(min_size_, max_size_);
}
InitiatorWebContentsObserver initiator_observer_;
std::unique_ptr<ConstrainedWebDialogDelegateViews> impl_;
// Minimum and maximum sizes to determine dialog bounds for auto-resizing.
const gfx::Size min_size_;
const gfx::Size max_size_;
DISALLOW_COPY_AND_ASSIGN(ConstrainedWebDialogDelegateViewViews);
};
} // namespace
ConstrainedWebDialogDelegate* ShowConstrainedWebDialog(
content::BrowserContext* browser_context,
ui::WebDialogDelegate* delegate,
content::WebContents* web_contents) {
ConstrainedWebDialogDelegateViewViews* dialog =
new ConstrainedWebDialogDelegateViewViews(
browser_context, delegate, web_contents,
gfx::Size(), gfx::Size());
constrained_window::ShowWebModalDialogViews(dialog, web_contents);
return dialog;
}
ConstrainedWebDialogDelegate* ShowConstrainedWebDialogWithAutoResize(
content::BrowserContext* browser_context,
ui::WebDialogDelegate* delegate,
content::WebContents* web_contents,
const gfx::Size& min_size,
const gfx::Size& max_size) {
DCHECK(!min_size.IsEmpty());
DCHECK(!max_size.IsEmpty());
ConstrainedWebDialogDelegateViewViews* dialog =
new ConstrainedWebDialogDelegateViewViews(
browser_context, delegate, web_contents,
min_size, max_size);
// For embedded WebContents, use the embedder's WebContents for constrained
// window.
content::WebContents* top_level_web_contents =
constrained_window::GetTopLevelWebContents(web_contents);
DCHECK(top_level_web_contents);
constrained_window::CreateWebModalDialogViews(dialog, top_level_web_contents);
return dialog;
}