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 "chrome/browser/ui/views/constrained_window_views.h" 6 7 #include "base/memory/scoped_ptr.h" 8 #include "chrome/browser/ui/browser.h" 9 #include "chrome/browser/ui/browser_commands.h" 10 #include "chrome/browser/ui/host_desktop.h" 11 #include "chrome/browser/ui/tabs/tab_strip_model.h" 12 #include "chrome/browser/ui/views/tab_modal_confirm_dialog_views.h" 13 #include "chrome/common/url_constants.h" 14 #include "chrome/test/base/in_process_browser_test.h" 15 #include "chrome/test/base/interactive_test_utils.h" 16 #include "chrome/test/base/ui_test_utils.h" 17 #include "components/web_modal/web_contents_modal_dialog_host.h" 18 #include "components/web_modal/web_contents_modal_dialog_manager.h" 19 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" 20 #include "content/public/browser/web_contents.h" 21 #include "content/public/test/browser_test_utils.h" 22 #include "ui/base/accelerators/accelerator.h" 23 #include "ui/views/focus/focus_manager.h" 24 #include "ui/views/widget/widget.h" 25 26 #if defined(OS_WIN) 27 #include "base/win/windows_version.h" 28 #endif 29 30 namespace { 31 32 class TestDialog : public views::DialogDelegateView { 33 public: 34 TestDialog() { SetFocusable(true); } 35 virtual ~TestDialog() {} 36 37 virtual views::View* GetInitiallyFocusedView() OVERRIDE { return this; } 38 // Don't delete the delegate yet. Keep it around for inspection later. 39 virtual void DeleteDelegate() OVERRIDE {} 40 41 virtual ui::ModalType GetModalType() const OVERRIDE { 42 #if defined(USE_ASH) 43 return ui::MODAL_TYPE_CHILD; 44 #else 45 return views::WidgetDelegate::GetModalType(); 46 #endif 47 } 48 49 private: 50 DISALLOW_COPY_AND_ASSIGN(TestDialog); 51 }; 52 53 // A helper function to create and show a web contents modal dialog. 54 scoped_ptr<TestDialog> ShowModalDialog(content::WebContents* web_contents) { 55 scoped_ptr<TestDialog> dialog(new TestDialog()); 56 ShowWebModalDialogViews(dialog.get(), web_contents); 57 return dialog.Pass(); 58 } 59 60 } // namespace 61 62 typedef InProcessBrowserTest ConstrainedWindowViewTest; 63 64 // Tests the intial focus of tab-modal dialogs, the restoration of focus to the 65 // browser when they close, and that queued dialogs don't register themselves as 66 // accelerator targets until they are displayed. 67 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, FocusTest) { 68 content::WebContents* web_contents = 69 browser()->tab_strip_model()->GetActiveWebContents(); 70 EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX)); 71 scoped_ptr<TestDialog> dialog1 = ShowModalDialog(web_contents); 72 73 // |dialog1| should be active and focused. 74 EXPECT_TRUE(dialog1->GetWidget()->IsVisible()); 75 views::FocusManager* focus_manager = dialog1->GetWidget()->GetFocusManager(); 76 EXPECT_EQ(dialog1->GetContentsView(), focus_manager->GetFocusedView()); 77 78 // Create a second dialog. This will also be modal to |web_contents|, but will 79 // remain hidden since the |dialog1| is still showing. 80 scoped_ptr<TestDialog> dialog2 = ShowModalDialog(web_contents); 81 EXPECT_FALSE(dialog2->GetWidget()->IsVisible()); 82 EXPECT_TRUE(dialog1->GetWidget()->IsVisible()); 83 EXPECT_EQ(focus_manager, dialog2->GetWidget()->GetFocusManager()); 84 EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX)); 85 EXPECT_EQ(dialog1->GetContentsView(), focus_manager->GetFocusedView()); 86 87 // Pressing return should close |dialog1|. 88 EXPECT_TRUE(focus_manager->ProcessAccelerator( 89 ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE))); 90 content::RunAllPendingInMessageLoop(); 91 EXPECT_EQ(NULL, dialog1->GetWidget()); 92 93 // |dialog2| should be visible and focused. 94 EXPECT_TRUE(dialog2->GetWidget()->IsVisible()); 95 EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX)); 96 EXPECT_EQ(dialog2->GetContentsView(), focus_manager->GetFocusedView()); 97 98 // Creating a new tab should take focus away from the other tab's dialog. 99 const int tab_with_dialog = browser()->tab_strip_model()->active_index(); 100 chrome::NewTab(browser()); 101 EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX)); 102 EXPECT_NE(dialog2->GetContentsView(), focus_manager->GetFocusedView()); 103 104 // Activating the previous tab should bring focus to the dialog. 105 browser()->tab_strip_model()->ActivateTabAt(tab_with_dialog, false); 106 EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX)); 107 EXPECT_EQ(dialog2->GetContentsView(), focus_manager->GetFocusedView()); 108 109 // Pressing enter again should close |dialog2|. 110 EXPECT_TRUE(focus_manager->ProcessAccelerator( 111 ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE))); 112 content::RunAllPendingInMessageLoop(); 113 EXPECT_EQ(NULL, dialog2->GetWidget()); 114 EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER)); 115 } 116 117 // Tests that the tab-modal window is closed properly when its tab is closed. 118 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabCloseTest) { 119 scoped_ptr<TestDialog> dialog = ShowModalDialog( 120 browser()->tab_strip_model()->GetActiveWebContents()); 121 EXPECT_TRUE(dialog->GetWidget()->IsVisible()); 122 chrome::CloseTab(browser()); 123 content::RunAllPendingInMessageLoop(); 124 EXPECT_EQ(NULL, dialog->GetWidget()); 125 } 126 127 // Tests that the tab-modal window is hidden when an other tab is selected and 128 // shown when its tab is selected again. 129 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabSwitchTest) { 130 scoped_ptr<TestDialog> dialog = ShowModalDialog( 131 browser()->tab_strip_model()->GetActiveWebContents()); 132 EXPECT_TRUE(dialog->GetWidget()->IsVisible()); 133 134 // Open a new tab. The tab-modal window should hide itself. 135 chrome::NewTab(browser()); 136 EXPECT_FALSE(dialog->GetWidget()->IsVisible()); 137 138 // Close the new tab. The tab-modal window should show itself again. 139 chrome::CloseTab(browser()); 140 EXPECT_TRUE(dialog->GetWidget()->IsVisible()); 141 142 // Close the original tab. 143 chrome::CloseTab(browser()); 144 content::RunAllPendingInMessageLoop(); 145 EXPECT_EQ(NULL, dialog->GetWidget()); 146 } 147 148 // Tests that tab-modal dialogs follow tabs dragged between browser windows. 149 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabMoveTest) { 150 content::WebContents* web_contents = 151 browser()->tab_strip_model()->GetActiveWebContents(); 152 scoped_ptr<TestDialog> dialog = ShowModalDialog(web_contents); 153 EXPECT_TRUE(dialog->GetWidget()->IsVisible()); 154 155 // Move the tab to a second browser window; but first create another tab. 156 // That prevents the first browser window from closing when its tab is moved. 157 chrome::NewTab(browser()); 158 browser()->tab_strip_model()->DetachWebContentsAt( 159 browser()->tab_strip_model()->GetIndexOfWebContents(web_contents)); 160 Browser* browser2 = CreateBrowser(browser()->profile()); 161 browser2->tab_strip_model()->AppendWebContents(web_contents, true); 162 EXPECT_TRUE(dialog->GetWidget()->IsVisible()); 163 164 // Close the first browser. 165 chrome::CloseWindow(browser()); 166 content::RunAllPendingInMessageLoop(); 167 EXPECT_TRUE(dialog->GetWidget()->IsVisible()); 168 169 // Close the dialog's browser window. 170 chrome::CloseTab(browser2); 171 content::RunAllPendingInMessageLoop(); 172 EXPECT_EQ(NULL, dialog->GetWidget()); 173 } 174 175 // Tests that the web contents navigates when backspace is pressed. 176 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, NavigationOnBackspace) { 177 content::WebContents* web_contents = 178 browser()->tab_strip_model()->GetActiveWebContents(); 179 content::WaitForLoadStop(web_contents); 180 const GURL original_url = web_contents->GetURL(); 181 EXPECT_NE(GURL(chrome::kChromeUIVersionURL), original_url); 182 ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIVersionURL)); 183 content::WaitForLoadStop(web_contents); 184 EXPECT_EQ(GURL(chrome::kChromeUIVersionURL), web_contents->GetURL()); 185 186 scoped_ptr<TestDialog> dialog = ShowModalDialog(web_contents); 187 EXPECT_TRUE(dialog->GetWidget()->IsVisible()); 188 EXPECT_EQ(dialog->GetContentsView(), 189 dialog->GetWidget()->GetFocusManager()->GetFocusedView()); 190 191 // Pressing backspace should navigate back and close the dialog. 192 EXPECT_TRUE(chrome::CanGoBack(browser())); 193 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_BACK, 194 false, false, false, false)); 195 content::RunAllPendingInMessageLoop(); 196 content::WaitForLoadStop(web_contents); 197 EXPECT_EQ(NULL, dialog->GetWidget()); 198 EXPECT_EQ(original_url, web_contents->GetURL()); 199 } 200 201 // Tests that the dialog closes when the escape key is pressed. 202 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, ClosesOnEscape) { 203 #if defined(OS_WIN) 204 // TODO(msw): The widget is not made NULL on XP. http://crbug.com/177482 205 if (base::win::GetVersion() < base::win::VERSION_VISTA) 206 return; 207 #endif 208 209 scoped_ptr<TestDialog> dialog = ShowModalDialog( 210 browser()->tab_strip_model()->GetActiveWebContents()); 211 EXPECT_TRUE(dialog->GetWidget()->IsVisible()); 212 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE, 213 false, false, false, false)); 214 content::RunAllPendingInMessageLoop(); 215 EXPECT_EQ(NULL, dialog->GetWidget()); 216 } 217