summaryrefslogtreecommitdiffstats
path: root/components/constrained_window
diff options
context:
space:
mode:
authoroshima <oshima@chromium.org>2014-10-24 14:54:11 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-24 21:54:36 +0000
commit136691ad8af860f6bd89f17b9f3c9693c7d780bc (patch)
tree44c7ebe133c57520addb5bb911e76803eca1695a /components/constrained_window
parent728b25306a408de0e0cee7b1db99e037ebba0b5f (diff)
downloadchromium_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.gn29
-rw-r--r--components/constrained_window/DEPS6
-rw-r--r--components/constrained_window/OWNERS2
-rw-r--r--components/constrained_window/constrained_window_views.cc174
-rw-r--r--components/constrained_window/constrained_window_views.h56
-rw-r--r--components/constrained_window/constrained_window_views_client.h32
-rw-r--r--components/constrained_window/constrained_window_views_unittest.cc154
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