diff options
author | oshima <oshima@chromium.org> | 2014-10-24 14:54:11 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-24 21:54:36 +0000 |
commit | 136691ad8af860f6bd89f17b9f3c9693c7d780bc (patch) | |
tree | 44c7ebe133c57520addb5bb911e76803eca1695a /components/constrained_window | |
parent | 728b25306a408de0e0cee7b1db99e037ebba0b5f (diff) | |
download | chromium_src-136691ad8af860f6bd89f17b9f3c9693c7d780bc.zip chromium_src-136691ad8af860f6bd89f17b9f3c9693c7d780bc.tar.gz chromium_src-136691ad8af860f6bd89f17b9f3c9693c7d780bc.tar.bz2 |
Componentize Constrained Window Views
* Added ConstrainedWindowViewsClient to delegate browser/extensions dependent code.
BUG=410499
Review URL: https://codereview.chromium.org/658383003
Cr-Commit-Position: refs/heads/master@{#301203}
Diffstat (limited to 'components/constrained_window')
-rw-r--r-- | components/constrained_window/BUILD.gn | 29 | ||||
-rw-r--r-- | components/constrained_window/DEPS | 6 | ||||
-rw-r--r-- | components/constrained_window/OWNERS | 2 | ||||
-rw-r--r-- | components/constrained_window/constrained_window_views.cc | 174 | ||||
-rw-r--r-- | components/constrained_window/constrained_window_views.h | 56 | ||||
-rw-r--r-- | components/constrained_window/constrained_window_views_client.h | 32 | ||||
-rw-r--r-- | components/constrained_window/constrained_window_views_unittest.cc | 154 |
7 files changed, 453 insertions, 0 deletions
diff --git a/components/constrained_window/BUILD.gn b/components/constrained_window/BUILD.gn new file mode 100644 index 0000000..7ffac17 --- /dev/null +++ b/components/constrained_window/BUILD.gn @@ -0,0 +1,29 @@ +# Copyright 2014 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. + +static_library("constrained_window") { + sources = [ + "constrained_window_views.cc", + "constrained_window_views.h", + "constranied_window/constrained_window_views_client.h", + ] + + deps = [ + "//components/web_modal", + "//ui/views", + "//skia", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "constrained_window_views_unittest.cc", + ] + + deps = [ + ":constrained_window", + "//ui/views:test_support", + ] +} diff --git a/components/constrained_window/DEPS b/components/constrained_window/DEPS new file mode 100644 index 0000000..9f4c9d1 --- /dev/null +++ b/components/constrained_window/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+components/web_modal", + "+ui/base", + "+ui/gfx", + "+ui/views", +] diff --git a/components/constrained_window/OWNERS b/components/constrained_window/OWNERS new file mode 100644 index 0000000..90c129e --- /dev/null +++ b/components/constrained_window/OWNERS @@ -0,0 +1,2 @@ +per-file constrained_window*=gbillock@chromium.org +per-file constrained_window*=msw@chromium.org diff --git a/components/constrained_window/constrained_window_views.cc b/components/constrained_window/constrained_window_views.cc new file mode 100644 index 0000000..43d4830 --- /dev/null +++ b/components/constrained_window/constrained_window_views.cc @@ -0,0 +1,174 @@ +// 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 "components/constrained_window/constrained_window_views.h" + +#include <algorithm> + +#include "components/constrained_window/constrained_window_views_client.h" +#include "components/web_modal/popup_manager.h" +#include "components/web_modal/web_contents_modal_dialog_host.h" +#include "ui/views/border.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" +#include "ui/views/window/dialog_delegate.h" + +using web_modal::ModalDialogHost; +using web_modal::ModalDialogHostObserver; + +namespace { + +ConstrainedWindowViewsClient* constrained_window_views_client = NULL; + +// The name of a key to store on the window handle to associate +// WidgetModalDialogHostObserverViews with the Widget. +const char* const kWidgetModalDialogHostObserverViewsKey = + "__WIDGET_MODAL_DIALOG_HOST_OBSERVER_VIEWS__"; + +// Applies positioning changes from the ModalDialogHost to the Widget. +class WidgetModalDialogHostObserverViews + : public views::WidgetObserver, + public ModalDialogHostObserver { + public: + WidgetModalDialogHostObserverViews(ModalDialogHost* host, + views::Widget* target_widget, + const char *const native_window_property) + : host_(host), + target_widget_(target_widget), + native_window_property_(native_window_property) { + DCHECK(host_); + DCHECK(target_widget_); + host_->AddObserver(this); + target_widget_->AddObserver(this); + } + + virtual ~WidgetModalDialogHostObserverViews() { + if (host_) + host_->RemoveObserver(this); + target_widget_->RemoveObserver(this); + target_widget_->SetNativeWindowProperty(native_window_property_, NULL); + } + + // WidgetObserver overrides + virtual void OnWidgetClosing(views::Widget* widget) override { + delete this; + } + + // WebContentsModalDialogHostObserver overrides + virtual void OnPositionRequiresUpdate() override { + UpdateWidgetModalDialogPosition(target_widget_, host_); + } + + virtual void OnHostDestroying() override { + host_->RemoveObserver(this); + host_ = NULL; + } + + private: + ModalDialogHost* host_; + views::Widget* target_widget_; + const char* const native_window_property_; + + DISALLOW_COPY_AND_ASSIGN(WidgetModalDialogHostObserverViews); +}; + +void UpdateModalDialogPosition(views::Widget* widget, + web_modal::ModalDialogHost* dialog_host, + const gfx::Size& size) { + // Do not forcibly update the dialog widget position if it is being dragged. + if (widget->HasCapture()) + return; + + gfx::Point position = dialog_host->GetDialogPosition(size); + views::Border* border = widget->non_client_view()->frame_view()->border(); + // Border may be null during widget initialization. + if (border) { + // Align the first row of pixels inside the border. This is the apparent + // top of the dialog. + position.set_y(position.y() - border->GetInsets().top()); + } + + if (widget->is_top_level()) { + position += + views::Widget::GetWidgetForNativeView(dialog_host->GetHostView())-> + GetClientAreaBoundsInScreen().OffsetFromOrigin(); + } + + widget->SetBounds(gfx::Rect(position, size)); +} + +} // namespace + +// static +void SetConstrainedWindowViewsClient( + scoped_ptr<ConstrainedWindowViewsClient> new_client) { + delete constrained_window_views_client; + constrained_window_views_client = new_client.release(); +} + +void UpdateWebContentsModalDialogPosition( + views::Widget* widget, + web_modal::WebContentsModalDialogHost* dialog_host) { + gfx::Size size = widget->GetRootView()->GetPreferredSize(); + gfx::Size max_size = dialog_host->GetMaximumDialogSize(); + // Enlarge the max size by the top border, as the dialog will be shifted + // outside the area specified by the dialog host by this amount later. + views::Border* border = + widget->non_client_view()->frame_view()->border(); + // Border may be null during widget initialization. + if (border) + max_size.Enlarge(0, border->GetInsets().top()); + size.SetToMin(max_size); + UpdateModalDialogPosition(widget, dialog_host, size); +} + +void UpdateWidgetModalDialogPosition(views::Widget* widget, + web_modal::ModalDialogHost* dialog_host) { + UpdateModalDialogPosition(widget, dialog_host, + widget->GetRootView()->GetPreferredSize()); +} + +views::Widget* ShowWebModalDialogViews( + views::WidgetDelegate* dialog, + content::WebContents* initiator_web_contents) { + DCHECK(constrained_window_views_client); + // For embedded WebContents, use the embedder's WebContents for constrained + // window. + content::WebContents* web_contents = constrained_window_views_client-> + GetEmbedderWebContents(initiator_web_contents); + views::Widget* widget = CreateWebModalDialogViews(dialog, web_contents); + web_modal::PopupManager* popup_manager = + web_modal::PopupManager::FromWebContents(web_contents); + popup_manager->ShowModalDialog(widget->GetNativeWindow(), web_contents); + return widget; +} + +views::Widget* CreateWebModalDialogViews(views::WidgetDelegate* dialog, + content::WebContents* web_contents) { + DCHECK_EQ(ui::MODAL_TYPE_CHILD, dialog->GetModalType()); + web_modal::PopupManager* popup_manager = + web_modal::PopupManager::FromWebContents(web_contents); + const gfx::NativeWindow parent = popup_manager->GetHostView(); + return views::DialogDelegate::CreateDialogWidget(dialog, NULL, parent); +} + +// TODO(gbillock): Replace this with PopupManager calls. +views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog, + gfx::NativeWindow parent) { + views::Widget* widget = + views::DialogDelegate::CreateDialogWidget(dialog, NULL, parent); + if (!dialog->UseNewStyleForThisDialog()) + return widget; + DCHECK(constrained_window_views_client); + ModalDialogHost* host = constrained_window_views_client-> + GetModalDialogHost(parent); + if (host) { + DCHECK_EQ(parent, host->GetHostView()); + ModalDialogHostObserver* dialog_host_observer = + new WidgetModalDialogHostObserverViews( + host, widget, kWidgetModalDialogHostObserverViewsKey); + dialog_host_observer->OnPositionRequiresUpdate(); + } + return widget; +} diff --git a/components/constrained_window/constrained_window_views.h b/components/constrained_window/constrained_window_views.h new file mode 100644 index 0000000..c638f7e --- /dev/null +++ b/components/constrained_window/constrained_window_views.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef COMPONENTS_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_VIEWS_H_ +#define COMPONENTS_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_VIEWS_H_ + +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/native_widget_types.h" + +namespace content { +class WebContents; +} + +namespace views { +class DialogDelegate; +class Widget; +class WidgetDelegate; +} + +namespace web_modal { +class ModalDialogHost; +class WebContentsModalDialogHost; +} + +class ConstrainedWindowViewsClient; + +// Sets the ConstrainedWindowClient impl. +void SetConstrainedWindowViewsClient( + scoped_ptr<ConstrainedWindowViewsClient> client); + +// Update the position of dialog |widget| against |dialog_host|. This is used to +// reposition widgets e.g. when the host dimensions change. +void UpdateWebContentsModalDialogPosition( + views::Widget* widget, + web_modal::WebContentsModalDialogHost* dialog_host); + +void UpdateWidgetModalDialogPosition( + views::Widget* widget, + web_modal::ModalDialogHost* dialog_host); + +// Calls CreateWebModalDialogViews, shows the dialog, and returns its widget. +views::Widget* ShowWebModalDialogViews( + views::WidgetDelegate* dialog, + content::WebContents* initiator_web_contents); + +// Create a widget for |dialog| that is modal to |web_contents|. +views::Widget* CreateWebModalDialogViews(views::WidgetDelegate* dialog, + content::WebContents* web_contents); + +// Create a widget for |dialog| that is modal to the browser window |parent|. +// This places the dialog appropriately if |parent| is a valid browser window. +views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog, + gfx::NativeWindow parent); + +#endif // COMPONENTS_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_VIEWS_H_ diff --git a/components/constrained_window/constrained_window_views_client.h b/components/constrained_window/constrained_window_views_client.h new file mode 100644 index 0000000..06671de --- /dev/null +++ b/components/constrained_window/constrained_window_views_client.h @@ -0,0 +1,32 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_VIEWS_CLIENT_H_ +#define COMPONENTS_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_VIEWS_CLIENT_H_ + +#include "ui/gfx/native_widget_types.h" + +namespace content { +class WebContents; +} + +namespace web_modal { +class ModalDialogHost; +} + +class ConstrainedWindowViewsClient { + public: + virtual ~ConstrainedWindowViewsClient() {} + + // Returns the web contents that a constrained window should be modal to + // in the embedder's context. + virtual content::WebContents* GetEmbedderWebContents( + content::WebContents* initiator_web_contents) = 0; + + // Returns the modal window host for the |parent| native window. + virtual web_modal::ModalDialogHost* GetModalDialogHost( + gfx::NativeWindow parent) = 0; +}; + +#endif // COMPONENTS_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_VIEWS_CLIENT_H_ diff --git a/components/constrained_window/constrained_window_views_unittest.cc b/components/constrained_window/constrained_window_views_unittest.cc new file mode 100644 index 0000000..9334feb --- /dev/null +++ b/components/constrained_window/constrained_window_views_unittest.cc @@ -0,0 +1,154 @@ +// Copyright 2013 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 "components/constrained_window/constrained_window_views.h" + +#include "components/web_modal/test_web_contents_modal_dialog_host.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/point.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" +#include "ui/views/border.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" +#include "ui/views/window/dialog_delegate.h" + +namespace views { + +class DialogContents : public DialogDelegateView { + public: + DialogContents() {} + virtual ~DialogContents() {} + + void set_preferred_size(const gfx::Size& preferred_size) { + preferred_size_ = preferred_size; + } + + // Overriden from DialogDelegateView: + virtual View* GetContentsView() override { return this; } + virtual gfx::Size GetPreferredSize() const override { + return preferred_size_; + } + virtual gfx::Size GetMinimumSize() const override { return gfx::Size(); } + + private: + gfx::Size preferred_size_; + + DISALLOW_COPY_AND_ASSIGN(DialogContents); +}; + +class ConstrainedWindowViewsTest : public ViewsTestBase { + public: + ConstrainedWindowViewsTest() : contents_(NULL) {} + virtual ~ConstrainedWindowViewsTest() {} + + virtual void SetUp() override { + ViewsTestBase::SetUp(); + contents_ = new DialogContents; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.delegate = contents_; + dialog_.reset(new Widget); + dialog_->Init(params); + dialog_host_.reset(new web_modal::TestWebContentsModalDialogHost( + dialog_->GetNativeView())); + dialog_host_->set_max_dialog_size(gfx::Size(5000, 5000)); + + // Make sure the dialog size is dominated by the preferred size of the + // contents. + gfx::Size preferred_size = dialog()->GetRootView()->GetPreferredSize(); + preferred_size.Enlarge(500, 500); + contents()->set_preferred_size(preferred_size); + } + + virtual void TearDown() override { + ViewsTestBase::TearDown(); + contents_ = NULL; + dialog_host_.reset(); + dialog_.reset(); + } + + gfx::Size GetDialogSize() { + return dialog()->GetRootView()->GetBoundsInScreen().size(); + } + + DialogContents* contents() { return contents_; } + web_modal::TestWebContentsModalDialogHost* dialog_host() { + return dialog_host_.get(); + } + Widget* dialog() { return dialog_.get(); } + + private: + DialogContents* contents_; + scoped_ptr<web_modal::TestWebContentsModalDialogHost> dialog_host_; + scoped_ptr<Widget> dialog_; + + DISALLOW_COPY_AND_ASSIGN(ConstrainedWindowViewsTest); +}; + +// Make sure a dialog that increases its preferred size grows on the next +// position update. +TEST_F(ConstrainedWindowViewsTest, GrowModalDialogSize) { + UpdateWidgetModalDialogPosition(dialog(), dialog_host()); + gfx::Size expected_size = GetDialogSize(); + gfx::Size preferred_size = contents()->GetPreferredSize(); + expected_size.Enlarge(50, 50); + preferred_size.Enlarge(50, 50); + contents()->set_preferred_size(preferred_size); + UpdateWidgetModalDialogPosition(dialog(), dialog_host()); + EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString()); +} + +// Make sure a dialog that reduces its preferred size shrinks on the next +// position update. +TEST_F(ConstrainedWindowViewsTest, ShrinkModalDialogSize) { + UpdateWidgetModalDialogPosition(dialog(), dialog_host()); + gfx::Size expected_size = GetDialogSize(); + gfx::Size preferred_size = contents()->GetPreferredSize(); + expected_size.Enlarge(-50, -50); + preferred_size.Enlarge(-50, -50); + contents()->set_preferred_size(preferred_size); + UpdateWidgetModalDialogPosition(dialog(), dialog_host()); + EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString()); +} + +// Make sure browser modal dialogs are not affected by restrictions on web +// content modal dialog maximum sizes. +TEST_F(ConstrainedWindowViewsTest, MaximumBrowserDialogSize) { + UpdateWidgetModalDialogPosition(dialog(), dialog_host()); + gfx::Size dialog_size = GetDialogSize(); + gfx::Size max_dialog_size = dialog_size; + max_dialog_size.Enlarge(-50, -50); + dialog_host()->set_max_dialog_size(max_dialog_size); + UpdateWidgetModalDialogPosition(dialog(), dialog_host()); + EXPECT_EQ(dialog_size.ToString(), GetDialogSize().ToString()); +} + +// Web content modal dialogs should not get a size larger than what the dialog +// host gives as the maximum size. +TEST_F(ConstrainedWindowViewsTest, MaximumWebContentsDialogSize) { + UpdateWebContentsModalDialogPosition(dialog(), dialog_host()); + gfx::Size full_dialog_size = GetDialogSize(); + gfx::Size max_dialog_size = full_dialog_size; + max_dialog_size.Enlarge(-50, -50); + dialog_host()->set_max_dialog_size(max_dialog_size); + UpdateWebContentsModalDialogPosition(dialog(), dialog_host()); + // The top border of the dialog is intentionally drawn outside the area + // specified by the dialog host, so add it to the size the dialog is expected + // to occupy. + gfx::Size expected_size = max_dialog_size; + Border* border = dialog()->non_client_view()->frame_view()->border(); + if (border) + expected_size.Enlarge(0, border->GetInsets().top()); + EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString()); + + // Increasing the maximum dialog size should bring the dialog back to its + // original size. + max_dialog_size.Enlarge(100, 100); + dialog_host()->set_max_dialog_size(max_dialog_size); + UpdateWebContentsModalDialogPosition(dialog(), dialog_host()); + EXPECT_EQ(full_dialog_size.ToString(), GetDialogSize().ToString()); +} + +} // namespace views |