constrained_window_views_browsertest.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/memory/weak_ptr.h" 6#include "chrome/browser/platform_util.h" 7#include "chrome/browser/profiles/profile.h" 8#include "chrome/browser/ui/browser.h" 9#include "chrome/browser/ui/browser_commands.h" 10#include "chrome/browser/ui/tabs/tab_strip_model.h" 11#include "chrome/browser/ui/views/constrained_window_views.h" 12#include "chrome/browser/ui/views/frame/browser_view.h" 13#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h" 14#include "chrome/common/url_constants.h" 15#include "chrome/test/base/in_process_browser_test.h" 16#include "chrome/test/base/ui_test_utils.h" 17#include "components/web_modal/web_contents_modal_dialog_manager.h" 18#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" 19#include "content/public/browser/native_web_keyboard_event.h" 20#include "content/public/browser/render_view_host.h" 21#include "content/public/browser/web_contents.h" 22#include "content/public/browser/web_contents_view.h" 23#include "ipc/ipc_message.h" 24#include "ui/base/accelerators/accelerator.h" 25#include "ui/views/controls/textfield/textfield.h" 26#include "ui/views/focus/focus_manager.h" 27#include "ui/views/layout/fill_layout.h" 28#include "ui/views/test/test_widget_observer.h" 29#include "ui/views/window/dialog_delegate.h" 30#include "ui/web_dialogs/test/test_web_dialog_delegate.h" 31 32#if defined(USE_AURA) && defined(USE_X11) 33#include <X11/Xlib.h> 34#include "ui/base/x/x11_util.h" 35#endif 36 37using web_modal::WebContentsModalDialogManager; 38 39namespace { 40 41class TestConstrainedDialogContentsView 42 : public views::View, 43 public base::SupportsWeakPtr<TestConstrainedDialogContentsView> { 44 public: 45 TestConstrainedDialogContentsView() 46 : text_field_(new views::Textfield) { 47 SetLayoutManager(new views::FillLayout); 48 AddChildView(text_field_); 49 } 50 51 views::View* GetInitiallyFocusedView() { 52 return text_field_; 53 } 54 55 private: 56 views::Textfield* text_field_; 57 DISALLOW_COPY_AND_ASSIGN(TestConstrainedDialogContentsView); 58}; 59 60class TestConstrainedDialog : public views::DialogDelegate { 61 public: 62 TestConstrainedDialog() 63 : contents_((new TestConstrainedDialogContentsView())->AsWeakPtr()), 64 done_(false) { 65 } 66 67 virtual ~TestConstrainedDialog() {} 68 69 virtual views::View* GetInitiallyFocusedView() OVERRIDE { 70 return contents_ ? contents_->GetInitiallyFocusedView() : NULL; 71 } 72 73 virtual views::View* GetContentsView() OVERRIDE { 74 return contents_.get(); 75 } 76 77 virtual views::Widget* GetWidget() OVERRIDE { 78 return contents_ ? contents_->GetWidget() : NULL; 79 } 80 81 virtual const views::Widget* GetWidget() const OVERRIDE { 82 return contents_ ? contents_->GetWidget() : NULL; 83 } 84 85 virtual void DeleteDelegate() OVERRIDE { 86 // Don't delete the delegate yet. We need to keep it around for inspection 87 // later. 88 EXPECT_TRUE(done_); 89 } 90 91 virtual bool Accept() OVERRIDE { 92 done_ = true; 93 return true; 94 } 95 96 virtual bool Cancel() OVERRIDE { 97 done_ = true; 98 return true; 99 } 100 101 virtual ui::ModalType GetModalType() const OVERRIDE { 102#if defined(USE_ASH) 103 return ui::MODAL_TYPE_CHILD; 104#else 105 return views::WidgetDelegate::GetModalType(); 106#endif 107 } 108 109 bool done() { 110 return done_; 111 } 112 113 private: 114 // contents_ will be freed when the View goes away. 115 base::WeakPtr<TestConstrainedDialogContentsView> contents_; 116 bool done_; 117 118 DISALLOW_COPY_AND_ASSIGN(TestConstrainedDialog); 119}; 120 121} // namespace 122 123class ConstrainedWindowViewTest : public InProcessBrowserTest { 124 public: 125 ConstrainedWindowViewTest() { 126 } 127}; 128 129// Tests the following: 130// 131// *) Initially focused view in a constrained dialog receives focus reliably. 132// 133// *) Constrained windows that are queued don't register themselves as 134// accelerator targets until they are displayed. 135IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, FocusTest) { 136 content::WebContents* web_contents = 137 browser()->tab_strip_model()->GetActiveWebContents(); 138 ASSERT_TRUE(web_contents != NULL); 139 WebContentsModalDialogManager* web_contents_modal_dialog_manager = 140 WebContentsModalDialogManager::FromWebContents(web_contents); 141 ASSERT_TRUE(web_contents_modal_dialog_manager != NULL); 142 143 // Create a constrained dialog. It will attach itself to web_contents. 144 scoped_ptr<TestConstrainedDialog> test_dialog1(new TestConstrainedDialog); 145 views::Widget* window1 = CreateWebContentsModalDialogViews( 146 test_dialog1.get(), 147 web_contents->GetView()->GetNativeView(), 148 web_contents_modal_dialog_manager->delegate()-> 149 GetWebContentsModalDialogHost()); 150 web_contents_modal_dialog_manager->ShowDialog(window1->GetNativeView()); 151 152 views::FocusManager* focus_manager = window1->GetFocusManager(); 153 ASSERT_TRUE(focus_manager); 154 155 // test_dialog1's text field should be focused. 156 EXPECT_EQ(test_dialog1->GetInitiallyFocusedView(), 157 focus_manager->GetFocusedView()); 158 159 // Now create a second constrained dialog. This will also be attached to 160 // web_contents, but will remain hidden since the test_dialog1 is still 161 // showing. 162 scoped_ptr<TestConstrainedDialog> test_dialog2(new TestConstrainedDialog); 163 views::Widget* window2 = CreateWebContentsModalDialogViews( 164 test_dialog2.get(), 165 web_contents->GetView()->GetNativeView(), 166 web_contents_modal_dialog_manager->delegate()-> 167 GetWebContentsModalDialogHost()); 168 web_contents_modal_dialog_manager->ShowDialog(window2->GetNativeView()); 169 // Should be the same focus_manager. 170 ASSERT_EQ(focus_manager, window2->GetFocusManager()); 171 172 // test_dialog1's text field should still be the view that has focus. 173 EXPECT_EQ(test_dialog1->GetInitiallyFocusedView(), 174 focus_manager->GetFocusedView()); 175 ASSERT_TRUE(web_contents_modal_dialog_manager->IsShowingDialog()); 176 177 // Now send a VKEY_RETURN to the browser. This should result in closing 178 // test_dialog1. 179 EXPECT_TRUE(focus_manager->ProcessAccelerator( 180 ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE))); 181 content::RunAllPendingInMessageLoop(); 182 183 EXPECT_TRUE(test_dialog1->done()); 184 EXPECT_FALSE(test_dialog2->done()); 185 EXPECT_TRUE(web_contents_modal_dialog_manager->IsShowingDialog()); 186 187 // test_dialog2 will be shown. Focus should be on test_dialog2's text field. 188 EXPECT_EQ(test_dialog2->GetInitiallyFocusedView(), 189 focus_manager->GetFocusedView()); 190 191 int tab_with_constrained_window = 192 browser()->tab_strip_model()->active_index(); 193 194 // Create a new tab. 195 chrome::NewTab(browser()); 196 197 // The constrained dialog should no longer be selected. 198 EXPECT_NE(test_dialog2->GetInitiallyFocusedView(), 199 focus_manager->GetFocusedView()); 200 201 browser()->tab_strip_model()->ActivateTabAt(tab_with_constrained_window, 202 false); 203 204 // Activating the previous tab should bring focus to the constrained window. 205 EXPECT_EQ(test_dialog2->GetInitiallyFocusedView(), 206 focus_manager->GetFocusedView()); 207 208 // Send another VKEY_RETURN, closing test_dialog2 209 EXPECT_TRUE(focus_manager->ProcessAccelerator( 210 ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE))); 211 content::RunAllPendingInMessageLoop(); 212 EXPECT_TRUE(test_dialog2->done()); 213 EXPECT_FALSE(web_contents_modal_dialog_manager->IsShowingDialog()); 214} 215 216// Tests that the constrained window is closed properly when its tab is 217// closed. 218IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabCloseTest) { 219 content::WebContents* web_contents = 220 browser()->tab_strip_model()->GetActiveWebContents(); 221 ASSERT_TRUE(web_contents != NULL); 222 WebContentsModalDialogManager* web_contents_modal_dialog_manager = 223 WebContentsModalDialogManager::FromWebContents(web_contents); 224 ASSERT_TRUE(web_contents_modal_dialog_manager != NULL); 225 226 // Create a constrained dialog. It will attach itself to web_contents. 227 scoped_ptr<TestConstrainedDialog> test_dialog(new TestConstrainedDialog); 228 views::Widget* window = CreateWebContentsModalDialogViews( 229 test_dialog.get(), 230 web_contents->GetView()->GetNativeView(), 231 web_contents_modal_dialog_manager->delegate()-> 232 GetWebContentsModalDialogHost()); 233 web_contents_modal_dialog_manager->ShowDialog(window->GetNativeView()); 234 235 bool closed = 236 browser()->tab_strip_model()->CloseWebContentsAt( 237 browser()->tab_strip_model()->active_index(), 238 TabStripModel::CLOSE_NONE); 239 EXPECT_TRUE(closed); 240 content::RunAllPendingInMessageLoop(); 241 EXPECT_TRUE(test_dialog->done()); 242} 243 244// Tests that the constrained window is hidden when an other tab is selected and 245// shown when its tab is selected again. 246IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabSwitchTest) { 247 content::WebContents* web_contents = 248 browser()->tab_strip_model()->GetActiveWebContents(); 249 ASSERT_TRUE(web_contents != NULL); 250 251 // Create a constrained dialog. It will attach itself to web_contents. 252 scoped_ptr<TestConstrainedDialog> test_dialog(new TestConstrainedDialog); 253 WebContentsModalDialogManager* web_contents_modal_dialog_manager = 254 WebContentsModalDialogManager::FromWebContents(web_contents); 255 views::Widget* window = CreateWebContentsModalDialogViews( 256 test_dialog.get(), 257 web_contents->GetView()->GetNativeView(), 258 web_contents_modal_dialog_manager->delegate()-> 259 GetWebContentsModalDialogHost()); 260 web_contents_modal_dialog_manager->ShowDialog(window->GetNativeView()); 261 EXPECT_TRUE(window->IsVisible()); 262 263 // Open a new tab. The constrained window should hide itself. 264 browser()->tab_strip_model()->AppendWebContents( 265 content::WebContents::Create( 266 content::WebContents::CreateParams(browser()->profile())), 267 true); 268 EXPECT_FALSE(window->IsVisible()); 269 270 // Close the new tab. The constrained window should show itself again. 271 bool closed = 272 browser()->tab_strip_model()->CloseWebContentsAt( 273 browser()->tab_strip_model()->active_index(), 274 TabStripModel::CLOSE_NONE); 275 EXPECT_TRUE(closed); 276 EXPECT_TRUE(window->IsVisible()); 277 278 // Close the original tab. 279 browser()->tab_strip_model()->CloseWebContentsAt( 280 browser()->tab_strip_model()->active_index(), 281 TabStripModel::CLOSE_NONE); 282 content::RunAllPendingInMessageLoop(); 283 EXPECT_TRUE(test_dialog->done()); 284} 285 286#if defined(OS_WIN) || (defined(USE_AURA) && defined(USE_X11)) 287 288// Forwards the key event which has |key_code| to the renderer. 289void ForwardKeyEvent(content::RenderViewHost* host, ui::KeyboardCode key_code) { 290#if defined(OS_WIN) 291 MSG native_key_event = { NULL, WM_KEYDOWN, key_code, 0 }; 292#elif defined(USE_X11) 293 XEvent x_event; 294 ui::InitXKeyEventForTesting( 295 ui::ET_KEY_PRESSED, key_code, ui::EF_NONE, &x_event); 296 XEvent* native_key_event = &x_event; 297#endif 298 299#if defined(USE_AURA) 300 ui::KeyEvent key(native_key_event, false); 301 ui::KeyEvent* native_ui_key_event = &key; 302#elif defined(OS_WIN) 303 MSG native_ui_key_event = native_key_event; 304#endif 305 306 host->ForwardKeyboardEvent( 307 content::NativeWebKeyboardEvent(native_ui_key_event)); 308} 309 310// Tests that backspace is not processed before it's sent to the web contents. 311// Flaky on Win Aura and Linux ChromiumOS. See http://crbug.com/170331 312#if defined(USE_AURA) 313#define MAYBE_BackspaceSentToWebContent DISABLED_BackspaceSentToWebContent 314#else 315#define MAYBE_BackspaceSentToWebContent BackspaceSentToWebContent 316#endif 317IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, 318 MAYBE_BackspaceSentToWebContent) { 319 content::WebContents* web_contents = 320 browser()->tab_strip_model()->GetActiveWebContents(); 321 ASSERT_TRUE(web_contents != NULL); 322 323 GURL new_tab_url(chrome::kChromeUINewTabURL); 324 ui_test_utils::NavigateToURL(browser(), new_tab_url); 325 GURL about_url(chrome::kChromeUIAboutURL); 326 ui_test_utils::NavigateToURL(browser(), about_url); 327 328 ConstrainedWebDialogDelegate* cwdd = CreateConstrainedWebDialog( 329 browser()->profile(), 330 new ui::test::TestWebDialogDelegate(about_url), 331 NULL, 332 web_contents); 333 334 content::RenderViewHost* render_view_host = 335 cwdd->GetWebContents()->GetRenderViewHost(); 336 ForwardKeyEvent(render_view_host, ui::VKEY_BACK); 337 338 // Backspace is not processed as accelerator before it's sent to web contents. 339 EXPECT_EQ(about_url.spec(), web_contents->GetURL().spec()); 340 341 content::RunAllPendingInMessageLoop(); 342 343 // Backspace is processed as accelerator after it's sent to web contents. 344 EXPECT_EQ(new_tab_url.spec(), web_contents->GetURL().spec()); 345} 346 347// Fails flakily (once per 10-20 runs) on Win Aura only. http://crbug.com/177482 348// Also fails on CrOS. 349#if defined(OS_WIN) || defined(OS_CHROMEOS) 350#define MAYBE_EscapeCloseConstrainedWindow DISABLED_EscapeCloseConstrainedWindow 351#else 352#define MAYBE_EscapeCloseConstrainedWindow EscapeCloseConstrainedWindow 353#endif 354 355// Tests that escape closes the constrained window. 356IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, 357 MAYBE_EscapeCloseConstrainedWindow) { 358 content::WebContents* web_contents = 359 browser()->tab_strip_model()->GetActiveWebContents(); 360 ASSERT_TRUE(web_contents != NULL); 361 362 GURL new_tab_url(chrome::kChromeUINewTabURL); 363 ui_test_utils::NavigateToURL(browser(), new_tab_url); 364 ConstrainedWebDialogDelegate* cwdd = CreateConstrainedWebDialog( 365 browser()->profile(), 366 new ui::test::TestWebDialogDelegate(new_tab_url), 367 NULL, 368 web_contents); 369 370 views::Widget* widget = 371 views::Widget::GetWidgetForNativeView(cwdd->GetNativeDialog()); 372 views::test::TestWidgetObserver observer(widget); 373 374 content::RenderViewHost* render_view_host = 375 cwdd->GetWebContents()->GetRenderViewHost(); 376 ForwardKeyEvent(render_view_host, ui::VKEY_ESCAPE); 377 378 // Escape is not processed as accelerator before it's sent to web contents. 379 EXPECT_FALSE(observer.widget_closed()); 380 381 content::RunAllPendingInMessageLoop(); 382 383 // Escape is processed as accelerator after it's sent to web contents. 384 EXPECT_TRUE(observer.widget_closed()); 385} 386 387#endif // defined(OS_WIN) || (defined(USE_AURA) && defined(USE_X11)) 388