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 "components/web_modal/web_contents_modal_dialog_manager.h" 6 7 #include <map> 8 9 #include "base/memory/scoped_ptr.h" 10 #include "components/web_modal/single_web_contents_dialog_manager.h" 11 #include "components/web_modal/test_web_contents_modal_dialog_manager_delegate.h" 12 #include "content/public/test/test_renderer_host.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 namespace web_modal { 16 17 // Tracks persistent state changes of the native WC-modal dialog manager. 18 class NativeManagerTracker { 19 public: 20 enum DialogState { 21 UNKNOWN, 22 NOT_SHOWN, 23 SHOWN, 24 HIDDEN, 25 CLOSED 26 }; 27 28 NativeManagerTracker() : state_(UNKNOWN), was_shown_(false) {} 29 30 void SetState(DialogState state) { 31 state_ = state; 32 if (state_ == SHOWN) 33 was_shown_ = true; 34 } 35 36 DialogState state_; 37 bool was_shown_; 38 }; 39 40 NativeManagerTracker unused_tracker; 41 42 class TestNativeWebContentsModalDialogManager 43 : public SingleWebContentsDialogManager { 44 public: 45 TestNativeWebContentsModalDialogManager( 46 NativeWebContentsModalDialog dialog, 47 SingleWebContentsDialogManagerDelegate* delegate, 48 NativeManagerTracker* tracker) 49 : delegate_(delegate), 50 dialog_(dialog), 51 tracker_(tracker) { 52 if (tracker_) 53 tracker_->SetState(NativeManagerTracker::NOT_SHOWN); 54 } 55 56 virtual void Show() OVERRIDE { 57 if (tracker_) 58 tracker_->SetState(NativeManagerTracker::SHOWN); 59 } 60 virtual void Hide() OVERRIDE { 61 if (tracker_) 62 tracker_->SetState(NativeManagerTracker::HIDDEN); 63 } 64 virtual void Close() OVERRIDE { 65 if (tracker_) 66 tracker_->SetState(NativeManagerTracker::CLOSED); 67 delegate_->WillClose(dialog_); 68 } 69 virtual void Focus() OVERRIDE { 70 } 71 virtual void Pulse() OVERRIDE { 72 } 73 virtual void HostChanged(WebContentsModalDialogHost* new_host) OVERRIDE { 74 } 75 virtual NativeWebContentsModalDialog dialog() OVERRIDE { 76 return dialog_; 77 } 78 79 void StopTracking() { 80 tracker_ = NULL; 81 } 82 83 private: 84 SingleWebContentsDialogManagerDelegate* delegate_; 85 NativeWebContentsModalDialog dialog_; 86 NativeManagerTracker* tracker_; 87 88 DISALLOW_COPY_AND_ASSIGN(TestNativeWebContentsModalDialogManager); 89 }; 90 91 class WebContentsModalDialogManagerTest 92 : public content::RenderViewHostTestHarness { 93 public: 94 WebContentsModalDialogManagerTest() 95 : next_dialog_id(1), 96 manager(NULL) { 97 } 98 99 virtual void SetUp() { 100 content::RenderViewHostTestHarness::SetUp(); 101 102 delegate.reset(new TestWebContentsModalDialogManagerDelegate); 103 WebContentsModalDialogManager::CreateForWebContents(web_contents()); 104 manager = WebContentsModalDialogManager::FromWebContents(web_contents()); 105 manager->SetDelegate(delegate.get()); 106 test_api.reset(new WebContentsModalDialogManager::TestApi(manager)); 107 } 108 109 virtual void TearDown() { 110 test_api.reset(); 111 content::RenderViewHostTestHarness::TearDown(); 112 } 113 114 protected: 115 NativeWebContentsModalDialog MakeFakeDialog() { 116 // WebContentsModalDialogManager treats the NativeWebContentsModalDialog as 117 // an opaque type, so creating fake NativeWebContentsModalDialogs using 118 // reinterpret_cast is valid. 119 return reinterpret_cast<NativeWebContentsModalDialog>(next_dialog_id++); 120 } 121 122 int next_dialog_id; 123 scoped_ptr<TestWebContentsModalDialogManagerDelegate> delegate; 124 WebContentsModalDialogManager* manager; 125 scoped_ptr<WebContentsModalDialogManager::TestApi> test_api; 126 127 DISALLOW_COPY_AND_ASSIGN(WebContentsModalDialogManagerTest); 128 }; 129 130 SingleWebContentsDialogManager* 131 WebContentsModalDialogManager::CreateNativeWebModalManager( 132 NativeWebContentsModalDialog dialog, 133 SingleWebContentsDialogManagerDelegate* native_delegate) { 134 NOTREACHED(); 135 return new TestNativeWebContentsModalDialogManager( 136 dialog, 137 native_delegate, 138 &unused_tracker); 139 } 140 141 // Test that the dialog is shown immediately when the delegate indicates the web 142 // contents is visible. 143 TEST_F(WebContentsModalDialogManagerTest, WebContentsVisible) { 144 // Dialog should be shown while WebContents is visible. 145 const NativeWebContentsModalDialog dialog = MakeFakeDialog(); 146 147 NativeManagerTracker tracker; 148 TestNativeWebContentsModalDialogManager* native_manager = 149 new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker); 150 manager->ShowDialogWithManager(dialog, 151 scoped_ptr<SingleWebContentsDialogManager>(native_manager).Pass()); 152 153 EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_); 154 EXPECT_TRUE(manager->IsDialogActive()); 155 EXPECT_TRUE(delegate->web_contents_blocked()); 156 EXPECT_TRUE(tracker.was_shown_); 157 158 native_manager->StopTracking(); 159 } 160 161 // Test that the dialog is not shown immediately when the delegate indicates the 162 // web contents is not visible. 163 TEST_F(WebContentsModalDialogManagerTest, WebContentsNotVisible) { 164 // Dialog should not be shown while WebContents is not visible. 165 delegate->set_web_contents_visible(false); 166 167 const NativeWebContentsModalDialog dialog = MakeFakeDialog(); 168 169 NativeManagerTracker tracker; 170 TestNativeWebContentsModalDialogManager* native_manager = 171 new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker); 172 manager->ShowDialogWithManager(dialog, 173 scoped_ptr<SingleWebContentsDialogManager>(native_manager).Pass()); 174 175 EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker.state_); 176 EXPECT_TRUE(manager->IsDialogActive()); 177 EXPECT_TRUE(delegate->web_contents_blocked()); 178 EXPECT_FALSE(tracker.was_shown_); 179 180 native_manager->StopTracking(); 181 } 182 183 // Test that only the first of multiple dialogs is shown. 184 TEST_F(WebContentsModalDialogManagerTest, ShowDialogs) { 185 const NativeWebContentsModalDialog dialog1 = MakeFakeDialog(); 186 const NativeWebContentsModalDialog dialog2 = MakeFakeDialog(); 187 const NativeWebContentsModalDialog dialog3 = MakeFakeDialog(); 188 189 NativeManagerTracker tracker1; 190 NativeManagerTracker tracker2; 191 NativeManagerTracker tracker3; 192 TestNativeWebContentsModalDialogManager* native_manager1 = 193 new TestNativeWebContentsModalDialogManager(dialog1, manager, &tracker1); 194 TestNativeWebContentsModalDialogManager* native_manager2 = 195 new TestNativeWebContentsModalDialogManager(dialog2, manager, &tracker2); 196 TestNativeWebContentsModalDialogManager* native_manager3 = 197 new TestNativeWebContentsModalDialogManager(dialog3, manager, &tracker3); 198 manager->ShowDialogWithManager(dialog1, 199 scoped_ptr<SingleWebContentsDialogManager>(native_manager1).Pass()); 200 manager->ShowDialogWithManager(dialog2, 201 scoped_ptr<SingleWebContentsDialogManager>(native_manager2).Pass()); 202 manager->ShowDialogWithManager(dialog3, 203 scoped_ptr<SingleWebContentsDialogManager>(native_manager3).Pass()); 204 205 EXPECT_TRUE(delegate->web_contents_blocked()); 206 EXPECT_EQ(NativeManagerTracker::SHOWN, tracker1.state_); 207 EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker2.state_); 208 EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker3.state_); 209 210 native_manager1->StopTracking(); 211 native_manager2->StopTracking(); 212 native_manager3->StopTracking(); 213 } 214 215 // Test that the dialog is shown/hidden when the WebContents is shown/hidden. 216 TEST_F(WebContentsModalDialogManagerTest, VisibilityObservation) { 217 const NativeWebContentsModalDialog dialog = MakeFakeDialog(); 218 219 NativeManagerTracker tracker; 220 TestNativeWebContentsModalDialogManager* native_manager = 221 new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker); 222 manager->ShowDialogWithManager(dialog, 223 scoped_ptr<SingleWebContentsDialogManager>(native_manager).Pass()); 224 225 EXPECT_TRUE(manager->IsDialogActive()); 226 EXPECT_TRUE(delegate->web_contents_blocked()); 227 EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_); 228 229 test_api->WebContentsWasHidden(); 230 231 EXPECT_TRUE(manager->IsDialogActive()); 232 EXPECT_TRUE(delegate->web_contents_blocked()); 233 EXPECT_EQ(NativeManagerTracker::HIDDEN, tracker.state_); 234 235 test_api->WebContentsWasShown(); 236 237 EXPECT_TRUE(manager->IsDialogActive()); 238 EXPECT_TRUE(delegate->web_contents_blocked()); 239 EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_); 240 241 native_manager->StopTracking(); 242 } 243 244 // Test that attaching an interstitial page closes all dialogs. 245 TEST_F(WebContentsModalDialogManagerTest, InterstitialPage) { 246 const NativeWebContentsModalDialog dialog1 = MakeFakeDialog(); 247 const NativeWebContentsModalDialog dialog2 = MakeFakeDialog(); 248 249 NativeManagerTracker tracker1; 250 NativeManagerTracker tracker2; 251 TestNativeWebContentsModalDialogManager* native_manager1 = 252 new TestNativeWebContentsModalDialogManager(dialog1, manager, &tracker1); 253 TestNativeWebContentsModalDialogManager* native_manager2 = 254 new TestNativeWebContentsModalDialogManager(dialog2, manager, &tracker2); 255 manager->ShowDialogWithManager(dialog1, 256 scoped_ptr<SingleWebContentsDialogManager>(native_manager1).Pass()); 257 manager->ShowDialogWithManager(dialog2, 258 scoped_ptr<SingleWebContentsDialogManager>(native_manager2).Pass()); 259 260 test_api->DidAttachInterstitialPage(); 261 262 #if defined(USE_AURA) 263 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); 264 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker2.state_); 265 #else 266 EXPECT_EQ(NativeManagerTracker::SHOWN, tracker1.state_); 267 EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker2.state_); 268 #endif 269 270 EXPECT_TRUE(tracker1.was_shown_); 271 EXPECT_FALSE(tracker2.was_shown_); 272 273 #if !defined(USE_AURA) 274 native_manager1->StopTracking(); 275 native_manager2->StopTracking(); 276 #endif 277 } 278 279 280 // Test that the first dialog is always shown, regardless of the order in which 281 // dialogs are closed. 282 TEST_F(WebContentsModalDialogManagerTest, CloseDialogs) { 283 // The front dialog is always shown regardless of dialog close order. 284 const NativeWebContentsModalDialog dialog1 = MakeFakeDialog(); 285 const NativeWebContentsModalDialog dialog2 = MakeFakeDialog(); 286 const NativeWebContentsModalDialog dialog3 = MakeFakeDialog(); 287 const NativeWebContentsModalDialog dialog4 = MakeFakeDialog(); 288 289 NativeManagerTracker tracker1; 290 NativeManagerTracker tracker2; 291 NativeManagerTracker tracker3; 292 NativeManagerTracker tracker4; 293 TestNativeWebContentsModalDialogManager* native_manager1 = 294 new TestNativeWebContentsModalDialogManager(dialog1, manager, &tracker1); 295 TestNativeWebContentsModalDialogManager* native_manager2 = 296 new TestNativeWebContentsModalDialogManager(dialog2, manager, &tracker2); 297 TestNativeWebContentsModalDialogManager* native_manager3 = 298 new TestNativeWebContentsModalDialogManager(dialog3, manager, &tracker3); 299 TestNativeWebContentsModalDialogManager* native_manager4 = 300 new TestNativeWebContentsModalDialogManager(dialog4, manager, &tracker4); 301 manager->ShowDialogWithManager(dialog1, 302 scoped_ptr<SingleWebContentsDialogManager>(native_manager1).Pass()); 303 manager->ShowDialogWithManager(dialog2, 304 scoped_ptr<SingleWebContentsDialogManager>(native_manager2).Pass()); 305 manager->ShowDialogWithManager(dialog3, 306 scoped_ptr<SingleWebContentsDialogManager>(native_manager3).Pass()); 307 manager->ShowDialogWithManager(dialog4, 308 scoped_ptr<SingleWebContentsDialogManager>(native_manager4).Pass()); 309 310 native_manager1->Close(); 311 312 EXPECT_TRUE(manager->IsDialogActive()); 313 EXPECT_TRUE(delegate->web_contents_blocked()); 314 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); 315 EXPECT_EQ(NativeManagerTracker::SHOWN, tracker2.state_); 316 EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker3.state_); 317 EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker4.state_); 318 319 native_manager3->Close(); 320 321 EXPECT_TRUE(manager->IsDialogActive()); 322 EXPECT_TRUE(delegate->web_contents_blocked()); 323 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); 324 EXPECT_EQ(NativeManagerTracker::SHOWN, tracker2.state_); 325 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_); 326 EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker4.state_); 327 EXPECT_FALSE(tracker3.was_shown_); 328 329 native_manager2->Close(); 330 331 EXPECT_TRUE(manager->IsDialogActive()); 332 EXPECT_TRUE(delegate->web_contents_blocked()); 333 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); 334 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker2.state_); 335 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_); 336 EXPECT_EQ(NativeManagerTracker::SHOWN, tracker4.state_); 337 EXPECT_FALSE(tracker3.was_shown_); 338 339 native_manager4->Close(); 340 341 EXPECT_FALSE(manager->IsDialogActive()); 342 EXPECT_FALSE(delegate->web_contents_blocked()); 343 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); 344 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker2.state_); 345 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_); 346 EXPECT_EQ(NativeManagerTracker::CLOSED, tracker4.state_); 347 EXPECT_TRUE(tracker1.was_shown_); 348 EXPECT_TRUE(tracker2.was_shown_); 349 EXPECT_FALSE(tracker3.was_shown_); 350 EXPECT_TRUE(tracker4.was_shown_); 351 } 352 353 // Test that CloseAllDialogs does what it says. 354 TEST_F(WebContentsModalDialogManagerTest, CloseAllDialogs) { 355 const int kWindowCount = 4; 356 NativeManagerTracker trackers[kWindowCount]; 357 TestNativeWebContentsModalDialogManager* native_managers[kWindowCount]; 358 for (int i = 0; i < kWindowCount; i++) { 359 const NativeWebContentsModalDialog dialog = MakeFakeDialog(); 360 native_managers[i] = 361 new TestNativeWebContentsModalDialogManager( 362 dialog, manager, &(trackers[i])); 363 manager->ShowDialogWithManager(dialog, 364 scoped_ptr<SingleWebContentsDialogManager>( 365 native_managers[i]).Pass()); 366 } 367 368 for (int i = 0; i < kWindowCount; i++) 369 EXPECT_NE(NativeManagerTracker::CLOSED, trackers[i].state_); 370 371 test_api->CloseAllDialogs(); 372 373 EXPECT_FALSE(delegate->web_contents_blocked()); 374 EXPECT_FALSE(manager->IsDialogActive()); 375 for (int i = 0; i < kWindowCount; i++) 376 EXPECT_EQ(NativeManagerTracker::CLOSED, trackers[i].state_); 377 } 378 379 } // namespace web_modal 380