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
NativeManagerTracker()28 NativeManagerTracker() : state_(UNKNOWN), was_shown_(false) {}
29
SetState(DialogState state)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:
TestNativeWebContentsModalDialogManager(NativeWebContentsModalDialog dialog,SingleWebContentsDialogManagerDelegate * delegate,NativeManagerTracker * tracker)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
Show()56 virtual void Show() OVERRIDE {
57 if (tracker_)
58 tracker_->SetState(NativeManagerTracker::SHOWN);
59 }
Hide()60 virtual void Hide() OVERRIDE {
61 if (tracker_)
62 tracker_->SetState(NativeManagerTracker::HIDDEN);
63 }
Close()64 virtual void Close() OVERRIDE {
65 if (tracker_)
66 tracker_->SetState(NativeManagerTracker::CLOSED);
67 delegate_->WillClose(dialog_);
68 }
Focus()69 virtual void Focus() OVERRIDE {
70 }
Pulse()71 virtual void Pulse() OVERRIDE {
72 }
HostChanged(WebContentsModalDialogHost * new_host)73 virtual void HostChanged(WebContentsModalDialogHost* new_host) OVERRIDE {
74 }
dialog()75 virtual NativeWebContentsModalDialog dialog() OVERRIDE {
76 return dialog_;
77 }
78
StopTracking()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:
WebContentsModalDialogManagerTest()94 WebContentsModalDialogManagerTest()
95 : next_dialog_id(1),
96 manager(NULL) {
97 }
98
SetUp()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
TearDown()109 virtual void TearDown() {
110 test_api.reset();
111 content::RenderViewHostTestHarness::TearDown();
112 }
113
114 protected:
MakeFakeDialog()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*
CreateNativeWebModalManager(NativeWebContentsModalDialog dialog,SingleWebContentsDialogManagerDelegate * native_delegate)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.
TEST_F(WebContentsModalDialogManagerTest,WebContentsVisible)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.
TEST_F(WebContentsModalDialogManagerTest,WebContentsNotVisible)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.
TEST_F(WebContentsModalDialogManagerTest,ShowDialogs)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.
TEST_F(WebContentsModalDialogManagerTest,VisibilityObservation)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.
TEST_F(WebContentsModalDialogManagerTest,InterstitialPage)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.
TEST_F(WebContentsModalDialogManagerTest,CloseDialogs)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.
TEST_F(WebContentsModalDialogManagerTest,CloseAllDialogs)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