web_contents_modal_dialog_manager_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. #include "components/web_modal/web_contents_modal_dialog_manager.h"
  5. #include <map>
  6. #include <memory>
  7. #include "base/memory/ptr_util.h"
  8. #include "base/memory/raw_ptr.h"
  9. #include "build/build_config.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. namespace web_modal {
  15. // Tracks persistent state changes of the native WC-modal dialog manager.
  16. class NativeManagerTracker {
  17. public:
  18. enum DialogState {
  19. UNKNOWN,
  20. NOT_SHOWN,
  21. SHOWN,
  22. HIDDEN,
  23. CLOSED
  24. };
  25. NativeManagerTracker() : state_(UNKNOWN), was_shown_(false) {}
  26. void SetState(DialogState state) {
  27. state_ = state;
  28. if (state_ == SHOWN)
  29. was_shown_ = true;
  30. }
  31. DialogState state_;
  32. bool was_shown_;
  33. };
  34. NativeManagerTracker unused_tracker;
  35. class TestNativeWebContentsModalDialogManager
  36. : public SingleWebContentsDialogManager {
  37. public:
  38. TestNativeWebContentsModalDialogManager(
  39. gfx::NativeWindow dialog,
  40. SingleWebContentsDialogManagerDelegate* delegate,
  41. NativeManagerTracker* tracker)
  42. : delegate_(delegate),
  43. dialog_(dialog),
  44. tracker_(tracker) {
  45. if (tracker_)
  46. tracker_->SetState(NativeManagerTracker::NOT_SHOWN);
  47. }
  48. TestNativeWebContentsModalDialogManager(
  49. const TestNativeWebContentsModalDialogManager&) = delete;
  50. TestNativeWebContentsModalDialogManager& operator=(
  51. const TestNativeWebContentsModalDialogManager&) = delete;
  52. void Show() override {
  53. if (tracker_)
  54. tracker_->SetState(NativeManagerTracker::SHOWN);
  55. }
  56. void Hide() override {
  57. if (tracker_)
  58. tracker_->SetState(NativeManagerTracker::HIDDEN);
  59. }
  60. void Close() override {
  61. if (tracker_)
  62. tracker_->SetState(NativeManagerTracker::CLOSED);
  63. delegate_->WillClose(dialog_);
  64. }
  65. void Focus() override {}
  66. void Pulse() override {}
  67. void HostChanged(WebContentsModalDialogHost* new_host) override {}
  68. gfx::NativeWindow dialog() override { return dialog_; }
  69. void StopTracking() { tracker_ = nullptr; }
  70. private:
  71. raw_ptr<SingleWebContentsDialogManagerDelegate> delegate_;
  72. gfx::NativeWindow dialog_;
  73. raw_ptr<NativeManagerTracker> tracker_;
  74. };
  75. class WebContentsModalDialogManagerTest
  76. : public content::RenderViewHostTestHarness {
  77. public:
  78. WebContentsModalDialogManagerTest() : next_dialog_id(1), manager(nullptr) {}
  79. WebContentsModalDialogManagerTest(const WebContentsModalDialogManagerTest&) =
  80. delete;
  81. WebContentsModalDialogManagerTest& operator=(
  82. const WebContentsModalDialogManagerTest&) = delete;
  83. void SetUp() override {
  84. content::RenderViewHostTestHarness::SetUp();
  85. delegate = std::make_unique<TestWebContentsModalDialogManagerDelegate>();
  86. WebContentsModalDialogManager::CreateForWebContents(web_contents());
  87. manager = WebContentsModalDialogManager::FromWebContents(web_contents());
  88. manager->SetDelegate(delegate.get());
  89. test_api =
  90. std::make_unique<WebContentsModalDialogManager::TestApi>(manager);
  91. }
  92. void TearDown() override {
  93. test_api.reset();
  94. content::RenderViewHostTestHarness::TearDown();
  95. }
  96. protected:
  97. gfx::NativeWindow MakeFakeDialog() {
  98. // WebContentsModalDialogManager treats the dialog window as an opaque
  99. // type, so creating fake dialog windows using reinterpret_cast is valid.
  100. #if BUILDFLAG(IS_APPLE)
  101. NSWindow* window = reinterpret_cast<NSWindow*>(next_dialog_id++);
  102. return gfx::NativeWindow(window);
  103. #else
  104. return reinterpret_cast<gfx::NativeWindow>(next_dialog_id++);
  105. #endif
  106. }
  107. int next_dialog_id;
  108. std::unique_ptr<TestWebContentsModalDialogManagerDelegate> delegate;
  109. raw_ptr<WebContentsModalDialogManager> manager;
  110. std::unique_ptr<WebContentsModalDialogManager::TestApi> test_api;
  111. };
  112. // Test that the dialog is shown immediately when the delegate indicates the web
  113. // contents is visible.
  114. TEST_F(WebContentsModalDialogManagerTest, WebContentsVisible) {
  115. // Dialog should be shown while WebContents is visible.
  116. const gfx::NativeWindow dialog = MakeFakeDialog();
  117. NativeManagerTracker tracker;
  118. TestNativeWebContentsModalDialogManager* native_manager =
  119. new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker);
  120. manager->ShowDialogWithManager(dialog, base::WrapUnique(native_manager));
  121. EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_);
  122. EXPECT_TRUE(manager->IsDialogActive());
  123. EXPECT_TRUE(delegate->web_contents_blocked());
  124. EXPECT_TRUE(tracker.was_shown_);
  125. native_manager->StopTracking();
  126. }
  127. // Test that the dialog is not shown immediately when the delegate indicates the
  128. // web contents is not visible.
  129. TEST_F(WebContentsModalDialogManagerTest, WebContentsNotVisible) {
  130. // Dialog should not be shown while WebContents is not visible.
  131. delegate->set_web_contents_visible(false);
  132. const gfx::NativeWindow dialog = MakeFakeDialog();
  133. NativeManagerTracker tracker;
  134. TestNativeWebContentsModalDialogManager* native_manager =
  135. new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker);
  136. manager->ShowDialogWithManager(dialog, base::WrapUnique(native_manager));
  137. EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker.state_);
  138. EXPECT_TRUE(manager->IsDialogActive());
  139. EXPECT_TRUE(delegate->web_contents_blocked());
  140. EXPECT_FALSE(tracker.was_shown_);
  141. native_manager->StopTracking();
  142. }
  143. // Test that only the first of multiple dialogs is shown.
  144. TEST_F(WebContentsModalDialogManagerTest, ShowDialogs) {
  145. const gfx::NativeWindow dialog1 = MakeFakeDialog();
  146. const gfx::NativeWindow dialog2 = MakeFakeDialog();
  147. const gfx::NativeWindow dialog3 = MakeFakeDialog();
  148. NativeManagerTracker tracker1;
  149. NativeManagerTracker tracker2;
  150. NativeManagerTracker tracker3;
  151. TestNativeWebContentsModalDialogManager* native_manager1 =
  152. new TestNativeWebContentsModalDialogManager(dialog1, manager, &tracker1);
  153. TestNativeWebContentsModalDialogManager* native_manager2 =
  154. new TestNativeWebContentsModalDialogManager(dialog2, manager, &tracker2);
  155. TestNativeWebContentsModalDialogManager* native_manager3 =
  156. new TestNativeWebContentsModalDialogManager(dialog3, manager, &tracker3);
  157. manager->ShowDialogWithManager(dialog1, base::WrapUnique(native_manager1));
  158. manager->ShowDialogWithManager(dialog2, base::WrapUnique(native_manager2));
  159. manager->ShowDialogWithManager(dialog3, base::WrapUnique(native_manager3));
  160. EXPECT_TRUE(delegate->web_contents_blocked());
  161. EXPECT_EQ(NativeManagerTracker::SHOWN, tracker1.state_);
  162. EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker2.state_);
  163. EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker3.state_);
  164. native_manager1->StopTracking();
  165. native_manager2->StopTracking();
  166. native_manager3->StopTracking();
  167. }
  168. // Test that the dialog is shown/hidden when the WebContents is shown/hidden.
  169. TEST_F(WebContentsModalDialogManagerTest, VisibilityObservation) {
  170. const gfx::NativeWindow dialog = MakeFakeDialog();
  171. NativeManagerTracker tracker;
  172. TestNativeWebContentsModalDialogManager* native_manager =
  173. new TestNativeWebContentsModalDialogManager(dialog, manager, &tracker);
  174. manager->ShowDialogWithManager(dialog, base::WrapUnique(native_manager));
  175. EXPECT_TRUE(manager->IsDialogActive());
  176. EXPECT_TRUE(delegate->web_contents_blocked());
  177. EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_);
  178. test_api->WebContentsVisibilityChanged(content::Visibility::HIDDEN);
  179. EXPECT_TRUE(manager->IsDialogActive());
  180. EXPECT_TRUE(delegate->web_contents_blocked());
  181. EXPECT_EQ(NativeManagerTracker::HIDDEN, tracker.state_);
  182. test_api->WebContentsVisibilityChanged(content::Visibility::VISIBLE);
  183. EXPECT_TRUE(manager->IsDialogActive());
  184. EXPECT_TRUE(delegate->web_contents_blocked());
  185. EXPECT_EQ(NativeManagerTracker::SHOWN, tracker.state_);
  186. native_manager->StopTracking();
  187. }
  188. // Test that the first dialog is always shown, regardless of the order in which
  189. // dialogs are closed.
  190. TEST_F(WebContentsModalDialogManagerTest, CloseDialogs) {
  191. // The front dialog is always shown regardless of dialog close order.
  192. const gfx::NativeWindow dialog1 = MakeFakeDialog();
  193. const gfx::NativeWindow dialog2 = MakeFakeDialog();
  194. const gfx::NativeWindow dialog3 = MakeFakeDialog();
  195. const gfx::NativeWindow dialog4 = MakeFakeDialog();
  196. NativeManagerTracker tracker1;
  197. NativeManagerTracker tracker2;
  198. NativeManagerTracker tracker3;
  199. NativeManagerTracker tracker4;
  200. TestNativeWebContentsModalDialogManager* native_manager1 =
  201. new TestNativeWebContentsModalDialogManager(dialog1, manager, &tracker1);
  202. TestNativeWebContentsModalDialogManager* native_manager2 =
  203. new TestNativeWebContentsModalDialogManager(dialog2, manager, &tracker2);
  204. TestNativeWebContentsModalDialogManager* native_manager3 =
  205. new TestNativeWebContentsModalDialogManager(dialog3, manager, &tracker3);
  206. TestNativeWebContentsModalDialogManager* native_manager4 =
  207. new TestNativeWebContentsModalDialogManager(dialog4, manager, &tracker4);
  208. manager->ShowDialogWithManager(dialog1, base::WrapUnique(native_manager1));
  209. manager->ShowDialogWithManager(dialog2, base::WrapUnique(native_manager2));
  210. manager->ShowDialogWithManager(dialog3, base::WrapUnique(native_manager3));
  211. manager->ShowDialogWithManager(dialog4, base::WrapUnique(native_manager4));
  212. native_manager1->Close();
  213. EXPECT_TRUE(manager->IsDialogActive());
  214. EXPECT_TRUE(delegate->web_contents_blocked());
  215. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_);
  216. EXPECT_EQ(NativeManagerTracker::SHOWN, tracker2.state_);
  217. EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker3.state_);
  218. EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker4.state_);
  219. native_manager3->Close();
  220. EXPECT_TRUE(manager->IsDialogActive());
  221. EXPECT_TRUE(delegate->web_contents_blocked());
  222. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_);
  223. EXPECT_EQ(NativeManagerTracker::SHOWN, tracker2.state_);
  224. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_);
  225. EXPECT_EQ(NativeManagerTracker::NOT_SHOWN, tracker4.state_);
  226. EXPECT_FALSE(tracker3.was_shown_);
  227. native_manager2->Close();
  228. EXPECT_TRUE(manager->IsDialogActive());
  229. EXPECT_TRUE(delegate->web_contents_blocked());
  230. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_);
  231. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker2.state_);
  232. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_);
  233. EXPECT_EQ(NativeManagerTracker::SHOWN, tracker4.state_);
  234. EXPECT_FALSE(tracker3.was_shown_);
  235. native_manager4->Close();
  236. EXPECT_FALSE(manager->IsDialogActive());
  237. EXPECT_FALSE(delegate->web_contents_blocked());
  238. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker1.state_);
  239. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker2.state_);
  240. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker3.state_);
  241. EXPECT_EQ(NativeManagerTracker::CLOSED, tracker4.state_);
  242. EXPECT_TRUE(tracker1.was_shown_);
  243. EXPECT_TRUE(tracker2.was_shown_);
  244. EXPECT_FALSE(tracker3.was_shown_);
  245. EXPECT_TRUE(tracker4.was_shown_);
  246. }
  247. // Test that CloseAllDialogs does what it says.
  248. TEST_F(WebContentsModalDialogManagerTest, CloseAllDialogs) {
  249. const int kWindowCount = 4;
  250. NativeManagerTracker trackers[kWindowCount];
  251. TestNativeWebContentsModalDialogManager* native_managers[kWindowCount];
  252. for (int i = 0; i < kWindowCount; i++) {
  253. const gfx::NativeWindow dialog = MakeFakeDialog();
  254. native_managers[i] =
  255. new TestNativeWebContentsModalDialogManager(
  256. dialog, manager, &(trackers[i]));
  257. manager->ShowDialogWithManager(dialog,
  258. base::WrapUnique(native_managers[i]));
  259. }
  260. for (int i = 0; i < kWindowCount; i++)
  261. EXPECT_NE(NativeManagerTracker::CLOSED, trackers[i].state_);
  262. test_api->CloseAllDialogs();
  263. EXPECT_FALSE(delegate->web_contents_blocked());
  264. EXPECT_FALSE(manager->IsDialogActive());
  265. for (int i = 0; i < kWindowCount; i++)
  266. EXPECT_EQ(NativeManagerTracker::CLOSED, trackers[i].state_);
  267. }
  268. } // namespace web_modal