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