// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/web_modal/web_contents_modal_dialog_manager.h" #include #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "components/web_modal/single_web_contents_dialog_manager.h" #include "components/web_modal/test_web_contents_modal_dialog_manager_delegate.h" #include "content/public/test/test_renderer_host.h" #include "testing/gtest/include/gtest/gtest.h" namespace web_modal { // Tracks persistent state changes of the native WC-modal dialog manager. class NativeManagerTracker { public: enum DialogState { UNKNOWN, NOT_SHOWN, SHOWN, HIDDEN, CLOSED }; NativeManagerTracker() : state_(UNKNOWN), was_shown_(false) {} void SetState(DialogState state) { state_ = state; if (state_ == SHOWN) was_shown_ = true; } DialogState state_; bool was_shown_; }; NativeManagerTracker unused_tracker; class TestNativeWebContentsModalDialogManager : public SingleWebContentsDialogManager { public: TestNativeWebContentsModalDialogManager( gfx::NativeWindow dialog, SingleWebContentsDialogManagerDelegate* delegate, NativeManagerTracker* tracker) : delegate_(delegate), dialog_(dialog), tracker_(tracker) { if (tracker_) tracker_->SetState(NativeManagerTracker::NOT_SHOWN); } void Show() override { if (tracker_) tracker_->SetState(NativeManagerTracker::SHOWN); } void Hide() override { if (tracker_) tracker_->SetState(NativeManagerTracker::HIDDEN); } void Close() override { if (tracker_) tracker_->SetState(NativeManagerTracker::CLOSED); delegate_->WillClose(dialog_); } void Focus() override {} void Pulse() override {} void HostChanged(WebContentsModalDialogHost* new_host) override {} gfx::NativeWindow dialog() override { return dialog_; } void StopTracking() { tracker_ = NULL; } private: SingleWebContentsDialogManagerDelegate* delegate_; gfx::NativeWindow dialog_; NativeManagerTracker* tracker_; DISALLOW_COPY_AND_ASSIGN(TestNativeWebContentsModalDialogManager); }; class WebContentsModalDialogManagerTest : public content::RenderViewHostTestHarness { public: WebContentsModalDialogManagerTest() : next_dialog_id(1), manager(NULL) { } void SetUp() override { content::RenderViewHostTestHarness::SetUp(); delegate.reset(new TestWebContentsModalDialogManagerDelegate); WebContentsModalDialogManager::CreateForWebContents(web_contents()); manager = WebContentsModalDialogManager::FromWebContents(web_contents()); manager->SetDelegate(delegate.get()); test_api.reset(new WebContentsModalDialogManager::TestApi(manager)); } void TearDown() override { test_api.reset(); content::RenderViewHostTestHarness::TearDown(); } protected: gfx::NativeWindow MakeFakeDialog() { // WebContentsModalDialogManager treats the dialog window as an opaque // type, so creating fake dialog windows using reinterpret_cast is valid. return reinterpret_cast(next_dialog_id++); } int next_dialog_id; scoped_ptr delegate; WebContentsModalDialogManager* manager; scoped_ptr test_api; DISALLOW_COPY_AND_ASSIGN(WebContentsModalDialogManagerTest); }; SingleWebContentsDialogManager* WebContentsModalDialogManager::CreateNativeWebModalManager( gfx::NativeWindow dialog, SingleWebContentsDialogManagerDelegate* native_delegate) { NOTREACHED(); return new TestNativeWebContentsModalDialogManager( dialog, native_delegate, &unused_tracker); } // Test that the dialog is shown immediately when the delegate indicates the web // contents is visible. TEST_F(WebContentsModalDialogManagerTest, WebContentsVisible) { // Dialog should be shown while WebContents is visible. const gfx::NativeWindow dialog = MakeFakeDialog(); NativeManagerTracker tracker; TestNativeWebContentsModalDialogManager* native_manager = new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker); manager->ShowDialogWithManager( dialog, scoped_ptr(native_manager)); EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_); EXPECT_TRUE(manager->IsDialogActive()); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_TRUE(tracker.was_shown_); native_manager->StopTracking(); } // Test that the dialog is not shown immediately when the delegate indicates the // web contents is not visible. TEST_F(WebContentsModalDialogManagerTest, WebContentsNotVisible) { // Dialog should not be shown while WebContents is not visible. delegate->set_web_contents_visible(false); const gfx::NativeWindow dialog = MakeFakeDialog(); NativeManagerTracker tracker; TestNativeWebContentsModalDialogManager* native_manager = new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker); manager->ShowDialogWithManager( dialog, scoped_ptr(native_manager)); EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker.state_); EXPECT_TRUE(manager->IsDialogActive()); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_FALSE(tracker.was_shown_); native_manager->StopTracking(); } // Test that only the first of multiple dialogs is shown. TEST_F(WebContentsModalDialogManagerTest, ShowDialogs) { const gfx::NativeWindow dialog1 = MakeFakeDialog(); const gfx::NativeWindow dialog2 = MakeFakeDialog(); const gfx::NativeWindow dialog3 = MakeFakeDialog(); NativeManagerTracker tracker1; NativeManagerTracker tracker2; NativeManagerTracker tracker3; TestNativeWebContentsModalDialogManager* native_manager1 = new TestNativeWebContentsModalDialogManager(dialog1, manager, &tracker1); TestNativeWebContentsModalDialogManager* native_manager2 = new TestNativeWebContentsModalDialogManager(dialog2, manager, &tracker2); TestNativeWebContentsModalDialogManager* native_manager3 = new TestNativeWebContentsModalDialogManager(dialog3, manager, &tracker3); manager->ShowDialogWithManager( dialog1, scoped_ptr(native_manager1)); manager->ShowDialogWithManager( dialog2, scoped_ptr(native_manager2)); manager->ShowDialogWithManager( dialog3, scoped_ptr(native_manager3)); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_EQ(NativeManagerTracker::SHOWN, tracker1.state_); EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker2.state_); EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker3.state_); native_manager1->StopTracking(); native_manager2->StopTracking(); native_manager3->StopTracking(); } // Test that the dialog is shown/hidden when the WebContents is shown/hidden. TEST_F(WebContentsModalDialogManagerTest, VisibilityObservation) { const gfx::NativeWindow dialog = MakeFakeDialog(); NativeManagerTracker tracker; TestNativeWebContentsModalDialogManager* native_manager = new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker); manager->ShowDialogWithManager( dialog, scoped_ptr(native_manager)); EXPECT_TRUE(manager->IsDialogActive()); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_); test_api->WebContentsWasHidden(); EXPECT_TRUE(manager->IsDialogActive()); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_EQ(NativeManagerTracker::HIDDEN, tracker.state_); test_api->WebContentsWasShown(); EXPECT_TRUE(manager->IsDialogActive()); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_); native_manager->StopTracking(); } // Test that attaching an interstitial page closes all dialogs. TEST_F(WebContentsModalDialogManagerTest, InterstitialPage) { const gfx::NativeWindow dialog1 = MakeFakeDialog(); const gfx::NativeWindow dialog2 = MakeFakeDialog(); NativeManagerTracker tracker1; NativeManagerTracker tracker2; TestNativeWebContentsModalDialogManager* native_manager1 = new TestNativeWebContentsModalDialogManager(dialog1, manager, &tracker1); TestNativeWebContentsModalDialogManager* native_manager2 = new TestNativeWebContentsModalDialogManager(dialog2, manager, &tracker2); manager->ShowDialogWithManager( dialog1, scoped_ptr(native_manager1)); manager->ShowDialogWithManager( dialog2, scoped_ptr(native_manager2)); test_api->DidAttachInterstitialPage(); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker2.state_); EXPECT_TRUE(tracker1.was_shown_); EXPECT_FALSE(tracker2.was_shown_); } // Test that the first dialog is always shown, regardless of the order in which // dialogs are closed. TEST_F(WebContentsModalDialogManagerTest, CloseDialogs) { // The front dialog is always shown regardless of dialog close order. const gfx::NativeWindow dialog1 = MakeFakeDialog(); const gfx::NativeWindow dialog2 = MakeFakeDialog(); const gfx::NativeWindow dialog3 = MakeFakeDialog(); const gfx::NativeWindow dialog4 = MakeFakeDialog(); NativeManagerTracker tracker1; NativeManagerTracker tracker2; NativeManagerTracker tracker3; NativeManagerTracker tracker4; TestNativeWebContentsModalDialogManager* native_manager1 = new TestNativeWebContentsModalDialogManager(dialog1, manager, &tracker1); TestNativeWebContentsModalDialogManager* native_manager2 = new TestNativeWebContentsModalDialogManager(dialog2, manager, &tracker2); TestNativeWebContentsModalDialogManager* native_manager3 = new TestNativeWebContentsModalDialogManager(dialog3, manager, &tracker3); TestNativeWebContentsModalDialogManager* native_manager4 = new TestNativeWebContentsModalDialogManager(dialog4, manager, &tracker4); manager->ShowDialogWithManager( dialog1, scoped_ptr(native_manager1)); manager->ShowDialogWithManager( dialog2, scoped_ptr(native_manager2)); manager->ShowDialogWithManager( dialog3, scoped_ptr(native_manager3)); manager->ShowDialogWithManager( dialog4, scoped_ptr(native_manager4)); native_manager1->Close(); EXPECT_TRUE(manager->IsDialogActive()); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); EXPECT_EQ(NativeManagerTracker::SHOWN, tracker2.state_); EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker3.state_); EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker4.state_); native_manager3->Close(); EXPECT_TRUE(manager->IsDialogActive()); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); EXPECT_EQ(NativeManagerTracker::SHOWN, tracker2.state_); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_); EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker4.state_); EXPECT_FALSE(tracker3.was_shown_); native_manager2->Close(); EXPECT_TRUE(manager->IsDialogActive()); EXPECT_TRUE(delegate->web_contents_blocked()); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker2.state_); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_); EXPECT_EQ(NativeManagerTracker::SHOWN, tracker4.state_); EXPECT_FALSE(tracker3.was_shown_); native_manager4->Close(); EXPECT_FALSE(manager->IsDialogActive()); EXPECT_FALSE(delegate->web_contents_blocked()); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker2.state_); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_); EXPECT_EQ(NativeManagerTracker::CLOSED, tracker4.state_); EXPECT_TRUE(tracker1.was_shown_); EXPECT_TRUE(tracker2.was_shown_); EXPECT_FALSE(tracker3.was_shown_); EXPECT_TRUE(tracker4.was_shown_); } // Test that CloseAllDialogs does what it says. TEST_F(WebContentsModalDialogManagerTest, CloseAllDialogs) { const int kWindowCount = 4; NativeManagerTracker trackers[kWindowCount]; TestNativeWebContentsModalDialogManager* native_managers[kWindowCount]; for (int i = 0; i < kWindowCount; i++) { const gfx::NativeWindow dialog = MakeFakeDialog(); native_managers[i] = new TestNativeWebContentsModalDialogManager( dialog, manager, &(trackers[i])); manager->ShowDialogWithManager( dialog, scoped_ptr(native_managers[i])); } for (int i = 0; i < kWindowCount; i++) EXPECT_NE(NativeManagerTracker::CLOSED, trackers[i].state_); test_api->CloseAllDialogs(); EXPECT_FALSE(delegate->web_contents_blocked()); EXPECT_FALSE(manager->IsDialogActive()); for (int i = 0; i < kWindowCount; i++) EXPECT_EQ(NativeManagerTracker::CLOSED, trackers[i].state_); } } // namespace web_modal