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
15namespace web_modal {
16
17// Tracks persistent state changes of the native WC-modal dialog manager.
18class 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
40NativeManagerTracker unused_tracker;
41
42class 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
91class 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
130SingleWebContentsDialogManager*
131WebContentsModalDialogManager::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.
143TEST_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.
163TEST_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.
184TEST_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.
216TEST_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.
245TEST_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.
282TEST_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.
354TEST_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