1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/basictypes.h" 6#include "base/strings/utf_string_conversions.h" 7#include "ui/base/ui_base_types.h" 8#include "ui/views/controls/button/label_button.h" 9#include "ui/views/test/test_views.h" 10#include "ui/views/test/views_test_base.h" 11#include "ui/views/widget/widget.h" 12#include "ui/views/window/dialog_client_view.h" 13#include "ui/views/window/dialog_delegate.h" 14 15namespace views { 16 17class TestDialogClientView : public DialogClientView { 18 public: 19 TestDialogClientView(View* contents_view, 20 DialogDelegate* dialog_delegate) 21 : DialogClientView(contents_view), 22 dialog_(dialog_delegate) {} 23 virtual ~TestDialogClientView() {} 24 25 // DialogClientView implementation. 26 virtual DialogDelegate* GetDialogDelegate() const OVERRIDE { return dialog_; } 27 28 View* GetContentsView() { return contents_view(); } 29 30 void CreateExtraViews() { 31 CreateExtraView(); 32 CreateFootnoteView(); 33 } 34 35 private: 36 DialogDelegate* dialog_; 37 38 DISALLOW_COPY_AND_ASSIGN(TestDialogClientView); 39}; 40 41class DialogClientViewTest : public ViewsTestBase, 42 public DialogDelegateView { 43 public: 44 DialogClientViewTest() 45 : dialog_buttons_(ui::DIALOG_BUTTON_NONE), 46 extra_view_(NULL), 47 footnote_view_(NULL) {} 48 virtual ~DialogClientViewTest() {} 49 50 // testing::Test implementation. 51 virtual void SetUp() OVERRIDE { 52 dialog_buttons_ = ui::DIALOG_BUTTON_NONE; 53 contents_.reset(new StaticSizedView(gfx::Size(100, 200))); 54 client_view_.reset(new TestDialogClientView(contents_.get(), this)); 55 56 ViewsTestBase::SetUp(); 57 } 58 59 // DialogDelegateView implementation. 60 virtual View* GetContentsView() OVERRIDE { return contents_.get(); } 61 virtual View* CreateExtraView() OVERRIDE { return extra_view_; } 62 virtual View* CreateFootnoteView() OVERRIDE { return footnote_view_; } 63 virtual int GetDialogButtons() const OVERRIDE { return dialog_buttons_; } 64 65 protected: 66 gfx::Rect GetUpdatedClientBounds() { 67 client_view_->SizeToPreferredSize(); 68 client_view_->Layout(); 69 return client_view_->bounds(); 70 } 71 72 // Makes sure that the content view is sized correctly. Width must be at least 73 // the requested amount, but height should always match exactly. 74 void CheckContentsIsSetToPreferredSize() { 75 const gfx::Rect client_bounds = GetUpdatedClientBounds(); 76 const gfx::Size preferred_size = contents_->GetPreferredSize(); 77 EXPECT_EQ(preferred_size.height(), contents_->bounds().height()); 78 EXPECT_LE(preferred_size.width(), contents_->bounds().width()); 79 EXPECT_EQ(contents_->bounds().origin(), client_bounds.origin()); 80 EXPECT_EQ(contents_->bounds().right(), client_bounds.right()); 81 } 82 83 // Sets the buttons to show in the dialog and refreshes the dialog. 84 void SetDialogButtons(int dialog_buttons) { 85 dialog_buttons_ = dialog_buttons; 86 client_view_->UpdateDialogButtons(); 87 } 88 89 // Sets the extra view. 90 void SetExtraView(View* view) { 91 DCHECK(!extra_view_); 92 extra_view_ = view; 93 client_view_->CreateExtraViews(); 94 } 95 96 // Sets the footnote view. 97 void SetFootnoteView(View* view) { 98 DCHECK(!footnote_view_); 99 footnote_view_ = view; 100 client_view_->CreateExtraViews(); 101 } 102 103 TestDialogClientView* client_view() { return client_view_.get(); } 104 105 private: 106 // The contents of the dialog. 107 scoped_ptr<View> contents_; 108 // The DialogClientView that's being tested. 109 scoped_ptr<TestDialogClientView> client_view_; 110 // The bitmask of buttons to show in the dialog. 111 int dialog_buttons_; 112 View* extra_view_; // weak 113 View* footnote_view_; // weak 114 115 DISALLOW_COPY_AND_ASSIGN(DialogClientViewTest); 116}; 117 118TEST_F(DialogClientViewTest, UpdateButtons) { 119 // This dialog should start with no buttons. 120 EXPECT_EQ(GetDialogButtons(), ui::DIALOG_BUTTON_NONE); 121 EXPECT_EQ(NULL, client_view()->ok_button()); 122 EXPECT_EQ(NULL, client_view()->cancel_button()); 123 const int height_without_buttons = GetUpdatedClientBounds().height(); 124 125 // Update to use both buttons. 126 SetDialogButtons(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); 127 EXPECT_TRUE(client_view()->ok_button()->is_default()); 128 EXPECT_FALSE(client_view()->cancel_button()->is_default()); 129 const int height_with_buttons = GetUpdatedClientBounds().height(); 130 EXPECT_GT(height_with_buttons, height_without_buttons); 131 132 // Remove the dialog buttons. 133 SetDialogButtons(ui::DIALOG_BUTTON_NONE); 134 EXPECT_EQ(NULL, client_view()->ok_button()); 135 EXPECT_EQ(NULL, client_view()->cancel_button()); 136 EXPECT_EQ(GetUpdatedClientBounds().height(), height_without_buttons); 137 138 // Reset with just an ok button. 139 SetDialogButtons(ui::DIALOG_BUTTON_OK); 140 EXPECT_TRUE(client_view()->ok_button()->is_default()); 141 EXPECT_EQ(NULL, client_view()->cancel_button()); 142 EXPECT_EQ(GetUpdatedClientBounds().height(), height_with_buttons); 143 144 // Reset with just a cancel button. 145 SetDialogButtons(ui::DIALOG_BUTTON_CANCEL); 146 EXPECT_EQ(NULL, client_view()->ok_button()); 147 EXPECT_TRUE(client_view()->cancel_button()->is_default()); 148 EXPECT_EQ(GetUpdatedClientBounds().height(), height_with_buttons); 149} 150 151TEST_F(DialogClientViewTest, RemoveAndUpdateButtons) { 152 // Removing buttons from another context should clear the local pointer. 153 SetDialogButtons(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); 154 delete client_view()->ok_button(); 155 EXPECT_EQ(NULL, client_view()->ok_button()); 156 delete client_view()->cancel_button(); 157 EXPECT_EQ(NULL, client_view()->cancel_button()); 158 159 // Updating should restore the requested buttons properly. 160 SetDialogButtons(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); 161 EXPECT_TRUE(client_view()->ok_button()->is_default()); 162 EXPECT_FALSE(client_view()->cancel_button()->is_default()); 163} 164 165// Test that the contents view gets its preferred size in the basic dialog 166// configuration. 167TEST_F(DialogClientViewTest, ContentsSize) { 168 CheckContentsIsSetToPreferredSize(); 169 EXPECT_EQ(GetContentsView()->bounds().bottom(), 170 client_view()->bounds().bottom()); 171} 172 173// Test the effect of the button strip on layout. 174TEST_F(DialogClientViewTest, LayoutWithButtons) { 175 SetDialogButtons(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); 176 CheckContentsIsSetToPreferredSize(); 177 EXPECT_LT(GetContentsView()->bounds().bottom(), 178 client_view()->bounds().bottom()); 179 gfx::Size no_extra_view_size = client_view()->bounds().size(); 180 181 View* extra_view = new StaticSizedView(gfx::Size(200, 200)); 182 SetExtraView(extra_view); 183 CheckContentsIsSetToPreferredSize(); 184 EXPECT_GT(client_view()->bounds().height(), no_extra_view_size.height()); 185 int width_of_extra_view = extra_view->bounds().width(); 186 187 // Visibility of extra view is respected. 188 extra_view->SetVisible(false); 189 CheckContentsIsSetToPreferredSize(); 190 EXPECT_EQ(no_extra_view_size.height(), client_view()->bounds().height()); 191 EXPECT_EQ(no_extra_view_size.width(), client_view()->bounds().width()); 192 193 // Try with a reduced-size dialog. 194 extra_view->SetVisible(true); 195 client_view()->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), no_extra_view_size)); 196 client_view()->Layout(); 197 DCHECK_GT(width_of_extra_view, extra_view->bounds().width()); 198} 199 200// Test the effect of the footnote view on layout. 201TEST_F(DialogClientViewTest, LayoutWithFootnote) { 202 CheckContentsIsSetToPreferredSize(); 203 gfx::Size no_footnote_size = client_view()->bounds().size(); 204 205 View* footnote_view = new StaticSizedView(gfx::Size(200, 200)); 206 SetFootnoteView(footnote_view); 207 CheckContentsIsSetToPreferredSize(); 208 EXPECT_GT(client_view()->bounds().height(), no_footnote_size.height()); 209 EXPECT_EQ(200, footnote_view->bounds().height()); 210 gfx::Size with_footnote_size = client_view()->bounds().size(); 211 EXPECT_EQ(with_footnote_size.width(), footnote_view->bounds().width()); 212 213 SetDialogButtons(ui::DIALOG_BUTTON_CANCEL); 214 CheckContentsIsSetToPreferredSize(); 215 EXPECT_LE(with_footnote_size.height(), client_view()->bounds().height()); 216 EXPECT_LE(with_footnote_size.width(), client_view()->bounds().width()); 217 gfx::Size with_footnote_and_button_size = client_view()->bounds().size(); 218 219 SetDialogButtons(ui::DIALOG_BUTTON_NONE); 220 footnote_view->SetVisible(false); 221 CheckContentsIsSetToPreferredSize(); 222 EXPECT_EQ(no_footnote_size.height(), client_view()->bounds().height()); 223 EXPECT_EQ(no_footnote_size.width(), client_view()->bounds().width()); 224} 225 226// Test that GetHeightForWidth is respected for the footnote view. 227TEST_F(DialogClientViewTest, LayoutWithFootnoteHeightForWidth) { 228 CheckContentsIsSetToPreferredSize(); 229 gfx::Size no_footnote_size = client_view()->bounds().size(); 230 231 View* footnote_view = new ProportionallySizedView(3); 232 SetFootnoteView(footnote_view); 233 CheckContentsIsSetToPreferredSize(); 234 EXPECT_GT(client_view()->bounds().height(), no_footnote_size.height()); 235 EXPECT_EQ(footnote_view->bounds().width() * 3, 236 footnote_view->bounds().height()); 237} 238 239// Test that the DialogClientView's FocusManager is properly updated when the 240// DialogClientView belongs to a non top level widget and the widget is 241// reparented. The DialogClientView belongs to a non top level widget in the 242// case of constrained windows. The constrained window's widget is reparented 243// when a browser tab is dragged to a different browser window. 244TEST_F(DialogClientViewTest, FocusManager) { 245 scoped_ptr<Widget> toplevel1(new Widget); 246 Widget::InitParams toplevel1_params = 247 CreateParams(Widget::InitParams::TYPE_WINDOW); 248 toplevel1_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 249 toplevel1->Init(toplevel1_params); 250 251 scoped_ptr<Widget> toplevel2(new Widget); 252 Widget::InitParams toplevel2_params = 253 CreateParams(Widget::InitParams::TYPE_WINDOW); 254 toplevel2_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 255 toplevel2->Init(toplevel2_params); 256 257 Widget* dialog = new Widget; 258 Widget::InitParams dialog_params = 259 CreateParams(Widget::InitParams::TYPE_WINDOW); 260 dialog_params.child = true; 261 dialog_params.delegate = new DialogDelegateView(); 262 dialog_params.parent = toplevel1->GetNativeView(); 263 dialog->Init(dialog_params); 264 265 // Test that the FocusManager has been properly set when the DialogClientView 266 // was parented to |dialog|. 267 DialogClientView* client_view = 268 static_cast<DialogClientView*>(dialog->client_view()); 269 EXPECT_EQ(toplevel1->GetFocusManager(), client_view->focus_manager_); 270 271 // Test that the FocusManager is properly updated when the DialogClientView's 272 // top level widget is changed. 273 Widget::ReparentNativeView(dialog->GetNativeView(), NULL); 274 EXPECT_EQ(NULL, client_view->focus_manager_); 275 Widget::ReparentNativeView(dialog->GetNativeView(), 276 toplevel2->GetNativeView()); 277 EXPECT_EQ(toplevel2->GetFocusManager(), client_view->focus_manager_); 278 Widget::ReparentNativeView(dialog->GetNativeView(), 279 toplevel1->GetNativeView()); 280 EXPECT_NE(toplevel1->GetFocusManager(), toplevel2->GetFocusManager()); 281 EXPECT_EQ(toplevel1->GetFocusManager(), client_view->focus_manager_); 282 283 // Test that the FocusManager is properly cleared when the DialogClientView is 284 // removed from |dialog| during the widget's destruction. 285 client_view->set_owned_by_client(); 286 scoped_ptr<DialogClientView> owned_client_view(client_view); 287 toplevel1->CloseNow(); 288 toplevel2->CloseNow(); 289 EXPECT_EQ(NULL, owned_client_view->focus_manager_); 290} 291 292} // namespace views 293